PluginProbe
Customify / 1.4.1
Customify v1.4.1
2.10.9 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.7.1 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.6.0 1.6.0.1 1.6.5 1.7.0 1.7.1 All 77 releases
customify / js / customizer.js

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

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