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

customizer_preview.js in Customify 2.10.1, at js/customizer_preview.js

753 lines 26.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ;(function ($, window, document) {
2
3 /* global customify.config */
4 /* global WebFont */
5
6 $(window).on('load', function () {
7 // We need to do this on window.load because on document.ready might be too early.
8 maybeLoadWebfontloaderScript()
9 })
10
11 const maybeLoadWebfontloaderScript = function () {
12 if (typeof WebFont === 'undefined') {
13 let tk = document.createElement('script')
14 tk.src = parent.customify.config.webfontloader_url
15 tk.type = 'text/javascript'
16 let s = document.getElementsByTagName('script')[0]
17 s.parentNode.insertBefore(tk, s)
18 }
19 }
20
21 const fontsCache = []
22
23 // Do everything at document.ready
24 $(function () {
25 const api = parent.wp.customize
26 const customify = parent.customify
27 const apiSettings = api.settings.settings
28 const regexForMultipleReplace = new RegExp('-', 'g')
29
30 $.each(customify.config.settings, function (key, settingConfig) {
31 const propertiesPrefix = typeof settingConfig.properties_prefix === 'undefined' ? '' : settingConfig.properties_prefix
32 if (settingConfig.type === 'font') {
33 api(key, function (setting) {
34 setting.bind(function (newValue) {
35 if (typeof newValue === 'undefined') {
36 return
37 }
38
39 if (typeof newValue.font_family !== 'undefined') {
40 maybeLoadFontFamily(newValue, this.id)
41 }
42
43 const $styleElement = $('#customify_font_output_for_' + settingConfig.html_safe_option_id)
44 if (!$styleElement.length) {
45 return
46 }
47
48 const cssValue = getFontFieldCSSValue(this.id, newValue)
49 if (_.isEmpty(cssValue)) {
50 // Empty the style element.
51 $styleElement.html('')
52 return
53 }
54
55 $styleElement.html(getFontFieldCSSCode(this.id, cssValue, propertiesPrefix, newValue))
56 })
57 })
58
59 } else if (typeof apiSettings !== 'undefined'
60 && typeof apiSettings[key] !== 'undefined'
61 && typeof settingConfig.css !== 'undefined'
62 && typeof settingConfig.live !== 'undefined'
63 && settingConfig.live === true) {
64
65 api(key, function (setting) {
66 setting.bind(function (newValue) {
67
68 $.each(settingConfig.css, function (idx, propertyConfig) {
69 // Replace all dashes with underscores thus making the CSS property safe to us in a HTML ID.
70 const $styleElement = $('.dynamic_setting_' + settingConfig.html_safe_option_id + '_property_' + propertyConfig.property.replace(regexForMultipleReplace, '_') + '_' + idx)
71 if (!$styleElement.length) {
72 return
73 }
74
75 const properties = {}
76 if (typeof propertyConfig.property !== 'undefined' && typeof propertyConfig.selector !== 'undefined') {
77 properties[propertyConfig.property] = propertyConfig.selector
78 }
79 if (typeof propertyConfig.callback_filter !== 'undefined') {
80 properties['callback'] = propertyConfig.callback_filter
81 }
82 if (_.isEmpty(properties)) {
83 return
84 }
85
86 const cssUpdateArgs = {
87 properties: properties,
88 propertyValue: newValue,
89 negative_value: propertyConfig.hasOwnProperty('negative_value') ? propertyConfig['negative_value'] : false
90 }
91
92 if (typeof this.unit !== 'undefined') {
93 cssUpdateArgs.unit = this.unit
94 }
95
96 $styleElement.cssUpdate(cssUpdateArgs)
97 })
98
99 })
100 })
101 } else if (typeof settingConfig.live === 'object' && settingConfig.live.length > 0) {
102 // If the live parameter is an object it means that this is a list of css classes.
103 // These classes should be affected by the change of the text fields.
104 const fieldClass = settingConfig.live.join()
105
106 // if this field is allowed to modify text then we'll edit this live
107 if ($.inArray(settingConfig.type, ['text', 'textarea', 'ace_editor']) > -1) {
108 api(key, function (value) {
109 value.bind(function (text) {
110 let sanitizer = document.createElement('div')
111
112 sanitizer.innerHTML = text
113 $(fieldClass).html(text)
114 })
115 })
116 }
117 }
118 })
119
120 /**
121 * HELPERS
122 **/
123
124 // Mirror logic of server-side Customify_Fonts_Global::getCSSValue()
125 const getFontFieldCSSValue = function (settingID, value) {
126
127 const CSSValue = {}
128
129 if (typeof value.font_family !== 'undefined' && !_.includes(['','false',false], value.font_family)) {
130 CSSValue['font-family'] = value.font_family
131 // "Expand" the font family by appending the fallback stack, if any is available.
132 // But only do this, if the value is not already a font stack!
133 if (CSSValue['font-family'].indexOf(',') === -1) {
134 const fallbackStack = getFontFamilyFallbackStack(CSSValue['font-family'])
135 if (fallbackStack.length) {
136 CSSValue['font-family'] += ',' + fallbackStack
137 }
138 }
139
140 CSSValue['font-family'] = sanitizeFontFamilyCSSValue(CSSValue['font-family'])
141 }
142
143 if (typeof value.font_variant !== 'undefined' && !_.includes(['','false',false], value.font_variant)) {
144 let variant = value.font_variant
145
146 if (_.isString(variant)) {
147 // We may have a style in the variant; attempt to split.
148 if (variant.indexOf('italic') !== -1) {
149 CSSValue['font-style'] = 'italic'
150 variant = variant.replace('italic', '')
151 } else if (variant.indexOf('oblique') !== -1) {
152 CSSValue['font-style'] = 'oblique'
153 variant = variant.replace('oblique', '')
154 }
155
156 // If anything remained, then we have a font weight also.
157 if (variant !== '') {
158 if (variant === 'regular' || variant === 'normal') {
159 variant = '400'
160 }
161
162 CSSValue['font-weight'] = variant
163 }
164 } else if (_.isNumeric(variant)) {
165 CSSValue['font-weight'] = String(variant);
166 }
167 }
168
169 if (typeof value.font_size !== 'undefined' && !_.includes(['','false',false], value.font_size)) {
170 let fontSizeUnit = false
171
172 CSSValue['font-size'] = value.font_size
173 // If the value already contains a unit (is not numeric), go with that.
174 if (isNaN(value.font_size)) {
175 // If we have a standardized value field (as array), use that.
176 if (typeof value.font_size.value !== 'undefined') {
177 CSSValue['font-size'] = value.font_size.value
178 if (typeof value.font_size.unit !== 'undefined') {
179 fontSizeUnit = value.font_size.unit
180 }
181 } else {
182 fontSizeUnit = getFieldUnit(settingID, 'font-size')
183 }
184 } else {
185 fontSizeUnit = getFieldUnit(settingID, 'font-size')
186 }
187
188 if (false !== fontSizeUnit) {
189 CSSValue['font-size'] += fontSizeUnit
190 }
191 }
192
193 if (typeof value.letter_spacing !== 'undefined' && !_.includes(['','false',false], value.letter_spacing)) {
194 let letterSpacingUnit = false
195
196 CSSValue['letter-spacing'] = value.letter_spacing
197 // If the value already contains a unit (is not numeric), go with that.
198 if (isNaN(value.letter_spacing)) {
199 // If we have a standardized value field (as array), use that.
200 if (typeof value.letter_spacing.value !== 'undefined') {
201 CSSValue['letter-spacing'] = value.letter_spacing.value
202 if (typeof value.letter_spacing.unit !== 'undefined') {
203 letterSpacingUnit = value.letter_spacing.unit
204 }
205 } else {
206 letterSpacingUnit = getFieldUnit(settingID, 'letter-spacing')
207 }
208 } else {
209 letterSpacingUnit = getFieldUnit(settingID, 'letter-spacing')
210 }
211
212 if (false !== letterSpacingUnit) {
213 CSSValue['letter-spacing'] += letterSpacingUnit
214 }
215 }
216
217 if (typeof value.line_height !== 'undefined' && !_.includes(['','false',false], value.line_height)) {
218 let lineHeightUnit = false
219
220 CSSValue['line-height'] = value.line_height
221 // If the value already contains a unit (is not numeric), go with that.
222 if (isNaN(value.line_height)) {
223 // If we have a standardized value field (as array), use that.
224 if (typeof value.line_height.value !== 'undefined') {
225 CSSValue['line-height'] = value.line_height.value
226 if (typeof value.line_height.unit !== 'undefined') {
227 lineHeightUnit = value.line_height.unit
228 }
229 } else {
230 lineHeightUnit = getFieldUnit(settingID, 'line-height')
231 }
232 } else {
233 lineHeightUnit = getFieldUnit(settingID, 'line-height')
234 }
235
236 if (false !== lineHeightUnit) {
237 CSSValue['line-height'] += lineHeightUnit
238 }
239 }
240
241 if (typeof value.text_align !== 'undefined' && !_.includes(['','false',false], value.text_align)) {
242 CSSValue['text-align'] = value.text_align
243 }
244
245 if (typeof value.text_transform !== 'undefined' && !_.includes(['','false',false], value.text_transform)) {
246 CSSValue['text-transform'] = value.text_transform
247 }
248
249 if (typeof value.text_decoration !== 'undefined' && !_.includes(['','false',false], value.text_decoration)) {
250 CSSValue['text-decoration'] = value.text_decoration
251 }
252
253 return CSSValue
254 }
255
256 // Mirror logic of server-side Customify_Fonts_Global::getFontStyle()
257 const getFontFieldCSSCode = function (settingID, cssValue, prefix, value) {
258 const fontConfig = customify.config.settings[settingID]
259 let output = ''
260
261 if (typeof window !== 'undefined' && typeof fontConfig.callback !== 'undefined' && typeof window[fontConfig.callback] === 'function') {
262 // The callbacks expect a string selector right now, not a standardized list.
263 // @todo Maybe migrate all callbacks to the new standardized data and remove all this.
264 const plainSelectors = []
265 _.each(fontConfig.selector, function (details, selector) {
266 plainSelectors.push(selector)
267 })
268 const adjustedFontConfig = $.extend(true,{},fontConfig)
269 adjustedFontConfig.selector = plainSelectors.join(', ')
270
271 // Also, "kill" all fields unit since we pass final CSS values.
272 // @todo For some reason, the client-side Typeline cbs are not consistent and expect the font-size value with unit.
273 _.each(adjustedFontConfig['fields'], function(fieldValue, fieldKey) {
274 if (typeof fieldValue.unit !== 'undefined') {
275 adjustedFontConfig['fields'][fieldKey]['unit'] = false;
276 }
277 })
278
279 // Callbacks want the value keys with underscores, not dashes.
280 // We will provide them in both versions for a smoother transition.
281 _.each(cssValue, function (propertyValue, property) {
282 const newKey = property.replace(regexForMultipleReplace, '_')
283 cssValue[newKey] = propertyValue
284 })
285
286 return window[fontConfig.callback](cssValue, adjustedFontConfig)
287 }
288
289 if (typeof fontConfig.selector === 'undefined' || _.isEmpty(fontConfig.selector) || _.isEmpty(cssValue)) {
290 return output
291 }
292
293 // The general CSS allowed properties.
294 const subFieldsCSSAllowedProperties = extractAllowedCSSPropertiesFromFontFields(fontConfig['fields'])
295
296 // The selector is standardized to a list of simple string selectors, or a list of complex selectors with details.
297 // In either case, the actual selector is in the key, and the value is an array (possibly empty).
298
299 // Since we might have simple CSS selectors and complex ones (with special details),
300 // for cleanliness we will group the simple ones under a single CSS rule,
301 // and output individual CSS rules for complex ones.
302 // Right now, for complex CSS selectors we are only interested in the `properties` sub-entry.
303 const simpleCSSSelectors = []
304 const complexCSSSelectors = {}
305
306 _.each(fontConfig.selector, function (details, selector) {
307 if (_.isEmpty(details.properties)) {
308 // This is a simple selector.
309 simpleCSSSelectors.push(selector)
310 } else {
311 complexCSSSelectors[selector] = details
312 }
313 })
314
315 if (!_.isEmpty(simpleCSSSelectors)) {
316 output += '\n' + simpleCSSSelectors.join(', ') + ' {\n'
317 output += getFontFieldCSSProperties(cssValue, subFieldsCSSAllowedProperties, prefix)
318 output += '}\n'
319 }
320
321 if (!_.isEmpty(complexCSSSelectors)) {
322 _.each(complexCSSSelectors, function (details, selector) {
323 output += '\n' + selector + ' {\n'
324 output += getFontFieldCSSProperties(cssValue, details.properties, prefix)
325 output += '}\n'
326 })
327 }
328
329 return output
330 }
331
332 // Mirror logic of server-side Customify_Fonts_Global::getCSSProperties()
333 const getFontFieldCSSProperties = function (cssValue, allowedProperties = false, prefix = '') {
334 let output = ''
335
336 $.each(cssValue, function (property, propertyValue) {
337 // We don't want to output empty CSS rules.
338 if ('' === propertyValue || false === propertyValue) {
339 return
340 }
341
342 // If the property is not allowed, skip it.
343 if (!isCSSPropertyAllowed(property, allowedProperties)) {
344 return
345 }
346
347 output += prefix + property + ': ' + propertyValue + ';\n'
348 })
349
350 return output
351 }
352
353 // Mirror logic of server-side Customify_Fonts_Global::isCSSPropertyAllowed()
354 const isCSSPropertyAllowed = function (property, allowedProperties = false) {
355 // Empty properties are not allowed.
356 if (_.isEmpty(property)) {
357 return false
358 }
359
360 // Everything is allowed if nothing is specified.
361 if (_.isEmpty(allowedProperties)) {
362 return true
363 }
364
365 // For arrays
366 if (_.includes(allowedProperties, property)) {
367 return true
368 }
369
370 // For objects
371 if (_.has(allowedProperties, property) && allowedProperties[property]) {
372 return true
373 }
374
375 return false
376 }
377
378 const extractAllowedCSSPropertiesFromFontFields = function (subfields) {
379 // Nothing is allowed by default.
380 const allowedProperties = {
381 'font-family': false,
382 'font-weight': false,
383 'font-style': false,
384 'font-size': false,
385 'line-height': false,
386 'letter-spacing': false,
387 'text-align': false,
388 'text-transform': false,
389 'text-decoration': false,
390 }
391
392 if (_.isEmpty(subfields)) {
393 return allowedProperties
394 }
395
396 // We will match the subfield keys with the CSS properties, but only those that properties that are allowed.
397 // Maybe at some point some more complex matching would be needed here.
398 _.each(subfields, function (value, key) {
399 if (typeof allowedProperties[key] !== 'undefined') {
400 // Convert values to boolean.
401 allowedProperties[key] = !!value
402
403 // For font-weight we want font-style to go the same way,
404 // since these two are generated from the same subfield: font-weight (actually holding the font variant value).
405 if ('font-weight' === key) {
406 allowedProperties['font-style'] = allowedProperties[key]
407 }
408 }
409 })
410
411 return allowedProperties
412 }
413
414 // This is a mirror logic of the server-side Customify_Fonts_Global::getSubFieldUnit()
415 const getFieldUnit = function (settingID, field) {
416 if (typeof customify.config.settings[settingID] === 'undefined' || typeof customify.config.settings[settingID].fields[field] === 'undefined') {
417 // These fields don't have an unit, by default.
418 if (_.includes(['font-family', 'font-weight', 'font-style', 'line-height', 'text-align', 'text-transform', 'text-decoration'], field)) {
419 return false
420 }
421
422 // The rest of the subfields have pixels as default units.
423 return 'px'
424 }
425
426 if (typeof customify.config.settings[settingID].fields[field].unit !== 'undefined') {
427 // Make sure that we convert all falsy unit values to the boolean false.
428 return _.includes(['', 'false', false], customify.config.settings[settingID].fields[field].unit) ? false : customify.config.settings[settingID].fields[field].unit
429 }
430
431 if (typeof customify.config.settings[settingID].fields[field][3] !== 'undefined') {
432 // Make sure that we convert all falsy unit values to the boolean false.
433 return _.includes(['', 'false', false], customify.config.settings[settingID].fields[field][3]) ? false : customify.config.settings[settingID].fields[field][3]
434 }
435
436 return 'px'
437 }
438
439 const maybeLoadFontFamily = function (font, settingID) {
440 if (typeof font.font_family === 'undefined') {
441 return
442 }
443
444 const fontConfig = customify.config.settings[settingID]
445
446 let family = font.font_family
447 // The font family may be a comma separated list like "Roboto, sans"
448 const fontType = customify.fontFields.determineFontType(family)
449
450 if ('system_font' === fontType) {
451 // Nothing to do for standard fonts
452 return
453 }
454
455 const fontDetails = customify.fontFields.getFontDetails(family, fontType)
456
457 // Handle theme defined fonts and cloud fonts together since they are very similar.
458 if (fontType === 'theme_font' || fontType === 'cloud_font') {
459
460 // Bail if we have no src.
461 if (typeof fontDetails.src === undefined) {
462 return
463 }
464
465 // Handle the font variants.
466 // If there is a selected font variant and we haven't been instructed to load all, load only that,
467 // otherwise load all the available variants.
468 let variants =
469 (
470 typeof font.font_variant !== 'undefined'
471 && (typeof fontConfig['fields']['font-weight']['loadAllVariants'] === 'undefined' || !fontConfig['fields']['font-weight']['loadAllVariants'])
472 ) ? font.font_variant : typeof fontDetails.variants !== 'undefined' ? fontDetails.variants : []
473
474 if (!_.isEmpty(variants)) {
475 variants = standardizeToArray(variants)
476
477 if (!_.isEmpty(variants)) {
478 family = family + ':' + variants.map(function (variant) {
479 return customify.fontFields.convertFontVariantToFVD(variant)
480 }).join(',')
481 }
482 }
483
484 if (fontsCache.indexOf(family) === -1) {
485 WebFont.load({
486 custom: {
487 families: [family],
488 urls: [fontDetails.src]
489 },
490 classes: false,
491 events: false,
492 })
493
494 // Remember we've loaded this family (with it's variants) so we don't load it again.
495 fontsCache.push(family)
496 }
497 }
498 // Handle Google fonts since Web Font Loader has a special module for them.
499 else if (fontType === 'google_font') {
500
501 // Handle the font variants
502 // If there is a selected font variant and we haven't been instructed to load all, load only that,
503 // otherwise load all the available variants.
504 let variants =
505 (
506 typeof font.font_variant !== 'undefined'
507 && (typeof fontConfig['fields']['font-weight']['loadAllVariants'] === 'undefined' || !fontConfig['fields']['font-weight']['loadAllVariants'])
508 ) ? font.font_variant : typeof fontDetails.variants !== 'undefined' ? fontDetails.variants : []
509
510 if (!_.isEmpty(variants)) {
511 variants = standardizeToArray(variants)
512
513 if (!_.isEmpty(variants)) {
514 family = family + ':' + variants.join(',')
515 }
516 }
517
518 if (fontsCache.indexOf(family) === -1) {
519 WebFont.load({
520 google: {families: [family]},
521 classes: false,
522 events: false,
523 })
524
525 // Remember we've loaded this family (with it's variants) so we don't load it again.
526 fontsCache.push(family)
527 }
528
529 } else {
530 // Maybe Typekit, Fonts.com or Fontdeck fonts
531 }
532 }
533
534 const standardizeToArray = function (value) {
535 if (typeof value === 'string' || typeof value === 'number') {
536 value = [value]
537 } else if (typeof value === 'object') {
538 value = Object.values(value)
539 }
540
541 return value
542 }
543
544 // This is a mirror logic of the server-side Customify_Fonts_Global::getFontFamilyFallbackStack()
545 const getFontFamilyFallbackStack = function (fontFamily) {
546 let fallbackStack = ''
547
548 const fontDetails = customify.fontFields.getFontDetails(fontFamily)
549 if (typeof fontDetails.fallback_stack !== 'undefined' && !_.isEmpty(fontDetails.fallback_stack)) {
550 fallbackStack = fontDetails.fallback_stack
551 } else if (typeof fontDetails.category !== 'undefined' && !_.isEmpty(fontDetails.category)) {
552 const category = fontDetails.category
553 // Search in the available categories for a match.
554 if (typeof customify.fonts.categories[category] !== 'undefined') {
555 // Matched by category ID/key
556 fallbackStack = typeof customify.fonts.categories[category].fallback_stack !== 'undefined' ? customify.fonts.categories[category].fallback_stack : ''
557 } else {
558 // We need to search for aliases.
559 _.find(customify.fonts.categories, function (categoryDetails) {
560 if (typeof categoryDetails.aliases !== 'undefined') {
561 const aliases = maybeImplodeList(categoryDetails.aliases)
562 if (aliases.indexOf(category) !== -1) {
563 // Found it.
564 fallbackStack = typeof categoryDetails.fallback_stack !== 'undefined' ? categoryDetails.fallback_stack : ''
565 return true
566 }
567 }
568
569 return false
570 })
571 }
572 }
573
574 return fallbackStack
575 }
576
577 // Mirror logic of server-side Customify_Fonts_Global::sanitizeFontFamilyCSSValue()
578 const sanitizeFontFamilyCSSValue = function (value) {
579 // Since we might get a stack, attempt to treat is a comma-delimited list.
580 let fontFamilies = maybeExplodeList(value)
581 if (!fontFamilies.length) {
582 return ''
583 }
584
585 _.each(fontFamilies, function (fontFamily, key) {
586 // Make sure that the font family is free from " or ' or whitespace, at the front.
587 fontFamily = fontFamily.replace(new RegExp(/^\s*["'‘’“”]*\s*/), '')
588 // Make sure that the font family is free from " or ' or whitespace, at the back.
589 fontFamily = fontFamily.replace(new RegExp(/\s*["'‘’“”]*\s*$/), '')
590
591 if ('' === fontFamily) {
592 delete fontFamilies[key]
593 return;
594 }
595
596 // Now, if the font family contains spaces, wrap it in ".
597 if (fontFamily.indexOf(' ') !== -1) {
598 fontFamily = '"' + fontFamily + '"'
599 }
600
601 // Finally, put it back.
602 fontFamilies[key] = fontFamily
603 })
604
605 return maybeImplodeList( fontFamilies );
606 }
607
608 const maybeExplodeList = function(str, delimiter = ',') {
609 if (typeof str === 'object') {
610 str = standardizeToArray(str)
611 }
612
613 // If by any chance we are given an array, just return it
614 if (Array.isArray(str)) {
615 return str
616 }
617
618 // Anything else we coerce to a string
619 if ( typeof str !== 'string' ) {
620 str = String(str);
621 }
622
623 // Make sure we trim it
624 str = str.trim();
625
626 // Bail on empty string
627 if ( !str.length ) {
628 return [];
629 }
630
631 // Return the whole string as an element if the delimiter is missing
632 if ( str.indexOf(delimiter) === -1 ) {
633 return [str];
634 }
635
636 // Explode it and return it
637 return explode(delimiter, str);
638 }
639
640 const maybeImplodeList = function(value, glue = ',') {
641 // If by any chance we are given a string, just return it
642 if (typeof value === 'string' || typeof value === 'number') {
643 return String(value)
644 }
645
646 if (typeof value === 'object') {
647 value = standardizeToArray(value)
648 }
649
650 if (Array.isArray(value)) {
651 return implode(glue, value)
652 }
653
654 // For anything else we return an empty string.
655 return ''
656 }
657
658 const explode = function (delimiter, string, limit) {
659 // discuss at: https://locutus.io/php/explode/
660 // original by: Kevin van Zonneveld (https://kvz.io)
661 // example 1: explode(' ', 'Kevin van Zonneveld')
662 // returns 1: [ 'Kevin', 'van', 'Zonneveld' ]
663
664 if (arguments.length < 2 ||
665 typeof delimiter === 'undefined' ||
666 typeof string === 'undefined') {
667 return null
668 }
669 if (delimiter === '' ||
670 delimiter === false ||
671 delimiter === null) {
672 return false
673 }
674 if (typeof delimiter === 'function' ||
675 typeof delimiter === 'object' ||
676 typeof string === 'function' ||
677 typeof string === 'object') {
678 return {
679 0: ''
680 }
681 }
682 if (delimiter === true) {
683 delimiter = '1'
684 }
685
686 // Here we go...
687 delimiter += ''
688 string += ''
689
690 let s = string.split(delimiter)
691
692 if (typeof limit === 'undefined') return s
693
694 // Support for limit
695 if (limit === 0) limit = 1
696
697 // Positive limit
698 if (limit > 0) {
699 if (limit >= s.length) {
700 return s
701 }
702 return s
703 .slice(0, limit - 1)
704 .concat([s.slice(limit - 1)
705 .join(delimiter)
706 ])
707 }
708
709 // Negative limit
710 if (-limit >= s.length) {
711 return []
712 }
713
714 s.splice(s.length + limit)
715 return s
716 }
717
718 const implode = function (glue, pieces) {
719 // discuss at: https://locutus.io/php/implode/
720 // original by: Kevin van Zonneveld (https://kvz.io)
721 // improved by: Waldo Malqui Silva (https://waldo.malqui.info)
722 // improved by: Itsacon (https://www.itsacon.net/)
723 // bugfixed by: Brett Zamir (https://brett-zamir.me)
724 // example 1: implode(' ', ['Kevin', 'van', 'Zonneveld'])
725 // returns 1: 'Kevin van Zonneveld'
726 // example 2: implode(' ', {first:'Kevin', last: 'van Zonneveld'})
727 // returns 2: 'Kevin van Zonneveld'
728
729 let i = ''
730 let retVal = ''
731 let tGlue = ''
732
733 if (arguments.length === 1) {
734 pieces = glue
735 glue = ''
736 }
737
738 if (typeof pieces === 'object') {
739 if (Object.prototype.toString.call(pieces) === '[object Array]') {
740 return pieces.join(glue)
741 }
742 for (i in pieces) {
743 retVal += tGlue + pieces[i]
744 tGlue = glue
745 }
746 return retVal
747 }
748
749 return pieces
750 }
751 })
752 })(jQuery, window, document)
753