PluginProbe
Customify / 1.5.7
Customify v1.5.7
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 1.5.7, at js/customizer.js

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