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

1,715 lines 54.1 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 // add ace editors
11 $( '.customify_ace_editor' ).each( function( key, el ) {
12 var id = $( this ).attr( 'id' ),
13 css_editor = ace.edit( id );
14
15 var editor_type = $( this ).data( 'editor_type' );
16 // init the ace editor
17 css_editor.setTheme( "ace/theme/github" );
18 css_editor.getSession().setMode( "ace/mode/" + editor_type );
19
20 // hide the textarea and enable the ace editor
21 var textarea = $( '#' + id + '_textarea' ).hide();
22 css_editor.getSession().setValue( textarea.val() );
23
24 // each time a change is triggered start a timeout of 1,5s and when is finished refresh the previewer
25 // if the user types faster than this delay then reset it
26 css_editor.getSession().on( 'change', function( e ) {
27 if ( timeout !== null ) {
28 clearTimeout( timeout );
29 timeout = null;
30 } else {
31 timeout = setTimeout( function() {
32 //var state = css_editor.session.getState();
33 textarea.val( css_editor.getSession().getValue() );
34 textarea.trigger( 'change' );
35 }, 1500 );
36 }
37 } );
38 } );
39
40 // simple select2 field
41 $( '.customify_select2' ).select2();
42
43 setTimeout( function() {
44 customifyFontSelect.init( this );
45 }, 333 );
46
47 prepare_typography_field();
48
49 /**
50 * Make the customizer save on CMD/CTRL+S action
51 * This is awesome!!!
52 */
53 $( window ).bind( 'keydown', function( event ) {
54 if ( event.ctrlKey || event.metaKey ) {
55 switch ( String.fromCharCode( event.which ).toLowerCase() ) {
56 case 's':
57 event.preventDefault();
58 api.previewer.save();
59 break;
60 }
61 }
62 } );
63
64 // for each range input add a value preview output
65 $( '.accordion-section-content[id*="' + customify_settings.options_name + '"]' ).each( function() {
66
67 // Initialize range fields logic
68 customifyHandleRangeFields( this );
69 } );
70
71 if ( $( 'button[data-action="reset_customify"]' ).length > 0 ) {
72 // reset_button
73 $( document ).on( 'click', '#customize-control-reset_customify button', function( ev ) {
74 ev.preventDefault();
75
76 var iAgree = confirm( 'Do you really want to reset to defaults all the fields? Watch out, this will reset all your Customify options and will save them!' );
77
78 if ( ! iAgree ) {
79 return;
80 }
81
82 $.each( api.settings.controls, function( key, ctrl ) {
83 var setting_id = key.replace( '_control', '' );
84 var setting = customify_settings.settings[setting_id];
85
86 if ( ! _.isUndefined( setting ) && ! _.isUndefined( setting.default ) ) {
87 api_set_setting_value( setting_id, setting.default );
88 }
89 } );
90
91 api.previewer.save();
92 } );
93
94 // add a reset button for each panel
95 $( '.panel-meta' ).each( function( el, key ) {
96 var container = $( this ).parents( '.control-panel' ),
97 id = container.attr( 'id' );
98
99 if ( typeof id !== 'undefined' ) {
100 var panel_id = id.replace( 'accordion-panel-', '' );
101 $( this ).parent().append( '<button class="reset_panel button" data-panel="' + panel_id + '">Panel\'s defaults</button>' );
102 }
103 } );
104
105 // reset panel
106 $( document ).on( 'click', '.reset_panel', function( e ) {
107 e.preventDefault();
108
109 var panel_id = $( this ).data( 'panel' ),
110 panel = api.panel( panel_id ),
111 sections = panel.sections(),
112 iAgree = confirm( "Do you really want to reset " + panel.params.title + "?" );
113
114 if ( ! iAgree ) {
115 return;
116 }
117 if ( sections.length > 0 ) {
118 $.each( sections, function() {
119 //var settings = this.settings();
120 var controls = this.controls();
121
122 if ( controls.length > 0 ) {
123 $.each( controls, function( key, ctrl ) {
124 var setting_id = ctrl.id.replace( '_control', '' ),
125 setting = customify_settings.settings[setting_id];
126
127 if ( ! _.isUndefined( setting ) && ! _.isUndefined( setting.default ) ) {
128 api_set_setting_value( setting_id, setting.default );
129 }
130 } );
131 }
132 } );
133 }
134 } );
135
136 //add reset section
137 $( '.accordion-section-content' ).each( function( el, key ) {
138 var section_id = $( this ).attr( 'id' );
139
140 if ( (
141 (
142 ! _.isUndefined( section_id )
143 ) ? section_id.indexOf( customify_settings.options_name ) : - 1
144 ) === - 1 ) {
145 return;
146 }
147
148 if ( ! _.isUndefined( section_id ) && section_id.indexOf( 'sub-accordion-section-' ) > - 1 ) {
149 var id = section_id.replace( 'sub-accordion-section-', '' );
150 $( this ).append( '<button class="reset_section button" data-section="' + id + '">Reset All Options for This Section</button>' );
151 }
152 } );
153
154 // reset section event
155 $( document ).on( 'click', '.reset_section', function( e ) {
156 e.preventDefault();
157
158 var section_id = $( this ).data( 'section' ),
159 section = api.section( section_id ),
160 controls = section.controls();
161
162 var iAgree = confirm( "Do you really want to reset " + section.params.title + "?" );
163
164 if ( ! iAgree ) {
165 return;
166 }
167
168 if ( controls.length > 0 ) {
169 $.each( controls, function( key, ctrl ) {
170 var setting_id = ctrl.id.replace( '_control', '' ),
171 setting = customify_settings.settings[setting_id];
172
173 if ( ! _.isUndefined( setting ) && ! _.isUndefined( setting.default ) ) {
174 api_set_setting_value( setting_id, setting.default );
175 }
176 } );
177 }
178 } );
179 }
180
181 $( document ).on( 'change keyup', '.customize-control-range input.range-value', function() {
182 var range = $( this ).siblings( 'input[type="range"]' );
183 range.val( $( this ).val() );
184 range.trigger( 'change' );
185 } );
186
187 $( document ).on( 'change', '.customify_typography_font_subsets', function( ev ) {
188
189 var $input = $( this ).parents( '.options' ).siblings( '.customify_typography' ).children( '.customify_typography_values' ),
190 current_val = $input.val();
191
192 current_val = JSON.parse( decodeURIComponent( current_val ) );
193
194 //maybe the selected option holds a JSON in its value
195 current_val.selected_subsets = maybeJsonParse( $( this ).val() );
196
197 $input.val( encodeURIComponent( JSON.stringify( current_val ) ) );
198
199 $input.trigger( 'change' );
200 } );
201
202 $( document ).on( 'change', '.customify_typography_font_weight', function( ev ) {
203
204 var $input = $( this ).parents( '.options' ).siblings( '.customify_typography' ).children( '.customify_typography_values' ),
205 current_val = $input.val();
206
207 current_val = maybeJsonParse( current_val );
208 // @todo currently the font weight selector works for one value only
209 // maybe make this a multiselect
210
211 //maybe the selected option holds a JSON in its value
212 current_val.selected_variants = {0: maybeJsonParse( $( this ).val() )};
213
214 $input.val( encodeURIComponent( JSON.stringify( current_val ) ) );
215 $input.trigger( 'change' );
216 } );
217
218 $( 'body' ).on( 'customify:preset-change', function( e ) {
219 const data = $( e.target ).data( 'options' );
220
221 if ( ! _.isUndefined( data ) ) {
222 $.each( data, function( setting_id, value ) {
223 api_set_setting_value( setting_id, value );
224 } );
225 }
226 } );
227
228 $( document ).on( 'change', '.customify_preset.select', function() {
229 const $source = $( this );
230 const $target = $source.children( '[value="' + $source.val() + '"]' );
231 $target.trigger( 'customify:preset-change' );
232 } );
233
234 $( document ).on( 'click', '.customify_preset.radio input, .customify_preset.radio_buttons input, .awesome_presets input', function() {
235 $( this ).trigger( 'customify:preset-change' );
236 } );
237
238 // bind our event on click
239 $( document ).on( 'click', '.customify_import_demo_data_button', function( event ) {
240 let key = $( this ).data( 'key' );
241 let import_queue = new Queue( api );
242 let steps = [];
243
244 if ( ! _.isUndefined( customify_settings.settings[key].imports ) ) {
245
246 $.each( customify_settings.settings[key].imports, function( i, import_setts, k ) {
247 if ( _.isUndefined( import_setts.steps ) ) {
248 steps.push( {id: i, type: import_setts.type} );
249 } else {
250 var count = import_setts.steps;
251
252 while ( count >= 1 ) {
253 steps.push( {id: i, type: import_setts.type, count: count} );
254 count = count - 1;
255 }
256 }
257 } );
258 }
259
260 import_queue.add_steps( 'import_demo_data_action_id', steps );
261 return false;
262 } );
263
264 customifyBackgroundJsControl.init();
265
266 // sometimes a php save may be needed
267 if ( getUrlVars( 'save_customizer_once' ) ) {
268 api.previewer.save();
269 }
270
271 setTimeout( function() {
272 customifyFoldingFields();
273 }, 1000 );
274
275
276 // Handle the section tabs (ex: Layout | Fonts | Colors)
277 (
278 function() {
279 var $navs = $( '.js-section-navigation' );
280
281 $navs.each( function() {
282 var $nav = $( this );
283 var $title = $nav.parents( '.accordion-section-content' ).find( '.customize-section-title' );
284
285 $nav.closest( '.customize-control' ).addClass( 'screen-reader-text' );
286 $title.append( $nav ).parent().addClass( 'has-nav' );
287 } );
288
289 $( '.js-section-navigation a' ).on( 'click', function( e ) {
290 e.preventDefault();
291
292 var $sidebar = $( this ).parents( '.customize-pane-child' );
293 var $parent = $( this ).parents( '.accordion-section-content' );
294 var href = $.attr( this, 'href' );
295
296 if ( href != '#' ) {
297 $sidebar.animate( {
298 scrollTop: $( $.attr( this, 'href' ) ).position().top - $parent.find( '.customize-section-title' ).outerHeight()
299 }, 500 );
300 }
301 } );
302 }
303 )();
304
305 (
306 function() {
307 // Close a font field when clicking on another field
308 $( '.customify_font_tooltip' ).on( 'click', function() {
309 if ( $( this ).prop( 'checked' ) === true ) {
310 $( '.customify_font_tooltip' ).prop( 'checked', false );
311 $( this ).prop( 'checked', true );
312 }
313 } );
314 }
315 )();
316 } );
317
318 const customifyHandleRangeFields = function( el ) {
319
320 // For each range input add a number field (for preview mainly - but it can also be used for input)
321 $( el ).find( 'input[type="range"]' ).each( function() {
322 if ( ! $( this ).siblings( '.range-value' ).length ) {
323 var $clone = $( this ).clone();
324
325 $clone
326 .attr( 'type', 'number' )
327 .attr( 'class', 'range-value' )
328 .removeAttr( 'data-field' );
329
330 $( this ).after( $clone );
331 }
332
333 // Update the number field when changing the range
334 $( this ).on( 'change', function() {
335 $( this ).siblings( '.range-value' ).val( $( this ).val() );
336 } );
337
338 // And the other way around, update the range field when changing the number
339 $( $clone ).on( 'change', function() {
340 $( this ).siblings( 'input[type="range"]' ).val( $( this ).val() );
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 * @TODO 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 (
452 value.indexOf( $( target_selector + ':checked' ).val() ) !== - 1
453 );
454 } );
455 } else { // in any other case we use a simple == comparison
456 $( parent_id ).reactIf( target_selector, function() {
457 return $( target_selector + ':checked' ).val() == value;
458 } );
459 }
460 break;
461
462 case 'range':
463 var x = IS.Between( between[0], between[1] );
464
465 $( parent_id ).reactIf( target_selector, x );
466 break;
467
468 default:
469 // in case of an array of values we use the ( val in array) condition
470 if ( _.isObject( value ) ) {
471 $( parent_id ).reactIf( target_selector, function() {
472 return (
473 value.indexOf( $( target_selector ).val() ) !== - 1
474 );
475 } );
476 } else { // in any other case we use a simple == comparison
477 $( parent_id ).reactIf( target_selector, function() {
478 return $( target_selector ).val() == value;
479 } );
480 }
481 break;
482 }
483
484 $( target_selector ).trigger( 'change' );
485 $( '.reactor' ).trigger( 'change.reactor' ); // triggers all events on load
486 };
487
488 $.each( customify_settings.settings, function( id, field ) {
489 /**
490 * Here we have the id of the fields. but we know for sure that we just need his parent selector
491 * So we just create it
492 */
493 var parent_id = id.replace( '[', '-' );
494 parent_id = parent_id.replace( ']', '' );
495 parent_id = '#customize-control-' + parent_id + '_control';
496
497 // get only the fields that have a 'show_if' property
498 if ( field.hasOwnProperty( 'show_if' ) ) {
499 var relation = 'AND';
500
501 if ( ! _.isUndefined( field.show_if.relation ) ) {
502 relation = field.show_if.relation;
503 // remove the relation property, we need the config to be array based only
504 delete field.show_if.relation;
505 }
506
507 /**
508 * The 'show_if' can be a simple array with one target like: [ id, value, comparison, action ]
509 * Or it could be an array of multiple targets and we need to process both cases
510 */
511
512 if ( ! _.isUndefined( field.show_if.id ) ) {
513 bind_folding_events( parent_id, field.show_if, relation );
514 } else if ( _.isObject( field.show_if ) ) {
515 $.each( field.show_if, function( i, j ) {
516 bind_folding_events( parent_id, j, relation );
517 } );
518 }
519 }
520 } );
521 };
522
523 var get_typography_font_family = function( $el ) {
524
525 var font_family_value = $el.val();
526 // first time this will not be a json so catch that error
527 try {
528 font_family_value = JSON.parse( font_family_value );
529 } catch ( e ) {
530 return {font_family: font_family_value};
531 }
532
533 if ( ! _.isUndefined( font_family_value.font_family ) ) {
534 return font_family_value.font_family;
535 }
536
537 return false;
538 };
539
540 // get each typography field and bind events
541 // @todo Are we still using the typography field since we have the font field?
542 var prepare_typography_field = function() {
543
544 var $typos = $( '.customify_typography_font_family' );
545
546 $typos.each( function() {
547 var font_family_select = this,
548 $input = $( font_family_select ).siblings( '.customify_typography_values' );
549 // on change
550 $( font_family_select ).on( 'change', function() {
551 update_siblings_selects( font_family_select );
552 $input.trigger( 'change' );
553 } );
554 update_siblings_selects( font_family_select );
555 } );
556 };
557
558 var api_set_setting_value = function( setting_id, value ) {
559 let setting = api( setting_id ),
560 field = $( '[data-customize-setting-link="' + setting_id + '"]' ),
561 field_class = $( field ).parent().attr( 'class' );
562
563 // Legacy field type
564 if ( ! _.isUndefined( field_class ) && field_class === 'customify_typography' ) {
565
566 let family_select = field.siblings( 'select' );
567
568 if ( _.isString( value ) ) {
569 let this_option = family_select.find( 'option[value="' + value + '"]' );
570 $( this_option[0] ).attr( 'selected', 'selected' );
571 update_siblings_selects( family_select );
572 } else if ( _.isObject( value ) ) {
573 let this_family_option = family_select.find( 'option[value="' + value['font_family'] + '"]' );
574
575 $( this_family_option[0] ).attr( 'selected', 'selected' );
576
577 update_siblings_selects( this_family_option );
578
579 setTimeout( function() {
580 let weight_select = field.parent().siblings( '.options' ).find( '.customify_typography_font_weight' ),
581 this_weight_option = weight_select.find( 'option[value="' + value['selected_variants'] + '"]' );
582
583 $( this_weight_option[0] ).attr( 'selected', 'selected' );
584
585 update_siblings_selects( this_family_option );
586
587 weight_select.trigger( 'change' );
588 }, 300 );
589 }
590
591 family_select.trigger( 'change' );
592
593 } else if ( ! _.isUndefined( field_class ) && field_class === 'font-options__wrapper' ) {
594
595 // if the value is a simple string it must be the font family
596 if ( _.isString( value ) ) {
597 let option = field.parent().find( 'option[value="' + value + '"]' );
598
599 option.attr( 'selected', 'selected' );
600 // option.parents('select').trigger('change');
601 } else if ( _.isObject( value ) ) {
602 // Find the options list wrapper
603 let optionsList = field.parent().children( '.font-options__options-list' );
604
605 if ( optionsList.length ) {
606 // We will process each font property and update it
607 _.each( value, function( val, key ) {
608 // We need to map the keys to the data attributes we are using - I know :(
609 let mappedKey = key;
610 switch ( key ) {
611 case 'font-family':
612 mappedKey = 'font_family';
613 break;
614 case 'font-size':
615 mappedKey = 'font_size';
616 break;
617 case 'font-weight':
618 mappedKey = 'selected_variants';
619 break;
620 case 'letter-spacing':
621 mappedKey = 'letter_spacing';
622 break;
623 case 'text-transform':
624 mappedKey = 'text_transform';
625 break;
626 default:
627 break;
628 }
629 let subField = optionsList.find( '[data-field="' + mappedKey + '"]' );
630 if ( subField.length ) {
631 subField.val( val );
632 subField.trigger( 'change' );
633 }
634 } );
635 }
636 }
637
638 } else {
639 setting.set( value );
640 }
641 };
642
643 var update_siblings_selects = function( font_select ) {
644 var selected_font = $( font_select ).val(),
645 $input = $( font_select ).siblings( '.customify_typography_values' ),
646 current_val = $input.attr( 'value' );
647
648 if ( current_val === '[object Object]' ) {
649 current_val = $input.data( 'default' );
650 } else if ( _.isString( current_val ) && ! isJsonString( current_val ) && current_val.substr( 0, 1 ) == '[' ) {
651 // a rare case when the value isn't a json but is a representative string like [family,weight]
652 current_val = current_val.split( ',' );
653 var new_current_value = {};
654 if ( ! _.isUndefined( current_val[0] ) ) {
655 new_current_value['font_family'] = current_val[0];
656 }
657
658 if ( ! _.isUndefined( current_val[1] ) ) {
659 new_current_value['selected_variants'] = current_val[1];
660 }
661
662 current_val = JSON.stringify( new_current_value );
663 }
664
665 var $font_weight = $( font_select ).parent().siblings( 'ul.options' ).find( '.customify_typography_font_weight' ),
666 $font_subsets = $( font_select ).parent().siblings( 'ul.options' ).find( '.customify_typography_font_subsets' );
667
668 try {
669 current_val = JSON.parse( decodeURIComponent( current_val ) );
670 } catch ( e ) {
671
672 // in case of an error, force the rebuild of the json
673 if ( _.isUndefined( $( font_select ).data( 'bound_once' ) ) ) {
674
675 $( font_select ).data( 'bound_once', true );
676
677 $( font_select ).change();
678 $font_weight.change();
679 $font_subsets.change();
680 }
681 }
682
683 // first try to get the font from sure sources, not from the recommended list.
684 var option_data = $( font_select ).find( ':not(optgroup[label=Recommended]) option[value="' + selected_font + '"]' );
685 // however, if there isn't an option found, get what you can
686 if ( option_data.length < 1 ) {
687 option_data = $( font_select ).find( 'option[value="' + selected_font + '"]' );
688 }
689
690 if ( option_data.length > 0 ) {
691
692 var font_type = option_data.data( 'type' ),
693 value_to_add = {'type': font_type, 'font_family': selected_font},
694 variants = null,
695 subsets = null;
696
697 if ( font_type == 'std' ) {
698 variants = {
699 0: '100',
700 1: '200',
701 3: '300',
702 4: '400',
703 5: '500',
704 6: '600',
705 7: '700',
706 8: '800',
707 9: '900'
708 };
709 if ( ! _.isUndefined( $( option_data[0] ).data( 'variants' ) ) ) {
710 //maybe the variants are a JSON
711 variants = maybeJsonParse( $( option_data[0] ).data( 'variants' ) );
712 }
713 } else {
714 //maybe the variants are a JSON
715 variants = maybeJsonParse( $( option_data[0] ).data( 'variants' ) );
716
717 //maybe the subsets are a JSON
718 subsets = maybeJsonParse( $( option_data[0] ).data( 'subsets' ) );
719 }
720
721 // make the variants selector
722 if ( ! _.isUndefined( variants ) && ! _.isNull( variants ) && ! _.isEmpty( variants ) ) {
723
724 value_to_add['variants'] = variants;
725 // when a font is selected force the first weight to load
726 value_to_add['selected_variants'] = {0: variants[0]};
727
728 var variants_options = '',
729 count_weights = 0;
730
731 if ( _.isArray( variants ) || _.isObject( variants ) ) {
732 // Take each variant and produce the option markup
733 $.each( variants, function( key, el ) {
734 var is_selected = '';
735 if ( _.isObject( current_val.selected_variants ) && inObject( el, current_val.selected_variants ) ) {
736 is_selected = ' selected="selected"';
737 } else if ( _.isString( current_val.selected_variants ) && el === current_val.selected_variants ) {
738 is_selected = ' selected="selected"';
739 }
740
741 // initialize
742 var variant_option_value = el,
743 variant_option_display = el;
744
745 // If we are dealing with a object variant then it means things get tricky (probably it's our fault but bear with us)
746 // This probably comes from our Fonto plugin - a font with individually named variants - hence each has its own font-family
747 if ( _.isObject( el ) ) {
748 //put the entire object in the variation value - we will need it when outputting the custom CSS
749 variant_option_value = encodeURIComponent( JSON.stringify( el ) );
750 variant_option_display = '';
751
752 //if we have weight and style then "compose" them into something standard
753 if ( ! _.isUndefined( el['font-weight'] ) ) {
754 variant_option_display += el['font-weight'];
755 }
756
757 if ( _.isString( el['font-style'] ) && $.inArray( el['font-style'].toLowerCase(), [
758 "normal",
759 "regular"
760 ] ) < 0 ) { //this comparison means it hasn't been found
761 variant_option_display += el['font-style'];
762 }
763 }
764
765 variants_options += '<option value="' + variant_option_value + '"' + is_selected + '>' + variant_option_display + '</option>';
766 count_weights ++;
767 } );
768 }
769
770 if ( ! _.isUndefined( $font_weight ) ) {
771 $font_weight.html( variants_options );
772 // if there is no weight or just 1 we hide the weight select ... cuz is useless
773 if ( $( font_select ).data( 'load_all_weights' ) === true || count_weights <= 1 ) {
774 $font_weight.parent().css( 'display', 'none' );
775 } else {
776 $font_weight.parent().css( 'display', 'inline-block' );
777 }
778 }
779 } else if ( ! _.isUndefined( $font_weight ) ) {
780 $font_weight.parent().css( 'display', 'none' );
781 }
782
783 // make the subsets selector
784 if ( ! _.isUndefined( subsets ) && ! _.isNull( subsets ) && ! _.isEmpty( subsets ) ) {
785
786 value_to_add['subsets'] = subsets;
787 // when a font is selected force the first subset to load
788 value_to_add['selected_subsets'] = {0: subsets[0]};
789 var subsets_options = '',
790 count_subsets = 0;
791 $.each( subsets, function( key, el ) {
792 var is_selected = '';
793 if ( _.isObject( current_val.selected_subsets ) && inObject( el, current_val.selected_subsets ) ) {
794 is_selected = ' selected="selected"';
795 }
796
797 subsets_options += '<option value="' + el + '"' + is_selected + '>' + el + '</option>';
798 count_subsets ++;
799 } );
800
801 if ( ! _.isUndefined( $font_subsets ) ) {
802 $font_subsets.html( subsets_options );
803
804 // if there is no subset or just 1 we hide the subsets select ... cuz is useless
805 if ( count_subsets <= 1 ) {
806 $font_subsets.parent().css( 'display', 'none' );
807 } else {
808 $font_subsets.parent().css( 'display', 'inline-block' );
809 }
810 }
811 } else if ( ! _.isUndefined( $font_subsets ) ) {
812 $font_subsets.parent().css( 'display', 'none' );
813 }
814
815 $input.val( encodeURIComponent( JSON.stringify( value_to_add ) ) );
816 }
817 };
818
819 /** Modules **/
820
821 var customifyBackgroundJsControl = (
822 function() {
823 "use strict";
824
825 function init() {
826 // Remove the image button
827 $( '.customize-control-custom_background .remove-image, .customize-control-custom_background .remove-file' ).unbind( 'click' ).on( 'click', function( e ) {
828 removeImage( $( this ).parents( '.customize-control-custom_background:first' ) );
829 preview( $( this ) );
830 return false;
831 } );
832
833 // Upload media button
834 $( '.customize-control-custom_background .background_upload_button' ).unbind().on( 'click', function( event ) {
835 addImage( event, $( this ).parents( '.customize-control-custom_background:first' ) );
836 } );
837
838 $( '.customify_background_select' ).on( 'change', function() {
839 preview( $( this ) );
840 } );
841 }
842
843 // Add a file via the wp.media function
844 function addImage( event, selector ) {
845
846 event.preventDefault();
847
848 var frame;
849 var jQueryel = jQuery( this );
850
851 // If the media frame already exists, reopen it.
852 if ( frame ) {
853 frame.open();
854 return;
855 }
856
857 // Create the media frame.
858 frame = wp.media( {
859 multiple: false,
860 library: {
861 //type: 'image' //Only allow images
862 },
863 // Set the title of the modal.
864 title: jQueryel.data( 'choose' ),
865
866 // Customize the submit button.
867 button: {
868 // Set the text of the button.
869 text: jQueryel.data( 'update' )
870 // Tell the button not to close the modal, since we're
871 // going to refresh the page when the image is selected.
872 }
873 } );
874
875 // When an image is selected, run a callback.
876 frame.on( 'select', function() {
877 // Grab the selected attachment.
878 var attachment = frame.state().get( 'selection' ).first();
879 frame.close();
880
881 if ( attachment.attributes.type !== "image" ) {
882 return;
883 }
884
885 selector.find( '.upload' ).attr( 'value', attachment.attributes.url );
886 selector.find( '.upload-id' ).attr( 'value', attachment.attributes.id );
887 selector.find( '.upload-height' ).attr( 'value', attachment.attributes.height );
888 selector.find( '.upload-width' ).attr( 'value', attachment.attributes.width );
889
890 var thumbSrc = attachment.attributes.url;
891 if ( ! _.isUndefined( attachment.attributes.sizes ) && ! _.isUndefined( attachment.attributes.sizes.thumbnail ) ) {
892 thumbSrc = attachment.attributes.sizes.thumbnail.url;
893 } else if ( ! _.isUndefined( attachment.attributes.sizes ) ) {
894 var height = attachment.attributes.height;
895 for ( var key in attachment.attributes.sizes ) {
896 var object = attachment.attributes.sizes[key];
897 if ( object.height < height ) {
898 height = object.height;
899 thumbSrc = object.url;
900 }
901 }
902 } else {
903 thumbSrc = attachment.attributes.icon;
904 }
905
906 selector.find( '.customify_background_input.background-image' ).val( attachment.attributes.url );
907
908 if ( ! selector.find( '.upload' ).hasClass( 'noPreview' ) ) {
909 selector.find( '.preview_screenshot' ).empty().hide().append( '<img class="preview_image" src="' + thumbSrc + '">' ).slideDown( 'fast' );
910 }
911 //selector.find('.media_upload_button').unbind();
912 selector.find( '.remove-image' ).removeClass( 'hide' );//show "Remove" button
913 selector.find( '.customify_background_select' ).removeClass( 'hide' );//show "Remove" button
914
915 preview( selector );
916 } );
917
918 // Finally, open the modal.
919 frame.open();
920 }
921
922 // Update the background preview
923 function preview( selector ) {
924
925 var $parent = selector.parents( '.customize-control-custom_background:first' );
926
927 if ( selector.hasClass( 'customize-control-custom_background' ) ) {
928 $parent = selector;
929 }
930
931 if ( $parent.length > 0 ) {
932 $parent = $( $parent[0] );
933 } else {
934 return;
935 }
936
937 var image_holder = $parent.find( '.background-preview' );
938
939 if ( ! image_holder ) { // No preview present
940 return;
941 }
942
943 var the_id = $parent.find( '.button.background_upload_button' ).data( 'setting_id' ),
944 this_setting = api.instance( the_id );
945
946 var background_data = {};
947
948 $parent.find( '.customify_background_select, .customify_background_input' ).each( function() {
949 var data = $( this ).serializeArray();
950
951 data = data[0];
952 if ( data && data.name.indexOf( '[background-' ) != - 1 ) {
953
954 background_data[$( this ).data( 'select_name' )] = data.value;
955
956 //default_default[data.name] = data.value;
957 //if (data.name == "background-image") {
958 // css += data.name + ':url("' + data.value + '");';
959 //} else {
960 // css += data.name + ':' + data.value + ';';
961 //}
962 }
963 } );
964
965 api.instance( the_id ).set( background_data );
966 //// Notify the customizer api about this change
967 api.trigger( 'change' );
968 api.previewer.refresh();
969
970 //image_holder.attr('style', css).fadeIn();
971 }
972
973 // Update the background preview
974 function removeImage( parent ) {
975 var selector = parent.find( '.upload_button_div' );
976 // This shouldn't have been run...
977 if ( ! selector.find( '.remove-image' ).addClass( 'hide' ) ) {
978 return;
979 }
980
981 selector.find( '.remove-image' ).addClass( 'hide' );//hide "Remove" button
982 parent.find( '.customify_background_select' ).addClass( 'hide' );
983
984 selector.find( '.upload' ).val( '' );
985 selector.find( '.upload-id' ).val( '' );
986 selector.find( '.upload-height' ).val( '' );
987 selector.find( '.upload-width' ).val( '' );
988 parent.find( '.customify_background_input.background-image' ).val( '' );
989
990 var customizer_id = selector.find( '.background_upload_button' ).data( 'setting_id' ),
991 this_setting = api.control( customizer_id + '_control' ),
992 current_vals = this_setting.setting(),
993 screenshot = parent.find( '.preview_screenshot' ),
994 to_array = $.map( current_vals, function( value, index ) {
995 return [value];
996 } );
997
998 // Hide the screenshot
999 screenshot.slideUp();
1000 selector.find( '.remove-file' ).unbind();
1001 to_array['background-image'] = '';
1002 this_setting.setting( to_array );
1003 }
1004
1005 return {
1006 init: init
1007 }
1008 }
1009 )( jQuery );
1010
1011 // This is for the Font control
1012 var customifyFontSelect = (
1013 function() {
1014 const
1015 wrapperSelector = '.font-options__wrapper',
1016 valueHolderSelector = '.customify_font_values',
1017 fontFamilySelector = '.customify_font_family',
1018 fontWeightSelector = '.customify_font_weight',
1019 fontSubsetsSelector = '.customify_font_subsets',
1020 selectPlaceholder = "Select a font family",
1021 weightPlaceholder = "Select a font weight",
1022 subsetPlaceholder = "Extra Subsets";
1023
1024 // We will use this to remember that we are self-updating the field from the subfields.
1025 // We will save this info for each setting ID.
1026 var updatingValue = {},
1027 loadingValue = {};
1028
1029 function init( wpapi ) {
1030 let $fontFamilyFields = $( fontFamilySelector );
1031
1032 // Initialize the select2 field for the font family
1033 $fontFamilyFields.select2( {
1034 placeholder: selectPlaceholder
1035 } ).on( 'change', function( e ) {
1036 let new_option = $( e.target ).find( 'option:selected' ),
1037 wrapper = $( e.target ).closest( wrapperSelector );
1038
1039 // Update the weight subfield with the new options given by the selected font family.
1040 update_weight_field( new_option, wrapper );
1041
1042 // Update the subset subfield with the new options given by the selected font family.
1043 update_subset_field( new_option, wrapper );
1044
1045 // Serialize subfield values and refresh the fonts in the preview window.
1046 update_font_value( wrapper );
1047 } );
1048
1049 // Initialize the select2 field for the font weight
1050 $( fontWeightSelector ).each( function( i, el ) {
1051
1052 let select2_args = {
1053 placeholder: weightPlaceholder
1054 };
1055
1056 // all this fuss is for the case when the font doesn't come with variants from PHP, like a theme_font
1057 if ( this.options.length === 0 ) {
1058 var wrapper = $( el ).closest( wrapperSelector ),
1059 font = wrapper.find( fontFamilySelector ),
1060 option = font[0].options[font[0].selectedIndex],
1061 variants = maybeJsonParse( $( option ).data( 'variants' ) ),
1062 data = [],
1063 selecter_variants = $( el ).data( 'default' ) || null;
1064
1065 if ( typeof variants === "undefined" ) {
1066 $( this ).hide();
1067 return;
1068 }
1069
1070 $.each( variants, function( index, weight ) {
1071 let this_value = {
1072 id: weight,
1073 text: weight
1074 };
1075
1076 if ( selecter_variants !== null && weight == selecter_variants ) {
1077 this_value.selected = true;
1078 }
1079
1080 data.push( this_value );
1081 } );
1082
1083 if ( data !== [] ) {
1084 select2_args.data = data;
1085 }
1086 }
1087
1088 $( this ).select2(
1089 select2_args
1090 ).on( 'change', function( e ) {
1091 let wrapper = $( e.target ).closest( wrapperSelector );
1092
1093 // Serialize subfield values and refresh the fonts in the preview window.
1094 update_font_value( wrapper );
1095 } );
1096 } );
1097
1098 // Initialize the select2 field for the font subsets
1099 $( fontSubsetsSelector )
1100 .select2( {
1101 placeholder: subsetPlaceholder
1102 } )
1103 .on( 'change', function( e ) {
1104 let wrapper = $( e.target ).closest( wrapperSelector );
1105
1106 // Serialize subfield values and refresh the fonts in the preview window.
1107 update_font_value( wrapper );
1108 } );
1109
1110 let rangers = $fontFamilyFields.parents( wrapperSelector ).find( 'input[type=range]' ),
1111 selects = $fontFamilyFields.parents( wrapperSelector ).find( 'select' ).not( "select[class*=' select2'],select[class^='select2']" );
1112
1113 // Initialize the all the regular selects in the font controls
1114 if ( selects.length > 0 ) {
1115 selects.on( 'change', function( e ) {
1116 let wrapper = $( e.target ).closest( wrapperSelector );
1117
1118 // Serialize subfield values and refresh the fonts in the preview window.
1119 update_font_value( wrapper );
1120 } );
1121 }
1122
1123 // Initialize the all the range fields in the font controls
1124 if ( rangers.length > 0 ) {
1125 rangers.on( 'change', function( e ) {
1126 let wrapper = $( e.target ).closest( wrapperSelector );
1127
1128 // Serialize subfield values and refresh the fonts in the preview window.
1129 update_font_value( wrapper );
1130
1131 wp.customize.previewer.send( 'font-changed' );
1132 } );
1133 }
1134
1135 // When the previewer window is ready, render the fonts
1136 var self = this;
1137 wp.customize.previewer.bind( 'ready', function() {
1138 self.render_fonts();
1139 } );
1140
1141 // Handle the reverse value direction, when the customize setting is updated and the subfields need to update their values.
1142 $fontFamilyFields.each( function( i, el ) {
1143 let wrapper = $( el ).closest( wrapperSelector ),
1144 value_holder = wrapper.children( valueHolderSelector ),
1145 setting_id = $( value_holder ).data( 'customize-setting-link' ),
1146 setting = wp.customize( setting_id );
1147
1148 setting.bind( function( newValue, oldValue ) {
1149 if ( ! updatingValue[this.id] ) {
1150 value_holder.val( newValue );
1151
1152 load_font_value( wrapper );
1153 }
1154 } )
1155 } )
1156 }
1157
1158 /**
1159 * This function updates the data in font weight selector from the given <option> element
1160 *
1161 * @param option
1162 * @param wraper
1163 */
1164 function update_weight_field( option, wraper ) {
1165 let variants = $( option ).data( 'variants' ),
1166 font_weights = wraper.find( fontWeightSelector ),
1167 selected_variant = font_weights.data( 'default' ),
1168 new_variants = [],
1169 id = wraper.find( valueHolderSelector ).data( 'customizeSettingLink' );
1170
1171 variants = maybeJsonParse( variants );
1172
1173 if ( customify_settings.settings[id].load_all_weights || typeof variants === "undefined" || Object.keys( variants ).length < 2 ) {
1174 font_weights.parent().hide();
1175 } else {
1176 font_weights.parent().show();
1177 }
1178
1179 // we need to turn the data array into a specific form like [{id:"id", text:"Text"}]
1180 $.each( variants, function( index, variant ) {
1181 new_variants[index] = {
1182 'id': variant,
1183 'text': variant
1184 };
1185
1186 if ( selected_variant == variant ) {
1187 new_variants[index].selected = true;
1188 }
1189 } );
1190
1191 // We need to clear the old select2 field and reinitialize it.
1192 $( font_weights ).select2().empty();
1193 $( font_weights ).select2( {
1194 data: new_variants
1195 } ).on( 'change', function( e ) {
1196 let wrapper = $( e.target ).closest( wrapperSelector );
1197
1198 // Serialize subfield values and refresh the fonts in the preview window.
1199 update_font_value( wrapper );
1200 } );
1201 }
1202
1203 /**
1204 * This function updates the data in font subset selector from the given <option> element
1205 * @param option
1206 * @param wraper
1207 */
1208 function update_subset_field( option, wraper ) {
1209 let subsets = $( option ).data( 'subsets' ),
1210 font_subsets = wraper.find( fontSubsetsSelector ),
1211 new_subsets = [],
1212 type = $( option ).data( 'type' );
1213
1214 if ( type !== 'google' ) {
1215 font_subsets.parent().hide();
1216 return;
1217 }
1218
1219 let current_value = wraper.children( valueHolderSelector ).val();
1220
1221 current_value = maybeJsonParse( current_value );
1222 if ( _.isUndefined( current_value.selected_subsets ) ) {
1223 return;
1224 }
1225 current_value = current_value.selected_subsets;
1226
1227 subsets = maybeJsonParse( subsets );
1228
1229 if ( Object.keys( subsets ).length < 2 ) {
1230 font_subsets.parent().hide();
1231 } else {
1232 font_subsets.parent().show();
1233 }
1234
1235 // we need to turn the data array into a specific form like [{id:"id", text:"Text"}]
1236 $.each( subsets, function( index, subset ) {
1237 new_subsets[index] = {
1238 'id': subset,
1239 'text': subset
1240 };
1241
1242 // current_subsets
1243 if ( typeof current_value !== 'undefined' && current_value !== null && current_value.indexOf( subset ) !== - 1 ) {
1244 new_subsets[index].selected = true;
1245 }
1246 } );
1247
1248 // We need to clear the old select2 field and reinitialize it.
1249 $( font_subsets ).select2().empty();
1250 $( font_subsets ).select2( {
1251 data: new_subsets
1252 } ).on( 'change', function( e ) {
1253 let wrapper = $( e.target ).closest( wrapperSelector );
1254
1255 // Serialize subfield values and refresh the fonts in the preview window.
1256 update_font_value( wrapper );
1257 } );
1258 }
1259
1260 /**
1261 * This function is a custom value serializer for our entire font field
1262 * It collects values and saves them (encoded) into the `.customify_font_values` input's value
1263 */
1264 function update_font_value( wraper ) {
1265 let options_list = $( wraper ).find( '.font-options__options-list' ),
1266 inputs = options_list.find( '[data-field]' ),
1267 value_holder = wraper.children( valueHolderSelector ),
1268 setting_id = $( value_holder ).data( 'customize-setting-link' ),
1269 setting = wp.customize( setting_id ),
1270 newFontData = {};
1271
1272 // If we are already self-updating this and we haven't finished, we need to stop here to prevent infinite loops
1273 // This call might have come from a subfield detecting the change the triggering a further update_font_value()
1274 if ( true === updatingValue[setting_id] ) {
1275 return;
1276 }
1277
1278 // If we are loading this setting value and haven't finished, there is no point in updating it as this would cause infinite loops.
1279 if ( true === loadingValue[setting_id] ) {
1280 return;
1281 }
1282
1283 // Mark the fact that we are self-updating the field value
1284 updatingValue[setting_id] = true;
1285
1286 inputs.each( function( key, el ) {
1287 let field = $( el ).data( 'field' ),
1288 value = $( el ).val();
1289
1290 if ( 'font_family' === field ) {
1291 // the font family also holds the type
1292 let selected_opt = $( el.options[el.selectedIndex] ),
1293 type = selected_opt.data( 'type' ),
1294 subsets = selected_opt.data( 'subsets' ),
1295 variants = selected_opt.data( 'variants' );
1296
1297 if ( ! _.isUndefined( type ) ) {
1298 newFontData['type'] = type;
1299 if ( type === 'theme_font' ) {
1300 newFontData['src'] = selected_opt.data( 'src' );
1301 }
1302 }
1303
1304 if ( ! _.isUndefined( variants ) ) {
1305 newFontData['variants'] = maybeJsonParse( variants );
1306 }
1307
1308 if ( ! _.isUndefined( subsets ) ) {
1309 newFontData['subsets'] = maybeJsonParse( subsets );
1310 }
1311 }
1312
1313
1314 if ( ! _.isUndefined( field ) && ! _.isUndefined( value ) && ! _.isNull( value ) && value !== '' ) {
1315 newFontData[field] = value;
1316 }
1317 } );
1318
1319 // Serialize the newly gathered font data
1320 let serializedNewFontData = encodeValues( newFontData );
1321 // Set the serialized value in the hidden field.
1322 value_holder.val( serializedNewFontData );
1323 // Update also the Customizer setting value.
1324 setting.set( serializedNewFontData );
1325
1326
1327 // Finished with the field value self-updating.
1328 updatingValue[setting_id] = false;
1329
1330 return newFontData;
1331 }
1332
1333 /**
1334 * This function is a reverse of update_font_value(), initializing the entire font field controls based on the value stored in the hidden input.
1335 */
1336 function load_font_value( wrapper ) {
1337 let options_list = $( wrapper ).find( '.font-options__options-list' ),
1338 inputs = options_list.find( '[data-field]' ),
1339 value_holder = wrapper.children( valueHolderSelector ),
1340 value = maybeJsonParse( value_holder.val() ),
1341 setting_id = $( value_holder ).data( 'customize-setting-link' );
1342
1343 // If we are already loading this setting value and haven't finished, there is no point in starting again.
1344 if ( true === loadingValue[setting_id] ) {
1345 return;
1346 }
1347
1348 // Mark the fact that we are loading the field value
1349 loadingValue[setting_id] = true;
1350
1351 inputs.each( function( key, el ) {
1352 let field = $( el ).data( 'field' );
1353
1354 // In the case of select2, only the original selects have the data field, thus excluding select2 created select DOM elements
1355 if ( typeof field !== "undefined" && field !== "" && typeof value[field] !== "undefined" ) {
1356 $( el ).val( value[field] ).trigger( 'change' );
1357 }
1358 } );
1359
1360 // Finished with the field value loading.
1361 loadingValue[setting_id] = false;
1362 }
1363
1364 var maybeJsonParse = function( value ) {
1365 let parsed;
1366
1367 //try and parse it, with decodeURIComponent
1368 try {
1369 parsed = JSON.parse( decodeURIComponent( value ) );
1370 } catch ( e ) {
1371
1372 // in case of an error, treat is as a string
1373 parsed = value;
1374 }
1375
1376 return parsed;
1377 };
1378
1379 function encodeValues( obj ) {
1380 return encodeURIComponent( JSON.stringify( obj ) );
1381 }
1382
1383 function render_fonts() {
1384 $( '.customify_font_family' ).select2().trigger( 'change' )
1385 }
1386
1387 return {
1388 render_fonts: render_fonts,
1389 init: init,
1390 update_font_value: update_font_value
1391 };
1392 }
1393 )();
1394
1395 var Queue = function() {
1396 var lastPromise = null;
1397 var queueDeferred = null;
1398 var methodDeferred = null;
1399
1400 this.add_steps = function( key, steps, args ) {
1401 var self = this;
1402 this.methodDeferred = $.Deferred();
1403 this.queueDeferred = this.setup();
1404
1405 $.each( steps, function( i, step ) {
1406 self.queue( key, step );
1407 } );
1408 };
1409
1410 this.process_remote_step = function( key, data, step ) {
1411 var self = this;
1412
1413 if ( _.isUndefined( data ) || _.isNull( data ) ) {
1414 return false;
1415 }
1416
1417 var new_step = step;
1418 $.each( data, function( i, k ) {
1419 debugger;
1420 // prepare data for new requests
1421 new_step.recall_data = k.data;
1422 new_step.recall_type = k.type;
1423 new_step.type = 'recall';
1424
1425 self.queue( key, new_step, k.id );
1426 } );
1427 };
1428
1429 this.log_action = function( action, key, msg ) {
1430 if ( action === 'start' ) {
1431 $( '.wpGrade-import-results' ).show();
1432 $( '.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>' );
1433 } else if ( action === 'end' ) {
1434 var $notice = $( '.imports_step_' + key + ' .step_info' );
1435
1436 if ( $notice.length > 0 || msg !== "undefined" ) {
1437 $notice.attr( 'data-balloon', msg );
1438 $notice.addClass( 'success' );
1439 } else {
1440 $notice.attr( 'data-balloon', 'Done' );
1441 $notice.addClass( 'failed' );
1442 }
1443 }
1444 };
1445
1446 this.queue = function( key, data, step_key ) {
1447 var self = this;
1448 if ( ! _.isUndefined( step_key ) ) {
1449 this.log_action( 'start', step_key );
1450 }
1451
1452 // execute next queue method
1453 this.queueDeferred.done( this.request( key, data, step_key ) );
1454 lastPromise = self.methodDeferred.promise();
1455 };
1456
1457 this.request = function( key, step, step_key ) {
1458 var self = this;
1459 // call actual method and wrap output in deferred
1460 //setTimeout( function() {
1461 var data_args = {
1462 action: 'customify_import_step',
1463 step_id: step.id,
1464 step_type: step.type,
1465 option_key: key
1466 };
1467
1468 if ( ! _.isUndefined( step.recall_data ) ) {
1469 data_args.recall_data = step.recall_data;
1470 }
1471
1472 if ( ! _.isUndefined( step.recall_type ) ) {
1473 data_args.recall_type = step.recall_type;
1474 }
1475
1476 $.ajax( {
1477 url: customify_settings.import_rest_url + 'customify/1.0/import',
1478 method: 'POST',
1479 beforeSend: function( xhr ) {
1480 xhr.setRequestHeader( 'X-WP-Nonce', WP_API_Settings.nonce );
1481 },
1482 dataType: 'json',
1483 contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
1484 data: data_args
1485 } ).done( function( response ) {
1486 if ( ! _.isUndefined( response.success ) && response.success ) {
1487 var results = response.data;
1488 if ( step.type === 'remote' ) {
1489 self.process_remote_step( key, results, step );
1490 }
1491 }
1492
1493 if ( ! _.isUndefined( step_key ) && ! _.isUndefined( response.message ) ) {
1494 self.log_action( 'end', step_key, response.message );
1495 }
1496 } );
1497
1498 self.methodDeferred.resolve();
1499 //}, 3450 );
1500 };
1501
1502 this.setup = function() {
1503 var self = this;
1504
1505 self.queueDeferred = $.Deferred();
1506
1507 // when the previous method returns, resolve this one
1508 $.when( lastPromise ).always( function() {
1509 self.queueDeferred.resolve();
1510 } );
1511
1512 return self.queueDeferred.promise();
1513 }
1514 };
1515
1516 /** HELPERS **/
1517
1518 /**
1519 * Function to check if a value exists in an object
1520 * @param value
1521 * @param obj
1522 * @returns {boolean}
1523 */
1524 var inObject = function( value, obj ) {
1525 for ( var k in obj ) {
1526 if ( ! obj.hasOwnProperty( k ) ) {
1527 continue;
1528 }
1529 if ( _.isEqual( obj[k], value ) ) {
1530 return true;
1531 }
1532 }
1533 return false;
1534 };
1535
1536 var maybeJsonParse = function( value ) {
1537 var parsed;
1538
1539 //try and parse it, with decodeURIComponent
1540 try {
1541 parsed = JSON.parse( decodeURIComponent( value ) );
1542 } catch ( e ) {
1543
1544 // in case of an error, treat is as a string
1545 parsed = value;
1546 }
1547
1548 return parsed;
1549 };
1550
1551 var getUrlVars = function( name ) {
1552 var vars = [], hash;
1553 var hashes = window.location.href.slice( window.location.href.indexOf( '?' ) + 1 ).split( '&' );
1554 for ( var i = 0; i < hashes.length; i ++ ) {
1555 hash = hashes[i].split( '=' );
1556
1557 vars.push( hash[0] );
1558 vars[hash[0]] = hash[1];
1559 }
1560
1561 if ( ! _.isUndefined( vars[name] ) ) {
1562 return vars[name];
1563 }
1564 return false;
1565 };
1566
1567 var isJsonString = function( str ) {
1568 try {
1569 JSON.parse( str );
1570 } catch ( e ) {
1571 return false;
1572 }
1573 return true;
1574 };
1575 }
1576 )( jQuery, window, wp );
1577
1578
1579 // Reverses a hex color to either black or white
1580 function customifyInverseHexColorToBlackOrWhite( hex ) {
1581 return customifyInverseHexColor( hex, true );
1582 }
1583
1584 // Taken from here: https://stackoverflow.com/a/35970186/6260836
1585 function customifyInverseHexColor( hex, bw ) {
1586 if ( hex.indexOf( '#' ) === 0 ) {
1587 hex = hex.slice( 1 );
1588 }
1589 // convert 3-digit hex to 6-digits.
1590 if ( hex.length === 3 ) {
1591 hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
1592 }
1593 if ( hex.length !== 6 ) {
1594 throw new Error( 'Invalid HEX color.' );
1595 }
1596 var r = parseInt( hex.slice( 0, 2 ), 16 ),
1597 g = parseInt( hex.slice( 2, 4 ), 16 ),
1598 b = parseInt( hex.slice( 4, 6 ), 16 );
1599 if ( bw ) {
1600 // http://stackoverflow.com/a/3943023/112731
1601 return (
1602 r * 0.299 + g * 0.587 + b * 0.114
1603 ) > 186
1604 ? '#000000'
1605 : '#FFFFFF';
1606 }
1607 // invert color components
1608 r = (
1609 255 - r
1610 ).toString( 16 );
1611 g = (
1612 255 - g
1613 ).toString( 16 );
1614 b = (
1615 255 - b
1616 ).toString( 16 );
1617 // pad each with zeros and return
1618 return "#" + customifyPadZero( r ) + customifyPadZero( g ) + customifyPadZero( b );
1619 }
1620
1621 function customifyPadZero( str, len ) {
1622 len = len || 2;
1623 var zeros = new Array( len ).join( '0' );
1624 return (
1625 zeros + str
1626 ).slice( - len );
1627 }
1628
1629 // Shading, Blending and Converting colors
1630 // Taken from here: https://github.com/PimpTrizkit/PJs/wiki/12.-Shade,-Blend-and-Convert-a-Web-Color-(pSBC.js)
1631 const pSBC = function( p, from, to ) {
1632 if ( typeof(
1633 p
1634 ) != "number" || p < - 1 || p > 1 || typeof(
1635 from
1636 ) != "string" || (
1637 from[0] != 'r' && from[0] != '#'
1638 ) || (
1639 to && typeof(
1640 to
1641 ) != "string"
1642 ) ) {
1643 return null;
1644 } //ErrorCheck
1645 if ( ! this.pSBCr ) {
1646 this.pSBCr = ( d ) => {
1647 let l = d.length, RGB = {};
1648 if ( l > 9 ) {
1649 d = d.split( "," );
1650 if ( d.length < 3 || d.length > 4 ) {
1651 return null;
1652 }//ErrorCheck
1653 RGB[0] = i( d[0].split( "(" )[1] ), RGB[1] = i( d[1] ), RGB[2] = i( d[2] ), RGB[3] = d[3] ? parseFloat( d[3] ) : - 1;
1654 } else {
1655 if ( l == 8 || l == 6 || l < 4 ) {
1656 return null;
1657 } //ErrorCheck
1658 if ( l < 6 ) {
1659 d = "#" + d[1] + d[1] + d[2] + d[2] + d[3] + d[3] + (
1660 l > 4 ? d[4] + "" + d[4] : ""
1661 );
1662 } //3 or 4 digit
1663 d = i( d.slice( 1 ), 16 ), RGB[0] = d >> 16 & 255, RGB[1] = d >> 8 & 255, RGB[2] = d & 255, RGB[3] = - 1;
1664 if ( l == 9 || l == 5 ) {
1665 RGB[3] = r( (
1666 RGB[2] / 255
1667 ) * 10000 ) / 10000, RGB[2] = RGB[1], RGB[1] = RGB[0], RGB[0] = d >> 24 & 255;
1668 }
1669 }
1670 return RGB;
1671 }
1672 }
1673 var i = parseInt, r = Math.round, h = from.length > 9, h = typeof(
1674 to
1675 ) == "string" ? to.length > 9 ? true : to == "c" ? ! h : false : h, b = p < 0, p = b ? p * - 1 : p,
1676 to = to && to != "c" ? to : b ? "#000000" : "#FFFFFF", f = this.pSBCr( from ), t = this.pSBCr( to );
1677 if ( ! f || ! t ) {
1678 return null;
1679 } //ErrorCheck
1680 if ( h ) {
1681 return "rgb" + (
1682 f[3] > - 1 || t[3] > - 1 ? "a(" : "("
1683 ) + r( (
1684 t[0] - f[0]
1685 ) * p + f[0] ) + "," + r( (
1686 t[1] - f[1]
1687 ) * p + f[1] ) + "," + r( (
1688 t[2] - f[2]
1689 ) * p + f[2] ) + (
1690 f[3] < 0 && t[3] < 0 ? ")" : "," + (
1691 f[3] > - 1 && t[3] > - 1 ? r( (
1692 (
1693 t[3] - f[3]
1694 ) * p + f[3]
1695 ) * 10000 ) / 10000 : t[3] < 0 ? f[3] : t[3]
1696 ) + ")"
1697 );
1698 } else {
1699 return "#" + (
1700 0x100000000 + r( (
1701 t[0] - f[0]
1702 ) * p + f[0] ) * 0x1000000 + r( (
1703 t[1] - f[1]
1704 ) * p + f[1] ) * 0x10000 + r( (
1705 t[2] - f[2]
1706 ) * p + f[2] ) * 0x100 + (
1707 f[3] > - 1 && t[3] > - 1 ? r( (
1708 (
1709 t[3] - f[3]
1710 ) * p + f[3]
1711 ) * 255 ) : t[3] > - 1 ? r( t[3] * 255 ) : f[3] > - 1 ? r( f[3] * 255 ) : 255
1712 )
1713 ).toString( 16 ).slice( 1, f[3] > - 1 || t[3] > - 1 ? undefined : - 2 );
1714 }
1715 };