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