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

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

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