UNPKG

40.7 kBJavaScriptView Raw
1
2/**
3 * jQuery fontIconPicker - 3.1.1
4 *
5 * An icon picker built on top of font icons and jQuery
6 *
7 * http://codeb.it/fontIconPicker
8 *
9 * @author Alessandro Benoit & Swashata Ghosh
10 * @license MIT License
11 *
12 * {@link https://github.com/micc83/fontIconPicker}
13 */
14
15(function (global, factory) {
16 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery')) :
17 typeof define === 'function' && define.amd ? define(['jquery'], factory) :
18 (global.initFontIconPickerNode = factory(global.jQuery));
19}(this, (function (jQuery) { 'use strict';
20
21 jQuery = jQuery && jQuery.hasOwnProperty('default') ? jQuery['default'] : jQuery;
22
23 function _typeof(obj) {
24 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
25 _typeof = function (obj) {
26 return typeof obj;
27 };
28 } else {
29 _typeof = function (obj) {
30 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
31 };
32 }
33
34 return _typeof(obj);
35 }
36
37 function _toConsumableArray(arr) {
38 return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
39 }
40
41 function _arrayWithoutHoles(arr) {
42 if (Array.isArray(arr)) {
43 for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
44
45 return arr2;
46 }
47 }
48
49 function _iterableToArray(iter) {
50 if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
51 }
52
53 function _nonIterableSpread() {
54 throw new TypeError("Invalid attempt to spread non-iterable instance");
55 }
56
57 /**
58 * Default configuration options of fontIconPicker
59 */
60
61 var options = {
62 theme: 'fip-grey',
63 // The CSS theme to use with this fontIconPicker. You can set different themes on multiple elements on the same page
64 source: false,
65 // Icons source (array|false|object)
66 emptyIcon: true,
67 // Empty icon should be shown?
68 emptyIconValue: '',
69 // The value of the empty icon, change if you select has something else, say "none"
70 autoClose: true,
71 // Whether or not to close the FIP automatically when clicked outside
72 iconsPerPage: 20,
73 // Number of icons per page
74 hasSearch: true,
75 // Is search enabled?
76 searchSource: false,
77 // Give a manual search values. If using attributes then for proper search feature we also need to pass icon names under the same order of source
78 appendTo: 'self',
79 // Where to append the selector popup. You can pass string selectors or jQuery objects
80 useAttribute: false,
81 // Whether to use attribute selector for printing icons
82 attributeName: 'data-icon',
83 // HTML Attribute name
84 convertToHex: true,
85 // Whether or not to convert to hexadecimal for attribute value. If true then please pass decimal integer value to the source (or as value="" attribute of the select field)
86 allCategoryText: 'From all categories',
87 // The text for the select all category option
88 unCategorizedText: 'Uncategorized',
89 // The text for the select uncategorized option
90 iconGenerator: null,
91 // Icon Generator function. Passes, item, flipBoxTitle and index
92 windowDebounceDelay: 150,
93 // Debounce delay while fixing position on windowResize
94 searchPlaceholder: 'Search Icons' // Placeholder for the search input
95
96 };
97
98 /**
99 * Implementation of debounce function
100 *
101 * {@link https://medium.com/a-developers-perspective/throttling-and-debouncing-in-javascript-b01cad5c8edf}
102 * @param {Function} func callback function
103 * @param {int} delay delay in milliseconds
104 */
105 var debounce = function debounce(func, delay) {
106 var inDebounce;
107 return function () {
108 var context = this;
109 var args = arguments;
110 clearTimeout(inDebounce);
111 inDebounce = setTimeout(function () {
112 return func.apply(context, args);
113 }, delay);
114 };
115 };
116
117 var $ = jQuery; // A guid for implementing namespaced event
118
119 var guid = 0;
120
121 function FontIconPicker(element, options$$1) {
122 this.element = $(element);
123 this.settings = $.extend({}, options, options$$1);
124
125 if (this.settings.emptyIcon) {
126 this.settings.iconsPerPage--;
127 }
128
129 this.iconPicker = $('<div/>', {
130 class: 'icons-selector',
131 style: 'position: relative',
132 html: this._getPickerTemplate(),
133 attr: {
134 'data-fip-origin': this.element.attr('id')
135 }
136 });
137 this.iconContainer = this.iconPicker.find('.fip-icons-container');
138 this.searchIcon = this.iconPicker.find('.selector-search i');
139 this.selectorPopup = this.iconPicker.find('.selector-popup-wrap');
140 this.selectorButton = this.iconPicker.find('.selector-button');
141 this.iconsSearched = [];
142 this.isSearch = false;
143 this.totalPage = 1;
144 this.currentPage = 1;
145 this.currentIcon = false;
146 this.iconsCount = 0;
147 this.open = false;
148 this.guid = guid++;
149 this.eventNameSpace = ".fontIconPicker".concat(guid); // Set the default values for the search related variables
150
151 this.searchValues = [];
152 this.availableCategoriesSearch = []; // The trigger event for change
153
154 this.triggerEvent = null; // Backups
155
156 this.backupSource = [];
157 this.backupSearch = []; // Set the default values of the category related variables
158
159 this.isCategorized = false; // Automatically detects if the icon listing is categorized
160
161 this.selectCategory = this.iconPicker.find('.icon-category-select'); // The category SELECT input field
162
163 this.selectedCategory = false; // false means all categories are selected
164
165 this.availableCategories = []; // Available categories, it is a two dimensional array which holds categorized icons
166
167 this.unCategorizedKey = null; // Key of the uncategorized category
168 // Initialize plugin
169
170 this.init();
171 }
172
173 FontIconPicker.prototype = {
174 /**
175 * Init
176 */
177 init: function init() {
178 // Add the theme CSS to the iconPicker
179 this.iconPicker.addClass(this.settings.theme); // To properly calculate iconPicker height and width
180 // We will first append it to body (with left: -9999px so that it is not visible)
181
182 this.iconPicker.css({
183 left: -9999
184 }).appendTo('body');
185 var iconPickerHeight = this.iconPicker.outerHeight(),
186 iconPickerWidth = this.iconPicker.outerWidth(); // Now reset the iconPicker CSS
187
188 this.iconPicker.css({
189 left: ''
190 }); // Add the icon picker after the select
191
192 this.element.before(this.iconPicker); // Hide source element
193 // Instead of doing a display:none, we would rather
194 // make the element invisible
195 // and adjust the margin
196
197 this.element.css({
198 visibility: 'hidden',
199 top: 0,
200 position: 'relative',
201 zIndex: '-1',
202 left: '-' + iconPickerWidth + 'px',
203 display: 'inline-block',
204 height: iconPickerHeight + 'px',
205 width: iconPickerWidth + 'px',
206 // Reset all margin, border and padding
207 padding: '0',
208 margin: '0 -' + iconPickerWidth + 'px 0 0',
209 // Left margin adjustment to account for dangling space
210 border: '0 none',
211 verticalAlign: 'top',
212 float: 'none' // Fixes positioning with floated elements
213
214 }); // Set the trigger event
215
216 if (!this.element.is('select')) {
217 // Drop IE9 support and use the standard input event
218 this.triggerEvent = 'input';
219 } // If current element is SELECT populate settings.source
220
221
222 if (!this.settings.source && this.element.is('select')) {
223 // Populate data from select
224 this._populateSourceFromSelect(); // Normalize the given source
225
226 } else {
227 this._initSourceIndex();
228 } // load the categories
229
230
231 this._loadCategories(); // Load icons
232
233
234 this._loadIcons(); // Initialize dropdown button
235
236
237 this._initDropDown(); // Category changer
238
239
240 this._initCategoryChanger(); // Pagination
241
242
243 this._initPagination(); // Icon Search
244
245
246 this._initIconSearch(); // Icon Select
247
248
249 this._initIconSelect();
250 /**
251 * On click out
252 * Add the functionality #9
253 * {@link https://github.com/micc83/fontIconPicker/issues/9}
254 */
255
256
257 this._initAutoClose(); // Window resize fix
258
259
260 this._initFixOnResize();
261 },
262
263 /**
264 * Set icons after the fip has been initialized
265 */
266 setIcons: function setIcons(newIcons, iconSearch) {
267 this.settings.source = Array.isArray(newIcons) ? _toConsumableArray(newIcons) : $.extend({}, newIcons);
268 this.settings.searchSource = Array.isArray(iconSearch) ? _toConsumableArray(iconSearch) : $.extend({}, iconSearch);
269
270 this._initSourceIndex();
271
272 this._loadCategories();
273
274 this._resetSearch();
275
276 this._loadIcons();
277 },
278
279 /**
280 * Set currently selected icon programmatically
281 *
282 * @param {string} theIcon current icon value
283 */
284 setIcon: function setIcon() {
285 var theIcon = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
286
287 this._setSelectedIcon(theIcon);
288 },
289
290 /**
291 * Destroy picker and all events
292 */
293 destroy: function destroy() {
294 this.iconPicker.off().remove();
295 this.element.css({
296 visibility: '',
297 top: '',
298 position: '',
299 zIndex: '',
300 left: '',
301 display: '',
302 height: '',
303 width: '',
304 padding: '',
305 margin: '',
306 border: '',
307 verticalAlign: '',
308 float: ''
309 }); // Remove the delegated events
310
311 $(window).off('resize' + this.eventNameSpace);
312 $('html').off('click' + this.eventNameSpace);
313 },
314
315 /**
316 * Manually reset position
317 */
318 resetPosition: function resetPosition() {
319 this._fixOnResize();
320 },
321
322 /**
323 * Manually set page
324 * @param {int} pageNum
325 */
326 setPage: function setPage(pageNum) {
327 if ('first' == pageNum) {
328 pageNum = 1;
329 }
330
331 if ('last' == pageNum) {
332 pageNum = this.totalPage;
333 }
334
335 pageNum = parseInt(pageNum, 10);
336
337 if (isNaN(pageNum)) {
338 pageNum = 1;
339 }
340
341 if (pageNum > this.totalPage) {
342 pageNum = this.totalPage;
343 }
344
345 if (1 > pageNum) {
346 pageNum = 1;
347 }
348
349 this.currentPage = pageNum;
350
351 this._renderIconContainer();
352 },
353
354 /**
355 * Initialize Fix on window resize with debouncing
356 * This helps reduce function call unnecessary times.
357 */
358 _initFixOnResize: function _initFixOnResize() {
359 var _this = this;
360
361 $(window).on('resize' + this.eventNameSpace, debounce(function () {
362 _this._fixOnResize();
363 }, this.settings.windowDebounceDelay));
364 },
365
366 /**
367 * Initiate autoClosing
368 *
369 * Checks for settings, and if set to yes, then autocloses the dropdown
370 */
371 _initAutoClose: function _initAutoClose() {
372 var _this2 = this;
373
374 if (this.settings.autoClose) {
375 $('html').on('click' + this.eventNameSpace, function (event) {
376 // Check if event is coming from selector popup or icon picker
377 var target = event.target;
378
379 if (_this2.selectorPopup.has(target).length || _this2.selectorPopup.is(target) || _this2.iconPicker.has(target).length || _this2.iconPicker.is(target)) {
380 // Return
381 return;
382 } // Close it
383
384
385 if (_this2.open) {
386 _this2._toggleIconSelector();
387 }
388 });
389 }
390 },
391
392 /**
393 * Select Icon
394 */
395 _initIconSelect: function _initIconSelect() {
396 var _this3 = this;
397
398 this.selectorPopup.on('click', '.fip-box', function (e) {
399 var fipBox = $(e.currentTarget);
400
401 _this3._setSelectedIcon(fipBox.attr('data-fip-value'));
402
403 _this3._toggleIconSelector();
404 });
405 },
406
407 /**
408 * Initiate realtime icon search
409 */
410 _initIconSearch: function _initIconSearch() {
411 var _this4 = this;
412
413 this.selectorPopup.on('input', '.icons-search-input', function (e) {
414 // Get the search string
415 var searchString = $(e.currentTarget).val(); // If the string is not empty
416
417 if ('' === searchString) {
418 _this4._resetSearch();
419
420 return;
421 } // Set icon search to X to reset search
422
423
424 _this4.searchIcon.removeClass('fip-icon-search');
425
426 _this4.searchIcon.addClass('fip-icon-cancel'); // Set this as a search
427
428
429 _this4.isSearch = true; // Reset current page
430
431 _this4.currentPage = 1; // Actual search
432 // This has been modified to search the searchValues instead
433 // Then return the value from the source if match is found
434
435 _this4.iconsSearched = [];
436 $.grep(_this4.searchValues, function (n, i) {
437 if (0 <= n.toLowerCase().search(searchString.toLowerCase())) {
438 _this4.iconsSearched[_this4.iconsSearched.length] = _this4.settings.source[i];
439 return true;
440 }
441 }); // Render icon list
442
443 _this4._renderIconContainer();
444 });
445 /**
446 * Quit search
447 */
448 // Quit search happens only if clicked on the cancel button
449
450 this.selectorPopup.on('click', '.selector-search .fip-icon-cancel', function () {
451 _this4.selectorPopup.find('.icons-search-input').focus();
452
453 _this4._resetSearch();
454 });
455 },
456
457 /**
458 * Initiate Pagination
459 */
460 _initPagination: function _initPagination() {
461 var _this5 = this;
462
463 /**
464 * Next page
465 */
466 this.selectorPopup.on('click', '.selector-arrow-right', function (e) {
467 if (_this5.currentPage < _this5.totalPage) {
468 _this5.currentPage = _this5.currentPage + 1;
469
470 _this5._renderIconContainer();
471 }
472 });
473 /**
474 * Prev page
475 */
476
477 this.selectorPopup.on('click', '.selector-arrow-left', function (e) {
478 if (1 < _this5.currentPage) {
479 _this5.currentPage = _this5.currentPage - 1;
480
481 _this5._renderIconContainer();
482 }
483 });
484 },
485
486 /**
487 * Initialize category changer dropdown
488 */
489 _initCategoryChanger: function _initCategoryChanger() {
490 var _this6 = this;
491
492 // Since the popup can be appended anywhere
493 // We will add the event listener to the popup
494 // And will stop the eventPropagation on click
495 // @since v2.1.0
496 this.selectorPopup.on('change keyup', '.icon-category-select', function (e) {
497 // Don't do anything if not categorized
498 if (false === _this6.isCategorized) {
499 return false;
500 }
501
502 var targetSelect = $(e.currentTarget),
503 currentCategory = targetSelect.val(); // Check if all categories are selected
504
505 if ('all' === targetSelect.val()) {
506 // Restore from the backups
507 // @note These backups must be rebuild on source change, otherwise it will lead to error
508 _this6.settings.source = _this6.backupSource;
509 _this6.searchValues = _this6.backupSearch; // No? So there is a specified category
510 } else {
511 var key = parseInt(currentCategory, 10);
512
513 if (_this6.availableCategories[key]) {
514 _this6.settings.source = _this6.availableCategories[key];
515 _this6.searchValues = _this6.availableCategoriesSearch[key];
516 }
517 }
518
519 _this6._resetSearch();
520
521 _this6._loadIcons();
522 });
523 },
524
525 /**
526 * Initialize Dropdown button
527 */
528 _initDropDown: function _initDropDown() {
529 var _this7 = this;
530
531 this.selectorButton.on('click', function (event) {
532 // Open/Close the icon picker
533 _this7._toggleIconSelector();
534 });
535 },
536
537 /**
538 * Get icon Picker Template String
539 */
540 _getPickerTemplate: function _getPickerTemplate() {
541 var pickerTemplate = "\n<div class=\"selector\" data-fip-origin=\"".concat(this.element.attr('id'), "\">\n\t<span class=\"selected-icon\">\n\t\t<i class=\"fip-icon-block\"></i>\n\t</span>\n\t<span class=\"selector-button\">\n\t\t<i class=\"fip-icon-down-dir\"></i>\n\t</span>\n</div>\n<div class=\"selector-popup-wrap\" data-fip-origin=\"").concat(this.element.attr('id'), "\">\n\t<div class=\"selector-popup\" style=\"display: none;\"> ").concat(this.settings.hasSearch ? "<div class=\"selector-search\">\n\t\t\t<input type=\"text\" name=\"\" value=\"\" placeholder=\"".concat(this.settings.searchPlaceholder, "\" class=\"icons-search-input\"/>\n\t\t\t<i class=\"fip-icon-search\"></i>\n\t\t</div>") : '', "\n\t\t<div class=\"selector-category\">\n\t\t\t<select name=\"\" class=\"icon-category-select\" style=\"display: none\"></select>\n\t\t</div>\n\t\t<div class=\"fip-icons-container\"></div>\n\t\t<div class=\"selector-footer\" style=\"display:none;\">\n\t\t\t<span class=\"selector-pages\">1/2</span>\n\t\t\t<span class=\"selector-arrows\">\n\t\t\t\t<span class=\"selector-arrow-left\" style=\"display:none;\">\n\t\t\t\t\t<i class=\"fip-icon-left-dir\"></i>\n\t\t\t\t</span>\n\t\t\t\t<span class=\"selector-arrow-right\">\n\t\t\t\t\t<i class=\"fip-icon-right-dir\"></i>\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t</div>\n\t</div>\n</div>");
542 return pickerTemplate;
543 },
544
545 /**
546 * Init the source & search index from the current settings
547 * @return {void}
548 */
549 _initSourceIndex: function _initSourceIndex() {
550 // First check for any sorts of errors
551 if ('object' !== _typeof(this.settings.source)) {
552 return;
553 } // We are going to check if the passed source is an array or an object
554 // If it is an array, then don't do anything
555 // otherwise it has to be an object and therefore is it a categorized icon set
556
557
558 if (Array.isArray(this.settings.source)) {
559 // This is not categorized since it is 1D array
560 this.isCategorized = false;
561 this.selectCategory.html('').hide(); // We are going to convert the source items to string
562 // This is necessary because passed source might not be "strings" for attribute related icons
563
564 this.settings.source = $.map(this.settings.source, function (e, i) {
565 if ('function' == typeof e.toString) {
566 return e.toString();
567 } else {
568 return e;
569 }
570 }); // Now update the search
571 // First check if the search is given by user
572
573 if (Array.isArray(this.settings.searchSource)) {
574 // Convert everything inside the searchSource to string
575 this.searchValues = $.map(this.settings.searchSource, function (e, i) {
576 if ('function' == typeof e.toString) {
577 return e.toString();
578 } else {
579 return e;
580 }
581 }); // Clone the searchSource
582 // Not given so use the source instead
583 } else {
584 this.searchValues = this.settings.source.slice(0); // Clone the source
585 } // Categorized icon set
586
587 } else {
588 var originalSource = $.extend(true, {}, this.settings.source); // Reset the source
589
590 this.settings.source = []; // Reset other variables
591
592 this.searchValues = [];
593 this.availableCategoriesSearch = [];
594 this.selectedCategory = false;
595 this.availableCategories = [];
596 this.unCategorizedKey = null; // Set the categorized to true and reset the HTML
597
598 this.isCategorized = true;
599 this.selectCategory.html(''); // Now loop through the source and add to the list
600
601 for (var categoryLabel in originalSource) {
602 // Get the key of the new category array
603 var thisCategoryKey = this.availableCategories.length,
604 // Create the new option for the selectCategory SELECT field
605 categoryOption = $('<option />'); // Set the value to this categorykey
606
607 categoryOption.attr('value', thisCategoryKey); // Set the label
608
609 categoryOption.html(categoryLabel); // Append to the DOM
610
611 this.selectCategory.append(categoryOption); // Init the availableCategories array
612
613 this.availableCategories[thisCategoryKey] = [];
614 this.availableCategoriesSearch[thisCategoryKey] = []; // Now loop through it's icons and add to the list
615
616 for (var newIconKey in originalSource[categoryLabel]) {
617 // Get the new icon value
618 var newIconValue = originalSource[categoryLabel][newIconKey]; // Get the label either from the searchSource if set, otherwise from the source itself
619
620 var newIconLabel = this.settings.searchSource && this.settings.searchSource[categoryLabel] && this.settings.searchSource[categoryLabel][newIconKey] ? this.settings.searchSource[categoryLabel][newIconKey] : newIconValue; // Try to convert to the source value string
621 // This is to avoid attribute related icon sets
622 // Where hexadecimal or decimal numbers might be passed
623
624 if ('function' == typeof newIconValue.toString) {
625 newIconValue = newIconValue.toString();
626 } // Check if the option element has value and this value does not equal to the empty value
627
628
629 if (newIconValue && newIconValue !== this.settings.emptyIconValue) {
630 // Push to the source, because at first all icons are selected
631 this.settings.source.push(newIconValue); // Push to the availableCategories child array
632
633 this.availableCategories[thisCategoryKey].push(newIconValue); // Push to the search values
634
635 this.searchValues.push(newIconLabel);
636 this.availableCategoriesSearch[thisCategoryKey].push(newIconLabel);
637 }
638 }
639 }
640 } // Clone and backup the original source and search
641
642
643 this.backupSource = this.settings.source.slice(0);
644 this.backupSearch = this.searchValues.slice(0);
645 },
646
647 /**
648 * Populate source from select element
649 * Check if select has optgroup, if so, then we are dealing with categorized
650 * data. Otherwise, plain data.
651 */
652 _populateSourceFromSelect: function _populateSourceFromSelect() {
653 var _this8 = this;
654
655 // Reset the source and searchSource
656 // These will be populated according to the available options
657 this.settings.source = [];
658 this.settings.searchSource = []; // Check if optgroup is present within the select
659 // If it is present then the source has to be grouped
660
661 if (this.element.find('optgroup').length) {
662 // Set the categorized to true
663 this.isCategorized = true;
664 this.element.find('optgroup').each(function (i, el) {
665 // Get the key of the new category array
666 var thisCategoryKey = _this8.availableCategories.length,
667 // Create the new option for the selectCategory SELECT field
668 categoryOption = $('<option />'); // Set the value to this categorykey
669
670 categoryOption.attr('value', thisCategoryKey); // Set the label
671
672 categoryOption.html($(el).attr('label')); // Append to the DOM
673
674 _this8.selectCategory.append(categoryOption); // Init the availableCategories array
675
676
677 _this8.availableCategories[thisCategoryKey] = [];
678 _this8.availableCategoriesSearch[thisCategoryKey] = []; // Now loop through it's option elements and add the icons
679
680 $(el).find('option').each(function (i, cel) {
681 var newIconValue = $(cel).val(),
682 newIconLabel = $(cel).html(); // Check if the option element has value and this value does not equal to the empty value
683
684 if (newIconValue && newIconValue !== _this8.settings.emptyIconValue) {
685 // Push to the source, because at first all icons are selected
686 _this8.settings.source.push(newIconValue); // Push to the availableCategories child array
687
688
689 _this8.availableCategories[thisCategoryKey].push(newIconValue); // Push to the search values
690
691
692 _this8.searchValues.push(newIconLabel);
693
694 _this8.availableCategoriesSearch[thisCategoryKey].push(newIconLabel);
695 }
696 });
697 }); // Additionally check for any first label option child
698
699 if (this.element.find('> option').length) {
700 this.element.find('> option').each(function (i, el) {
701 var newIconValue = $(el).val(),
702 newIconLabel = $(el).html(); // Don't do anything if the new icon value is empty
703
704 if (!newIconValue || '' === newIconValue || newIconValue == _this8.settings.emptyIconValue) {
705 return true;
706 } // Set the uncategorized key if not set already
707
708
709 if (null === _this8.unCategorizedKey) {
710 _this8.unCategorizedKey = _this8.availableCategories.length;
711 _this8.availableCategories[_this8.unCategorizedKey] = [];
712 _this8.availableCategoriesSearch[_this8.unCategorizedKey] = []; // Create an option and append to the category selector
713
714 $('<option />').attr('value', _this8.unCategorizedKey).html(_this8.settings.unCategorizedText).appendTo(_this8.selectCategory);
715 } // Push the icon to the category
716
717
718 _this8.settings.source.push(newIconValue);
719
720 _this8.availableCategories[_this8.unCategorizedKey].push(newIconValue); // Push the icon to the search
721
722
723 _this8.searchValues.push(newIconLabel);
724
725 _this8.availableCategoriesSearch[_this8.unCategorizedKey].push(newIconLabel);
726 });
727 } // Not categorized
728
729 } else {
730 this.element.find('option').each(function (i, el) {
731 var newIconValue = $(el).val(),
732 newIconLabel = $(el).html();
733
734 if (newIconValue) {
735 _this8.settings.source.push(newIconValue);
736
737 _this8.searchValues.push(newIconLabel);
738 }
739 });
740 } // Clone and backup the original source and search
741
742
743 this.backupSource = this.settings.source.slice(0);
744 this.backupSearch = this.searchValues.slice(0);
745 },
746
747 /**
748 * Load Categories
749 * @return {void}
750 */
751 _loadCategories: function _loadCategories() {
752 // Dont do anything if it is not categorized
753 if (false === this.isCategorized) {
754 return;
755 } // Now append all to the category selector
756
757
758 $('<option value="all">' + this.settings.allCategoryText + '</option>').prependTo(this.selectCategory); // Show it and set default value to all categories
759
760 this.selectCategory.show().val('all').trigger('change');
761 },
762
763 /**
764 * Load icons
765 */
766 _loadIcons: function _loadIcons() {
767 // Set the content of the popup as loading
768 this.iconContainer.html('<i class="fip-icon-spin3 animate-spin loading"></i>'); // If source is set
769
770 if (Array.isArray(this.settings.source)) {
771 // Render icons
772 this._renderIconContainer();
773 }
774 },
775
776 /**
777 * Generate icons
778 *
779 * Supports hookable third-party renderer function.
780 */
781 _iconGenerator: function _iconGenerator(icon) {
782 if ('function' === typeof this.settings.iconGenerator) {
783 return this.settings.iconGenerator(icon);
784 }
785
786 return '<i ' + (this.settings.useAttribute ? this.settings.attributeName + '="' + (this.settings.convertToHex ? '&#x' + parseInt(icon, 10).toString(16) + ';' : icon) + '"' : 'class="' + icon + '"') + '></i>';
787 },
788
789 /**
790 * Render icons inside the popup
791 */
792 _renderIconContainer: function _renderIconContainer() {
793 var _this9 = this;
794
795 var offset,
796 iconsPaged = [];
797 // Set a temporary array for icons
798
799 if (this.isSearch) {
800 iconsPaged = this.iconsSearched;
801 } else {
802 iconsPaged = this.settings.source;
803 } // Count elements
804
805
806 this.iconsCount = iconsPaged.length; // Calculate total page number
807
808 this.totalPage = Math.ceil(this.iconsCount / this.settings.iconsPerPage); // Hide footer if no pagination is needed
809
810 if (1 < this.totalPage) {
811 this.selectorPopup.find('.selector-footer').show(); // Reset the pager buttons
812 // Fix #8 {@link https://github.com/micc83/fontIconPicker/issues/8}
813 // It is better to set/hide the pager button here
814 // instead of all other functions that calls back _renderIconContainer
815
816 if (this.currentPage < this.totalPage) {
817 // current page is less than total, so show the arrow right
818 this.selectorPopup.find('.selector-arrow-right').show();
819 } else {
820 // else hide it
821 this.selectorPopup.find('.selector-arrow-right').hide();
822 }
823
824 if (1 < this.currentPage) {
825 // current page is greater than one, so show the arrow left
826 this.selectorPopup.find('.selector-arrow-left').show();
827 } else {
828 // else hide it
829 this.selectorPopup.find('.selector-arrow-left').hide();
830 }
831 } else {
832 this.selectorPopup.find('.selector-footer').hide();
833 } // Set the text for page number index and total icons
834
835
836 this.selectorPopup.find('.selector-pages').html(this.currentPage + '/' + this.totalPage + ' <em>(' + this.iconsCount + ')</em>'); // Set the offset for slice
837
838 offset = (this.currentPage - 1) * this.settings.iconsPerPage; // Should empty icon be shown?
839
840 if (this.settings.emptyIcon) {
841 // Reset icon container HTML and prepend empty icon
842 this.iconContainer.html('<span class="fip-box" data-fip-value="fip-icon-block"><i class="fip-icon-block"></i></span>'); // If not show an error when no icons are found
843 } else if (1 > iconsPaged.length) {
844 this.iconContainer.html('<span class="icons-picker-error" data-fip-value="fip-icon-block"><i class="fip-icon-block"></i></span>');
845 return; // else empty the container
846 } else {
847 this.iconContainer.html('');
848 } // Set an array of current page icons
849
850
851 iconsPaged = iconsPaged.slice(offset, offset + this.settings.iconsPerPage); // List icons
852
853 var _loop = function _loop(i, icon) {
854 // eslint-disable-line
855 // Set the icon title
856 var fipBoxTitle = icon;
857 $.grep(_this9.settings.source, $.proxy(function (e, i) {
858 if (e === icon) {
859 fipBoxTitle = this.searchValues[i];
860 return true;
861 }
862
863 return false;
864 }, _this9)); // Set the icon box
865
866 $('<span/>', {
867 html: _this9._iconGenerator(icon),
868 attr: {
869 'data-fip-value': icon
870 },
871 class: 'fip-box',
872 title: fipBoxTitle
873 }).appendTo(_this9.iconContainer);
874 };
875
876 for (var i = 0, icon; icon = iconsPaged[i++];) {
877 _loop(i, icon);
878 } // If no empty icon is allowed and no current value is set or current value is not inside the icon set
879
880
881 if (!this.settings.emptyIcon && (!this.element.val() || -1 === $.inArray(this.element.val(), this.settings.source))) {
882 // Get the first icon
883 this._setSelectedIcon(iconsPaged[0]);
884 } else if (-1 === $.inArray(this.element.val(), this.settings.source)) {
885 // Issue #7
886 // Need to pass empty string
887 // Set empty
888 // Otherwise DOM will be set to null value
889 // which would break the initial select value
890 this._setSelectedIcon('');
891 } else {
892 // Fix issue #7
893 // The trick is to check the element value
894 // Internally fip-icon-block must be used for empty values
895 // So if element.val == emptyIconValue then pass fip-icon-block
896 var passDefaultIcon = this.element.val();
897
898 if (passDefaultIcon === this.settings.emptyIconValue) {
899 passDefaultIcon = 'fip-icon-block';
900 } // Set the default selected icon even if not set
901
902
903 this._setSelectedIcon(passDefaultIcon);
904 }
905 },
906
907 /**
908 * Set Highlighted icon
909 */
910 _setHighlightedIcon: function _setHighlightedIcon() {
911 this.iconContainer.find('.current-icon').removeClass('current-icon');
912
913 if (this.currentIcon) {
914 this.iconContainer.find('[data-fip-value="' + this.currentIcon + '"]').addClass('current-icon');
915 }
916 },
917
918 /**
919 * Set selected icon
920 *
921 * @param {string} theIcon
922 */
923 _setSelectedIcon: function _setSelectedIcon(theIcon) {
924 if ('fip-icon-block' === theIcon) {
925 theIcon = '';
926 }
927
928 var selectedIcon = this.iconPicker.find('.selected-icon'); // if the icon is empty, then reset to empty
929
930 if ('' === theIcon) {
931 selectedIcon.html('<i class="fip-icon-block"></i>');
932 } else {
933 // Pass it to the render function
934 selectedIcon.html(this._iconGenerator(theIcon));
935 } // Check if actually changing the DOM element
936
937
938 var currentValue = this.element.val(); // Set the value of the element
939
940 this.element.val('' === theIcon ? this.settings.emptyIconValue : theIcon); // trigger event if change has actually occured
941
942 if (currentValue !== theIcon) {
943 this.element.trigger('change');
944
945 if (null !== this.triggerEvent) {
946 this.element.trigger(this.triggerEvent);
947 }
948 }
949
950 this.currentIcon = theIcon;
951
952 this._setHighlightedIcon();
953 },
954
955 /**
956 * Recalculate the position of the Popup
957 */
958 _repositionIconSelector: function _repositionIconSelector() {
959 // Calculate the position + width
960 var offset = this.iconPicker.offset(),
961 offsetTop = offset.top + this.iconPicker.outerHeight(true),
962 offsetLeft = offset.left;
963 this.selectorPopup.css({
964 left: offsetLeft,
965 top: offsetTop
966 });
967 },
968
969 /**
970 * Fix window overflow of popup dropdown if needed
971 *
972 * This can happen if appending to self or someplace else
973 */
974 _fixWindowOverflow: function _fixWindowOverflow() {
975 // Adjust the offsetLeft
976 // Resolves issue #10
977 // @link https://github.com/micc83/fontIconPicker/issues/10
978 var visibilityStatus = this.selectorPopup.find('.selector-popup').is(':visible');
979
980 if (!visibilityStatus) {
981 this.selectorPopup.find('.selector-popup').show();
982 }
983
984 var popupWidth = this.selectorPopup.outerWidth(),
985 windowWidth = $(window).width(),
986 popupOffsetLeft = this.selectorPopup.offset().left,
987 containerOffset = 'self' == this.settings.appendTo ? this.selectorPopup.parent().offset() : $(this.settings.appendTo).offset();
988
989 if (!visibilityStatus) {
990 this.selectorPopup.find('.selector-popup').hide();
991 }
992
993 if (popupOffsetLeft + popupWidth > windowWidth - 20
994 /* 20px adjustment for better appearance */
995 ) {
996 // First we try to position with right aligned
997 var pickerOffsetRight = this.selectorButton.offset().left + this.selectorButton.outerWidth();
998 var preferredLeft = Math.floor(pickerOffsetRight - popupWidth - 1);
999 /** 1px adjustment for sub-pixels */
1000 // If preferredLeft would put the popup out of window from left
1001 // then don't do it
1002
1003 if (0 > preferredLeft) {
1004 this.selectorPopup.css({
1005 left: windowWidth - 20 - popupWidth - containerOffset.left
1006 });
1007 } else {
1008 // Put it in the preferred position
1009 this.selectorPopup.css({
1010 left: preferredLeft
1011 });
1012 }
1013 }
1014 },
1015
1016 /**
1017 * Fix on Window Resize
1018 */
1019 _fixOnResize: function _fixOnResize() {
1020 // If the appendTo is not self, then we need to reposition the dropdown
1021 if ('self' !== this.settings.appendTo) {
1022 this._repositionIconSelector();
1023 } // In any-case, we need to fix for window overflow
1024
1025
1026 this._fixWindowOverflow();
1027 },
1028
1029 /**
1030 * Open/close popup (toggle)
1031 */
1032 _toggleIconSelector: function _toggleIconSelector() {
1033 this.open = !this.open ? 1 : 0; // Append the popup if needed
1034
1035 if (this.open) {
1036 // Check the origin
1037 if ('self' !== this.settings.appendTo) {
1038 // Append to the selector and set the CSS + theme
1039 this.selectorPopup.appendTo(this.settings.appendTo).css({
1040 zIndex: 1000 // Let's decrease the zIndex to something reasonable
1041
1042 }).addClass('icons-selector ' + this.settings.theme); // call resize()
1043
1044 this._repositionIconSelector();
1045 } // Fix positioning if needed
1046
1047
1048 this._fixWindowOverflow();
1049 }
1050
1051 this.selectorPopup.find('.selector-popup').slideToggle(300, $.proxy(function () {
1052 this.iconPicker.find('.selector-button i').toggleClass('fip-icon-down-dir');
1053 this.iconPicker.find('.selector-button i').toggleClass('fip-icon-up-dir');
1054
1055 if (this.open) {
1056 this.selectorPopup.find('.icons-search-input').trigger('focus').trigger('select');
1057 } else {
1058 // append and revert to the original position and reset theme
1059 this.selectorPopup.appendTo(this.iconPicker).css({
1060 left: '',
1061 top: '',
1062 zIndex: ''
1063 }).removeClass('icons-selector ' + this.settings.theme);
1064 }
1065 }, this));
1066 },
1067
1068 /**
1069 * Reset search
1070 */
1071 _resetSearch: function _resetSearch() {
1072 // Empty input
1073 this.selectorPopup.find('.icons-search-input').val(''); // Reset search icon class
1074
1075 this.searchIcon.removeClass('fip-icon-cancel');
1076 this.searchIcon.addClass('fip-icon-search'); // Go back to page 1
1077
1078 this.currentPage = 1;
1079 this.isSearch = false; // Rerender icons
1080
1081 this._renderIconContainer();
1082 }
1083 }; // ES6 Export it as module
1084
1085 /**
1086 * Light weight wrapper to inject fontIconPicker
1087 * into jQuery.fn
1088 */
1089
1090 function fontIconPickerShim($) {
1091 // Do not init if jQuery doesn't have needed stuff
1092 if (!$.fn) {
1093 return false;
1094 } // save from double init
1095
1096
1097 if ($.fn && $.fn.fontIconPicker) {
1098 return true;
1099 }
1100
1101 $.fn.fontIconPicker = function (options) {
1102 var _this = this;
1103
1104 // Instantiate the plugin
1105 this.each(function () {
1106 if (!$.data(this, 'fontIconPicker')) {
1107 $.data(this, 'fontIconPicker', new FontIconPicker(this, options));
1108 }
1109 }); // setIcons method
1110
1111 this.setIcons = function () {
1112 var newIcons = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
1113 var iconSearch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
1114
1115 _this.each(function () {
1116 $.data(this, 'fontIconPicker').setIcons(newIcons, iconSearch);
1117 });
1118 }; // setIcon method
1119
1120
1121 this.setIcon = function () {
1122 var newIcon = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
1123
1124 _this.each(function () {
1125 $.data(this, 'fontIconPicker').setIcon(newIcon);
1126 });
1127 }; // destroy method
1128
1129
1130 this.destroyPicker = function () {
1131 _this.each(function () {
1132 if (!$.data(this, 'fontIconPicker')) {
1133 return;
1134 } // Remove the iconPicker
1135
1136
1137 $.data(this, 'fontIconPicker').destroy(); // destroy data
1138
1139 $.removeData(this, 'fontIconPicker');
1140 });
1141 }; // reInit method
1142
1143
1144 this.refreshPicker = function (newOptions) {
1145 if (!newOptions) {
1146 newOptions = options;
1147 } // First destroy
1148
1149
1150 _this.destroyPicker(); // Now reset
1151
1152
1153 _this.each(function () {
1154 if (!$.data(this, 'fontIconPicker')) {
1155 $.data(this, 'fontIconPicker', new FontIconPicker(this, newOptions));
1156 }
1157 });
1158 }; // reposition method
1159
1160
1161 this.repositionPicker = function () {
1162 _this.each(function () {
1163 $.data(this, 'fontIconPicker').resetPosition();
1164 });
1165 }; // set page
1166
1167
1168 this.setPage = function (pageNum) {
1169 _this.each(function () {
1170 $.data(this, 'fontIconPicker').setPage(pageNum);
1171 });
1172 };
1173
1174 return this;
1175 };
1176
1177 return true;
1178 }
1179
1180 function initFontIconPicker(jQuery$$1) {
1181 return fontIconPickerShim(jQuery$$1);
1182 }
1183
1184 /**
1185 * jQuery fontIconPicker
1186 *
1187 * An icon picker built on top of font icons and jQuery
1188 *
1189 * http://codeb.it/fontIconPicker
1190 *
1191 * Made by Alessandro Benoit & Swashata
1192 * Licensed under MIT License
1193 *
1194 * {@link https://github.com/micc83/fontIconPicker}
1195 */
1196 // In browser this will work
1197 // But in node environment it might not.
1198 // because if jQu
1199
1200 if (jQuery && jQuery.fn) {
1201 initFontIconPicker(jQuery);
1202 } // Export the function anyway, so that it can be initiated
1203 // from node environment
1204
1205
1206 var jquery_fonticonpicker = (function (jQuery$$1) {
1207 return initFontIconPicker(jQuery$$1);
1208 });
1209
1210 return jquery_fonticonpicker;
1211
1212})));
1213//# sourceMappingURL=jquery.fonticonpicker.js.map