<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[38967] trunk/src: Customize: Add edit shortcuts in customizer preview to visually expose editable elements and focus on the corresponding controls when clicked.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/38967">38967</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/38967","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>westonruter</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2016-10-26 20:02:51 +0000 (Wed, 26 Oct 2016)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Customize: Add edit shortcuts in customizer preview to visually expose editable elements and focus on the corresponding controls when clicked. 

* Edit shortcuts show initially for a moment and then fade away so as to not get in the way of the preview. 
* Visibility of edit shortcuts is toggled by clicking/touching anywhere inert in the document.
* Implements UI for mobile and touch devices which do not support shift-click.
* Adds `editShortcutVisibility` state.
* Adds new methods to `wp.customize.selectiveRefresh.Partial` for managing edit shortcuts.

Incorporates aspects of the Customize Direct Manipulation feature plugin.

Props sirbrillig, mattwiebe, celloexpressions, melchoyce, westonruter, afercia.
Fixes <a href="https://core.trac.wordpress.org/ticket/27403">#27403</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadmincustomizephp">trunk/src/wp-admin/customize.php</a></li>
<li><a href="#trunksrcwpadminjscustomizecontrolsjs">trunk/src/wp-admin/js/customize-controls.js</a></li>
<li><a href="#trunksrcwpcontentthemestwentyfourteenstylecss">trunk/src/wp-content/themes/twentyfourteen/style.css</a></li>
<li><a href="#trunksrcwpincludescsscustomizepreviewcss">trunk/src/wp-includes/css/customize-preview.css</a></li>
<li><a href="#trunksrcwpincludescustomizeclasswpcustomizeselectiverefreshphp">trunk/src/wp-includes/customize/class-wp-customize-selective-refresh.php</a></li>
<li><a href="#trunksrcwpincludesjscustomizepreviewwidgetsjs">trunk/src/wp-includes/js/customize-preview-widgets.js</a></li>
<li><a href="#trunksrcwpincludesjscustomizeselectiverefreshjs">trunk/src/wp-includes/js/customize-selective-refresh.js</a></li>
<li><a href="#trunksrcwpincludesscriptloaderphp">trunk/src/wp-includes/script-loader.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadmincustomizephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/customize.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/customize.php  2016-10-26 18:42:44 UTC (rev 38966)
+++ trunk/src/wp-admin/customize.php    2016-10-26 20:02:51 UTC (rev 38967)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -161,7 +161,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        <button type="button" class="customize-help-toggle dashicons dashicons-editor-help" aria-expanded="false"><span class="screen-reader-text"><?php _e( 'Help' ); ?></span></button>
</span><span class="cx" style="display: block; padding: 0 10px">                                </div>
</span><span class="cx" style="display: block; padding: 0 10px">                                <div class="customize-panel-description"><?php
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        _e( 'The Customizer allows you to preview changes to your site before publishing them. You can also navigate to different pages on your site to preview them.' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 _e( 'The Customizer allows you to preview changes to your site before publishing them. You can navigate to different pages on your site within the preview. Edit shortcuts are shown for some editable elements; click anywhere in the preview to toggle them off and on.' );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 ?></div>
</span><span class="cx" style="display: block; padding: 0 10px">                        </div>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpadminjscustomizecontrolsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/js/customize-controls.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/js/customize-controls.js       2016-10-26 18:42:44 UTC (rev 38966)
+++ trunk/src/wp-admin/js/customize-controls.js 2016-10-26 20:02:51 UTC (rev 38967)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4302,6 +4302,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( 'resolved' !== previewer.deferred.active.state() || previewer.loading ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                synced.scroll = previewer.scroll;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        synced['edit-shortcut-visibility'] = api.state( 'editShortcutVisibility' ).get();
</ins><span class="cx" style="display: block; padding: 0 10px">                         previewer.send( 'sync', synced );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Set the previewUrl without causing the url to set the iframe.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5122,6 +5123,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                expandedSection = state.create( 'expandedSection' ),
</span><span class="cx" style="display: block; padding: 0 10px">                                changesetStatus = state.create( 'changesetStatus' ),
</span><span class="cx" style="display: block; padding: 0 10px">                                previewerAlive = state.create( 'previewerAlive' ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                editShortcutVisibility  = state.create( 'editShortcutVisibility' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                 populateChangesetUuidParam;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        state.bind( 'change', function() {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5158,6 +5160,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        expandedPanel( false );
</span><span class="cx" style="display: block; padding: 0 10px">                        expandedSection( false );
</span><span class="cx" style="display: block; padding: 0 10px">                        previewerAlive( true );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        editShortcutVisibility( 'initial' );
</ins><span class="cx" style="display: block; padding: 0 10px">                         changesetStatus( api.settings.changeset.status );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        api.bind( 'change', function() {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5751,6 +5754,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        api.previewer.refresh();
</span><span class="cx" style="display: block; padding: 0 10px">                });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Update the edit shortcut visibility state.
+               api.previewer.bind( 'edit-shortcut-visibility', function( visibility ) {
+                       api.state( 'editShortcutVisibility' ).set( visibility );
+               } );
+               api.state( 'editShortcutVisibility' ).bind( function( visibility ) {
+                       api.previewer.send( 'edit-shortcut-visibility', visibility );
+               } );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 // Autosave changeset.
</span><span class="cx" style="display: block; padding: 0 10px">                ( function() {
</span><span class="cx" style="display: block; padding: 0 10px">                        var timeoutId, updateChangesetWithReschedule, scheduleChangesetUpdate, updatePending = false;
</span></span></pre></div>
<a id="trunksrcwpcontentthemestwentyfourteenstylecss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-content/themes/twentyfourteen/style.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-content/themes/twentyfourteen/style.css      2016-10-26 18:42:44 UTC (rev 38966)
+++ trunk/src/wp-content/themes/twentyfourteen/style.css        2016-10-26 20:02:51 UTC (rev 38967)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1039,6 +1039,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">        outline: 1px dotted;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.secondary-navigation .customize-partial-edit-shortcut:before,
+.footer-sidebar .widget:first-child .customize-partial-edit-shortcut:before {
+       left: 0;
+}
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * 6.0 Content
</span></span></pre></div>
<a id="trunksrcwpincludescsscustomizepreviewcss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/css/customize-preview.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/css/customize-preview.css   2016-10-26 18:42:44 UTC (rev 38966)
+++ trunk/src/wp-includes/css/customize-preview.css     2016-10-26 20:02:51 UTC (rev 38967)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -10,3 +10,153 @@
</span><span class="cx" style="display: block; padding: 0 10px">        -webkit-box-shadow: none;
</span><span class="cx" style="display: block; padding: 0 10px">        box-shadow: none;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+/* Make shortcut buttons essentially invisible */
+.widget button.customize-partial-edit-shortcut,
+.customize-partial-edit-shortcut {
+       position: relative;
+       float: left;
+       width: 1px; /* required to have a size to be focusable in Safari */
+       height: 1px;
+       padding: 0;
+       margin: -1px 0 0 -1px;
+       border: 0;
+       background: transparent;
+       color: transparent;
+       box-shadow: none;
+       outline: none;
+       z-index: 5;
+}
+
+.customize-partial-edit-shortcut:active {
+       padding: 0;
+       border: 0;
+}
+
+/* Styles for the actual shortcut */
+.customize-partial-edit-shortcut:before {
+       position: absolute;
+       left: -36px;
+       color: #fff;
+       width: 30px;
+       height: 30px;
+       font-size: 18px;
+       font-family: dashicons;
+       content: '\f464';
+       z-index: 5;
+       background-color: #0085ba;
+       border-radius: 50%;
+       border: 2px solid #fff;
+       box-shadow: 0 2px 1px rgba(46,68,83,0.15);
+       text-align: center;
+       display: flex;
+       flex-direction: row;
+       justify-content: center;
+       align-items: center;
+       cursor: pointer;
+       padding: 0;
+       animation-fill-mode: both;
+       animation-duration: .4s;
+       opacity: 0;
+       pointer-events: none;
+       text-shadow: 0 -1px 1px #006799,
+                    1px 0 1px #006799,
+                    0 1px 1px #006799,
+                    -1px 0 1px #006799;
+}
+
+.customize-partial-edit-shortcut:hover:before,
+.customize-partial-edit-shortcut:focus:before {
+    background: #008ec2; /* matches primary buttons */
+       border-color: #191e23; /* provides visual focus style with 4.5 contrast against background color */
+}
+
+body.customize-partial-edit-shortcuts-shown .customize-partial-edit-shortcut:before {
+       animation-name: customize-partial-edit-shortcut-bounce-appear;
+       pointer-events: auto;
+}
+body.customize-partial-edit-shortcuts-hidden .customize-partial-edit-shortcut:before {
+       animation-name: customize-partial-edit-shortcut-bounce-disappear;
+       pointer-events: none;
+}
+
+body.customize-partial-edit-shortcuts-flash .customize-partial-edit-shortcut:before {
+       animation-duration: 1.5s;
+       animation-delay: 0.4s;
+       animation-name: customize-partial-edit-shortcut-bounce-disappear;
+       pointer-events: none;
+}
+
+.page-sidebar-collapsed .customize-partial-edit-shortcut:before,
+.customize-partial-edit-shortcut-hidden:before {
+       visibility: hidden;
+}
+
+.widget button.customize-partial-edit-shortcut-absolute,
+.customize-partial-edit-shortcut-absolute {
+       position: static;
+}
+
+.customize-partial-edit-shortcut-left-margin:before {
+       left: 0;
+}
+
+@keyframes customize-partial-edit-shortcut-bounce-appear {
+       from, 20%, 40%, 60%, 80%, to {
+               animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+       }
+       0% {
+               opacity: 0;
+               transform: scale3d(.3, .3, .3);
+       }
+       20% {
+               transform: scale3d(1.1, 1.1, 1.1);
+       }
+       40% {
+               transform: scale3d(.9, .9, .9);
+       }
+       60% {
+               opacity: 1;
+               transform: scale3d(1.03, 1.03, 1.03);
+       }
+       80% {
+               transform: scale3d(.97, .97, .97);
+       }
+       to {
+               opacity: 1;
+               transform: scale3d(1, 1, 1);
+       }
+}
+
+@keyframes customize-partial-edit-shortcut-bounce-disappear {
+       from, 20%, 40%, 60%, 80%, to {
+               animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+       }
+       0% {
+               opacity: 1;
+               transform: scale3d(1, 1, 1);
+       }
+       20% {
+               transform: scale3d(.97, .97, .97);
+       }
+       40% {
+               opacity: 1;
+               transform: scale3d(1.03, 1.03, 1.03);
+       }
+       60% {
+               transform: scale3d(.9, .9, .9);
+       }
+       80% {
+               transform: scale3d(1.1, 1.1, 1.1);
+       }
+       to {
+               opacity: 0;
+               transform: scale3d(.3, .3, .3);
+       }
+}
+
+@media screen and (max-width:800px) {
+       .customize-partial-edit-shortcut:before {
+               left: -18px; /* Assume there will be less of a margin available on smaller screens */
+       }
+}
</ins></span></pre></div>
<a id="trunksrcwpincludescustomizeclasswpcustomizeselectiverefreshphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/customize/class-wp-customize-selective-refresh.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/customize/class-wp-customize-selective-refresh.php  2016-10-26 18:42:44 UTC (rev 38966)
+++ trunk/src/wp-includes/customize/class-wp-customize-selective-refresh.php    2016-10-26 20:02:51 UTC (rev 38967)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -184,6 +184,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'renderQueryVar' => self::RENDER_QUERY_VAR,
</span><span class="cx" style="display: block; padding: 0 10px">                        'l10n'           => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'shiftClickToEdit' => __( 'Shift-click to edit this element.' ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                'clickEditMenu' => __( 'Click to edit this menu.' ),
+                               'clickEditWidget' => __( 'Click to edit this widget.' ),
+                               'clickEditTitle' => __( 'Click to edit the site title.' ),
+                               'clickEditMisc' => __( 'Click to edit this element.' ),
+                               'editShortcutVisibilityToggle'  => __( 'Toggle edit shortcuts' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                 /* translators: %s: document.write() */
</span><span class="cx" style="display: block; padding: 0 10px">                                'badDocumentWrite' => sprintf( __( '%s is forbidden' ), 'document.write()' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span></span></pre></div>
<a id="trunksrcwpincludesjscustomizepreviewwidgetsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/js/customize-preview-widgets.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/js/customize-preview-widgets.js     2016-10-26 18:42:44 UTC (rev 38966)
+++ trunk/src/wp-includes/js/customize-preview-widgets.js       2016-10-26 20:02:51 UTC (rev 38967)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -357,7 +357,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                widgetPartial = new self.WidgetPartial( partialId, {
</span><span class="cx" style="display: block; padding: 0 10px">                                        params: {}
</span><span class="cx" style="display: block; padding: 0 10px">                                } );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                api.selectiveRefresh.partial.add( widgetPartial.id, widgetPartial );
</del><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Make sure that there is a container element for the widget in the sidebar, if at least a placeholder.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -400,6 +399,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                wasInserted = true;
</span><span class="cx" style="display: block; padding: 0 10px">                        } );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        api.selectiveRefresh.partial.add( widgetPartial.id, widgetPartial );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         if ( wasInserted ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                sidebarPartial.reflowWidgets();
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span></span></pre></div>
<a id="trunksrcwpincludesjscustomizeselectiverefreshjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/js/customize-selective-refresh.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/js/customize-selective-refresh.js   2016-10-26 18:42:44 UTC (rev 38966)
+++ trunk/src/wp-includes/js/customize-selective-refresh.js     2016-10-26 20:02:51 UTC (rev 38967)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6,6 +6,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        self = {
</span><span class="cx" style="display: block; padding: 0 10px">                ready: $.Deferred(),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                editShortcutVisibility: new api.Value(),
</ins><span class="cx" style="display: block; padding: 0 10px">                 data: {
</span><span class="cx" style="display: block; padding: 0 10px">                        partials: {},
</span><span class="cx" style="display: block; padding: 0 10px">                        renderQueryVar: '',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -42,7 +43,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                id: null,
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 /**
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /**
</ins><span class="cx" style="display: block; padding: 0 10px">                  * Constructor.
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @since 4.5.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -82,8 +83,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                ready: function() {
</span><span class="cx" style="display: block; padding: 0 10px">                        var partial = this;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        _.each( _.pluck( partial.placements(), 'container' ), function( container ) {
-                               $( container ).attr( 'title', self.data.l10n.shiftClickToEdit );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 _.each( partial.placements(), function( placement ) {
+                               $( placement.container ).attr( 'title', self.data.l10n.shiftClickToEdit );
+                               partial.createEditShortcutForPlacement( placement );
</ins><span class="cx" style="display: block; padding: 0 10px">                         } );
</span><span class="cx" style="display: block; padding: 0 10px">                        $( document ).on( 'click', partial.params.selector, function( e ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                if ( ! e.shiftKey ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -99,6 +101,181 @@
</span><span class="cx" style="display: block; padding: 0 10px">                },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * Create and show the edit shortcut for a given partial placement container.
+                *
+                * @since 4.7
+                *
+                * @param {Placement} placement The placement container element.
+                * @returns {void}
+                */
+               createEditShortcutForPlacement: function( placement ) {
+                       var partial = this, $shortcut, $placementContainer;
+                       if ( ! placement.container ) {
+                               return;
+                       }
+                       $placementContainer = $( placement.container );
+                       if ( ! $placementContainer.length ) {
+                               return;
+                       }
+                       $shortcut = partial.createEditShortcut();
+                       partial.positionEditShortcut( placement, $shortcut );
+                       $shortcut.on( 'click', function( event ) {
+                               event.preventDefault();
+                               event.stopPropagation();
+                               partial.showControl();
+                       } );
+               },
+
+               /**
+                * Position an edit shortcut for the placement container.
+                *
+                * The shortcut must already be created and added to the page.
+                *
+                * @since 4.7
+                *
+                * @param {Placement} placement The placement for the partial.
+                * @param {jQuery} $editShortcut The shortcut element as a jQuery object.
+                * @returns {void}
+                */
+               positionEditShortcut: function( placement, $editShortcut ) {
+                       var partial = this, $placementContainer;
+                       $placementContainer = $( placement.container );
+                       $placementContainer.prepend( $editShortcut );
+                       if ( 'absolute' === $placementContainer.css( 'position' ) ) {
+                               $editShortcut.addClass( 'customize-partial-edit-shortcut-absolute' );
+                               $editShortcut.css( partial.getEditShortcutPositionStyles( $placementContainer ) );
+                               partial.whenPageChanges( function() {
+                                       $editShortcut.css( partial.getEditShortcutPositionStyles( $placementContainer ) );
+                               } );
+                       }
+                       if ( ! $placementContainer.is( ':visible' ) || 'none' === $placementContainer.css( 'display' ) ) {
+                               $editShortcut.addClass( 'customize-partial-edit-shortcut-hidden' );
+                       }
+                       $editShortcut.toggleClass( 'customize-partial-edit-shortcut-left-margin', $editShortcut.offset().left < 1 );
+               },
+
+               /**
+                * Call a callback function when the page changes.
+                *
+                * This calls a callback for any change that might require refreshing the edit shortcuts.
+                *
+                * @since 4.7
+                *
+                * @param {Function} callback The function to call when the page changes.
+                * @returns {void}
+                */
+               whenPageChanges: function( callback ) {
+                       var debouncedCallback, $document;
+                       debouncedCallback = _.debounce( function() {
+                               // Timeout allows any page animations to finish
+                               setTimeout( callback, 100 );
+                       }, 350 );
+                       // When window is resized.
+                       $( window ).resize( debouncedCallback );
+                       // When any customizer setting changes.
+                       api.bind( 'change', debouncedCallback );
+                       $document = $( window.document );
+                       // After scroll in case there are fixed position elements
+                       $document.on( 'scroll', debouncedCallback );
+                       // After page click (eg: hamburger menus)
+                       $document.on( 'click', debouncedCallback );
+               },
+
+               /**
+                * Return the CSS positioning for the edit shortcut for a given partial placement.
+                *
+                * @since 4.7
+                *
+                * @param {jQuery} $placementContainer The placement container element as a jQuery object.
+                * @return {Object} Object containing CSS positions.
+                */
+               getEditShortcutPositionStyles: function( $placementContainer ) {
+                       return {
+                               top: $placementContainer.css( 'top' ),
+                               left: $placementContainer.css( 'left' ),
+                               right: 'auto'
+                       };
+               },
+
+               /**
+                * Return the unique class name for the edit shortcut button for this partial.
+                *
+                * @since 4.7
+                *
+                * @return {string} Partial ID converted into a class name for use in shortcut.
+                */
+               getEditShortcutClassName: function() {
+                       var partial = this, cleanId;
+                       cleanId = partial.id.replace( /]/g, '' ).replace( /\[/g, '-' );
+                       return 'customize-partial-edit-shortcut-' + cleanId;
+               },
+
+               /**
+                * Return the appropriate translated string for the edit shortcut button.
+                *
+                * @since 4.7
+                *
+                * @return {string} Tooltip for edit shortcut.
+                */
+               getEditShortcutTitle: function() {
+                       var partial = this, l10n = self.data.l10n;
+                       switch ( partial.getType() ) {
+                               case 'widget':
+                                       return l10n.clickEditWidget;
+                               case 'blogname':
+                                       return l10n.clickEditTitle;
+                               case 'blogdescription':
+                                       return l10n.clickEditTitle;
+                               case 'nav_menu':
+                                       return l10n.clickEditMenu;
+                               default:
+                                       return l10n.clickEditMisc;
+                       }
+               },
+
+               /**
+                * Return the type of this partial
+                *
+                * Will use `params.type` if set, but otherwise will try to infer type from settingId.
+                *
+                * @since 4.7
+                *
+                * @return {string} Type of partial derived from type param or the related setting ID.
+                */
+               getType: function() {
+                       var partial = this, settingId;
+                       settingId = partial.params.primarySetting || _.first( partial.settings() ) || 'unknown';
+                       if ( partial.params.type ) {
+                               return partial.params.type;
+                       }
+                       if ( settingId.match( /^nav_menu_instance\[/ ) ) {
+                               return 'nav_menu';
+                       }
+                       if ( settingId.match( /^widget_.+\[\d+]$/ ) ) {
+                               return 'widget';
+                       }
+                       return settingId;
+               },
+
+               /**
+                * Create an edit shortcut button for this partial.
+                *
+                * @since 4.7
+                *
+                * @return {jQuery} The edit shortcut button element.
+                */
+               createEditShortcut: function() {
+                       var partial = this, shortcutTitle;
+                       shortcutTitle = partial.getEditShortcutTitle();
+                       return $( '<button>', {
+                               'aria-label': shortcutTitle,
+                               'title': shortcutTitle,
+                               'type': 'button',
+                               'class': 'customize-partial-edit-shortcut ' + partial.getEditShortcutClassName()
+                       } );
+               },
+
+               /**
</ins><span class="cx" style="display: block; padding: 0 10px">                  * Find all placements for this partial int he document.
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @since 4.5.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -175,10 +352,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * @since 4.5.0
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                showControl: function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        var partial = this, settingId = partial.params.primarySetting;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 var partial = this, settingId = partial.params.primarySetting, menuSlug;
</ins><span class="cx" style="display: block; padding: 0 10px">                         if ( ! settingId ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                settingId = _.first( partial.settings() );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        if ( partial.getType() === 'nav_menu' ) {
+                               menuSlug = partial.params.navMenuArgs.theme_location;
+                               if ( menuSlug ) {
+                                       settingId = 'nav_menu_locations[' + menuSlug + ']';
+                               }
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px">                         api.preview.send( 'focus-control-for-setting', settingId );
</span><span class="cx" style="display: block; padding: 0 10px">                },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -319,6 +502,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        self.orginalDocumentWrite = null;
</span><span class="cx" style="display: block; padding: 0 10px">                        /* jshint ignore:end */
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        partial.createEditShortcutForPlacement( placement );
</ins><span class="cx" style="display: block; padding: 0 10px">                         placement.container.removeClass( 'customize-partial-refreshing' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Prevent placement container from being being re-triggered as being rendered among nested partials.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -854,8 +1038,32 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                } );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                api.preview.bind( 'edit-shortcut-visibility', function( visibility ) {
+                       api.selectiveRefresh.editShortcutVisibility.set( visibility );
+               } );
+               api.selectiveRefresh.editShortcutVisibility.bind( function( visibility ) {
+                       var body = $( document.body ), shouldAnimateHide;
+
+                       shouldAnimateHide = ( 'hidden' === visibility && body.hasClass( 'customize-partial-edit-shortcuts-shown' ) && ! body.hasClass( 'customize-partial-edit-shortcuts-hidden' ) );
+                       body.toggleClass( 'customize-partial-edit-shortcuts-hidden', shouldAnimateHide );
+                       body.toggleClass( 'customize-partial-edit-shortcuts-shown', 'visible' === visibility );
+                       body.toggleClass( 'customize-partial-edit-shortcuts-flash', 'initial' === visibility );
+               } );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 api.preview.bind( 'active', function() {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        var body = $( document.body ), buttonText, $editShortcutVisibilityButton;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        // Add invisible button to toggle editShortcutVisibility.
+                       if ( $( '.edit-shortcut-visibility-button' ).length < 1 ) {
+                               buttonText = self.data.l10n.editShortcutVisibilityToggle || 'Toggle edit shortcuts';
+                               $editShortcutVisibilityButton = $( '<button type="button" class="edit-shortcut-visibility-button screen-reader-text"></button>' );
+                               $editShortcutVisibilityButton.text( buttonText );
+                               $editShortcutVisibilityButton.on( 'click', function() {
+                                       api.selectiveRefresh.editShortcutVisibility.set( 'visible' === api.selectiveRefresh.editShortcutVisibility.get() ? 'hidden' : 'visible' );
+                               } );
+                               body.prepend( $editShortcutVisibilityButton );
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         // Make all partials ready.
</span><span class="cx" style="display: block; padding: 0 10px">                        self.partial.each( function( partial ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                partial.deferred.ready.resolve();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -865,6 +1073,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        self.partial.bind( 'add', function( partial ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                partial.deferred.ready.resolve();
</span><span class="cx" style="display: block; padding: 0 10px">                        } );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       body.on( 'click', function( event ) {
+                               if ( $( event.target ).is( '.customize-partial-edit-shortcut, :input, a[href]' ) || 0 !== $( event.target ).closest( 'a' ).length ) {
+                                       return; // Don't toggle shortcuts on form, link, or link child clicks.
+                               }
+                               api.selectiveRefresh.editShortcutVisibility.set( 'visible' === api.selectiveRefresh.editShortcutVisibility.get() ? 'hidden' : 'visible' );
+                               api.preview.send( 'edit-shortcut-visibility', api.selectiveRefresh.editShortcutVisibility.get() );
+                       } );
</ins><span class="cx" style="display: block; padding: 0 10px">                 } );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        } );
</span></span></pre></div>
<a id="trunksrcwpincludesscriptloaderphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/script-loader.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/script-loader.php   2016-10-26 18:42:44 UTC (rev 38966)
+++ trunk/src/wp-includes/script-loader.php     2016-10-26 20:02:51 UTC (rev 38967)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -856,7 +856,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        $styles->add( 'editor-buttons',       "/wp-includes/css/editor$suffix.css", array( 'dashicons' ) );
</span><span class="cx" style="display: block; padding: 0 10px">        $styles->add( 'media-views',          "/wp-includes/css/media-views$suffix.css", array( 'buttons', 'dashicons', 'wp-mediaelement' ) );
</span><span class="cx" style="display: block; padding: 0 10px">        $styles->add( 'wp-pointer',           "/wp-includes/css/wp-pointer$suffix.css", array( 'dashicons' ) );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $styles->add( 'customize-preview',    "/wp-includes/css/customize-preview$suffix.css" );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $styles->add( 'customize-preview',    "/wp-includes/css/customize-preview$suffix.css", array( 'dashicons' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">         $styles->add( 'wp-embed-template-ie', "/wp-includes/css/wp-embed-template-ie$suffix.css" );
</span><span class="cx" style="display: block; padding: 0 10px">        $styles->add_data( 'wp-embed-template-ie', 'conditional', 'lte IE 8' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre>
</div>
</div>

</body>
</html>