PluginProbe
Customify / 2.5.2
Customify v2.5.2
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
customify / js / customizer.js

customizer.js in Customify 2.5.2, at js/customizer.js

1,116 lines 34.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (
2 function ($, exports, wp) {
3 var api = wp.customize
4 var $window = $(window)
5
6 // when the customizer is ready prepare our fields events
7 wp.customize.bind('ready', function () {
8 var timeout = null
9
10 // Create a stack of callbacks bound to parent settings to be able to unbind them
11 // when altering the connected_fields attribute.
12 if (typeof window.connectedFieldsCallbacks === 'undefined') {
13 window.connectedFieldsCallbacks = {}
14 }
15
16 // add ace editors
17 $('.customify_ace_editor').each(function (key, el) {
18 const id = $(this).attr('id'),
19 css_editor = ace.edit(id)
20
21 const editor_type = $(this).data('editor_type')
22 // init the ace editor
23 css_editor.setTheme('ace/theme/github')
24 css_editor.getSession().setMode('ace/mode/' + editor_type)
25
26 // hide the textarea and enable the ace editor
27 const textarea = $('#' + id + '_textarea').hide()
28 css_editor.getSession().setValue(textarea.val())
29
30 // each time a change is triggered start a timeout of 1,5s and when is finished refresh the previewer
31 // if the user types faster than this delay then reset it
32 css_editor.getSession().on('change', function (e) {
33 if (timeout !== null) {
34 clearTimeout(timeout)
35 timeout = null
36 } else {
37 timeout = setTimeout(function () {
38 //var state = css_editor.session.getState();
39 textarea.val(css_editor.getSession().getValue())
40 textarea.trigger('change')
41 }, 1500)
42 }
43 })
44 })
45
46 // simple select2 field
47 $('.customify_select2').select2()
48
49 setTimeout(function () {
50 CustomifyFontSelectFields.init()
51 }, 333)
52
53 prepare_typography_field()
54
55 /**
56 * Make the customizer save on CMD/CTRL+S action
57 * This is awesome!!!
58 */
59 $(window).bind('keydown', function (event) {
60 if (event.ctrlKey || event.metaKey) {
61 switch (String.fromCharCode(event.which).toLowerCase()) {
62 case 's':
63 event.preventDefault()
64 api.previewer.save()
65 break
66 }
67 }
68 })
69
70 // for each range input add a value preview output
71 $('.accordion-section-content[id*="' + customify_settings.options_name + '"], #sub-accordion-section-style_manager_section').each(function () {
72
73 // Initialize range fields logic
74 customifyHandleRangeFields(this)
75 })
76
77 if ($('button[data-action="reset_customify"]').length > 0) {
78 // reset_button
79 $(document).on('click', '#customize-control-reset_customify button', function (ev) {
80 ev.preventDefault()
81
82 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!')
83
84 if (!iAgree) {
85 return
86 }
87
88 $.each(api.settings.controls, function (key, ctrl) {
89 const setting_id = key.replace('_control', '')
90 const setting = customify_settings.settings[setting_id]
91
92 if (!_.isUndefined(setting) && !_.isUndefined(setting.default)) {
93 api_set_setting_value(setting_id, setting.default)
94 }
95 })
96
97 api.previewer.save()
98 })
99
100 // add a reset button for each panel
101 $('.panel-meta').each(function (el, key) {
102 const container = $(this).parents('.control-panel'),
103 id = container.attr('id')
104
105 if (typeof id !== 'undefined') {
106 const panel_id = id.replace('accordion-panel-', '')
107 $(this).parent().append('<button class="reset_panel button" data-panel="' + panel_id + '">Panel\'s defaults</button>')
108 }
109 })
110
111 // reset panel
112 $(document).on('click', '.reset_panel', function (e) {
113 e.preventDefault()
114
115 const panel_id = $(this).data('panel'),
116 panel = api.panel(panel_id),
117 sections = panel.sections(),
118 iAgree = confirm('Do you really want to reset ' + panel.params.title + '?')
119
120 if (!iAgree) {
121 return
122 }
123 if (sections.length > 0) {
124 $.each(sections, function () {
125 //var settings = this.settings();
126 const controls = this.controls()
127
128 if (controls.length > 0) {
129 $.each(controls, function (key, ctrl) {
130 const setting_id = ctrl.id.replace('_control', ''),
131 setting = customify_settings.settings[setting_id]
132
133 if (!_.isUndefined(setting) && !_.isUndefined(setting.default)) {
134 api_set_setting_value(setting_id, setting.default)
135 }
136 })
137 }
138 })
139 }
140 })
141
142 //add reset section
143 $('.accordion-section-content').each(function (el, key) {
144 const section_id = $(this).attr('id')
145
146 if ((
147 (
148 !_.isUndefined(section_id)
149 ) ? section_id.indexOf(customify_settings.options_name) : -1
150 ) === -1) {
151 return
152 }
153
154 if (!_.isUndefined(section_id) && section_id.indexOf('sub-accordion-section-') > -1) {
155 const id = section_id.replace('sub-accordion-section-', '')
156 $(this).append('<button class="reset_section button" data-section="' + id + '">Reset All Options for This Section</button>')
157 }
158 })
159
160 // reset section event
161 $(document).on('click', '.reset_section', function (e) {
162 e.preventDefault()
163
164 const section_id = $(this).data('section'),
165 section = api.section(section_id),
166 controls = section.controls()
167
168 const iAgree = confirm('Do you really want to reset ' + section.params.title + '?')
169
170 if (!iAgree) {
171 return
172 }
173
174 if (controls.length > 0) {
175 $.each(controls, function (key, ctrl) {
176 const setting_id = ctrl.id.replace('_control', ''),
177 setting = customify_settings.settings[setting_id]
178
179 if (!_.isUndefined(setting) && !_.isUndefined(setting.default)) {
180 api_set_setting_value(setting_id, setting.default)
181 }
182 })
183 }
184 })
185 }
186
187 $(document).on('change keyup', '.customize-control-range input.range-value', function () {
188 const range = $(this).siblings('input[type="range"]')
189 range.val($(this).val())
190 range.trigger('change')
191 })
192
193 $(document).on('change', '.customify_typography_font_subsets', function (ev) {
194
195 const $input = $(this).parents('.options').siblings('.customify_typography').children('.customify_typography_values');
196 let current_val = $input.val()
197
198 current_val = JSON.parse(decodeURIComponent(current_val))
199
200 //maybe the selected option holds a JSON in its value
201 current_val.selected_subsets = maybeJsonParse($(this).val())
202
203 $input.val(encodeURIComponent(JSON.stringify(current_val)))
204
205 $input.trigger('change')
206 })
207
208 $(document).on('change', '.customify_typography_font_weight', function (ev) {
209
210 const $input = $(this).parents('.options').siblings('.customify_typography').children('.customify_typography_values');
211 let current_val = $input.val();
212
213 current_val = maybeJsonParse(current_val)
214 // @todo currently the font weight selector works for one value only
215 // maybe make this a multiselect
216
217 //maybe the selected option holds a JSON in its value
218 current_val.selected_variants = {0: maybeJsonParse($(this).val())}
219
220 $input.val(encodeURIComponent(JSON.stringify(current_val)))
221 $input.trigger('change')
222 })
223
224 $('body').on('customify:preset-change', function (e) {
225 const data = $(e.target).data('options')
226
227 if (!_.isUndefined(data)) {
228 $.each(data, function (setting_id, value) {
229 api_set_setting_value(setting_id, value)
230 })
231 }
232 })
233
234 $(document).on('change', '.customify_preset.select', function () {
235 const $source = $(this)
236 const $target = $source.children('[value="' + $source.val() + '"]')
237 $target.trigger('customify:preset-change')
238 })
239
240 $(document).on('click', '.customify_preset.radio input, .customify_preset.radio_buttons input, .awesome_presets input', function () {
241 $(this).trigger('customify:preset-change')
242 })
243
244 customifyBackgroundJsControl.init()
245
246 // sometimes a php save may be needed
247 if (getUrlVars('save_customizer_once')) {
248 api.previewer.save()
249 }
250
251 setTimeout(function () {
252 customifyFoldingFields()
253 }, 1000);
254
255 // Handle the section tabs (ex: Layout | Fonts | Colors)
256 (
257 function () {
258 const $navs = $('.js-section-navigation')
259
260 $navs.each(function () {
261 const $nav = $(this)
262 const $title = $nav.parents('.accordion-section-content').find('.customize-section-title')
263
264 $nav.closest('.customize-control').addClass('screen-reader-text')
265 $title.append($nav).parent().addClass('has-nav')
266 })
267
268 $('.js-section-navigation a').on('click', function (e) {
269 e.preventDefault()
270
271 const $sidebar = $(this).parents('.customize-pane-child'),
272 $parent = $(this).parents('.accordion-section-content'),
273 href = $.attr(this, 'href')
274
275 if (href != '#') {
276 $sidebar.animate({
277 scrollTop: $($.attr(this, 'href')).position().top - $parent.find('.customize-section-title').outerHeight()
278 }, 500)
279 }
280 })
281 }
282 )();
283
284 (
285 function () {
286 // Close a font field when clicking on another field
287 $('.customify_font_tooltip').on('click', function () {
288 if ($(this).prop('checked') === true) {
289 $('.customify_font_tooltip').prop('checked', false)
290 $(this).prop('checked', true)
291 }
292 })
293 }
294 )()
295
296 // Bind any connected fields, except those in the Style Manager.
297 // Those are handled by the appropriate Style Manager component (Color Palettes, Font Palettes, etc ).
298 bindConnectedFields()
299
300 })
301
302 const getConnectedFieldsCallback = function (parent_setting_data, parent_setting_id) {
303 return function (new_value, old_value) {
304 _.each(parent_setting_data.connected_fields, function (connected_field_data) {
305 if (_.isUndefined(connected_field_data) || _.isUndefined(connected_field_data.setting_id) || !_.isString(connected_field_data.setting_id)) {
306 return
307 }
308 const setting = wp.customize(connected_field_data.setting_id)
309 if (_.isUndefined(setting)) {
310 return
311 }
312 setting.set(new_value)
313 })
314 }
315 }
316
317 const bindConnectedFields = function () {
318 _.each(wp.customize.settings.settings, function (parent_setting_data, parent_setting_id) {
319 // We don't want to handle the binding of the Style Manager settings
320 if (typeof ColorPalettes !== 'undefined'
321 && typeof ColorPalettes.masterSettingIds !== 'undefined'
322 && _.contains(ColorPalettes.masterSettingIds, parent_setting_id)) {
323 return
324 }
325 if (typeof FontPalettes !== 'undefined'
326 && typeof FontPalettes.masterSettingIds !== 'undefined'
327 && _.contains(FontPalettes.masterSettingIds, parent_setting_id)) {
328 return
329 }
330
331 let parent_setting = wp.customize(parent_setting_id)
332 if (typeof parent_setting_data.connected_fields !== 'undefined') {
333 connectedFieldsCallbacks[parent_setting_id] = getConnectedFieldsCallback(parent_setting_data, parent_setting_id)
334 parent_setting.bind(connectedFieldsCallbacks[parent_setting_id])
335 }
336 })
337 }
338
339 const unbindConnectedFields = function () {
340 _.each(wp.customize.settings.settings, function (parent_setting_data, parent_setting_id) {
341 // We don't want to handle the binding of the Style Manager settings
342 if (typeof ColorPalettes !== 'undefined'
343 && typeof ColorPalettes.masterSettingIds !== 'undefined'
344 && _.contains(ColorPalettes.masterSettingIds, parent_setting_id)) {
345 return
346 }
347 if (typeof FontPalettes !== 'undefined'
348 && typeof FontPalettes.masterSettingIds !== 'undefined'
349 && _.contains(FontPalettes.masterSettingIds, parent_setting_id)) {
350 return
351 }
352
353 let parent_setting = wp.customize(parent_setting_id)
354 if (typeof parent_setting_data.connected_fields !== 'undefined' && typeof connectedFieldsCallbacks[parent_setting_id] !== 'undefined') {
355 parent_setting.unbind(connectedFieldsCallbacks[parent_setting_id])
356 }
357 delete connectedFieldsCallbacks[parent_setting_id]
358 })
359 }
360
361 const customifyHandleRangeFields = function (el) {
362
363 // For each range input add a number field (for preview mainly - but it can also be used for input)
364 $(el).find('input[type="range"]').each(function () {
365 if (!$(this).siblings('.range-value').length) {
366 const $clone = $(this).clone()
367
368 $clone
369 .attr('type', 'number')
370 .attr('class', 'range-value')
371 .removeAttr('data-field')
372
373 $(this).after($clone)
374
375 // Update the range field when changing the number
376 $($clone).on('change', function () {
377 $(this).siblings('input[type="range"]').val($(this).val())
378 })
379 }
380
381 // Update the number field when changing the range
382 $(this).on('change', function () {
383 $(this).siblings('.range-value').val($(this).val())
384 })
385 })
386 }
387
388 /**
389 * This function will search for all the interdependend fields and make a bound between them.
390 * So whenever a target is changed, it will take actions to the dependent fields.
391 * @TODO this is still written in a barbaric way, refactor when needed
392 */
393 const customifyFoldingFields = function () {
394
395 if (_.isUndefined(customify_settings) || _.isUndefined(customify_settings.settings)) {
396 return // bail
397 }
398
399 /**
400 * Let's iterate through all the customify settings and gather all the fields that have a "show_if"
401 * property set.
402 *
403 * At the end `targets` will hold a list of [ target : [field, field,...], ... ]
404 * so when a target is changed we will change all the fields.
405 */
406 var targets = {}
407
408 $.fn.reactor.defaults.compliant = function () {
409 $(this).slideDown()
410 // $(this).animate({opacity: 1});
411 $(this).find(':disabled').attr({disabled: false})
412 }
413
414 $.fn.reactor.defaults.uncompliant = function () {
415 $(this).slideUp()
416 // $(this).animate({opacity: 0.25});
417 $(this).find(':enabled').attr({disabled: true})
418 }
419
420 let IS = $.extend({}, $.fn.reactor.helpers)
421
422 let bind_folding_events = function (parent_id, field, relation) {
423
424 let key = null
425
426 if (_.isString(field)) {
427 key = field
428 } else if (!_.isUndefined(field.id)) {
429 key = field.id
430 } else if (isString(field[0])) {
431 key = field[0]
432 } else {
433 return // no key, no fun
434 }
435
436 let value = 1, // by default we use 1 the most used value for checkboxes or inputs
437 compare = '==', // ... ye
438 action = 'show',
439 between = [0, 1] // can only be `show` or `hide`
440
441 const target_key = customify_settings.options_name + '[' + key + ']'
442 const target_type = customify_settings.settings[target_key].type
443
444 // we support the usual syntax like a config array like `array( 'id' => $id, 'value' => $value, 'compare' => $compare )`
445 // but we also support a non-associative array like `array( $id, $value, $compare )`
446 if (!_.isUndefined(field.value)) {
447 value = field.value
448 } else if (!_.isUndefined(field[1]) && !_.isString(field[1])) {
449 value = field[1]
450 }
451
452 if (!_.isUndefined(field.compare)) {
453 compare = field.compare
454 } else if (!_.isUndefined(field[2])) {
455 compare = field[2]
456 }
457
458 if (!_.isUndefined(field.action)) {
459 action = field.action
460 } else if (!_.isUndefined(field[3])) {
461 action = field[3]
462 }
463
464 // a field can also overwrite the parent relation
465 if (!_.isUndefined(field.relation)) {
466 action = field.relation
467 } else if (!_.isUndefined(field[4])) {
468 action = field[4]
469 }
470
471 if (!_.isUndefined(field.between)) {
472 between = field.between
473 }
474
475 /**
476 * Now for each target we have, we will bind a change event to hide or show the dependent fields
477 */
478 const target_selector = '[data-customize-setting-link="' + customify_settings.options_name + '[' + key + ']"]'
479
480 switch (target_type) {
481 case 'checkbox':
482 $(parent_id).reactIf(target_selector, function () {
483 return $(this).is(':checked') == value
484 })
485 break
486
487 case 'radio':
488 case 'sm_radio':
489 case 'sm_switch':
490 case 'radio_image':
491
492 // in case of an array of values we use the ( val in array) condition
493 if (_.isObject(value)) {
494 $(parent_id).reactIf(target_selector, function () {
495 return (
496 value.indexOf($(target_selector + ':checked').val()) !== -1
497 )
498 })
499 } else { // in any other case we use a simple == comparison
500 $(parent_id).reactIf(target_selector, function () {
501 return $(target_selector + ':checked').val() == value
502 })
503 }
504 break
505
506 case 'range':
507 const x = IS.Between(between[0], between[1])
508
509 $(parent_id).reactIf(target_selector, x)
510 break
511
512 default:
513 // in case of an array of values we use the ( val in array) condition
514 if (_.isObject(value)) {
515 $(parent_id).reactIf(target_selector, function () {
516 return (
517 value.indexOf($(target_selector).val()) !== -1
518 )
519 })
520 } else { // in any other case we use a simple == comparison
521 $(parent_id).reactIf(target_selector, function () {
522 return $(target_selector).val() == value
523 })
524 }
525 break
526 }
527
528 $(target_selector).trigger('change')
529 $('.reactor').trigger('change.reactor') // triggers all events on load
530 }
531
532 $.each(customify_settings.settings, function (id, field) {
533 /**
534 * Here we have the id of the fields. but we know for sure that we just need his parent selector
535 * So we just create it
536 */
537 let parent_id = id.replace('[', '-')
538 parent_id = parent_id.replace(']', '')
539 parent_id = '#customize-control-' + parent_id + '_control'
540
541 // get only the fields that have a 'show_if' property
542 if (field.hasOwnProperty('show_if')) {
543 let relation = 'AND'
544
545 if (!_.isUndefined(field.show_if.relation)) {
546 relation = field.show_if.relation
547 // remove the relation property, we need the config to be array based only
548 delete field.show_if.relation
549 }
550
551 /**
552 * The 'show_if' can be a simple array with one target like: [ id, value, comparison, action ]
553 * Or it could be an array of multiple targets and we need to process both cases
554 */
555
556 if (!_.isUndefined(field.show_if.id)) {
557 bind_folding_events(parent_id, field.show_if, relation)
558 } else if (_.isObject(field.show_if)) {
559 $.each(field.show_if, function (i, j) {
560 bind_folding_events(parent_id, j, relation)
561 })
562 }
563 }
564 })
565 }
566
567 const get_typography_font_family = function ($el) {
568
569 let font_family_value = $el.val()
570 // first time this will not be a json so catch that error
571 try {
572 font_family_value = JSON.parse(font_family_value)
573 } catch (e) {
574 return {font_family: font_family_value}
575 }
576
577 if (!_.isUndefined(font_family_value.font_family)) {
578 return font_family_value.font_family
579 }
580
581 return false
582 }
583
584 // get each typography field and bind events
585 // @todo Are we still using the typography field since we have the font field?
586 const prepare_typography_field = function () {
587
588 const $typos = $('.customify_typography_font_family')
589
590 $typos.each(function () {
591 var font_family_select = this,
592 $input = $(font_family_select).siblings('.customify_typography_values')
593 // on change
594 $(font_family_select).on('change', function () {
595 update_siblings_selects(font_family_select)
596 $input.trigger('change')
597 })
598 update_siblings_selects(font_family_select)
599 })
600 }
601
602 const api_set_setting_value = function (setting_id, value) {
603 let setting = api(setting_id),
604 field = $('[data-customize-setting-link="' + setting_id + '"]'),
605 field_class = $(field).parent().attr('class')
606
607 // Legacy field type
608 if (!_.isUndefined(field_class) && field_class === 'customify_typography') {
609
610 let family_select = field.siblings('select')
611
612 if (_.isString(value)) {
613 let this_option = family_select.find('option[value="' + value + '"]')
614 $(this_option[0]).attr('selected', 'selected')
615 update_siblings_selects(family_select)
616 } else if (_.isObject(value)) {
617 let this_family_option = family_select.find('option[value="' + value['font_family'] + '"]')
618
619 $(this_family_option[0]).attr('selected', 'selected')
620
621 update_siblings_selects(this_family_option)
622
623 setTimeout(function () {
624 let weight_select = field.parent().siblings('.options').find('.customify_typography_font_weight'),
625 this_weight_option = weight_select.find('option[value="' + value['selected_variants'] + '"]')
626
627 $(this_weight_option[0]).attr('selected', 'selected')
628
629 update_siblings_selects(this_family_option)
630
631 weight_select.trigger('change')
632 }, 300)
633 }
634
635 family_select.trigger('change')
636
637 } else if (!_.isUndefined(field_class) && field_class === 'font-options__wrapper') {
638
639 // if the value is a simple string it must be the font family
640 if (_.isString(value)) {
641 let option = field.parent().find('option[value="' + value + '"]')
642
643 option.attr('selected', 'selected')
644 // option.parents('select').trigger('change');
645 } else if (_.isObject(value)) {
646 // Find the options list wrapper
647 let optionsList = field.parent().children('.font-options__options-list')
648
649 if (optionsList.length) {
650 // We will process each font property and update it
651 _.each(value, function (val, key) {
652 // We need to map the keys to the data attributes we are using - I know :(
653 let mappedKey = key
654 switch (key) {
655 case 'font-family':
656 mappedKey = 'font_family'
657 break
658 case 'font-size':
659 mappedKey = 'font_size'
660 break
661 case 'font-weight':
662 mappedKey = 'selected_variants'
663 break
664 case 'letter-spacing':
665 mappedKey = 'letter_spacing'
666 break
667 case 'text-transform':
668 mappedKey = 'text_transform'
669 break
670 default:
671 break
672 }
673 let subField = optionsList.find('[data-field="' + mappedKey + '"]')
674 if (subField.length) {
675 subField.val(val)
676 subField.trigger('change')
677 }
678 })
679 }
680 }
681
682 } else {
683 setting.set(value)
684 }
685 }
686
687 const update_siblings_selects = function (font_select) {
688 let selected_font = $(font_select).val(),
689 $input = $(font_select).siblings('.customify_typography_values'),
690 current_val = $input.attr('value')
691
692 if (current_val === '[object Object]') {
693 current_val = $input.data('default')
694 } else if (_.isString(current_val) && !isJsonString(current_val) && current_val.substr(0, 1) == '[') {
695 // a rare case when the value isn't a json but is a representative string like [family,weight]
696 current_val = current_val.split(',')
697 let new_current_value = {}
698 if (!_.isUndefined(current_val[0])) {
699 new_current_value['font_family'] = current_val[0]
700 }
701
702 if (!_.isUndefined(current_val[1])) {
703 new_current_value['selected_variants'] = current_val[1]
704 }
705
706 current_val = JSON.stringify(new_current_value)
707 }
708
709 let $font_weight = $(font_select).parent().siblings('ul.options').find('.customify_typography_font_weight'),
710 $font_subsets = $(font_select).parent().siblings('ul.options').find('.customify_typography_font_subsets')
711
712 try {
713 current_val = JSON.parse(decodeURIComponent(current_val))
714 } catch (e) {
715
716 // in case of an error, force the rebuild of the json
717 if (_.isUndefined($(font_select).data('bound_once'))) {
718
719 $(font_select).data('bound_once', true)
720
721 $(font_select).change()
722 $font_weight.change()
723 $font_subsets.change()
724 }
725 }
726
727 // first try to get the font from sure sources, not from the recommended list.
728 let option_data = $(font_select).find(':not(optgroup[label=Recommended]) option[value="' + selected_font + '"]')
729 // however, if there isn't an option found, get what you can
730 if (option_data.length < 1) {
731 option_data = $(font_select).find('option[value="' + selected_font + '"]')
732 }
733
734 if (option_data.length > 0) {
735
736 let font_type = option_data.data('type'),
737 value_to_add = {'type': font_type, 'font_family': selected_font},
738 variants = null,
739 subsets = null
740
741 if (font_type == 'std') {
742 variants = {
743 0: '100',
744 1: '200',
745 3: '300',
746 4: '400',
747 5: '500',
748 6: '600',
749 7: '700',
750 8: '800',
751 9: '900'
752 }
753 if (!_.isUndefined($(option_data[0]).data('variants'))) {
754 //maybe the variants are a JSON
755 variants = maybeJsonParse($(option_data[0]).data('variants'))
756 }
757 } else {
758 //maybe the variants are a JSON
759 variants = maybeJsonParse($(option_data[0]).data('variants'))
760
761 //maybe the subsets are a JSON
762 subsets = maybeJsonParse($(option_data[0]).data('subsets'))
763 }
764
765 // make the variants selector
766 if (!_.isUndefined(variants) && !_.isNull(variants) && !_.isEmpty(variants)) {
767
768 value_to_add['variants'] = variants
769 // when a font is selected force the first weight to load
770 value_to_add['selected_variants'] = {0: variants[0]}
771
772 let variants_options = '',
773 count_weights = 0
774
775 if (_.isArray(variants) || _.isObject(variants)) {
776 // Take each variant and produce the option markup
777 $.each(variants, function (key, el) {
778 let is_selected = ''
779 if (_.isObject(current_val.selected_variants) && inObject(el, current_val.selected_variants)) {
780 is_selected = ' selected="selected"'
781 } else if (_.isString(current_val.selected_variants) && el === current_val.selected_variants) {
782 is_selected = ' selected="selected"'
783 }
784
785 // initialize
786 let variant_option_value = el,
787 variant_option_display = el
788
789 // If we are dealing with a object variant then it means things get tricky (probably it's our fault but bear with us)
790 // This probably comes from our Fonto plugin - a font with individually named variants - hence each has its own font-family
791 if (_.isObject(el)) {
792 //put the entire object in the variation value - we will need it when outputting the custom CSS
793 variant_option_value = encodeURIComponent(JSON.stringify(el))
794 variant_option_display = ''
795
796 //if we have weight and style then "compose" them into something standard
797 if (!_.isUndefined(el['font-weight'])) {
798 variant_option_display += el['font-weight']
799 }
800
801 if (_.isString(el['font-style']) && $.inArray(el['font-style'].toLowerCase(), [
802 'normal',
803 'regular'
804 ]) < 0) { //this comparison means it hasn't been found
805 variant_option_display += el['font-style']
806 }
807 }
808
809 variants_options += '<option value="' + variant_option_value + '"' + is_selected + '>' + variant_option_display + '</option>'
810 count_weights++
811 })
812 }
813
814 if (!_.isUndefined($font_weight)) {
815 $font_weight.html(variants_options)
816 // if there is no weight or just 1 we hide the weight select ... cuz is useless
817 if ($(font_select).data('load_all_weights') === true || count_weights <= 1) {
818 $font_weight.parent().css('display', 'none')
819 } else {
820 $font_weight.parent().css('display', 'inline-block')
821 }
822 }
823 } else if (!_.isUndefined($font_weight)) {
824 $font_weight.parent().css('display', 'none')
825 }
826
827 // make the subsets selector
828 if (!_.isUndefined(subsets) && !_.isNull(subsets) && !_.isEmpty(subsets)) {
829
830 value_to_add['subsets'] = subsets
831 // when a font is selected force the first subset to load
832 value_to_add['selected_subsets'] = {0: subsets[0]}
833 let subsets_options = '',
834 count_subsets = 0
835 $.each(subsets, function (key, el) {
836 let is_selected = ''
837 if (_.isObject(current_val.selected_subsets) && inObject(el, current_val.selected_subsets)) {
838 is_selected = ' selected="selected"'
839 }
840
841 subsets_options += '<option value="' + el + '"' + is_selected + '>' + el + '</option>'
842 count_subsets++
843 })
844
845 if (!_.isUndefined($font_subsets)) {
846 $font_subsets.html(subsets_options)
847
848 // if there is no subset or just 1 we hide the subsets select ... cuz is useless
849 if (count_subsets <= 1) {
850 $font_subsets.parent().css('display', 'none')
851 } else {
852 $font_subsets.parent().css('display', 'inline-block')
853 }
854 }
855 } else if (!_.isUndefined($font_subsets)) {
856 $font_subsets.parent().css('display', 'none')
857 }
858
859 $input.val(encodeURIComponent(JSON.stringify(value_to_add)))
860 }
861 }
862
863 /** Modules **/
864
865 const customifyBackgroundJsControl = (
866 function () {
867 'use strict'
868
869 function init () {
870 // Remove the image button
871 $('.customize-control-custom_background .remove-image, .customize-control-custom_background .remove-file').unbind('click').on('click', function (e) {
872 removeImage($(this).parents('.customize-control-custom_background:first'))
873 preview($(this))
874 return false
875 })
876
877 // Upload media button
878 $('.customize-control-custom_background .background_upload_button').unbind().on('click', function (event) {
879 addImage(event, $(this).parents('.customize-control-custom_background:first'))
880 })
881
882 $('.customify_background_select').on('change', function () {
883 preview($(this))
884 })
885 }
886
887 // Add a file via the wp.media function
888 function addImage (event, selector) {
889
890 event.preventDefault()
891
892 var frame
893 var jQueryel = jQuery(this)
894
895 // If the media frame already exists, reopen it.
896 if (frame) {
897 frame.open()
898 return
899 }
900
901 // Create the media frame.
902 frame = wp.media({
903 multiple: false,
904 library: {
905 //type: 'image' //Only allow images
906 },
907 // Set the title of the modal.
908 title: jQueryel.data('choose'),
909
910 // Customize the submit button.
911 button: {
912 // Set the text of the button.
913 text: jQueryel.data('update')
914 // Tell the button not to close the modal, since we're
915 // going to refresh the page when the image is selected.
916 }
917 })
918
919 // When an image is selected, run a callback.
920 frame.on('select', function () {
921 // Grab the selected attachment.
922 const attachment = frame.state().get('selection').first()
923 frame.close()
924
925 if (attachment.attributes.type !== 'image') {
926 return
927 }
928
929 selector.find('.upload').attr('value', attachment.attributes.url)
930 selector.find('.upload-id').attr('value', attachment.attributes.id)
931 selector.find('.upload-height').attr('value', attachment.attributes.height)
932 selector.find('.upload-width').attr('value', attachment.attributes.width)
933
934 let thumbSrc = attachment.attributes.url
935 if (!_.isUndefined(attachment.attributes.sizes) && !_.isUndefined(attachment.attributes.sizes.thumbnail)) {
936 thumbSrc = attachment.attributes.sizes.thumbnail.url
937 } else if (!_.isUndefined(attachment.attributes.sizes)) {
938 let height = attachment.attributes.height
939 for (let key in attachment.attributes.sizes) {
940 const object = attachment.attributes.sizes[key]
941 if (object.height < height) {
942 height = object.height
943 thumbSrc = object.url
944 }
945 }
946 } else {
947 thumbSrc = attachment.attributes.icon
948 }
949
950 selector.find('.customify_background_input.background-image').val(attachment.attributes.url)
951
952 if (!selector.find('.upload').hasClass('noPreview')) {
953 selector.find('.preview_screenshot').empty().hide().append('<img class="preview_image" src="' + thumbSrc + '">').slideDown('fast')
954 }
955 //selector.find('.media_upload_button').unbind();
956 selector.find('.remove-image').removeClass('hide')//show "Remove" button
957 selector.find('.customify_background_select').removeClass('hide')//show "Remove" button
958
959 preview(selector)
960 })
961
962 // Finally, open the modal.
963 frame.open()
964 }
965
966 // Update the background preview
967 function preview (selector) {
968
969 let $parent = selector.parents('.customize-control-custom_background:first')
970
971 if (selector.hasClass('customize-control-custom_background')) {
972 $parent = selector
973 }
974
975 if ($parent.length > 0) {
976 $parent = $($parent[0])
977 } else {
978 return
979 }
980
981 const image_holder = $parent.find('.background-preview')
982
983 if (!image_holder) { // No preview present
984 return
985 }
986
987 const the_id = $parent.find('.button.background_upload_button').data('setting_id'),
988 this_setting = api.instance(the_id)
989
990 let background_data = {}
991
992 $parent.find('.customify_background_select, .customify_background_input').each(function () {
993 var data = $(this).serializeArray()
994
995 data = data[0]
996 if (data && data.name.indexOf('[background-') != -1) {
997
998 background_data[$(this).data('select_name')] = data.value
999
1000 //default_default[data.name] = data.value;
1001 //if (data.name == "background-image") {
1002 // css += data.name + ':url("' + data.value + '");';
1003 //} else {
1004 // css += data.name + ':' + data.value + ';';
1005 //}
1006 }
1007 })
1008
1009 api.instance(the_id).set(background_data)
1010 //// Notify the customizer api about this change
1011 api.trigger('change')
1012 api.previewer.refresh()
1013
1014 //image_holder.attr('style', css).fadeIn();
1015 }
1016
1017 // Update the background preview
1018 function removeImage (parent) {
1019 const selector = parent.find('.upload_button_div')
1020 // This shouldn't have been run...
1021 if (!selector.find('.remove-image').addClass('hide')) {
1022 return
1023 }
1024
1025 selector.find('.remove-image').addClass('hide')//hide "Remove" button
1026 parent.find('.customify_background_select').addClass('hide')
1027
1028 selector.find('.upload').val('')
1029 selector.find('.upload-id').val('')
1030 selector.find('.upload-height').val('')
1031 selector.find('.upload-width').val('')
1032 parent.find('.customify_background_input.background-image').val('')
1033
1034 let customizer_id = selector.find('.background_upload_button').data('setting_id'),
1035 this_setting = api.control(customizer_id + '_control'),
1036 current_vals = this_setting.setting(),
1037 screenshot = parent.find('.preview_screenshot'),
1038 to_array = $.map(current_vals, function (value, index) {
1039 return [value]
1040 })
1041
1042 // Hide the screenshot
1043 screenshot.slideUp()
1044 selector.find('.remove-file').unbind()
1045 to_array['background-image'] = ''
1046 this_setting.setting(to_array)
1047 }
1048
1049 return {
1050 init: init
1051 }
1052 }
1053 )(jQuery)
1054
1055 /** HELPERS **/
1056
1057 /**
1058 * Function to check if a value exists in an object
1059 * @param value
1060 * @param obj
1061 * @returns {boolean}
1062 */
1063 const inObject = function (value, obj) {
1064 for (let k in obj) {
1065 if (!obj.hasOwnProperty(k)) {
1066 continue
1067 }
1068 if (_.isEqual(obj[k], value)) {
1069 return true
1070 }
1071 }
1072 return false
1073 }
1074
1075 const maybeJsonParse = function (value) {
1076 let parsed
1077
1078 //try and parse it, with decodeURIComponent
1079 try {
1080 parsed = JSON.parse(decodeURIComponent(value))
1081 } catch (e) {
1082
1083 // in case of an error, treat is as a string
1084 parsed = value
1085 }
1086
1087 return parsed
1088 }
1089
1090 const getUrlVars = function (name) {
1091 let vars = [], hash
1092 const hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&')
1093 for (let i = 0; i < hashes.length; i++) {
1094 hash = hashes[i].split('=')
1095
1096 vars.push(hash[0])
1097 vars[hash[0]] = hash[1]
1098 }
1099
1100 if (!_.isUndefined(vars[name])) {
1101 return vars[name]
1102 }
1103 return false
1104 }
1105
1106 const isJsonString = function (str) {
1107 try {
1108 JSON.parse(str)
1109 } catch (e) {
1110 return false
1111 }
1112 return true
1113 }
1114 }
1115 )(jQuery, window, wp)
1116