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

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