PluginProbe
Customify / 2.2.0
Customify v2.2.0
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 / font-select-fields.js

font-select-fields.js in Customify 2.2.0, at js/customizer/font-select-fields.js

449 lines 18.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // This is for the Customizer Font control
2 let CustomifyFontSelectFields = ( function( $, exports, wp ) {
3 const
4 wrapperSelector = '.font-options__wrapper',
5 valueHolderSelector = '.customify_font_values',
6 fontFamilySelector = '.customify_font_family',
7 fontWeightSelector = '.customify_font_weight',
8 fontSubsetsSelector = '.customify_font_subsets',
9 selectPlaceholder = "Select a font family",
10 weightPlaceholder = "Select a font weight",
11 subsetPlaceholder = "Extra Subsets";
12
13 // We will use this to remember that we are self-updating the field from the subfields.
14 // We will save this info for each setting ID.
15 var updatingValue = {},
16 loadingValue = {};
17
18 function init() {
19 let $fontFamilyFields = $( fontFamilySelector );
20
21 // Initialize the select2 field for the font family
22 $fontFamilyFields.select2( {
23 placeholder: selectPlaceholder
24 } ).on( 'change', function( e ) {
25 let new_option = $( e.target ).find( 'option:selected' ),
26 wrapper = $( e.target ).closest( wrapperSelector );
27
28 // Update the weight subfield with the new options given by the selected font family.
29 updateWeightField( new_option, wrapper );
30
31 // Update the subset subfield with the new options given by the selected font family.
32 updateSubsetField( new_option, wrapper );
33
34 // Serialize subfield values and refresh the fonts in the preview window.
35 selfUpdateValue( wrapper );
36 } );
37
38 // Initialize the select2 field for the font weight
39 $( fontWeightSelector ).each( function( i, el ) {
40
41 let select2_args = {
42 placeholder: weightPlaceholder
43 };
44
45 // all this fuss is for the case when the font doesn't come with variants from PHP, like a theme_font
46 if ( this.options.length === 0 ) {
47 let wrapper = $( el ).closest( wrapperSelector ),
48 font = wrapper.find( fontFamilySelector ),
49 option = font[0].options[font[0].selectedIndex],
50 variants = maybeJsonParse( $( option ).data( 'variants' ) ),
51 data = [],
52 selecter_variants = $( el ).data( 'default' ) || null;
53
54 if ( typeof variants === "undefined" ) {
55 $( this ).hide();
56 return;
57 }
58
59 $.each( variants, function( index, weight ) {
60 let this_value = {
61 id: weight,
62 text: weight
63 };
64
65 if ( selecter_variants !== null && weight == selecter_variants ) {
66 this_value.selected = true;
67 }
68
69 data.push( this_value );
70 } );
71
72 if ( data !== [] ) {
73 select2_args.data = data;
74 }
75 }
76
77 $( this ).select2(
78 select2_args
79 ).on( 'change', function( e ) {
80 let wrapper = $( e.target ).closest( wrapperSelector );
81
82 // Serialize subfield values and refresh the fonts in the preview window.
83 selfUpdateValue( wrapper );
84 } );
85 } );
86
87 // Initialize the select2 field for the font subsets
88 $( fontSubsetsSelector )
89 .select2( {
90 placeholder: subsetPlaceholder
91 } )
92 .on( 'change', function( e ) {
93 let wrapper = $( e.target ).closest( wrapperSelector );
94
95 // Serialize subfield values and refresh the fonts in the preview window.
96 selfUpdateValue( wrapper );
97 } );
98
99 let rangers = $fontFamilyFields.parents( wrapperSelector ).find( 'input[type=range]' ),
100 selects = $fontFamilyFields.parents( wrapperSelector ).find( 'select' ).not( "select[class*=' select2'],select[class^='select2']" );
101
102 // Initialize the all the regular selects in the font controls
103 if ( selects.length > 0 ) {
104 selects.on( 'change', function( e ) {
105 let wrapper = $( e.target ).closest( wrapperSelector );
106
107 // Serialize subfield values and refresh the fonts in the preview window.
108 selfUpdateValue( wrapper );
109 } );
110 }
111
112 // Initialize the all the range fields in the font controls
113 if ( rangers.length > 0 ) {
114 rangers.on( 'change', function( e ) {
115 let wrapper = $( e.target ).closest( wrapperSelector );
116
117 // Serialize subfield values and refresh the fonts in the preview window.
118 selfUpdateValue( wrapper );
119
120 wp.customize.previewer.send( 'font-changed' );
121 } );
122 }
123
124 // When the previewer window is ready, render the fonts
125 var self = this;
126 wp.customize.previewer.bind( 'ready', function() {
127 self.renderFonts();
128 } );
129
130 // Handle the reverse value direction, when the customize setting is updated and the subfields need to update their values.
131 $fontFamilyFields.each( function( i, el ) {
132 let wrapper = $( el ).closest( wrapperSelector ),
133 value_holder = wrapper.children( valueHolderSelector ),
134 setting_id = $( value_holder ).data( 'customize-setting-link' ),
135 setting = wp.customize( setting_id );
136
137 setting.bind( function( newValue, oldValue ) {
138 if ( ! updatingValue[this.id] ) {
139 value_holder.val( newValue );
140
141 loadFontValue( wrapper );
142 }
143 } )
144 } )
145 }
146
147 /**
148 * This function updates the data in font weight selector from the given <option> element
149 *
150 * @param option
151 * @param wraper
152 */
153 function updateWeightField( option, wraper ) {
154 let variants = $( option ).data( 'variants' ),
155 font_weights = wraper.find( fontWeightSelector ),
156 selected_variant = font_weights.val() ? font_weights.val() : font_weights.data( 'default' ),
157 new_variants = [],
158 id = wraper.find( valueHolderSelector ).data( 'customizeSettingLink' );
159
160 variants = maybeJsonParse( variants );
161
162 if ( customify_settings.settings[id].load_all_weights || typeof variants === "undefined" || Object.keys( variants ).length < 2 || font_weights.data('disabled') !== undefined ) {
163 font_weights.parent().hide();
164 } else {
165 font_weights.parent().show();
166 }
167
168 // we need to turn the data array into a specific form like [{id:"id", text:"Text"}]
169 $.each( variants, function( index, variant ) {
170 new_variants[index] = {
171 'id': variant,
172 'text': variant
173 };
174
175 if ( selected_variant == variant ) {
176 new_variants[index].selected = true;
177 }
178 } );
179
180 // We need to clear the old select2 field and reinitialize it.
181 $( font_weights ).select2().empty();
182 $( font_weights ).select2( {
183 data: new_variants
184 } ).on( 'change', function( e ) {
185 let wrapper = $( e.target ).closest( wrapperSelector );
186
187 // Serialize subfield values and refresh the fonts in the preview window.
188 selfUpdateValue( wrapper );
189 } );
190 }
191
192 /**
193 * This function updates the data in font subset selector from the given <option> element
194 * @param option
195 * @param wraper
196 */
197 function updateSubsetField( option, wraper ) {
198 let subsets = $( option ).data( 'subsets' ),
199 font_subsets = wraper.find( fontSubsetsSelector ),
200 new_subsets = [],
201 type = $( option ).data( 'type' );
202
203 if ( type !== 'google' ) {
204 font_subsets.parent().hide();
205 return;
206 }
207
208 let current_value = wraper.children( valueHolderSelector ).val();
209
210 current_value = maybeJsonParse( current_value );
211 if ( _.isUndefined( current_value.selected_subsets ) ) {
212 return;
213 }
214 current_value = current_value.selected_subsets;
215
216 subsets = maybeJsonParse( subsets );
217
218 if ( typeof subsets !== "undefined" && Object.keys( subsets ).length < 2 || font_subsets.data('disabled') !== undefined ) {
219 font_subsets.parent().hide();
220 } else {
221 font_subsets.parent().show();
222 }
223
224 // we need to turn the data array into a specific form like [{id:"id", text:"Text"}]
225 $.each( subsets, function( index, subset ) {
226 new_subsets[index] = {
227 'id': subset,
228 'text': subset
229 };
230
231 // current_subsets
232 if ( typeof current_value !== "undefined" && current_value !== null && current_value.indexOf( subset ) !== - 1 ) {
233 new_subsets[index].selected = true;
234 }
235 } );
236
237 // We need to clear the old select2 field and reinitialize it.
238 $( font_subsets ).select2().empty();
239 $( font_subsets ).select2( {
240 data: new_subsets
241 } ).on( 'change', function( e ) {
242 let wrapper = $( e.target ).closest( wrapperSelector );
243
244 // Serialize subfield values and refresh the fonts in the preview window.
245 selfUpdateValue( wrapper );
246 } );
247 }
248
249 function getValue( wrapper ) {
250 let value_holder = wrapper.children( valueHolderSelector );
251
252 if ( value_holder.length ) {
253 return maybeJsonParse( value_holder.val() );
254 }
255
256 return [];
257 }
258
259 function updateValue( wrapper, value ) {
260 let value_holder = wrapper.children( valueHolderSelector ),
261 setting_id = $( value_holder ).data( 'customize-setting-link' ),
262 setting = wp.customize( setting_id );
263
264 if ( ! value_holder.length ) {
265 return;
266 }
267
268 if ( _.isArrayLikeObject( value ) ) {
269 value = encodeValues( value );
270 }
271
272 // Set the serialized value in the hidden field.
273 value_holder.val( value );
274 // Update also the Customizer setting value.
275 setting.set( value );
276 }
277
278 /**
279 * This function is a custom value serializer for our entire font field
280 * It collects values and saves them (encoded) into the `.customify_font_values` input's value
281 */
282 function selfUpdateValue( wrapper ) {
283 let options_list = $( wrapper ).find( '.font-options__options-list' ),
284 inputs = options_list.find( '[data-field]' ),
285 value_holder = wrapper.children( valueHolderSelector ),
286 setting_id = $( value_holder ).data( 'customize-setting-link' ),
287 setting = wp.customize( setting_id ),
288 newFontData = {};
289
290 // If we are already self-updating this and we haven't finished, we need to stop here to prevent infinite loops
291 // This call might have come from a subfield detecting the change the triggering a further update_font_value()
292 if ( true === updatingValue[setting_id] ) {
293 return;
294 }
295
296 // If we are loading this setting value and haven't finished, there is no point in updating it as this would cause infinite loops.
297 if ( true === loadingValue[setting_id] ) {
298 return;
299 }
300
301 // Mark the fact that we are self-updating the field value
302 updatingValue[setting_id] = true;
303
304 inputs.each( function( key, el ) {
305 let field = $( el ).data( 'field' ),
306 value = $( el ).val();
307
308 if ( 'font_family' === field ) {
309 // the font family also holds the type
310 let selected_opt = $( el.options[el.selectedIndex] ),
311 type = selected_opt.data( 'type' ),
312 subsets = selected_opt.data( 'subsets' ),
313 variants = selected_opt.data( 'variants' );
314
315 if ( ! _.isUndefined( type ) ) {
316 newFontData['type'] = type;
317 if ( type === 'theme_font' ) {
318 newFontData['src'] = selected_opt.data( 'src' );
319 }
320 }
321
322 if ( ! _.isUndefined( variants ) ) {
323 newFontData['variants'] = maybeJsonParse( variants );
324 }
325
326 if ( ! _.isUndefined( subsets ) ) {
327 newFontData['subsets'] = maybeJsonParse( subsets );
328 }
329 }
330
331 if ( ! _.isUndefined( field ) && ! _.isUndefined( value ) && ! _.isNull( value ) && value !== '' ) {
332 newFontData[field] = value;
333 }
334 } );
335
336 // Serialize the newly gathered font data
337 let serializedNewFontData = encodeValues( newFontData );
338 // Set the serialized value in the hidden field.
339 value_holder.val( serializedNewFontData );
340 // Update also the Customizer setting value.
341 setting.set( serializedNewFontData );
342
343
344 // Finished with the field value self-updating.
345 updatingValue[setting_id] = false;
346
347 return newFontData;
348 }
349
350 /**
351 * This function is a reverse of update_font_value(), initializing the entire font field controls based on the value stored in the hidden input.
352 */
353 function loadFontValue( wrapper ) {
354 let options_list = $( wrapper ).find( '.font-options__options-list' ),
355 inputs = options_list.find( '[data-field]' ),
356 value_holder = wrapper.children( valueHolderSelector ),
357 value = maybeJsonParse( value_holder.val() ),
358 setting_id = $( value_holder ).data( 'customize-setting-link' );
359
360 // If we are already loading this setting value and haven't finished, there is no point in starting again.
361 if ( true === loadingValue[setting_id] ) {
362 return;
363 }
364
365 // Mark the fact that we are loading the field value
366 loadingValue[setting_id] = true;
367
368 inputs.each( function( key, el ) {
369 let field = $( el ).data( 'field' );
370
371 // In the case of select2, only the original selects have the data field, thus excluding select2 created select DOM elements
372 if ( typeof field !== "undefined" && field !== "" && typeof value[field] !== "undefined" ) {
373 // If the value contains also the unit (it is not a number) we need to split it and change the subfield accordingly.
374 let cleanValue = value[field],
375 unit = '';
376 // We will do this only for numerical fields.
377 if ( _.contains( ['letter_spacing','line_height', 'font_size'], field ) && isNaN( cleanValue ) ) {
378 // If we have a standardized value field (as array), use that.
379 if ( typeof cleanValue.value !== "undefined" ) {
380 if ( typeof cleanValue.unit !== "undefined" ) {
381 unit = cleanValue.unit;
382 }
383
384 cleanValue = cleanValue.value;
385 } else {
386 // Treat the case when the value is a string.
387 let matches = cleanValue.match(/^([\d.\-+]+)(.+)/i);
388 if (matches !== null && typeof matches[1] !== "undefined") {
389 cleanValue = matches[1];
390 unit = matches[2];
391 }
392 }
393 }
394
395 if ( unit !== '' ) {
396 $( el ).attr( 'unit', unit );
397 }
398
399 // If this field has a min/max attribute we need to make sure that those attributes allow for the value we are trying to impose.
400 // But only for numerical values.
401 if ( ! isNaN( cleanValue ) ) {
402 if ( $(el).attr('min') && $(el).attr('min') > cleanValue ) {
403 $(el).attr('min', cleanValue );
404 }
405 if ( $(el).attr('max') && $(el).attr('max') < cleanValue ) {
406 $(el).attr('max', cleanValue );
407 }
408 }
409
410 $( el ).val( cleanValue ).trigger( 'change' );
411 }
412 } );
413
414 // Finished with the field value loading.
415 loadingValue[setting_id] = false;
416 }
417
418 const maybeJsonParse = function( value ) {
419 let parsed;
420
421 //try and parse it, with decodeURIComponent
422 try {
423 parsed = JSON.parse( decodeURIComponent( value ) );
424 } catch ( e ) {
425
426 // in case of an error, treat is as a string
427 parsed = value;
428 }
429
430 return parsed;
431 };
432
433 const encodeValues = function( obj ) {
434 return encodeURIComponent( JSON.stringify( obj ) );
435 };
436
437 const renderFonts = function() {
438 $( '.customify_font_family' ).select2().trigger( 'change' )
439 };
440
441 return {
442 renderFonts: renderFonts,
443 init: init,
444 getValue: getValue,
445 updateValue: updateValue,
446 selfUpdateValue: selfUpdateValue,
447 encodeValues: encodeValues,
448 };
449 } )( jQuery, window, wp );