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

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

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