PluginProbe
Customify / 2.10.1
Customify v2.10.1
2.10.9 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.7.1 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.6.0 1.6.0.1 1.6.5 1.7.0 1.7.1 All 77 releases
← All changes | js/customizer.js +672 -1662 1.7.12.10.1 View file →
@@ -1,1813 +1,823 @@
1 -(
2 - function( $, exports, wp ) {
3 - var api = wp.customize;
4 - var $window = $( window );
1 +/** @namespace customify */
2 +window.customify = window.customify || parent.customify || {};
5 3
6 - // when the customizer is ready prepare our fields events
7 - wp.customize.bind( 'ready', function() {
8 - var timeout = null;
4 +(function ($, customify, wp) {
5 + const api = wp.customize
6 + const $window = $(window)
7 + const $document = $(document)
8 + let timeout = null
9 9
10 - // add ace editors
11 - $( '.customify_ace_editor' ).each( function( key, el ) {
12 - var id = $( this ).attr( 'id' ),
13 - css_editor = ace.edit( id );
10 + // when the customizer is ready prepare our fields events
11 + api.bind('ready', function () {
14 12
15 - var editor_type = $( this ).data( 'editor_type' );
16 - // init the ace editor
17 - css_editor.setTheme( "ace/theme/github" );
18 - css_editor.getSession().setMode( "ace/mode/" + editor_type );
13 + // Create a stack of callbacks bound to parent settings to be able to unbind them
14 + // when altering the connected_fields attribute.
15 + if (typeof customify.connectedFieldsCallbacks === 'undefined') {
16 + customify.connectedFieldsCallbacks = {}
17 + }
19 18
20 - // hide the textarea and enable the ace editor
21 - var textarea = $( '#' + id + '_textarea' ).hide();
22 - css_editor.getSession().setValue( textarea.val() );
19 + // Initialize ACE editors.
20 + handleAceEditors()
23 21
24 - // each time a change is triggered start a timeout of 1,5s and when is finished refresh the previewer
25 - // if the user types faster than this delay then reset it
26 - css_editor.getSession().on( 'change', function( e ) {
27 - if ( timeout !== null ) {
28 - clearTimeout( timeout );
29 - timeout = null;
30 - } else {
31 - timeout = setTimeout( function() {
32 - //var state = css_editor.session.getState();
33 - textarea.val( css_editor.getSession().getValue() );
34 - textarea.trigger( 'change' );
35 - }, 1500 );
36 - }
37 - } );
38 - } );
22 + // Initialize simple select2 fields.
23 + $('.customify_select2').select2()
39 24
40 - // simple select2 field
41 - $( '.customify_select2' ).select2();
25 + // Initialize font fields.
26 + customify.fontFields.init()
42 27
43 - setTimeout( function() {
44 - customifyFontSelect.init( this );
45 - }, 333 );
28 + // For each range input add a value preview output.
29 + $('.accordion-section-content[id*="' + customify.config.options_name + '"], #sub-accordion-section-style_manager_section').each(function () {
30 + // Initialize range fields logic
31 + handleRangeFields(this)
32 + })
46 33
47 - prepare_typography_field();
34 + // Handle presets (legacy).
35 + handlePresets()
48 36
49 - /**
50 - * Make the customizer save on CMD/CTRL+S action
51 - * This is awesome!!!
52 - */
53 - $( window ).bind( 'keydown', function( event ) {
54 - if ( event.ctrlKey || event.metaKey ) {
55 - switch ( String.fromCharCode( event.which ).toLowerCase() ) {
56 - case 's':
57 - event.preventDefault();
58 - api.previewer.save();
59 - break;
60 - }
61 - }
62 - } );
37 + // Initialize custom background fields.
38 + customifyBackgroundJsControl.init()
63 39
64 - // for each range input add a value preview output
65 - $( '.accordion-section-content[id*="' + customify_settings.options_name + '"]' ).each( function() {
40 + setTimeout(function () {
41 + customifyFoldingFields()
42 + }, 1000)
66 43
67 - // Initialize range fields logic
68 - customifyHandleRangeFields( this );
69 - } );
44 + // Handle reset buttons
45 + handleResetButtons()
70 46
71 - if ( $( 'button[data-action="reset_customify"]' ).length > 0 ) {
72 - // reset_button
73 - $( document ).on( 'click', '#customize-control-reset_customify button', function( ev ) {
74 - ev.preventDefault();
47 + // Handle the section tabs (ex: Layout | Fonts | Colors)
48 + handleSectionTabs()
75 49
76 - var iAgree = confirm( 'Do you really want to reset to defaults all the fields? Watch out, this will reset all your Customify options and will save them!' );
50 + // Bind any connected fields, except those in the Style Manager.
51 + // Those are handled by the appropriate Style Manager component (Color Palettes, Font Palettes, etc ).
52 + bindConnectedFields()
77 53
78 - if ( ! iAgree ) {
79 - return;
80 - }
54 + // Handle the preview iframe.
55 + handlePreviewIframe()
81 56
82 - $.each( api.settings.controls, function( key, ctrl ) {
83 - var setting_id = key.replace( '_control', '' );
84 - var setting = customify_settings.settings[setting_id];
57 + // Sometimes a php save may be needed. Trigger it if the appropiate URL var is present.
58 + if (getUrlVar('save_customizer_once')) {
59 + api.previewer.save()
60 + }
61 + })
85 62
86 - if ( ! _.isUndefined( setting ) && ! _.isUndefined( setting.default ) ) {
87 - api_set_setting_value( setting_id, setting.default );
88 - }
89 - } );
63 + /**
64 + * Handle the ACE editor fields.
65 + */
66 + function handleAceEditors () {
67 + $('.customify_ace_editor').each(function (key, el) {
68 + const id = $(this).attr('id'),
69 + cssEditorInstance = ace.edit(id)
90 70
91 - api.previewer.save();
92 - } );
71 + const editor_type = $(this).data('editor_type')
72 + // init the ace editor
73 + cssEditorInstance.setTheme('ace/theme/github')
74 + cssEditorInstance.getSession().setMode('ace/mode/' + editor_type)
93 75
94 - // add a reset button for each panel
95 - $( '.panel-meta' ).each( function( el, key ) {
96 - var container = $( this ).parents( '.control-panel' ),
97 - id = container.attr( 'id' );
76 + // hide the textarea and enable the ace editor
77 + const textarea = $('#' + id + '_textarea').hide()
78 + cssEditorInstance.getSession().setValue(textarea.val())
98 79
99 - if ( typeof id !== 'undefined' ) {
100 - var panel_id = id.replace( 'accordion-panel-', '' );
101 - $( this ).parent().append( '<button class="reset_panel button" data-panel="' + panel_id + '">Panel\'s defaults</button>' );
102 - }
103 - } );
80 + // each time a change is triggered start a timeout of 1,5s and when is finished refresh the previewer
81 + // if the user types faster than this delay then reset it
82 + cssEditorInstance.getSession().on('change', function (event) {
83 + if (timeout !== null) {
84 + clearTimeout(timeout)
85 + timeout = null
86 + } else {
87 + timeout = setTimeout(function () {
88 + textarea.val(cssEditorInstance.getSession().getValue())
89 + textarea.trigger('change', ['customify'])
90 + }, 1500)
91 + }
92 + })
93 + })
94 + }
104 95
105 - // reset panel
106 - $( document ).on( 'click', '.reset_panel', function( e ) {
107 - e.preventDefault();
96 + /**
97 + * Handle the presets (legacy).
98 + */
99 + function handlePresets () {
100 + $('body').on('customify:preset-change', function (event) {
101 + const data = $(event.target).data('options')
108 102
109 - var panel_id = $( this ).data( 'panel' ),
110 - panel = api.panel( panel_id ),
111 - sections = panel.sections(),
112 - iAgree = confirm( "Do you really want to reset " + panel.params.title + "?" );
103 + if (!_.isUndefined(data)) {
104 + $.each(data, function (settingID, value) {
105 + apiSetSettingValue(settingID, value)
106 + })
107 + }
108 + })
109 + $document.on('change', 'select.js-customify-preset', function () {
110 + const $source = $(this)
111 + const $target = $source.children('[value="' + $source.val() + '"]')
112 + $target.trigger('customify:preset-change')
113 + })
114 + $document.on('click', '.js-customify-preset input', function () {
115 + $(this).trigger('customify:preset-change')
116 + })
117 + }
113 118
114 - if ( ! iAgree ) {
115 - return;
116 - }
117 - if ( sections.length > 0 ) {
118 - $.each( sections, function() {
119 - //var settings = this.settings();
120 - var controls = this.controls();
119 + const handleRangeFields = function (el) {
121 120
122 - if ( controls.length > 0 ) {
123 - $.each( controls, function( key, ctrl ) {
124 - var setting_id = ctrl.id.replace( '_control', '' ),
125 - setting = customify_settings.settings[setting_id];
121 + // For each range input add a number field (for preview mainly - but it can also be used for input)
122 + $(el).find('input[type="range"]').each(function () {
123 + const $range = $(this)
124 + let $number = $range.siblings('.range-value')
126 125
127 - if ( ! _.isUndefined( setting ) && ! _.isUndefined( setting.default ) ) {
128 - api_set_setting_value( setting_id, setting.default );
129 - }
130 - } );
131 - }
132 - } );
133 - }
134 - } );
126 + if (!$number.length) {
127 + $number = $range.clone()
135 128
136 - //add reset section
137 - $( '.accordion-section-content' ).each( function( el, key ) {
138 - var section_id = $( this ).attr( 'id' );
129 + $number
130 + .attr('type', 'number')
131 + .attr('class', 'range-value')
132 + .removeAttr('data-value_entry')
139 133
140 - if ( (
141 - (
142 - ! _.isUndefined( section_id )
143 - ) ? section_id.indexOf( customify_settings.options_name ) : - 1
144 - ) === - 1 ) {
145 - return;
146 - }
134 + if ($range.first().attr('id')) {
135 + $number.attr('id', $range.first().attr('id') + '_number')
136 + }
137 + $number.insertAfter($range)
138 + }
147 139
148 - if ( ! _.isUndefined( section_id ) && section_id.indexOf( 'sub-accordion-section-' ) > - 1 ) {
149 - var id = section_id.replace( 'sub-accordion-section-', '' );
150 - $( this ).append( '<button class="reset_section button" data-section="' + id + '">Reset All Options for This Section</button>' );
151 - }
152 - } );
140 + function hasValidValue ($input) {
141 + const min = $input.attr('min')
142 + const max = $input.attr('max')
143 + const value = $input.val()
153 144
154 - // reset section event
155 - $( document ).on( 'click', '.reset_section', function( e ) {
156 - e.preventDefault();
145 + if (typeof min !== 'undefined' && parseFloat(min) > parseFloat(value)) {
146 + return false
147 + }
157 148
158 - var section_id = $( this ).data( 'section' ),
159 - section = api.section( section_id ),
160 - controls = section.controls();
149 + return !(typeof max !== 'undefined' && parseFloat(max) < parseFloat(value))
150 + }
161 151
162 - var iAgree = confirm( "Do you really want to reset " + section.params.title + "?" );
152 + // Put the value into the number field.
153 + $range.on('input change', function (event) {
154 + if (event.target.value === $number.val()) {
155 + // Nothing to do if the values are identical.
156 + return;
157 + }
163 158
164 - if ( ! iAgree ) {
165 - return;
166 - }
159 + $number.val($range.val())
160 + })
167 161
168 - if ( controls.length > 0 ) {
169 - $.each( controls, function( key, ctrl ) {
170 - var setting_id = ctrl.id.replace( '_control', '' ),
171 - setting = customify_settings.settings[setting_id];
162 + // When clicking outside the number field or on Enter.
163 + $number.on('blur keyup', function (event) {
164 + if ('keyup' === event.type && event.keyCode !== 13) {
165 + return
166 + }
172 167
173 - if ( ! _.isUndefined( setting ) && ! _.isUndefined( setting.default ) ) {
174 - api_set_setting_value( setting_id, setting.default );
175 - }
176 - } );
177 - }
178 - } );
179 - }
168 + if (event.target.value === $range.val()) {
169 + // Nothing to do if the values are identical.
170 + return;
171 + }
180 172
181 - $( document ).on( 'change keyup', '.customize-control-range input.range-value', function() {
182 - var range = $( this ).siblings( 'input[type="range"]' );
183 - range.val( $( this ).val() );
184 - range.trigger( 'change' );
185 - } );
173 + if (!hasValidValue($number)) {
174 + $number.val($range.val())
175 + shake($number)
176 + } else {
177 + // Do not mark this trigger as being programmatically triggered by Customify since it is a result of a user input.
178 + $range.val($number.val()).trigger('change')
179 + }
180 + })
186 181
187 - $( document ).on( 'change', '.customify_typography_font_subsets', function( ev ) {
182 + function shake ($field) {
183 + $field.addClass('input-shake input-error')
184 + $field.one('animationend', function () {
185 + $field.removeClass('input-shake input-error')
186 + })
187 + }
188 + })
189 + }
188 190
189 - var $input = $( this ).parents( '.options' ).siblings( '.customify_typography' ).children( '.customify_typography_values' ),
190 - current_val = $input.val();
191 + /**
192 + * Handle reset buttons in the Customizer.
193 + */
194 + function handleResetButtons () {
195 + const showResetButtons = $('button[data-action="reset_customify"]').length > 0
191 196
192 - current_val = JSON.parse( decodeURIComponent( current_val ) );
197 + if (showResetButtons) {
198 + createResetPanelButtons()
199 + createResetSectionButtons()
193 200
194 - //maybe the selected option holds a JSON in its value
195 - current_val.selected_subsets = maybeJsonParse( $( this ).val() );
201 + $document.on('click', '.js-reset-panel', onResetPanel)
202 + $document.on('click', '.js-reset-section', onResetSection)
203 + $document.on('click', '#customize-control-reset_customify button', onReset)
204 + }
205 + }
196 206
197 - $input.val( encodeURIComponent( JSON.stringify( current_val ) ) );
207 + function createResetPanelButtons () {
198 208
199 - $input.trigger( 'change' );
200 - } );
209 + $('.panel-meta').each(function (i, obj) {
210 + const $this = $(obj)
211 + const container = $this.parents('.control-panel')
212 + let id = container.attr('id')
201 213
202 - $( document ).on( 'change', '.customify_typography_font_weight', function( ev ) {
214 + if (typeof id !== 'undefined') {
215 + id = id.replace('sub-accordion-panel-', '')
216 + id = id.replace('accordion-panel-', '')
217 + const $buttonWrapper = $('<li class="customize-control customize-control-reset"></li>')
218 + const $button = $('<button class="button js-reset-panel" data-panel="' + id + '"></button>')
203 219
204 - var $input = $( this ).parents( '.options' ).siblings( '.customify_typography' ).children( '.customify_typography_values' ),
205 - current_val = $input.val();
220 + $button.text(customify.l10n.panelResetButton).appendTo($buttonWrapper)
221 + $this.parent().append($buttonWrapper)
222 + }
223 + })
224 + }
206 225
207 - current_val = maybeJsonParse( current_val );
208 - // @todo currently the font weight selector works for one value only
209 - // maybe make this a multiselect
226 + function createResetSectionButtons () {
227 + $('.accordion-section-content').each(function (el, key) {
228 + const $this = $(this)
229 + const sectionID = $this.attr('id')
210 230
211 - //maybe the selected option holds a JSON in its value
212 - current_val.selected_variants = {0: maybeJsonParse( $( this ).val() )};
231 + if (_.isUndefined(sectionID) || sectionID.indexOf(customify.config.options_name) === -1) {
232 + return
233 + }
213 234
214 - $input.val( encodeURIComponent( JSON.stringify( current_val ) ) );
215 - $input.trigger( 'change' );
216 - } );
235 + const id = sectionID.replace('sub-accordion-section-', '')
236 + const $button = $('<button class="button js-reset-section" data-section="' + id + '"></button>')
237 + const $buttonWrapper = $('<li class="customize-control customize-control-reset"></li>')
217 238
218 - $( 'body' ).on( 'customify:preset-change', function( e ) {
219 - const data = $( e.target ).data( 'options' );
239 + $button.text(customify.l10n.sectionResetButton)
240 + $buttonWrapper.append($button)
220 241
221 - if ( ! _.isUndefined( data ) ) {
222 - $.each( data, function( setting_id, value ) {
223 - api_set_setting_value( setting_id, value );
224 - } );
225 - }
226 - } );
242 + $this.append($buttonWrapper)
243 + })
244 + }
227 245
228 - $( document ).on( 'change', '.customify_preset.select', function() {
229 - const $source = $( this );
230 - const $target = $source.children( '[value="' + $source.val() + '"]' );
231 - $target.trigger( 'customify:preset-change' );
232 - } );
246 + function onReset (ev) {
247 + ev.preventDefault()
233 248
234 - $( document ).on( 'click', '.customify_preset.radio input, .customify_preset.radio_buttons input, .awesome_presets input', function() {
235 - $( this ).trigger( 'customify:preset-change' );
236 - } );
249 + const iAgree = confirm(customify.l10n.resetGlobalConfirmMessage)
237 250
238 - // bind our event on click
239 - $( document ).on( 'click', '.customify_import_demo_data_button', function( event ) {
240 - let key = $( this ).data( 'key' );
241 - let import_queue = new Queue( api );
242 - let steps = [];
251 + if (!iAgree) {
252 + return
253 + }
243 254
244 - if ( ! _.isUndefined( customify_settings.settings[key].imports ) ) {
255 + $.each(api.settings.controls, function (key, ctrl) {
256 + const settingID = key.replace('_control', '')
257 + const setting = customify.config.settings[settingID]
245 258
246 - $.each( customify_settings.settings[key].imports, function( i, import_setts, k ) {
247 - if ( _.isUndefined( import_setts.steps ) ) {
248 - steps.push( {id: i, type: import_setts.type} );
249 - } else {
250 - var count = import_setts.steps;
259 + if (!_.isUndefined(setting) && !_.isUndefined(setting.default)) {
260 + apiSetSettingValue(settingID, setting.default)
261 + }
262 + })
251 263
252 - while ( count >= 1 ) {
253 - steps.push( {id: i, type: import_setts.type, count: count} );
254 - count = count - 1;
255 - }
256 - }
257 - } );
258 - }
264 + api.previewer.save()
265 + }
259 266
260 - import_queue.add_steps( 'import_demo_data_action_id', steps );
261 - return false;
262 - } );
267 + function onResetPanel (e) {
268 + e.preventDefault()
263 269
264 - customifyBackgroundJsControl.init();
270 + const panelID = $(this).data('panel'),
271 + panel = api.panel(panelID),
272 + sections = panel.sections(),
273 + iAgree = confirm(customify.l10n.resetPanelConfirmMessage)
265 274
266 - // sometimes a php save may be needed
267 - if ( getUrlVars( 'save_customizer_once' ) ) {
268 - api.previewer.save();
269 - }
275 + if (!iAgree) {
276 + return
277 + }
278 + if (sections.length > 0) {
279 + $.each(sections, function () {
280 + const controls = this.controls()
270 281
271 - setTimeout( function() {
272 - customifyFoldingFields();
273 - }, 1000 );
282 + if (controls.length > 0) {
283 + $.each(controls, function (key, ctrl) {
284 + const settingID = ctrl.id.replace('_control', ''),
285 + setting = customify.config.settings[settingID]
274 286
275 -
276 - // Handle the section tabs (ex: Layout | Fonts | Colors)
277 - (
278 - function() {
279 - var $navs = $( '.js-section-navigation' );
280 -
281 - $navs.each( function() {
282 - var $nav = $( this );
283 - var $title = $nav.parents( '.accordion-section-content' ).find( '.customize-section-title' );
284 -
285 - $nav.closest( '.customize-control' ).addClass( 'screen-reader-text' );
286 - $title.append( $nav ).parent().addClass( 'has-nav' );
287 - } );
288 -
289 - $( '.js-section-navigation a' ).on( 'click', function( e ) {
290 - e.preventDefault();
291 -
292 - var $sidebar = $( this ).parents( '.customize-pane-child' );
293 - var $parent = $( this ).parents( '.accordion-section-content' );
294 - var href = $.attr( this, 'href' );
295 -
296 - if ( href != '#' ) {
297 - $sidebar.animate( {
298 - scrollTop: $( $.attr( this, 'href' ) ).position().top - $parent.find( '.customize-section-title' ).outerHeight()
299 - }, 500 );
300 - }
301 - } );
302 - }
303 - )();
304 -
305 - (
306 - function() {
307 - // Close a font field when clicking on another field
308 - $( '.customify_font_tooltip' ).on( 'click', function() {
309 - if ( $( this ).prop( 'checked' ) === true ) {
310 - $( '.customify_font_tooltip' ).prop( 'checked', false );
311 - $( this ).prop( 'checked', true );
312 - }
313 - } );
314 - }
315 - )();
316 -
317 - // Handle the Style Manager user feedback logic.
318 - var $styleManagerUserFeedbackModal = $('#style-manager-user-feedback-modal');
319 - if ( $styleManagerUserFeedbackModal.length ) {
320 - var $styleManagerUserFeedbackForm = $styleManagerUserFeedbackModal.find('form'),
321 - $styleManagerUserFeedbackCloseBtn = $styleManagerUserFeedbackModal.find('.close'),
322 - $styleManagerUserFeedbackFirstStep = $styleManagerUserFeedbackModal.find('.first-step'),
323 - $styleManagerUserFeedbackSecondStep = $styleManagerUserFeedbackModal.find('.second-step'),
324 - $styleManagerUserFeedbackThanksStep = $styleManagerUserFeedbackModal.find('.thanks-step'),
325 - $styleManagerUserFeedbackErrorStep = $styleManagerUserFeedbackModal.find('.error-step'),
326 - styleManagerUserFeedbackModalShown = false,
327 - styleManagerColorPaletteChanged = false;
328 -
329 - // Handle when to open the modal.
330 - api.bind('saved', function () {
331 - // We will only show the modal once per Customizer session.
332 - if (!styleManagerUserFeedbackModalShown && styleManagerColorPaletteChanged) {
333 - $('body').addClass('modal-open');
334 - styleManagerUserFeedbackModalShown = true;
335 - }
336 - });
337 -
338 - // Handle the color palette changed info update.
339 - const colorPaletteSetting = api( 'sm_color_palette' );
340 - if ( !_.isUndefined(colorPaletteSetting) ) {
341 - colorPaletteSetting.bind( function( new_value, old_value ) {
342 - if ( new_value != old_value ) {
343 - styleManagerColorPaletteChanged = true;
344 - }
345 - } )
346 - }
347 - const colorPaletteVariationSetting = api( 'sm_color_palette_variation' );
348 - if ( !_.isUndefined(colorPaletteVariationSetting) ) {
349 - colorPaletteVariationSetting.bind( function( new_value, old_value ) {
350 - if ( new_value != old_value ) {
351 - styleManagerColorPaletteChanged = true;
352 - }
353 - } )
354 - }
355 -
356 - // Handle the modal submit.
357 - $styleManagerUserFeedbackForm.on('submit', function (event) {
358 - event.preventDefault();
359 -
360 - let $form = $(event.target);
361 -
362 - let data = {
363 - action: 'customify_style_manager_user_feedback',
364 - nonce: customify_settings.style_manager_user_feedback_nonce,
365 - type: $form.find('input[name=type]').val(),
366 - rating: $form.find('input[name=rating]:checked').val(),
367 - message: $form.find('textarea[name=message]').val()
368 - };
369 -
370 - $.post(
371 - customify_settings.ajax_url,
372 - data,
373 - function (response) {
374 - if (true === response.success) {
375 - $styleManagerUserFeedbackFirstStep.hide();
376 - $styleManagerUserFeedbackSecondStep.hide();
377 - $styleManagerUserFeedbackThanksStep.show();
378 - $styleManagerUserFeedbackErrorStep.hide();
379 - } else {
380 - $styleManagerUserFeedbackFirstStep.hide();
381 - $styleManagerUserFeedbackSecondStep.hide();
382 - $styleManagerUserFeedbackThanksStep.hide();
383 - $styleManagerUserFeedbackErrorStep.show();
384 - }
385 - }
386 - );
387 - });
388 -
389 - $styleManagerUserFeedbackForm.find('input[name=rating]').on('change', function (event) {
390 - // Leave everything in working order
391 - setTimeout(function () {
392 - $styleManagerUserFeedbackSecondStep.show();
393 - }, 300);
394 -
395 - let rating = $styleManagerUserFeedbackForm.find('input[name=rating]:checked').val();
396 -
397 - $styleManagerUserFeedbackForm.find('.rating-placeholder').text(rating);
398 - });
399 -
400 - $styleManagerUserFeedbackCloseBtn.on('click', function (event) {
401 - event.preventDefault();
402 -
403 - $('body').removeClass('modal-open');
404 -
405 - // Leave everything in working order
406 - setTimeout(function () {
407 - $styleManagerUserFeedbackFirstStep.show();
408 - $styleManagerUserFeedbackSecondStep.hide();
409 - $styleManagerUserFeedbackThanksStep.hide();
410 - $styleManagerUserFeedbackErrorStep.hide();
411 - }, 300);
412 - });
287 + if (!_.isUndefined(setting) && !_.isUndefined(setting.default)) {
288 + apiSetSettingValue(settingID, setting.default)
413 289 }
414 - } );
290 + })
291 + }
292 + })
293 + }
294 + }
415 295
416 - const customifyHandleRangeFields = function( el ) {
296 + function onResetSection (e) {
297 + e.preventDefault()
417 298
418 - // For each range input add a number field (for preview mainly - but it can also be used for input)
419 - $( el ).find( 'input[type="range"]' ).each( function() {
420 - if ( ! $( this ).siblings( '.range-value' ).length ) {
421 - var $clone = $( this ).clone();
299 + const sectionID = $(this).data('section'),
300 + section = api.section(sectionID),
301 + controls = section.controls()
422 302
423 - $clone
424 - .attr( 'type', 'number' )
425 - .attr( 'class', 'range-value' )
426 - .removeAttr( 'data-field' );
303 + const iAgree = confirm(customify.l10n.resetSectionConfirmMessage)
427 304
428 - $( this ).after( $clone );
429 - }
305 + if (!iAgree) {
306 + return
307 + }
430 308
431 - // Update the number field when changing the range
432 - $( this ).on( 'change', function() {
433 - $( this ).siblings( '.range-value' ).val( $( this ).val() );
434 - } );
309 + if (controls.length > 0) {
310 + $.each(controls, function (key, ctrl) {
311 + const setting_id = ctrl.id.replace('_control', ''),
312 + setting = customify.config.settings[setting_id]
435 313
436 - // And the other way around, update the range field when changing the number
437 - $( $clone ).on( 'change', function() {
438 - $( this ).siblings( 'input[type="range"]' ).val( $( this ).val() );
439 - } );
440 - } );
441 - };
314 + if (!_.isUndefined(setting) && !_.isUndefined(setting.default)) {
315 + apiSetSettingValue(setting_id, setting.default)
316 + }
317 + })
318 + }
319 + }
442 320
443 - /**
444 - * This function will search for all the interdependend fields and make a bound between them.
445 - * So whenever a target is changed, it will take actions to the dependent fields.
446 - * @TODO this is still written in a barbaric way, refactor when needed
447 - */
448 - var customifyFoldingFields = function() {
321 + function handleSectionTabs () {
322 + const $navs = $('.js-section-navigation')
449 323
450 - if ( _.isUndefined( customify_settings ) || _.isUndefined( customify_settings.settings ) ) {
451 - return; // bail
452 - }
324 + $navs.each(function () {
325 + const $nav = $(this)
326 + const $title = $nav.parents('.accordion-section-content').find('.customize-section-title')
327 + const $parent = $nav.closest('.customize-control')
453 328
454 - /**
455 - * Let's iterate through all the customify settings and gather all the fields that have a "show_if"
456 - * property set.
457 - *
458 - * At the end `targets` will hold a list of [ target : [field, field,...], ... ]
459 - * so when a target is changed we will change all the fields.
460 - */
461 - var targets = {};
329 + $nav.appendTo($title)
330 + $title.parent().addClass('has-nav')
331 + $parent.addClass('screen-reader-text')
332 + })
462 333
463 - $.fn.reactor.defaults.compliant = function() {
464 - $( this ).slideDown();
465 - // $(this).animate({opacity: 1});
466 - $( this ).find( ':disabled' ).attr( {disabled: false} );
467 - };
334 + $('.js-section-navigation a').on('click', function (e) {
335 + e.preventDefault()
468 336
469 - $.fn.reactor.defaults.uncompliant = function() {
470 - $( this ).slideUp();
471 - // $(this).animate({opacity: 0.25});
472 - $( this ).find( ':enabled' ).attr( {disabled: true} );
473 - };
337 + const $this = $(this)
338 + const $sidebar = $this.parents('.wp-full-overlay-sidebar-content')
339 + const $parent = $this.parents('.accordion-section-content')
340 + const href = $this.attr('href')
474 341
475 - var IS = $.extend( {}, $.fn.reactor.helpers );
342 + if (href !== '#') {
343 + const actionsHeight = $('#customize-header-actions').outerHeight()
344 + const titleHeight = $parent.find('.customize-section-title').outerHeight()
345 + const $target = $(href)
346 + const offset = $target.position().top
476 347
477 - var bind_folding_events = function( parent_id, field, relation ) {
348 + $sidebar.animate({scrollTop: offset - titleHeight - actionsHeight}, 500)
349 + }
350 + })
351 + }
478 352
479 - var key = null;
353 + const getConnectedFieldsCallback = function (parentSettingData, parentSettingID) {
354 + return function (newValue, oldValue) {
355 + _.each(parentSettingData.connected_fields, function (connectedFieldData) {
356 + if (_.isUndefined(connectedFieldData) || _.isUndefined(connectedFieldData.setting_id) || !_.isString(connectedFieldData.setting_id)) {
357 + return
358 + }
359 + const setting = api(connectedFieldData.setting_id)
360 + if (_.isUndefined(setting)) {
361 + return
362 + }
363 + setting.set(newValue)
364 + })
365 + }
366 + }
480 367
481 - if ( _.isString( field ) ) {
482 - key = field;
483 - } else if ( ! _.isUndefined( field.id ) ) {
484 - key = field.id;
485 - } else if ( isString( field[0] ) ) {
486 - key = field[0];
487 - } else {
488 - return; // no key, no fun
489 - }
368 + const bindConnectedFields = function () {
369 + _.each(api.settings.settings, function (parentSettingData, parentSettingID) {
370 + // We don't want to handle the binding of the Style Manager settings
371 + if (typeof customify.colorPalettes !== 'undefined'
372 + && typeof customify.colorPalettes.masterSettingIds !== 'undefined'
373 + && _.includes(customify.colorPalettes.masterSettingIds, parentSettingID)) {
374 + return
375 + }
376 + if (typeof customify.fontPalettes !== 'undefined'
377 + && typeof customify.fontPalettes.masterSettingIds !== 'undefined'
378 + && _.includes(customify.fontPalettes.masterSettingIds, parentSettingID)) {
379 + return
380 + }
490 381
491 - var value = 1, // by default we use 1 the most used value for checkboxes or inputs
492 - compare = '==', // ... ye
493 - action = "show",
494 - between = [0, 1]; // can only be `show` or `hide`
382 + const parent_setting = api(parentSettingID)
383 + if (typeof parentSettingData.connected_fields !== 'undefined') {
384 + customify.connectedFieldsCallbacks[parentSettingID] = getConnectedFieldsCallback(parentSettingData, parentSettingID)
385 + parent_setting.bind(customify.connectedFieldsCallbacks[parentSettingID])
386 + }
387 + })
388 + }
495 389
496 - var target_key = customify_settings.options_name + '[' + key + ']';
390 + /**
391 + * This function will search for all the interdependend fields and make a bound between them.
392 + * So whenever a target is changed, it will take actions to the dependent fields.
393 + * @TODO this is still written in a barbaric way, refactor when needed
394 + */
395 + const customifyFoldingFields = function () {
497 396
498 - var target_type = customify_settings.settings[target_key].type;
397 + if (_.isUndefined(customify.config) || _.isUndefined(customify.config.settings)) {
398 + return // bail
399 + }
499 400
500 - // we support the usual syntax like a config array like `array( 'id' => $id, 'value' => $value, 'compare' => $compare )`
501 - // but we also support a non-associative array like `array( $id, $value, $compare )`
502 - if ( ! _.isUndefined( field.value ) ) {
503 - value = field.value;
504 - } else if ( ! _.isUndefined( field[1] ) && ! _.isString( field[1] ) ) {
505 - value = field[1];
506 - }
401 + $.fn.reactor.defaults.compliant = function () {
402 + $(this).slideDown()
403 + $(this).find(':disabled').attr({disabled: false})
404 + }
507 405
508 - if ( ! _.isUndefined( field.compare ) ) {
509 - compare = field.compare;
510 - } else if ( ! _.isUndefined( field[2] ) ) {
511 - compare = field[2];
512 - }
406 + $.fn.reactor.defaults.uncompliant = function () {
407 + $(this).slideUp()
408 + $(this).find(':enabled').attr({disabled: true})
409 + }
513 410
514 - if ( ! _.isUndefined( field.action ) ) {
515 - action = field.action;
516 - } else if ( ! _.isUndefined( field[3] ) ) {
517 - action = field[3];
518 - }
411 + let IS = $.extend({}, $.fn.reactor.helpers)
519 412
520 - // a field can also overwrite the parent relation
521 - if ( ! _.isUndefined( field.relation ) ) {
522 - action = field.relation;
523 - } else if ( ! _.isUndefined( field[4] ) ) {
524 - action = field[4];
525 - }
413 + const bindFoldingEvents = function (parentID, field, relation) {
526 414
527 - if ( ! _.isUndefined( field.between ) ) {
528 - between = field.between;
529 - }
415 + let key = null
530 416
531 - /**
532 - * Now for each target we have, we will bind a change event to hide or show the dependent fields
533 - */
534 - var target_selector = '[data-customize-setting-link="' + customify_settings.options_name + '[' + key + ']"]';
417 + if (_.isString(field)) {
418 + key = field
419 + } else if (!_.isUndefined(field.id)) {
420 + key = field.id
421 + } else if (_.isString(field[0])) {
422 + key = field[0]
423 + } else {
424 + return // no key, no fun
425 + }
535 426
536 - switch ( target_type ) {
537 - case 'checkbox':
538 - $( parent_id ).reactIf( target_selector, function() {
539 - return $( this ).is( ':checked' ) == value;
540 - } );
541 - break;
427 + let value = 1, // by default we use 1 the most used value for checkboxes or inputs
428 + between = [0, 1] // can only be `show` or `hide`
542 429
543 - case 'radio':
544 - case 'radio_image':
430 + const target_key = customify.config.options_name + '[' + key + ']'
431 + const target_type = customify.config.settings[target_key].type
545 432
546 - // in case of an array of values we use the ( val in array) condition
547 - if ( _.isObject( value ) ) {
548 - $( parent_id ).reactIf( target_selector, function() {
549 - return (
550 - value.indexOf( $( target_selector + ':checked' ).val() ) !== - 1
551 - );
552 - } );
553 - } else { // in any other case we use a simple == comparison
554 - $( parent_id ).reactIf( target_selector, function() {
555 - return $( target_selector + ':checked' ).val() == value;
556 - } );
557 - }
558 - break;
433 + // we support the usual syntax like a config array like `array( 'id' => $id, 'value' => $value, 'compare' => $compare )`
434 + // but we also support a non-associative array like `array( $id, $value, $compare )`
435 + if (!_.isUndefined(field.value)) {
436 + value = field.value
437 + } else if (!_.isUndefined(field[1]) && !_.isString(field[1])) {
438 + value = field[1]
439 + }
559 440
560 - case 'range':
561 - var x = IS.Between( between[0], between[1] );
441 + if (!_.isUndefined(field.between)) {
442 + between = field.between
443 + }
562 444
563 - $( parent_id ).reactIf( target_selector, x );
564 - break;
445 + /**
446 + * Now for each target we have, we will bind a change event to hide or show the dependent fields
447 + */
448 + const target_selector = '[data-customize-setting-link="' + customify.config.options_name + '[' + key + ']"]'
565 449
566 - default:
567 - // in case of an array of values we use the ( val in array) condition
568 - if ( _.isObject( value ) ) {
569 - $( parent_id ).reactIf( target_selector, function() {
570 - return (
571 - value.indexOf( $( target_selector ).val() ) !== - 1
572 - );
573 - } );
574 - } else { // in any other case we use a simple == comparison
575 - $( parent_id ).reactIf( target_selector, function() {
576 - return $( target_selector ).val() == value;
577 - } );
578 - }
579 - break;
580 - }
450 + switch (target_type) {
451 + case 'checkbox':
452 + $(parentID).reactIf(target_selector, function () {
453 + return $(this).is(':checked') == value
454 + })
455 + break
581 456
582 - $( target_selector ).trigger( 'change' );
583 - $( '.reactor' ).trigger( 'change.reactor' ); // triggers all events on load
584 - };
457 + case 'radio':
458 + case 'sm_radio':
459 + case 'sm_switch':
460 + case 'radio_image':
461 + case 'radio_html':
585 462
586 - $.each( customify_settings.settings, function( id, field ) {
587 - /**
588 - * Here we have the id of the fields. but we know for sure that we just need his parent selector
589 - * So we just create it
590 - */
591 - var parent_id = id.replace( '[', '-' );
592 - parent_id = parent_id.replace( ']', '' );
593 - parent_id = '#customize-control-' + parent_id + '_control';
463 + // in case of an array of values we use the ( val in array) condition
464 + if (_.isObject(value)) {
465 + value = _.toArray(value)
466 + $(parentID).reactIf(target_selector, function () {
467 + return (
468 + value.indexOf($(target_selector + ':checked').val()) !== -1
469 + )
470 + })
471 + } else { // in any other case we use a simple == comparison
472 + $(parentID).reactIf(target_selector, function () {
473 + return $(target_selector + ':checked').val() == value
474 + })
475 + }
476 + break
594 477
595 - // get only the fields that have a 'show_if' property
596 - if ( field.hasOwnProperty( 'show_if' ) ) {
597 - var relation = 'AND';
478 + case 'range':
479 + const x = IS.Between(between[0], between[1])
598 480
599 - if ( ! _.isUndefined( field.show_if.relation ) ) {
600 - relation = field.show_if.relation;
601 - // remove the relation property, we need the config to be array based only
602 - delete field.show_if.relation;
603 - }
481 + $(parentID).reactIf(target_selector, x)
482 + break
604 483
605 - /**
606 - * The 'show_if' can be a simple array with one target like: [ id, value, comparison, action ]
607 - * Or it could be an array of multiple targets and we need to process both cases
608 - */
484 + default:
485 + // in case of an array of values we use the ( val in array) condition
486 + if (_.isObject(value)) {
487 + value = _.toArray(value)
488 + $(parentID).reactIf(target_selector, function () {
489 + return (
490 + value.indexOf($(target_selector).val()) !== -1
491 + )
492 + })
493 + } else { // in any other case we use a simple == comparison
494 + $(parentID).reactIf(target_selector, function () {
495 + return $(target_selector).val() == value
496 + })
497 + }
498 + break
499 + }
609 500
610 - if ( ! _.isUndefined( field.show_if.id ) ) {
611 - bind_folding_events( parent_id, field.show_if, relation );
612 - } else if ( _.isObject( field.show_if ) ) {
613 - $.each( field.show_if, function( i, j ) {
614 - bind_folding_events( parent_id, j, relation );
615 - } );
616 - }
617 - }
618 - } );
619 - };
501 + $(target_selector).trigger('change', ['customify'])
502 + $('.reactor').trigger('change.reactor') // triggers all events on load
503 + }
620 504
621 - var get_typography_font_family = function( $el ) {
505 + $.each(customify.config.settings, function (id, field) {
506 + /**
507 + * Here we have the id of the fields. but we know for sure that we just need his parent selector
508 + * So we just create it
509 + */
510 + let parentID = id.replace('[', '-')
511 + parentID = parentID.replace(']', '')
512 + parentID = '#customize-control-' + parentID + '_control'
622 513
623 - var font_family_value = $el.val();
624 - // first time this will not be a json so catch that error
625 - try {
626 - font_family_value = JSON.parse( font_family_value );
627 - } catch ( e ) {
628 - return {font_family: font_family_value};
629 - }
514 + // get only the fields that have a 'show_if' property
515 + if (field.hasOwnProperty('show_if')) {
516 + let relation = 'AND'
630 517
631 - if ( ! _.isUndefined( font_family_value.font_family ) ) {
632 - return font_family_value.font_family;
633 - }
518 + if (!_.isUndefined(field.show_if.relation)) {
519 + relation = field.show_if.relation
520 + // remove the relation property, we need the config to be array based only
521 + delete field.show_if.relation
522 + }
634 523
635 - return false;
636 - };
524 + /**
525 + * The 'show_if' can be a simple array with one target like: [ id, value, comparison, action ]
526 + * Or it could be an array of multiple targets and we need to process both cases
527 + */
637 528
638 - // get each typography field and bind events
639 - // @todo Are we still using the typography field since we have the font field?
640 - var prepare_typography_field = function() {
529 + if (!_.isUndefined(field.show_if.id)) {
530 + bindFoldingEvents(parentID, field.show_if, relation)
531 + } else if (_.isObject(field.show_if)) {
532 + $.each(field.show_if, function (i, j) {
533 + bindFoldingEvents(parentID, j, relation)
534 + })
535 + }
536 + }
537 + })
538 + }
641 539
642 - var $typos = $( '.customify_typography_font_family' );
540 + /**
541 + * Set a setting value.
542 + *
543 + * Mostly used for resetting settings (via the reset buttons) but also for the preset (legacy) field.
544 + *
545 + * @param settingID
546 + * @param value
547 + */
548 + const apiSetSettingValue = function (settingID, value) {
549 + const setting = api(settingID),
550 + field = $('[data-customize-setting-link="' + settingID + '"]'),
551 + fieldClass = $(field).parent().attr('class')
643 552
644 - $typos.each( function() {
645 - var font_family_select = this,
646 - $input = $( font_family_select ).siblings( '.customify_typography_values' );
647 - // on change
648 - $( font_family_select ).on( 'change', function() {
649 - update_siblings_selects( font_family_select );
650 - $input.trigger( 'change' );
651 - } );
652 - update_siblings_selects( font_family_select );
653 - } );
654 - };
553 + if (!_.isUndefined(fieldClass) && fieldClass === 'font-options__wrapper') {
655 554
656 - var api_set_setting_value = function( setting_id, value ) {
657 - let setting = api( setting_id ),
658 - field = $( '[data-customize-setting-link="' + setting_id + '"]' ),
659 - field_class = $( field ).parent().attr( 'class' );
555 + // if the value is a simple string it must be the font family
556 + if (_.isString(value)) {
557 + setting.set({'font_family': value})
558 + } else if (_.isObject(value)) {
559 + const standardValue = {}
560 + // We will process each font property and update it
561 + _.each(value, function (val, key) {
562 + // We need to map the keys to the data attributes we are using - I know :(
563 + let mappedKey = key
564 + switch (key) {
565 + case 'font-family':
566 + mappedKey = 'font_family'
567 + break
568 + case 'font-size':
569 + mappedKey = 'font_size'
570 + break
571 + case 'font-weight':
572 + mappedKey = 'font_variant'
573 + break
574 + case 'letter-spacing':
575 + mappedKey = 'letter_spacing'
576 + break
577 + case 'text-transform':
578 + mappedKey = 'text_transform'
579 + break
580 + default:
581 + break
582 + }
660 583
661 - // Legacy field type
662 - if ( ! _.isUndefined( field_class ) && field_class === 'customify_typography' ) {
584 + standardValue[mappedKey] = val
585 + })
663 586
664 - let family_select = field.siblings( 'select' );
587 + setting.set(standardValue)
588 + }
589 + } else {
590 + setting.set(value)
591 + }
592 + }
665 593
666 - if ( _.isString( value ) ) {
667 - let this_option = family_select.find( 'option[value="' + value + '"]' );
668 - $( this_option[0] ).attr( 'selected', 'selected' );
669 - update_siblings_selects( family_select );
670 - } else if ( _.isObject( value ) ) {
671 - let this_family_option = family_select.find( 'option[value="' + value['font_family'] + '"]' );
594 + const handlePreviewIframe = function () {
595 + api.previewer.bind('synced', function () {
596 + scaleIframe()
672 597
673 - $( this_family_option[0] ).attr( 'selected', 'selected' );
598 + api.previewedDevice.bind(scaleIframe)
599 + $window.on('resize', scaleIframe)
600 + })
674 601
675 - update_siblings_selects( this_family_option );
602 + $('.collapse-sidebar').on('click', function () {
603 + setTimeout(scaleIframe, 300)
604 + })
605 + }
676 606
677 - setTimeout( function() {
678 - let weight_select = field.parent().siblings( '.options' ).find( '.customify_typography_font_weight' ),
679 - this_weight_option = weight_select.find( 'option[value="' + value['selected_variants'] + '"]' );
607 + const scaleIframe = function () {
608 + const $previewIframe = $('.wp-full-overlay')
680 609
681 - $( this_weight_option[0] ).attr( 'selected', 'selected' );
610 + // remove CSS properties that may have been previously added
611 + $previewIframe.find('iframe').css({
612 + width: '',
613 + height: '',
614 + transformOrigin: '',
615 + transform: ''
616 + })
682 617
683 - update_siblings_selects( this_family_option );
618 + // scaling of the site preview should be done only in desktop preview mode
619 + if (api.previewedDevice.get() !== 'desktop') {
620 + return
621 + }
684 622
685 - weight_select.trigger( 'change' );
686 - }, 300 );
687 - }
623 + const iframeWidth = $previewIframe.width()
624 + const windowWidth = $window.width()
625 + const windowHeight = $window.height()
688 626
689 - family_select.trigger( 'change' );
627 + // get the ratio between the site preview and actual browser width
628 + const scale = windowWidth / iframeWidth
690 629
691 - } else if ( ! _.isUndefined( field_class ) && field_class === 'font-options__wrapper' ) {
630 + // for an accurate preview at resolutions where media queries may intervene
631 + // increase the width of the iframe and use CSS transforms to scale it back down
632 + if (iframeWidth > 720 && iframeWidth < 1100) {
633 + $previewIframe.find('iframe').css({
634 + width: iframeWidth * scale,
635 + height: windowHeight * scale,
636 + transformOrigin: 'left top',
637 + transform: 'scale(' + 1 / scale + ')'
638 + })
639 + }
640 + }
692 641
693 - // if the value is a simple string it must be the font family
694 - if ( _.isString( value ) ) {
695 - let option = field.parent().find( 'option[value="' + value + '"]' );
642 + /** Modules **/
696 643
697 - option.attr( 'selected', 'selected' );
698 - // option.parents('select').trigger('change');
699 - } else if ( _.isObject( value ) ) {
700 - // Find the options list wrapper
701 - let optionsList = field.parent().children( '.font-options__options-list' );
644 + const customifyBackgroundJsControl = (
645 + function () {
646 + 'use strict'
702 647
703 - if ( optionsList.length ) {
704 - // We will process each font property and update it
705 - _.each( value, function( val, key ) {
706 - // We need to map the keys to the data attributes we are using - I know :(
707 - let mappedKey = key;
708 - switch ( key ) {
709 - case 'font-family':
710 - mappedKey = 'font_family';
711 - break;
712 - case 'font-size':
713 - mappedKey = 'font_size';
714 - break;
715 - case 'font-weight':
716 - mappedKey = 'selected_variants';
717 - break;
718 - case 'letter-spacing':
719 - mappedKey = 'letter_spacing';
720 - break;
721 - case 'text-transform':
722 - mappedKey = 'text_transform';
723 - break;
724 - default:
725 - break;
726 - }
727 - let subField = optionsList.find( '[data-field="' + mappedKey + '"]' );
728 - if ( subField.length ) {
729 - subField.val( val );
730 - subField.trigger( 'change' );
731 - }
732 - } );
733 - }
734 - }
648 + function init () {
649 + // Upload media button
650 + $('.customize-control-custom_background .background_upload_button').unbind().on('click', function (event) {
651 + addImage(event, $(this).parents('.customize-control-custom_background:first'))
652 + })
735 653
736 - } else {
737 - setting.set( value );
738 - }
739 - };
654 + // Remove the image button
655 + $('.customize-control-custom_background .remove-image, .customize-control-custom_background .remove-file').unbind('click').on('click', function (e) {
656 + removeImage($(this).parents('.customize-control-custom_background:first'))
657 + })
658 + }
740 659
741 - var update_siblings_selects = function( font_select ) {
742 - var selected_font = $( font_select ).val(),
743 - $input = $( font_select ).siblings( '.customify_typography_values' ),
744 - current_val = $input.attr( 'value' );
660 + // Add a file via the wp.media function
661 + function addImage (event, selector) {
662 + // Stop this from propagating.
663 + event.preventDefault()
745 664
746 - if ( current_val === '[object Object]' ) {
747 - current_val = $input.data( 'default' );
748 - } else if ( _.isString( current_val ) && ! isJsonString( current_val ) && current_val.substr( 0, 1 ) == '[' ) {
749 - // a rare case when the value isn't a json but is a representative string like [family,weight]
750 - current_val = current_val.split( ',' );
751 - var new_current_value = {};
752 - if ( ! _.isUndefined( current_val[0] ) ) {
753 - new_current_value['font_family'] = current_val[0];
754 - }
665 + let frame
666 + const $thisElement = $(this)
755 667
756 - if ( ! _.isUndefined( current_val[1] ) ) {
757 - new_current_value['selected_variants'] = current_val[1];
758 - }
668 + // If the media frame already exists, reopen it.
669 + if (frame) {
670 + frame.open()
671 + return
672 + }
759 673
760 - current_val = JSON.stringify( new_current_value );
761 - }
674 + // Create the media frame.
675 + frame = wp.media({
676 + multiple: false,
677 + library: {
678 + //type: 'image' //Only allow images
679 + },
680 + // Set the title of the modal.
681 + title: $thisElement.data('choose'),
762 682
763 - var $font_weight = $( font_select ).parent().siblings( 'ul.options' ).find( '.customify_typography_font_weight' ),
764 - $font_subsets = $( font_select ).parent().siblings( 'ul.options' ).find( '.customify_typography_font_subsets' );
683 + // Customize the submit button.
684 + button: {
685 + // Set the text of the button.
686 + text: $thisElement.data('update')
687 + // Tell the button not to close the modal, since we're
688 + // going to refresh the page when the image is selected.
689 + }
690 + })
765 691
766 - try {
767 - current_val = JSON.parse( decodeURIComponent( current_val ) );
768 - } catch ( e ) {
692 + // When an image is selected, run a callback.
693 + frame.on('select', function () {
694 + // Grab the selected attachment.
695 + const attachment = frame.state().get('selection').first()
696 + frame.close()
769 697
770 - // in case of an error, force the rebuild of the json
771 - if ( _.isUndefined( $( font_select ).data( 'bound_once' ) ) ) {
698 + if (attachment.attributes.type !== 'image') {
699 + return
700 + }
772 701
773 - $( font_select ).data( 'bound_once', true );
702 + selector.find('.customify_background_input.background-image').val(attachment.attributes.url)
774 703
775 - $( font_select ).change();
776 - $font_weight.change();
777 - $font_subsets.change();
778 - }
779 - }
704 + selector.find('.upload').attr('value', attachment.attributes.url)
705 + selector.find('.upload-id').attr('value', attachment.attributes.id)
706 + selector.find('.upload-height').attr('value', attachment.attributes.height)
707 + selector.find('.upload-width').attr('value', attachment.attributes.width)
780 708
781 - // first try to get the font from sure sources, not from the recommended list.
782 - var option_data = $( font_select ).find( ':not(optgroup[label=Recommended]) option[value="' + selected_font + '"]' );
783 - // however, if there isn't an option found, get what you can
784 - if ( option_data.length < 1 ) {
785 - option_data = $( font_select ).find( 'option[value="' + selected_font + '"]' );
786 - }
709 + let thumbSrc = attachment.attributes.url
710 + if (!_.isUndefined(attachment.attributes.sizes) && !_.isUndefined(attachment.attributes.sizes.thumbnail)) {
711 + thumbSrc = attachment.attributes.sizes.thumbnail.url
712 + } else if (!_.isUndefined(attachment.attributes.sizes)) {
713 + let height = attachment.attributes.height
714 + for (let key in attachment.attributes.sizes) {
715 + const object = attachment.attributes.sizes[key]
716 + if (object.height < height) {
717 + height = object.height
718 + thumbSrc = object.url
719 + }
720 + }
721 + } else {
722 + thumbSrc = attachment.attributes.icon
723 + }
787 724
788 - if ( option_data.length > 0 ) {
725 + if (!selector.find('.upload').hasClass('noPreview')) {
726 + selector.find('.preview_screenshot').empty().hide().append('<img class="preview_image" src="' + thumbSrc + '">').slideDown('fast')
727 + }
728 + selector.find('.remove-image').removeClass('hide') // Show "Remove" button
729 + selector.find('.customify_background_select').removeClass('hide') // Show background selects
789 730
790 - var font_type = option_data.data( 'type' ),
791 - value_to_add = {'type': font_type, 'font_family': selected_font},
792 - variants = null,
793 - subsets = null;
731 + updateData(selector)
732 + })
794 733
795 - if ( font_type == 'std' ) {
796 - variants = {
797 - 0: '100',
798 - 1: '200',
799 - 3: '300',
800 - 4: '400',
801 - 5: '500',
802 - 6: '600',
803 - 7: '700',
804 - 8: '800',
805 - 9: '900'
806 - };
807 - if ( ! _.isUndefined( $( option_data[0] ).data( 'variants' ) ) ) {
808 - //maybe the variants are a JSON
809 - variants = maybeJsonParse( $( option_data[0] ).data( 'variants' ) );
810 - }
811 - } else {
812 - //maybe the variants are a JSON
813 - variants = maybeJsonParse( $( option_data[0] ).data( 'variants' ) );
734 + // Finally, open the modal.
735 + frame.open()
736 + }
814 737
815 - //maybe the subsets are a JSON
816 - subsets = maybeJsonParse( $( option_data[0] ).data( 'subsets' ) );
817 - }
738 + // Update the background data
739 + function updateData (selector) {
818 740
819 - // make the variants selector
820 - if ( ! _.isUndefined( variants ) && ! _.isNull( variants ) && ! _.isEmpty( variants ) ) {
741 + let $parent = selector.parents('.customize-control-custom_background:first')
821 742
822 - value_to_add['variants'] = variants;
823 - // when a font is selected force the first weight to load
824 - value_to_add['selected_variants'] = {0: variants[0]};
743 + if (selector.hasClass('customize-control-custom_background')) {
744 + $parent = selector
745 + }
825 746
826 - var variants_options = '',
827 - count_weights = 0;
747 + if ($parent.length > 0) {
748 + $parent = $($parent[0])
749 + } else {
750 + return
751 + }
828 752
829 - if ( _.isArray( variants ) || _.isObject( variants ) ) {
830 - // Take each variant and produce the option markup
831 - $.each( variants, function( key, el ) {
832 - var is_selected = '';
833 - if ( _.isObject( current_val.selected_variants ) && inObject( el, current_val.selected_variants ) ) {
834 - is_selected = ' selected="selected"';
835 - } else if ( _.isString( current_val.selected_variants ) && el === current_val.selected_variants ) {
836 - is_selected = ' selected="selected"';
837 - }
753 + const settingID = $parent.find('.button.background_upload_button').data('setting_id'),
754 + setting = api.instance(settingID)
838 755
839 - // initialize
840 - var variant_option_value = el,
841 - variant_option_display = el;
756 + const background_data = {}
842 757
843 - // If we are dealing with a object variant then it means things get tricky (probably it's our fault but bear with us)
844 - // This probably comes from our Fonto plugin - a font with individually named variants - hence each has its own font-family
845 - if ( _.isObject( el ) ) {
846 - //put the entire object in the variation value - we will need it when outputting the custom CSS
847 - variant_option_value = encodeURIComponent( JSON.stringify( el ) );
848 - variant_option_display = '';
758 + $parent.find('.customify_background_select, .customify_background_input').each(function () {
759 + let data = $(this).serializeArray()[0]
760 + if (data && data.name.indexOf('[background-') !== -1) {
761 + background_data[$(this).data('select_name')] = data.value
762 + }
763 + })
849 764
850 - //if we have weight and style then "compose" them into something standard
851 - if ( ! _.isUndefined( el['font-weight'] ) ) {
852 - variant_option_display += el['font-weight'];
853 - }
765 + background_data.media = {}
766 + background_data.media.id = $parent.find('.upload-id').val()
767 + background_data.media.height = $parent.find('.upload-height').val()
768 + background_data.media.width = $parent.find('.upload-width').val()
769 + background_data.media.thumbnail = $parent.find('.upload-thumbnail').val()
854 770
855 - if ( _.isString( el['font-style'] ) && $.inArray( el['font-style'].toLowerCase(), [
856 - "normal",
857 - "regular"
858 - ] ) < 0 ) { //this comparison means it hasn't been found
859 - variant_option_display += el['font-style'];
860 - }
861 - }
771 + setting.set(background_data)
772 + }
862 773
863 - variants_options += '<option value="' + variant_option_value + '"' + is_selected + '>' + variant_option_display + '</option>';
864 - count_weights ++;
865 - } );
866 - }
774 + // Update the background preview
775 + function removeImage (parent) {
776 + const selector = parent.find('.upload_button_div')
777 + // This shouldn't have been run...
778 + if (!selector.find('.remove-image').addClass('hide')) {
779 + return
780 + }
867 781
868 - if ( ! _.isUndefined( $font_weight ) ) {
869 - $font_weight.html( variants_options );
870 - // if there is no weight or just 1 we hide the weight select ... cuz is useless
871 - if ( $( font_select ).data( 'load_all_weights' ) === true || count_weights <= 1 ) {
872 - $font_weight.parent().css( 'display', 'none' );
873 - } else {
874 - $font_weight.parent().css( 'display', 'inline-block' );
875 - }
876 - }
877 - } else if ( ! _.isUndefined( $font_weight ) ) {
878 - $font_weight.parent().css( 'display', 'none' );
879 - }
782 + // Hide "Remove" button.
783 + selector.find('.remove-image').addClass('hide')
784 + parent.find('.customify_background_select').addClass('hide')
880 785
881 - // make the subsets selector
882 - if ( ! _.isUndefined( subsets ) && ! _.isNull( subsets ) && ! _.isEmpty( subsets ) ) {
786 + parent.find('.upload').val(null)
787 + parent.find('.upload-id').val(null)
788 + parent.find('.upload-height').val(null)
789 + parent.find('.upload-width').val(null)
790 + parent.find('.customify_background_input.background-image').val(null)
883 791
884 - value_to_add['subsets'] = subsets;
885 - // when a font is selected force the first subset to load
886 - value_to_add['selected_subsets'] = {0: subsets[0]};
887 - var subsets_options = '',
888 - count_subsets = 0;
889 - $.each( subsets, function( key, el ) {
890 - var is_selected = '';
891 - if ( _.isObject( current_val.selected_subsets ) && inObject( el, current_val.selected_subsets ) ) {
892 - is_selected = ' selected="selected"';
893 - }
792 + // Hide the screenshot
793 + parent.find('.preview_screenshot').slideUp()
894 794
895 - subsets_options += '<option value="' + el + '"' + is_selected + '>' + el + '</option>';
896 - count_subsets ++;
897 - } );
795 + updateData(parent)
796 + }
898 797
899 - if ( ! _.isUndefined( $font_subsets ) ) {
900 - $font_subsets.html( subsets_options );
798 + return {
799 + init: init
800 + }
801 + }
802 + )(jQuery)
901 803
902 - // if there is no subset or just 1 we hide the subsets select ... cuz is useless
903 - if ( count_subsets <= 1 ) {
904 - $font_subsets.parent().css( 'display', 'none' );
905 - } else {
906 - $font_subsets.parent().css( 'display', 'inline-block' );
907 - }
908 - }
909 - } else if ( ! _.isUndefined( $font_subsets ) ) {
910 - $font_subsets.parent().css( 'display', 'none' );
911 - }
804 + /** HELPERS **/
912 805
913 - $input.val( encodeURIComponent( JSON.stringify( value_to_add ) ) );
914 - }
915 - };
806 + const getUrlVar = function (name) {
807 + const vars = []
808 + let hash
809 + const hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&')
916 810
917 - /** Modules **/
811 + for (let i = 0; i < hashes.length; i++) {
812 + hash = hashes[i].split('=')
918 813
919 - var customifyBackgroundJsControl = (
920 - function() {
921 - "use strict";
814 + vars.push(hash[0])
815 + vars[hash[0]] = hash[1]
816 + }
922 817
923 - function init() {
924 - // Remove the image button
925 - $( '.customize-control-custom_background .remove-image, .customize-control-custom_background .remove-file' ).unbind( 'click' ).on( 'click', function( e ) {
926 - removeImage( $( this ).parents( '.customize-control-custom_background:first' ) );
927 - preview( $( this ) );
928 - return false;
929 - } );
930 -
931 - // Upload media button
932 - $( '.customize-control-custom_background .background_upload_button' ).unbind().on( 'click', function( event ) {
933 - addImage( event, $( this ).parents( '.customize-control-custom_background:first' ) );
934 - } );
935 -
936 - $( '.customify_background_select' ).on( 'change', function() {
937 - preview( $( this ) );
938 - } );
939 - }
940 -
941 - // Add a file via the wp.media function
942 - function addImage( event, selector ) {
943 -
944 - event.preventDefault();
945 -
946 - var frame;
947 - var jQueryel = jQuery( this );
948 -
949 - // If the media frame already exists, reopen it.
950 - if ( frame ) {
951 - frame.open();
952 - return;
953 - }
954 -
955 - // Create the media frame.
956 - frame = wp.media( {
957 - multiple: false,
958 - library: {
959 - //type: 'image' //Only allow images
960 - },
961 - // Set the title of the modal.
962 - title: jQueryel.data( 'choose' ),
963 -
964 - // Customize the submit button.
965 - button: {
966 - // Set the text of the button.
967 - text: jQueryel.data( 'update' )
968 - // Tell the button not to close the modal, since we're
969 - // going to refresh the page when the image is selected.
970 - }
971 - } );
972 -
973 - // When an image is selected, run a callback.
974 - frame.on( 'select', function() {
975 - // Grab the selected attachment.
976 - var attachment = frame.state().get( 'selection' ).first();
977 - frame.close();
978 -
979 - if ( attachment.attributes.type !== "image" ) {
980 - return;
981 - }
982 -
983 - selector.find( '.upload' ).attr( 'value', attachment.attributes.url );
984 - selector.find( '.upload-id' ).attr( 'value', attachment.attributes.id );
985 - selector.find( '.upload-height' ).attr( 'value', attachment.attributes.height );
986 - selector.find( '.upload-width' ).attr( 'value', attachment.attributes.width );
987 -
988 - var thumbSrc = attachment.attributes.url;
989 - if ( ! _.isUndefined( attachment.attributes.sizes ) && ! _.isUndefined( attachment.attributes.sizes.thumbnail ) ) {
990 - thumbSrc = attachment.attributes.sizes.thumbnail.url;
991 - } else if ( ! _.isUndefined( attachment.attributes.sizes ) ) {
992 - var height = attachment.attributes.height;
993 - for ( var key in attachment.attributes.sizes ) {
994 - var object = attachment.attributes.sizes[key];
995 - if ( object.height < height ) {
996 - height = object.height;
997 - thumbSrc = object.url;
998 - }
999 - }
1000 - } else {
1001 - thumbSrc = attachment.attributes.icon;
1002 - }
1003 -
1004 - selector.find( '.customify_background_input.background-image' ).val( attachment.attributes.url );
1005 -
1006 - if ( ! selector.find( '.upload' ).hasClass( 'noPreview' ) ) {
1007 - selector.find( '.preview_screenshot' ).empty().hide().append( '<img class="preview_image" src="' + thumbSrc + '">' ).slideDown( 'fast' );
1008 - }
1009 - //selector.find('.media_upload_button').unbind();
1010 - selector.find( '.remove-image' ).removeClass( 'hide' );//show "Remove" button
1011 - selector.find( '.customify_background_select' ).removeClass( 'hide' );//show "Remove" button
1012 -
1013 - preview( selector );
1014 - } );
1015 -
1016 - // Finally, open the modal.
1017 - frame.open();
1018 - }
1019 -
1020 - // Update the background preview
1021 - function preview( selector ) {
1022 -
1023 - var $parent = selector.parents( '.customize-control-custom_background:first' );
1024 -
1025 - if ( selector.hasClass( 'customize-control-custom_background' ) ) {
1026 - $parent = selector;
1027 - }
1028 -
1029 - if ( $parent.length > 0 ) {
1030 - $parent = $( $parent[0] );
1031 - } else {
1032 - return;
1033 - }
1034 -
1035 - var image_holder = $parent.find( '.background-preview' );
1036 -
1037 - if ( ! image_holder ) { // No preview present
1038 - return;
1039 - }
1040 -
1041 - var the_id = $parent.find( '.button.background_upload_button' ).data( 'setting_id' ),
1042 - this_setting = api.instance( the_id );
1043 -
1044 - var background_data = {};
1045 -
1046 - $parent.find( '.customify_background_select, .customify_background_input' ).each( function() {
1047 - var data = $( this ).serializeArray();
1048 -
1049 - data = data[0];
1050 - if ( data && data.name.indexOf( '[background-' ) != - 1 ) {
1051 -
1052 - background_data[$( this ).data( 'select_name' )] = data.value;
1053 -
1054 - //default_default[data.name] = data.value;
1055 - //if (data.name == "background-image") {
1056 - // css += data.name + ':url("' + data.value + '");';
1057 - //} else {
1058 - // css += data.name + ':' + data.value + ';';
1059 - //}
1060 - }
1061 - } );
1062 -
1063 - api.instance( the_id ).set( background_data );
1064 - //// Notify the customizer api about this change
1065 - api.trigger( 'change' );
1066 - api.previewer.refresh();
1067 -
1068 - //image_holder.attr('style', css).fadeIn();
1069 - }
1070 -
1071 - // Update the background preview
1072 - function removeImage( parent ) {
1073 - var selector = parent.find( '.upload_button_div' );
1074 - // This shouldn't have been run...
1075 - if ( ! selector.find( '.remove-image' ).addClass( 'hide' ) ) {
1076 - return;
1077 - }
1078 -
1079 - selector.find( '.remove-image' ).addClass( 'hide' );//hide "Remove" button
1080 - parent.find( '.customify_background_select' ).addClass( 'hide' );
1081 -
1082 - selector.find( '.upload' ).val( '' );
1083 - selector.find( '.upload-id' ).val( '' );
1084 - selector.find( '.upload-height' ).val( '' );
1085 - selector.find( '.upload-width' ).val( '' );
1086 - parent.find( '.customify_background_input.background-image' ).val( '' );
1087 -
1088 - var customizer_id = selector.find( '.background_upload_button' ).data( 'setting_id' ),
1089 - this_setting = api.control( customizer_id + '_control' ),
1090 - current_vals = this_setting.setting(),
1091 - screenshot = parent.find( '.preview_screenshot' ),
1092 - to_array = $.map( current_vals, function( value, index ) {
1093 - return [value];
1094 - } );
1095 -
1096 - // Hide the screenshot
1097 - screenshot.slideUp();
1098 - selector.find( '.remove-file' ).unbind();
1099 - to_array['background-image'] = '';
1100 - this_setting.setting( to_array );
1101 - }
1102 -
1103 - return {
1104 - init: init
1105 - }
1106 - }
1107 - )( jQuery );
1108 -
1109 - // This is for the Font control
1110 - var customifyFontSelect = (
1111 - function() {
1112 - const
1113 - wrapperSelector = '.font-options__wrapper',
1114 - valueHolderSelector = '.customify_font_values',
1115 - fontFamilySelector = '.customify_font_family',
1116 - fontWeightSelector = '.customify_font_weight',
1117 - fontSubsetsSelector = '.customify_font_subsets',
1118 - selectPlaceholder = "Select a font family",
1119 - weightPlaceholder = "Select a font weight",
1120 - subsetPlaceholder = "Extra Subsets";
1121 -
1122 - // We will use this to remember that we are self-updating the field from the subfields.
1123 - // We will save this info for each setting ID.
1124 - var updatingValue = {},
1125 - loadingValue = {};
1126 -
1127 - function init( wpapi ) {
1128 - let $fontFamilyFields = $( fontFamilySelector );
1129 -
1130 - // Initialize the select2 field for the font family
1131 - $fontFamilyFields.select2( {
1132 - placeholder: selectPlaceholder
1133 - } ).on( 'change', function( e ) {
1134 - let new_option = $( e.target ).find( 'option:selected' ),
1135 - wrapper = $( e.target ).closest( wrapperSelector );
1136 -
1137 - // Update the weight subfield with the new options given by the selected font family.
1138 - update_weight_field( new_option, wrapper );
1139 -
1140 - // Update the subset subfield with the new options given by the selected font family.
1141 - update_subset_field( new_option, wrapper );
1142 -
1143 - // Serialize subfield values and refresh the fonts in the preview window.
1144 - update_font_value( wrapper );
1145 - } );
1146 -
1147 - // Initialize the select2 field for the font weight
1148 - $( fontWeightSelector ).each( function( i, el ) {
1149 -
1150 - let select2_args = {
1151 - placeholder: weightPlaceholder
1152 - };
1153 -
1154 - // all this fuss is for the case when the font doesn't come with variants from PHP, like a theme_font
1155 - if ( this.options.length === 0 ) {
1156 - var wrapper = $( el ).closest( wrapperSelector ),
1157 - font = wrapper.find( fontFamilySelector ),
1158 - option = font[0].options[font[0].selectedIndex],
1159 - variants = maybeJsonParse( $( option ).data( 'variants' ) ),
1160 - data = [],
1161 - selecter_variants = $( el ).data( 'default' ) || null;
1162 -
1163 - if ( typeof variants === "undefined" ) {
1164 - $( this ).hide();
1165 - return;
1166 - }
1167 -
1168 - $.each( variants, function( index, weight ) {
1169 - let this_value = {
1170 - id: weight,
1171 - text: weight
1172 - };
1173 -
1174 - if ( selecter_variants !== null && weight == selecter_variants ) {
1175 - this_value.selected = true;
1176 - }
1177 -
1178 - data.push( this_value );
1179 - } );
1180 -
1181 - if ( data !== [] ) {
1182 - select2_args.data = data;
1183 - }
1184 - }
1185 -
1186 - $( this ).select2(
1187 - select2_args
1188 - ).on( 'change', function( e ) {
1189 - let wrapper = $( e.target ).closest( wrapperSelector );
1190 -
1191 - // Serialize subfield values and refresh the fonts in the preview window.
1192 - update_font_value( wrapper );
1193 - } );
1194 - } );
1195 -
1196 - // Initialize the select2 field for the font subsets
1197 - $( fontSubsetsSelector )
1198 - .select2( {
1199 - placeholder: subsetPlaceholder
1200 - } )
1201 - .on( 'change', function( e ) {
1202 - let wrapper = $( e.target ).closest( wrapperSelector );
1203 -
1204 - // Serialize subfield values and refresh the fonts in the preview window.
1205 - update_font_value( wrapper );
1206 - } );
1207 -
1208 - let rangers = $fontFamilyFields.parents( wrapperSelector ).find( 'input[type=range]' ),
1209 - selects = $fontFamilyFields.parents( wrapperSelector ).find( 'select' ).not( "select[class*=' select2'],select[class^='select2']" );
1210 -
1211 - // Initialize the all the regular selects in the font controls
1212 - if ( selects.length > 0 ) {
1213 - selects.on( 'change', function( e ) {
1214 - let wrapper = $( e.target ).closest( wrapperSelector );
1215 -
1216 - // Serialize subfield values and refresh the fonts in the preview window.
1217 - update_font_value( wrapper );
1218 - } );
1219 - }
1220 -
1221 - // Initialize the all the range fields in the font controls
1222 - if ( rangers.length > 0 ) {
1223 - rangers.on( 'change', function( e ) {
1224 - let wrapper = $( e.target ).closest( wrapperSelector );
1225 -
1226 - // Serialize subfield values and refresh the fonts in the preview window.
1227 - update_font_value( wrapper );
1228 -
1229 - wp.customize.previewer.send( 'font-changed' );
1230 - } );
1231 - }
1232 -
1233 - // When the previewer window is ready, render the fonts
1234 - var self = this;
1235 - wp.customize.previewer.bind( 'ready', function() {
1236 - self.render_fonts();
1237 - } );
1238 -
1239 - // Handle the reverse value direction, when the customize setting is updated and the subfields need to update their values.
1240 - $fontFamilyFields.each( function( i, el ) {
1241 - let wrapper = $( el ).closest( wrapperSelector ),
1242 - value_holder = wrapper.children( valueHolderSelector ),
1243 - setting_id = $( value_holder ).data( 'customize-setting-link' ),
1244 - setting = wp.customize( setting_id );
1245 -
1246 - setting.bind( function( newValue, oldValue ) {
1247 - if ( ! updatingValue[this.id] ) {
1248 - value_holder.val( newValue );
1249 -
1250 - load_font_value( wrapper );
1251 - }
1252 - } )
1253 - } )
1254 - }
1255 -
1256 - /**
1257 - * This function updates the data in font weight selector from the given <option> element
1258 - *
1259 - * @param option
1260 - * @param wraper
1261 - */
1262 - function update_weight_field( option, wraper ) {
1263 - let variants = $( option ).data( 'variants' ),
1264 - font_weights = wraper.find( fontWeightSelector ),
1265 - selected_variant = font_weights.data( 'default' ),
1266 - new_variants = [],
1267 - id = wraper.find( valueHolderSelector ).data( 'customizeSettingLink' );
1268 -
1269 - variants = maybeJsonParse( variants );
1270 -
1271 - if ( customify_settings.settings[id].load_all_weights || typeof variants === "undefined" || Object.keys( variants ).length < 2 ) {
1272 - font_weights.parent().hide();
1273 - } else {
1274 - font_weights.parent().show();
1275 - }
1276 -
1277 - // we need to turn the data array into a specific form like [{id:"id", text:"Text"}]
1278 - $.each( variants, function( index, variant ) {
1279 - new_variants[index] = {
1280 - 'id': variant,
1281 - 'text': variant
1282 - };
1283 -
1284 - if ( selected_variant == variant ) {
1285 - new_variants[index].selected = true;
1286 - }
1287 - } );
1288 -
1289 - // We need to clear the old select2 field and reinitialize it.
1290 - $( font_weights ).select2().empty();
1291 - $( font_weights ).select2( {
1292 - data: new_variants
1293 - } ).on( 'change', function( e ) {
1294 - let wrapper = $( e.target ).closest( wrapperSelector );
1295 -
1296 - // Serialize subfield values and refresh the fonts in the preview window.
1297 - update_font_value( wrapper );
1298 - } );
1299 - }
1300 -
1301 - /**
1302 - * This function updates the data in font subset selector from the given <option> element
1303 - * @param option
1304 - * @param wraper
1305 - */
1306 - function update_subset_field( option, wraper ) {
1307 - let subsets = $( option ).data( 'subsets' ),
1308 - font_subsets = wraper.find( fontSubsetsSelector ),
1309 - new_subsets = [],
1310 - type = $( option ).data( 'type' );
1311 -
1312 - if ( type !== 'google' ) {
1313 - font_subsets.parent().hide();
1314 - return;
1315 - }
1316 -
1317 - let current_value = wraper.children( valueHolderSelector ).val();
1318 -
1319 - current_value = maybeJsonParse( current_value );
1320 - if ( _.isUndefined( current_value.selected_subsets ) ) {
1321 - return;
1322 - }
1323 - current_value = current_value.selected_subsets;
1324 -
1325 - subsets = maybeJsonParse( subsets );
1326 -
1327 - if ( Object.keys( subsets ).length < 2 ) {
1328 - font_subsets.parent().hide();
1329 - } else {
1330 - font_subsets.parent().show();
1331 - }
1332 -
1333 - // we need to turn the data array into a specific form like [{id:"id", text:"Text"}]
1334 - $.each( subsets, function( index, subset ) {
1335 - new_subsets[index] = {
1336 - 'id': subset,
1337 - 'text': subset
1338 - };
1339 -
1340 - // current_subsets
1341 - if ( typeof current_value !== 'undefined' && current_value !== null && current_value.indexOf( subset ) !== - 1 ) {
1342 - new_subsets[index].selected = true;
1343 - }
1344 - } );
1345 -
1346 - // We need to clear the old select2 field and reinitialize it.
1347 - $( font_subsets ).select2().empty();
1348 - $( font_subsets ).select2( {
1349 - data: new_subsets
1350 - } ).on( 'change', function( e ) {
1351 - let wrapper = $( e.target ).closest( wrapperSelector );
1352 -
1353 - // Serialize subfield values and refresh the fonts in the preview window.
1354 - update_font_value( wrapper );
1355 - } );
1356 - }
1357 -
1358 - /**
1359 - * This function is a custom value serializer for our entire font field
1360 - * It collects values and saves them (encoded) into the `.customify_font_values` input's value
1361 - */
1362 - function update_font_value( wraper ) {
1363 - let options_list = $( wraper ).find( '.font-options__options-list' ),
1364 - inputs = options_list.find( '[data-field]' ),
1365 - value_holder = wraper.children( valueHolderSelector ),
1366 - setting_id = $( value_holder ).data( 'customize-setting-link' ),
1367 - setting = wp.customize( setting_id ),
1368 - newFontData = {};
1369 -
1370 - // If we are already self-updating this and we haven't finished, we need to stop here to prevent infinite loops
1371 - // This call might have come from a subfield detecting the change the triggering a further update_font_value()
1372 - if ( true === updatingValue[setting_id] ) {
1373 - return;
1374 - }
1375 -
1376 - // If we are loading this setting value and haven't finished, there is no point in updating it as this would cause infinite loops.
1377 - if ( true === loadingValue[setting_id] ) {
1378 - return;
1379 - }
1380 -
1381 - // Mark the fact that we are self-updating the field value
1382 - updatingValue[setting_id] = true;
1383 -
1384 - inputs.each( function( key, el ) {
1385 - let field = $( el ).data( 'field' ),
1386 - value = $( el ).val();
1387 -
1388 - if ( 'font_family' === field ) {
1389 - // the font family also holds the type
1390 - let selected_opt = $( el.options[el.selectedIndex] ),
1391 - type = selected_opt.data( 'type' ),
1392 - subsets = selected_opt.data( 'subsets' ),
1393 - variants = selected_opt.data( 'variants' );
1394 -
1395 - if ( ! _.isUndefined( type ) ) {
1396 - newFontData['type'] = type;
1397 - if ( type === 'theme_font' ) {
1398 - newFontData['src'] = selected_opt.data( 'src' );
1399 - }
1400 - }
1401 -
1402 - if ( ! _.isUndefined( variants ) ) {
1403 - newFontData['variants'] = maybeJsonParse( variants );
1404 - }
1405 -
1406 - if ( ! _.isUndefined( subsets ) ) {
1407 - newFontData['subsets'] = maybeJsonParse( subsets );
1408 - }
1409 - }
1410 -
1411 -
1412 - if ( ! _.isUndefined( field ) && ! _.isUndefined( value ) && ! _.isNull( value ) && value !== '' ) {
1413 - newFontData[field] = value;
1414 - }
1415 - } );
1416 -
1417 - // Serialize the newly gathered font data
1418 - let serializedNewFontData = encodeValues( newFontData );
1419 - // Set the serialized value in the hidden field.
1420 - value_holder.val( serializedNewFontData );
1421 - // Update also the Customizer setting value.
1422 - setting.set( serializedNewFontData );
1423 -
1424 -
1425 - // Finished with the field value self-updating.
1426 - updatingValue[setting_id] = false;
1427 -
1428 - return newFontData;
1429 - }
1430 -
1431 - /**
1432 - * This function is a reverse of update_font_value(), initializing the entire font field controls based on the value stored in the hidden input.
1433 - */
1434 - function load_font_value( wrapper ) {
1435 - let options_list = $( wrapper ).find( '.font-options__options-list' ),
1436 - inputs = options_list.find( '[data-field]' ),
1437 - value_holder = wrapper.children( valueHolderSelector ),
1438 - value = maybeJsonParse( value_holder.val() ),
1439 - setting_id = $( value_holder ).data( 'customize-setting-link' );
1440 -
1441 - // If we are already loading this setting value and haven't finished, there is no point in starting again.
1442 - if ( true === loadingValue[setting_id] ) {
1443 - return;
1444 - }
1445 -
1446 - // Mark the fact that we are loading the field value
1447 - loadingValue[setting_id] = true;
1448 -
1449 - inputs.each( function( key, el ) {
1450 - let field = $( el ).data( 'field' );
1451 -
1452 - // In the case of select2, only the original selects have the data field, thus excluding select2 created select DOM elements
1453 - if ( typeof field !== "undefined" && field !== "" && typeof value[field] !== "undefined" ) {
1454 - $( el ).val( value[field] ).trigger( 'change' );
1455 - }
1456 - } );
1457 -
1458 - // Finished with the field value loading.
1459 - loadingValue[setting_id] = false;
1460 - }
1461 -
1462 - var maybeJsonParse = function( value ) {
1463 - let parsed;
1464 -
1465 - //try and parse it, with decodeURIComponent
1466 - try {
1467 - parsed = JSON.parse( decodeURIComponent( value ) );
1468 - } catch ( e ) {
1469 -
1470 - // in case of an error, treat is as a string
1471 - parsed = value;
1472 - }
1473 -
1474 - return parsed;
1475 - };
1476 -
1477 - function encodeValues( obj ) {
1478 - return encodeURIComponent( JSON.stringify( obj ) );
1479 - }
1480 -
1481 - function render_fonts() {
1482 - $( '.customify_font_family' ).select2().trigger( 'change' )
1483 - }
1484 -
1485 - return {
1486 - render_fonts: render_fonts,
1487 - init: init,
1488 - update_font_value: update_font_value
1489 - };
1490 - }
1491 - )();
1492 -
1493 - var Queue = function() {
1494 - var lastPromise = null;
1495 - var queueDeferred = null;
1496 - var methodDeferred = null;
1497 -
1498 - this.add_steps = function( key, steps, args ) {
1499 - var self = this;
1500 - this.methodDeferred = $.Deferred();
1501 - this.queueDeferred = this.setup();
1502 -
1503 - $.each( steps, function( i, step ) {
1504 - self.queue( key, step );
1505 - } );
1506 - };
1507 -
1508 - this.process_remote_step = function( key, data, step ) {
1509 - var self = this;
1510 -
1511 - if ( _.isUndefined( data ) || _.isNull( data ) ) {
1512 - return false;
1513 - }
1514 -
1515 - var new_step = step;
1516 - $.each( data, function( i, k ) {
1517 - debugger;
1518 - // prepare data for new requests
1519 - new_step.recall_data = k.data;
1520 - new_step.recall_type = k.type;
1521 - new_step.type = 'recall';
1522 -
1523 - self.queue( key, new_step, k.id );
1524 - } );
1525 - };
1526 -
1527 - this.log_action = function( action, key, msg ) {
1528 - if ( action === 'start' ) {
1529 - $( '.wpGrade-import-results' ).show();
1530 - $( '.wpGrade-import-results' ).append( '<span class="import_step_note imports_step_' + key + '" ><span class="step_info" data-balloon="Working on it" data-balloon-pos="up"></span>Importing ' + key + '</span>' );
1531 - } else if ( action === 'end' ) {
1532 - var $notice = $( '.imports_step_' + key + ' .step_info' );
1533 -
1534 - if ( $notice.length > 0 || msg !== "undefined" ) {
1535 - $notice.attr( 'data-balloon', msg );
1536 - $notice.addClass( 'success' );
1537 - } else {
1538 - $notice.attr( 'data-balloon', 'Done' );
1539 - $notice.addClass( 'failed' );
1540 - }
1541 - }
1542 - };
1543 -
1544 - this.queue = function( key, data, step_key ) {
1545 - var self = this;
1546 - if ( ! _.isUndefined( step_key ) ) {
1547 - this.log_action( 'start', step_key );
1548 - }
1549 -
1550 - // execute next queue method
1551 - this.queueDeferred.done( this.request( key, data, step_key ) );
1552 - lastPromise = self.methodDeferred.promise();
1553 - };
1554 -
1555 - this.request = function( key, step, step_key ) {
1556 - var self = this;
1557 - // call actual method and wrap output in deferred
1558 - //setTimeout( function() {
1559 - var data_args = {
1560 - action: 'customify_import_step',
1561 - step_id: step.id,
1562 - step_type: step.type,
1563 - option_key: key
1564 - };
1565 -
1566 - if ( ! _.isUndefined( step.recall_data ) ) {
1567 - data_args.recall_data = step.recall_data;
1568 - }
1569 -
1570 - if ( ! _.isUndefined( step.recall_type ) ) {
1571 - data_args.recall_type = step.recall_type;
1572 - }
1573 -
1574 - $.ajax( {
1575 - url: customify_settings.import_rest_url + 'customify/1.0/import',
1576 - method: 'POST',
1577 - beforeSend: function( xhr ) {
1578 - xhr.setRequestHeader( 'X-WP-Nonce', WP_API_Settings.nonce );
1579 - },
1580 - dataType: 'json',
1581 - contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
1582 - data: data_args
1583 - } ).done( function( response ) {
1584 - if ( ! _.isUndefined( response.success ) && response.success ) {
1585 - var results = response.data;
1586 - if ( step.type === 'remote' ) {
1587 - self.process_remote_step( key, results, step );
1588 - }
1589 - }
1590 -
1591 - if ( ! _.isUndefined( step_key ) && ! _.isUndefined( response.message ) ) {
1592 - self.log_action( 'end', step_key, response.message );
1593 - }
1594 - } );
1595 -
1596 - self.methodDeferred.resolve();
1597 - //}, 3450 );
1598 - };
1599 -
1600 - this.setup = function() {
1601 - var self = this;
1602 -
1603 - self.queueDeferred = $.Deferred();
1604 -
1605 - // when the previous method returns, resolve this one
1606 - $.when( lastPromise ).always( function() {
1607 - self.queueDeferred.resolve();
1608 - } );
1609 -
1610 - return self.queueDeferred.promise();
1611 - }
1612 - };
1613 -
1614 - /** HELPERS **/
1615 -
1616 - /**
1617 - * Function to check if a value exists in an object
1618 - * @param value
1619 - * @param obj
1620 - * @returns {boolean}
1621 - */
1622 - var inObject = function( value, obj ) {
1623 - for ( var k in obj ) {
1624 - if ( ! obj.hasOwnProperty( k ) ) {
1625 - continue;
1626 - }
1627 - if ( _.isEqual( obj[k], value ) ) {
1628 - return true;
1629 - }
1630 - }
1631 - return false;
1632 - };
1633 -
1634 - var maybeJsonParse = function( value ) {
1635 - var parsed;
1636 -
1637 - //try and parse it, with decodeURIComponent
1638 - try {
1639 - parsed = JSON.parse( decodeURIComponent( value ) );
1640 - } catch ( e ) {
1641 -
1642 - // in case of an error, treat is as a string
1643 - parsed = value;
1644 - }
1645 -
1646 - return parsed;
1647 - };
1648 -
1649 - var getUrlVars = function( name ) {
1650 - var vars = [], hash;
1651 - var hashes = window.location.href.slice( window.location.href.indexOf( '?' ) + 1 ).split( '&' );
1652 - for ( var i = 0; i < hashes.length; i ++ ) {
1653 - hash = hashes[i].split( '=' );
1654 -
1655 - vars.push( hash[0] );
1656 - vars[hash[0]] = hash[1];
1657 - }
1658 -
1659 - if ( ! _.isUndefined( vars[name] ) ) {
1660 - return vars[name];
1661 - }
1662 - return false;
1663 - };
1664 -
1665 - var isJsonString = function( str ) {
1666 - try {
1667 - JSON.parse( str );
1668 - } catch ( e ) {
1669 - return false;
1670 - }
1671 - return true;
1672 - };
1673 - }
1674 -)( jQuery, window, wp );
1675 -
1676 -
1677 -// Reverses a hex color to either black or white
1678 -function customifyInverseHexColorToBlackOrWhite( hex ) {
1679 - return customifyInverseHexColor( hex, true );
1680 -}
1681 -
1682 -// Taken from here: https://stackoverflow.com/a/35970186/6260836
1683 -function customifyInverseHexColor( hex, bw ) {
1684 - if ( hex.indexOf( '#' ) === 0 ) {
1685 - hex = hex.slice( 1 );
1686 - }
1687 - // convert 3-digit hex to 6-digits.
1688 - if ( hex.length === 3 ) {
1689 - hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
1690 - }
1691 - if ( hex.length !== 6 ) {
1692 - throw new Error( 'Invalid HEX color.' );
1693 - }
1694 - var r = parseInt( hex.slice( 0, 2 ), 16 ),
1695 - g = parseInt( hex.slice( 2, 4 ), 16 ),
1696 - b = parseInt( hex.slice( 4, 6 ), 16 );
1697 - if ( bw ) {
1698 - // http://stackoverflow.com/a/3943023/112731
1699 - return (
1700 - r * 0.299 + g * 0.587 + b * 0.114
1701 - ) > 186
1702 - ? '#000000'
1703 - : '#FFFFFF';
1704 - }
1705 - // invert color components
1706 - r = (
1707 - 255 - r
1708 - ).toString( 16 );
1709 - g = (
1710 - 255 - g
1711 - ).toString( 16 );
1712 - b = (
1713 - 255 - b
1714 - ).toString( 16 );
1715 - // pad each with zeros and return
1716 - return "#" + customifyPadZero( r ) + customifyPadZero( g ) + customifyPadZero( b );
1717 -}
1718 -
1719 -function customifyPadZero( str, len ) {
1720 - len = len || 2;
1721 - var zeros = new Array( len ).join( '0' );
1722 - return (
1723 - zeros + str
1724 - ).slice( - len );
1725 -}
1726 -
1727 -// Shading, Blending and Converting colors
1728 -// Taken from here: https://github.com/PimpTrizkit/PJs/wiki/12.-Shade,-Blend-and-Convert-a-Web-Color-(pSBC.js)
1729 -const pSBC = function( p, from, to ) {
1730 - if ( typeof(
1731 - p
1732 - ) != "number" || p < - 1 || p > 1 || typeof(
1733 - from
1734 - ) != "string" || (
1735 - from[0] != 'r' && from[0] != '#'
1736 - ) || (
1737 - to && typeof(
1738 - to
1739 - ) != "string"
1740 - ) ) {
1741 - return null;
1742 - } //ErrorCheck
1743 - if ( ! this.pSBCr ) {
1744 - this.pSBCr = ( d ) => {
1745 - let l = d.length, RGB = {};
1746 - if ( l > 9 ) {
1747 - d = d.split( "," );
1748 - if ( d.length < 3 || d.length > 4 ) {
1749 - return null;
1750 - }//ErrorCheck
1751 - RGB[0] = i( d[0].split( "(" )[1] ), RGB[1] = i( d[1] ), RGB[2] = i( d[2] ), RGB[3] = d[3] ? parseFloat( d[3] ) : - 1;
1752 - } else {
1753 - if ( l == 8 || l == 6 || l < 4 ) {
1754 - return null;
1755 - } //ErrorCheck
1756 - if ( l < 6 ) {
1757 - d = "#" + d[1] + d[1] + d[2] + d[2] + d[3] + d[3] + (
1758 - l > 4 ? d[4] + "" + d[4] : ""
1759 - );
1760 - } //3 or 4 digit
1761 - d = i( d.slice( 1 ), 16 ), RGB[0] = d >> 16 & 255, RGB[1] = d >> 8 & 255, RGB[2] = d & 255, RGB[3] = - 1;
1762 - if ( l == 9 || l == 5 ) {
1763 - RGB[3] = r( (
1764 - RGB[2] / 255
1765 - ) * 10000 ) / 10000, RGB[2] = RGB[1], RGB[1] = RGB[0], RGB[0] = d >> 24 & 255;
1766 - }
1767 - }
1768 - return RGB;
1769 - }
1770 - }
1771 - var i = parseInt, r = Math.round, h = from.length > 9, h = typeof(
1772 - to
1773 - ) == "string" ? to.length > 9 ? true : to == "c" ? ! h : false : h, b = p < 0, p = b ? p * - 1 : p,
1774 - to = to && to != "c" ? to : b ? "#000000" : "#FFFFFF", f = this.pSBCr( from ), t = this.pSBCr( to );
1775 - if ( ! f || ! t ) {
1776 - return null;
1777 - } //ErrorCheck
1778 - if ( h ) {
1779 - return "rgb" + (
1780 - f[3] > - 1 || t[3] > - 1 ? "a(" : "("
1781 - ) + r( (
1782 - t[0] - f[0]
1783 - ) * p + f[0] ) + "," + r( (
1784 - t[1] - f[1]
1785 - ) * p + f[1] ) + "," + r( (
1786 - t[2] - f[2]
1787 - ) * p + f[2] ) + (
1788 - f[3] < 0 && t[3] < 0 ? ")" : "," + (
1789 - f[3] > - 1 && t[3] > - 1 ? r( (
1790 - (
1791 - t[3] - f[3]
1792 - ) * p + f[3]
1793 - ) * 10000 ) / 10000 : t[3] < 0 ? f[3] : t[3]
1794 - ) + ")"
1795 - );
1796 - } else {
1797 - return "#" + (
1798 - 0x100000000 + r( (
1799 - t[0] - f[0]
1800 - ) * p + f[0] ) * 0x1000000 + r( (
1801 - t[1] - f[1]
1802 - ) * p + f[1] ) * 0x10000 + r( (
1803 - t[2] - f[2]
1804 - ) * p + f[2] ) * 0x100 + (
1805 - f[3] > - 1 && t[3] > - 1 ? r( (
1806 - (
1807 - t[3] - f[3]
1808 - ) * p + f[3]
1809 - ) * 255 ) : t[3] > - 1 ? r( t[3] * 255 ) : f[3] > - 1 ? r( f[3] * 255 ) : 255
1810 - )
1811 - ).toString( 16 ).slice( 1, f[3] > - 1 || t[3] > - 1 ? undefined : - 2 );
1812 - }
1813 -};
818 + if (!_.isUndefined(vars[name])) {
819 + return vars[name]
820 + }
821 + return false
822 + }
823 +})(jQuery, customify, wp)