| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 /** 2 * @output wp-includes/js/customize-loader.js 3 */ 4 5 /* global _wpCustomizeLoaderSettings */ 6 7 /** 8 * Expose a public API that allows the customizer to be 9 * loaded on any page. 10 * 11 * @namespace wp 12 */ 13 window.wp = window.wp || {}; 14 15 /** 16 * The WordPress Customizer loader API. 17 * 18 * @param {Object} wp The WordPress global object. 19 * @param {JQueryStatic} $ The jQuery object. 20 */ 21 (function( wp, $ ){ 22 var api = wp.customize, 23 Loader; 24 25 $.extend( $.support, { 26 history: !! ( window.history && history.pushState ), 27 hashchange: ('onhashchange' in window) && (document.documentMode === undefined || document.documentMode > 7) 28 }); 29 30 /** 31 * Allows the Customizer to be overlaid on any page. 32 * 33 * By default, any element in the body with the load-customize class will open 34 * an iframe overlay with the URL specified. 35 * 36 * e.g. <a class="load-customize" href="<?php echo wp_customize_url(); ?>">Open Customizer</a> 37 * 38 * @memberOf wp.customize 39 * 40 * @class 41 * @augments wp.customize.Events 42 */ 43 Loader = $.extend( {}, api.Events,/** @lends wp.customize.Loader.prototype */{ 44 /** 45 * Setup the Loader; triggered on document#ready. 46 */ 47 initialize: function() { 48 this.body = $( document.body ); 49 50 // Ensure the loader is supported. 51 // Check for settings, postMessage support, and whether we require CORS support. 52 if ( ! Loader.settings || ! $.support.postMessage || ( ! $.support.cors && Loader.settings.isCrossDomain ) ) { 53 return; 54 } 55 56 this.window = $( window ); 57 this.element = $( '<div id="customize-container" />' ).appendTo( this.body ); 58 59 // Bind events for opening and closing the overlay. 60 this.bind( 'open', this.overlay.show ); 61 this.bind( 'close', this.overlay.hide ); 62 63 // Any element in the body with the `load-customize` class opens 64 // the Customizer. 65 $('#wpbody').on( 'click', '.load-customize', function( event ) { 66 event.preventDefault(); 67 68 // Store a reference to the link that opened the Customizer. 69 Loader.link = $(this); 70 // Load the theme. 71 Loader.open( Loader.link.attr('href') ); 72 }); 73 74 // Add navigation listeners. 75 if ( $.support.history ) { 76 this.window.on( 'popstate', Loader.popstate ); 77 } 78 79 if ( $.support.hashchange ) { 80 this.window.on( 'hashchange', Loader.hashchange ); 81 this.window.triggerHandler( 'hashchange' ); 82 } 83 }, 84 85 /** 86 * Handle popstate event. 87 * 88 * @param {JQuery.Event} e The popstate event. 89 * @return {void} 90 */ 91 popstate: function( e ) { 92 var state = e.originalEvent.state; 93 if ( state && state.customize ) { 94 Loader.open( state.customize ); 95 } else if ( Loader.active ) { 96 Loader.close(); 97 } 98 }, 99 100 /** 101 * Handle hashchange event. 102 */ 103 hashchange: function() { 104 var hash = window.location.toString().split('#')[1]; 105 106 if ( hash && 0 === hash.indexOf( 'wp_customize=on' ) ) { 107 Loader.open( Loader.settings.url + '?' + hash ); 108 } 109 110 if ( ! hash && ! $.support.history ) { 111 Loader.close(); 112 } 113 }, 114 115 /** 116 * Handle beforeunload event. 117 * 118 * @return {string|void} Confirmation message if there are unsaved changes. 119 */ 120 beforeunload: function () { 121 if ( ! Loader.saved() ) { 122 return Loader.settings.l10n.saveAlert; 123 } 124 }, 125 126 /** 127 * Open the Customizer overlay for a specific URL. 128 * 129 * @param {string} src URL to load in the Customizer. 130 * @return {string|void} The URL, when navigating to it directly on mobile. 131 */ 132 open: function( src ) { 133 134 if ( this.active ) { 135 return; 136 } 137 138 // Load the full page on mobile devices. 139 if ( Loader.settings.browser.mobile ) { 140 return window.location = src; 141 } 142 143 // Store the document title prior to opening the Live Preview. 144 this.originalDocumentTitle = document.title; 145 146 this.active = true; 147 this.body.addClass('customize-loading'); 148 149 /* 150 * Track the dirtiness state (whether the drafted changes have been published) 151 * of the Customizer in the iframe. This is used to decide whether to display 152 * an AYS alert if the user tries to close the window before saving changes. 153 */ 154 this.saved = new api.Value( true ); 155 156 this.iframe = $( '<iframe />', { 'src': src, 'title': Loader.settings.l10n.mainIframeTitle } ).appendTo( this.element ); 157 this.iframe.one( 'load', this.loaded ); 158 159 // Create a postMessage connection with the iframe. 160 this.messenger = new api.Messenger({ 161 url: src, 162 channel: 'loader', 163 targetWindow: this.iframe[0].contentWindow 164 }); 165 166 // Expose the changeset UUID on the parent window's URL so that the customized state can survive a refresh. 167 if ( history.replaceState ) { 168 this.messenger.bind( 'changeset-uuid', function( changesetUuid ) { 169 var urlParser = document.createElement( 'a' ); 170 urlParser.href = location.href; 171 urlParser.search = $.param( _.extend( 172 api.utils.parseQueryString( urlParser.search.substr( 1 ) ), 173 { changeset_uuid: changesetUuid } 174 ) ); 175 history.replaceState( { customize: urlParser.href }, '', urlParser.href ); 176 } ); 177 } 178 179 // Wait for the connection from the iframe before sending any postMessage events. 180 this.messenger.bind( 'ready', function() { 181 Loader.messenger.send( 'back' ); 182 }); 183 184 this.messenger.bind( 'close', function() { 185 if ( $.support.history ) { 186 history.back(); 187 } else if ( $.support.hashchange ) { 188 window.location.hash = ''; 189 } else { 190 Loader.close(); 191 } 192 }); 193 194 // Prompt AYS dialog when navigating away. 195 $( window ).on( 'beforeunload', this.beforeunload ); 196 197 this.messenger.bind( 'saved', function () { 198 Loader.saved( true ); 199 } ); 200 this.messenger.bind( 'change', function () { 201 Loader.saved( false ); 202 } ); 203 204 this.messenger.bind( 'title', function( newTitle ){ 205 window.document.title = newTitle; 206 }); 207 208 this.pushState( src ); 209 210 this.trigger( 'open' ); 211 }, 212 213 /** 214 * Push the state of the Customizer onto the history stack. 215 * 216 * @param {string} src URL to push. 217 */ 218 pushState: function ( src ) { 219 var hash = src.split( '?' )[1]; 220 221 // Ensure we don't call pushState if the user hit the forward button. 222 if ( $.support.history && window.location.href !== src ) { 223 history.pushState( { customize: src }, '', src ); 224 } else if ( ! $.support.history && $.support.hashchange && hash ) { 225 window.location.hash = 'wp_customize=on&' + hash; 226 } 227 228 this.trigger( 'open' ); 229 }, 230 231 /** 232 * Callback after the Customizer has been opened. 233 */ 234 opened: function() { 235 Loader.body.addClass( 'customize-active full-overlay-active' ).attr( 'aria-busy', 'true' ); 236 }, 237 238 /** 239 * Close the Customizer overlay. 240 */ 241 close: function() { 242 var self = this, onConfirmClose; 243 if ( ! self.active ) { 244 return; 245 } 246 247 onConfirmClose = function( confirmed ) { 248 if ( confirmed ) { 249 self.active = false; 250 self.trigger( 'close' ); 251 252 // Restore document title prior to opening the Live Preview. 253 if ( self.originalDocumentTitle ) { 254 document.title = self.originalDocumentTitle; 255 } 256 } else { 257 258 // Go forward since Customizer is exited by history.back(). 259 history.forward(); 260 } 261 self.messenger.unbind( 'confirmed-close', onConfirmClose ); 262 }; 263 self.messenger.bind( 'confirmed-close', onConfirmClose ); 264 265 Loader.messenger.send( 'confirm-close' ); 266 }, 267 268 /** 269 * Callback after the Customizer has been closed. 270 */ 271 closed: function() { 272 Loader.iframe.remove(); 273 Loader.messenger.destroy(); 274 Loader.iframe = null; 275 Loader.messenger = null; 276 Loader.saved = null; 277 Loader.body.removeClass( 'customize-active full-overlay-active' ).removeClass( 'customize-loading' ); 278 $( window ).off( 'beforeunload', Loader.beforeunload ); 279 /* 280 * Return focus to the link that opened the Customizer overlay after 281 * the body element visibility is restored. 282 */ 283 if ( Loader.link ) { 284 Loader.link.focus(); 285 } 286 }, 287 288 /** 289 * Callback for the `load` event on the Customizer iframe. 290 */ 291 loaded: function() { 292 Loader.body.removeClass( 'customize-loading' ).attr( 'aria-busy', 'false' ); 293 }, 294 295 /** 296 * Overlay hide/show utility methods. 297 */ 298 overlay: { 299 /** 300 * Show the overlay. 301 */ 302 show: function() { 303 this.element.fadeIn( 200, Loader.opened ); 304 }, 305 306 /** 307 * Hide the overlay. 308 */ 309 hide: function() { 310 this.element.fadeOut( 200, Loader.closed ); 311 } 312 } 313 }); 314 315 // Bootstrap the Loader on document#ready. 316 $( function() { 317 Loader.settings = _wpCustomizeLoaderSettings; 318 Loader.initialize(); 319 }); 320 321 // Expose the API publicly on window.wp.customize.Loader. 322 api.Loader = Loader; 323 })( wp, jQuery );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sat Sep 12 08:20:32 2026 | Cross-referenced by PHPXref |