1 /* global _wpCustomizeLoaderSettings, confirm */ |
1 /* global _wpCustomizeLoaderSettings */ |
|
2 /** |
|
3 * Expose a public API that allows the customizer to be |
|
4 * loaded on any page. |
|
5 * |
|
6 * @namespace wp |
|
7 */ |
2 window.wp = window.wp || {}; |
8 window.wp = window.wp || {}; |
3 |
9 |
4 (function( exports, $ ){ |
10 (function( exports, $ ){ |
5 var api = wp.customize, |
11 var api = wp.customize, |
6 Loader; |
12 Loader; |
16 * By default, any element in the body with the load-customize class will open |
22 * By default, any element in the body with the load-customize class will open |
17 * an iframe overlay with the URL specified. |
23 * an iframe overlay with the URL specified. |
18 * |
24 * |
19 * e.g. <a class="load-customize" href="<?php echo wp_customize_url(); ?>">Open Customizer</a> |
25 * e.g. <a class="load-customize" href="<?php echo wp_customize_url(); ?>">Open Customizer</a> |
20 * |
26 * |
|
27 * @memberOf wp.customize |
|
28 * |
|
29 * @class |
21 * @augments wp.customize.Events |
30 * @augments wp.customize.Events |
22 */ |
31 */ |
23 Loader = $.extend( {}, api.Events, { |
32 Loader = $.extend( {}, api.Events,/** @lends wp.customize.Loader.prototype */{ |
24 /** |
33 /** |
25 * Setup the Loader; triggered on document#ready. |
34 * Setup the Loader; triggered on document#ready. |
26 */ |
35 */ |
27 initialize: function() { |
36 initialize: function() { |
28 this.body = $( document.body ); |
37 this.body = $( document.body ); |
109 this.originalDocumentTitle = document.title; |
118 this.originalDocumentTitle = document.title; |
110 |
119 |
111 this.active = true; |
120 this.active = true; |
112 this.body.addClass('customize-loading'); |
121 this.body.addClass('customize-loading'); |
113 |
122 |
114 // Dirty state of Customizer in iframe |
123 /* |
|
124 * Track the dirtiness state (whether the drafted changes have been published) |
|
125 * of the Customizer in the iframe. This is used to decide whether to display |
|
126 * an AYS alert if the user tries to close the window before saving changes. |
|
127 */ |
115 this.saved = new api.Value( true ); |
128 this.saved = new api.Value( true ); |
116 |
129 |
117 this.iframe = $( '<iframe />', { 'src': src, 'title': Loader.settings.l10n.mainIframeTitle } ).appendTo( this.element ); |
130 this.iframe = $( '<iframe />', { 'src': src, 'title': Loader.settings.l10n.mainIframeTitle } ).appendTo( this.element ); |
118 this.iframe.one( 'load', this.loaded ); |
131 this.iframe.one( 'load', this.loaded ); |
119 |
132 |
121 this.messenger = new api.Messenger({ |
134 this.messenger = new api.Messenger({ |
122 url: src, |
135 url: src, |
123 channel: 'loader', |
136 channel: 'loader', |
124 targetWindow: this.iframe[0].contentWindow |
137 targetWindow: this.iframe[0].contentWindow |
125 }); |
138 }); |
|
139 |
|
140 // Expose the changeset UUID on the parent window's URL so that the customized state can survive a refresh. |
|
141 if ( history.replaceState ) { |
|
142 this.messenger.bind( 'changeset-uuid', function( changesetUuid ) { |
|
143 var urlParser = document.createElement( 'a' ); |
|
144 urlParser.href = location.href; |
|
145 urlParser.search = $.param( _.extend( |
|
146 api.utils.parseQueryString( urlParser.search.substr( 1 ) ), |
|
147 { changeset_uuid: changesetUuid } |
|
148 ) ); |
|
149 history.replaceState( { customize: urlParser.href }, '', urlParser.href ); |
|
150 } ); |
|
151 } |
126 |
152 |
127 // Wait for the connection from the iframe before sending any postMessage events. |
153 // Wait for the connection from the iframe before sending any postMessage events. |
128 this.messenger.bind( 'ready', function() { |
154 this.messenger.bind( 'ready', function() { |
129 Loader.messenger.send( 'back' ); |
155 Loader.messenger.send( 'back' ); |
130 }); |
156 }); |
140 }); |
166 }); |
141 |
167 |
142 // Prompt AYS dialog when navigating away |
168 // Prompt AYS dialog when navigating away |
143 $( window ).on( 'beforeunload', this.beforeunload ); |
169 $( window ).on( 'beforeunload', this.beforeunload ); |
144 |
170 |
145 this.messenger.bind( 'activated', function( location ) { |
|
146 if ( location ) { |
|
147 window.location = location; |
|
148 } |
|
149 }); |
|
150 |
|
151 this.messenger.bind( 'saved', function () { |
171 this.messenger.bind( 'saved', function () { |
152 Loader.saved( true ); |
172 Loader.saved( true ); |
153 } ); |
173 } ); |
154 this.messenger.bind( 'change', function () { |
174 this.messenger.bind( 'change', function () { |
155 Loader.saved( false ); |
175 Loader.saved( false ); |
179 |
199 |
180 /** |
200 /** |
181 * Callback after the Customizer has been opened. |
201 * Callback after the Customizer has been opened. |
182 */ |
202 */ |
183 opened: function() { |
203 opened: function() { |
184 Loader.body.addClass( 'customize-active full-overlay-active' ); |
204 Loader.body.addClass( 'customize-active full-overlay-active' ).attr( 'aria-busy', 'true' ); |
185 }, |
205 }, |
186 |
206 |
187 /** |
207 /** |
188 * Close the Customizer overlay and return focus to the link that opened it. |
208 * Close the Customizer overlay. |
189 */ |
209 */ |
190 close: function() { |
210 close: function() { |
191 if ( ! this.active ) { |
211 var self = this, onConfirmClose; |
|
212 if ( ! self.active ) { |
192 return; |
213 return; |
193 } |
214 } |
194 |
215 |
195 // Display AYS dialog if Customizer is dirty |
216 onConfirmClose = function( confirmed ) { |
196 if ( ! this.saved() && ! confirm( Loader.settings.l10n.saveAlert ) ) { |
217 if ( confirmed ) { |
197 // Go forward since Customizer is exited by history.back() |
218 self.active = false; |
198 history.forward(); |
219 self.trigger( 'close' ); |
199 return; |
220 |
200 } |
221 // Restore document title prior to opening the Live Preview |
201 |
222 if ( self.originalDocumentTitle ) { |
202 this.active = false; |
223 document.title = self.originalDocumentTitle; |
203 |
224 } |
204 this.trigger( 'close' ); |
225 } else { |
205 |
226 |
206 // Restore document title prior to opening the Live Preview |
227 // Go forward since Customizer is exited by history.back() |
207 if ( this.originalDocumentTitle ) { |
228 history.forward(); |
208 document.title = this.originalDocumentTitle; |
229 } |
209 } |
230 self.messenger.unbind( 'confirmed-close', onConfirmClose ); |
210 |
231 }; |
211 // Return focus to link that was originally clicked. |
232 self.messenger.bind( 'confirmed-close', onConfirmClose ); |
212 if ( this.link ) { |
233 |
213 this.link.focus(); |
234 Loader.messenger.send( 'confirm-close' ); |
214 } |
|
215 }, |
235 }, |
216 |
236 |
217 /** |
237 /** |
218 * Callback after the Customizer has been closed. |
238 * Callback after the Customizer has been closed. |
219 */ |
239 */ |
223 Loader.iframe = null; |
243 Loader.iframe = null; |
224 Loader.messenger = null; |
244 Loader.messenger = null; |
225 Loader.saved = null; |
245 Loader.saved = null; |
226 Loader.body.removeClass( 'customize-active full-overlay-active' ).removeClass( 'customize-loading' ); |
246 Loader.body.removeClass( 'customize-active full-overlay-active' ).removeClass( 'customize-loading' ); |
227 $( window ).off( 'beforeunload', Loader.beforeunload ); |
247 $( window ).off( 'beforeunload', Loader.beforeunload ); |
|
248 /* |
|
249 * Return focus to the link that opened the Customizer overlay after |
|
250 * the body element visibility is restored. |
|
251 */ |
|
252 if ( Loader.link ) { |
|
253 Loader.link.focus(); |
|
254 } |
228 }, |
255 }, |
229 |
256 |
230 /** |
257 /** |
231 * Callback for the `load` event on the Customizer iframe. |
258 * Callback for the `load` event on the Customizer iframe. |
232 */ |
259 */ |
233 loaded: function() { |
260 loaded: function() { |
234 Loader.body.removeClass('customize-loading'); |
261 Loader.body.removeClass( 'customize-loading' ).attr( 'aria-busy', 'false' ); |
235 }, |
262 }, |
236 |
263 |
237 /** |
264 /** |
238 * Overlay hide/show utility methods. |
265 * Overlay hide/show utility methods. |
239 */ |
266 */ |