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