PageSourceSearch

https://fedme.es/wp-content/plugins/wp-file-download/app/admin/assets/js/jquery.tagit.js?ver=6.3.9

js fedme.es collected 2026-09-24 09:03:38 UTC 24,143 bytes, 591 lines download raw bytes

1/*
2* jQuery UI Tag-it!
3*
4* @version v2.0 (06/2011)
5*
6* Copyright 2011, Levy Carneiro Jr.
7* Released under the MIT license.
8* http://aehlke.github.com/tag-it/LICENSE
9*
10* Homepage:
11*   http://aehlke.github.com/tag-it/
12*
13* Authors:
14*   Levy Carneiro Jr.
15*   Martin Rehfeld
16*   Tobias Schmidt
17*   Skylar Challand
18*   Alex Ehlke
19*
20* Maintainer:
21*   Alex Ehlke - Twitter: @aehlke
22*
23* Dependencies:
24*   jQuery v1.4+
25*   jQuery UI v1.8+
26*/
27(function($) {
28
29    $.widget('ui.tagit', {
30        options: {
31            allowDuplicates   : false,
32            caseSensitive     : true,
33            fieldName         : 'tags',
34            placeholderText   : null,   // Sets `placeholder` attr on input field.
35            readOnly          : false,  // Disables editing.
36            removeConfirmation: false,  // Require confirmation to remove tags.
37            tagLimit          : null,   // Max number of tags allowed (null for unlimited).
38
39            // Used for autocomplete, unless you override `autocomplete.source`.
40            availableTags     : [],
41
42            // Use to override or add any options to the autocomplete widget.
43            //
44            // By default, autocomplete.source will map to availableTags,
45            // unless overridden.
46            autocomplete: {},
47
48            // Shows autocomplete before the user even types anything.
49            showAutocompleteOnFocus: false,
50
51            // When enabled, quotes are unneccesary for inputting multi-word tags.
52            allowSpaces: false,
53
54            // The below options are for using a single field instead of several
55            // for our form values.
56            //
57            // When enabled, will use a single hidden field for the form,
58            // rather than one per tag. It will delimit tags in the field
59            // with singleFieldDelimiter.
60            //
61            // The easiest way to use singleField is to just instantiate tag-it
62            // on an INPUT element, in which case singleField is automatically
63            // set to true, and singleFieldNode is set to that element. This
64            // way, you don't need to fiddle with these options.
65            singleField: false,
66
67            // This is just used when preloading data from the field, and for
68            // populating the field with delimited tags as the user adds them.
69            singleFieldDelimiter: ',',
70
71            // Set this to an input DOM node to use an existing form field.
72            // Any text in it will be erased on init. But it will be
73            // populated with the text of tags as they are created,
74            // delimited by singleFieldDelimiter.
75            //
76            // If this is not set, we create an input node for it,
77            // with the name given in settings.fieldName.
78            singleFieldNode: null,
79
80            // Whether to animate tag removals or not.
81            animate: true,
82
83            // Optionally set a tabindex attribute on the input that gets
84            // created for tag-it.
85            tabIndex: null,
86
87            // Event callbacks.
88            beforeTagAdded      : null,
89            afterTagAdded       : null,
90
91            beforeTagRemoved    : null,
92            afterTagRemoved     : null,
93
94            onTagClicked        : null,
95            onTagLimitExceeded  : null,
96
97
98            // DEPRECATED:
99            //
100            // /!\ These event callbacks are deprecated and WILL BE REMOVED at some
101            // point in the future. They're here for backwards-compatibility.
102            // Use the above before/after event callbacks instead.
103            onTagAdded  : null,
104            onTagRemoved: null,
105            // `autocomplete.source` is the replacement for tagSource.
106            tagSource: null
107            // Do not use the above deprecated options.
108        },
109
110        _create: function() {
111            // for handling static scoping inside callbacks
112            var that = this;
113
114            // There are 2 kinds of DOM nodes this widget can be instantiated on:
115            //     1. UL, OL, or some element containing either of these.
116            //     2. INPUT, in which case 'singleField' is overridden to true,
117            //        a UL is created and the INPUT is hidden.
118            if (this.element.is('input')) {
119                this.tagList = $('<ul></ul>').insertAfter(this.element);
120                this.options.singleField = true;
121                this.options.singleFieldNode = this.element;
122                this.element.addClass('tagit-hidden-field');
123            } else {
124                this.tagList = this.element.find('ul, ol').andSelf().last();
125            }
126
127            this.tagInput = $('<input type="text" />').addClass('ui-widget-content');
128
129            if (this.options.readOnly) this.tagInput.attr('disabled', 'disabled');
130
131            if (this.options.tabIndex) {
132                this.tagInput.attr('tabindex', this.options.tabIndex);
133            }
134
135            if (this.options.placeholderText) {
136                this.tagInput.attr('placeholder', this.options.placeholderText);
137            }
138
139            if (!this.options.autocomplete.source) {
140                this.options.autocomplete.source = function(search, showChoices) {
141                    var filter = search.term.toLowerCase();
142                    var choices = $.grep(this.options.availableTags, function(element) {
143                        // Only match autocomplete options that begin with the search term.
144                        // (Case insensitive.)
145                        return (element.toLowerCase().indexOf(filter) === 0);
146                    });
147                    if (!this.options.allowDuplicates) {
148                        choices = this._subtractArray(choices, this.assignedTags());
149                    }
150                    showChoices(choices);
151                };
152            }
153
154            if (this.options.showAutocompleteOnFocus) {
155                this.tagInput.focus(function(event, ui) {
156                    that._showAutocomplete();
157                });
158
159                if (typeof this.options.autocomplete.minLength === 'undefined') {
160                    this.options.autocomplete.minLength = 0;
161                }
162            }
163
164            // Bind autocomplete.source callback functions to this context.
165            if ($.isFunction(this.options.autocomplete.source)) {
166                this.options.autocomplete.source = $.proxy(this.options.autocomplete.source, this);
167            }
168
169            // DEPRECATED.
170            if ($.isFunction(this.options.tagSource)) {
171                this.options.tagSource = $.proxy(this.options.tagSource, this);
172            }
173
174            this.tagList
175                .addClass('tagit')
176                .addClass('ui-widget ui-widget-content ui-corner-all')
177                // Create the input field.
178                .append($('<li class="tagit-new"></li>').append(this.tagInput))
179                .click(function(e) {
180                    var target = $(e.target);
181                    if (target.hasClass('tagit-label')) {
182                        var tag = target.closest('.tagit-choice');
183                        if (!tag.hasClass('removed')) {
184                            that._trigger('onTagClicked', e, {tag: tag, tagLabel: that.tagLabel(tag)});
185                        }
186                    } else {
187                        // Sets the focus() to the input field, if the user
188                        // clicks anywhere inside the UL. This is needed
189                        // because the input field needs to be of a small size.
190                        that.tagInput.focus();
191                    }
192                });
193
194            // Single field support.
195            var addedExistingFromSingleFieldNode = false;
196            if (this.options.singleField) {
197                if (this.options.singleFieldNode) {
198                    // Add existing tags from the input field.
199                    var node = $(this.options.singleFieldNode);
200                    var tags = node.val().split(this.options.singleFieldDelimiter);
201                    node.val('');
202                    $.each(tags, function(index, tag) {
203                        that.createTag(tag, null, true);
204                        addedExistingFromSingleFieldNode = true;
205                    });
206                } else {
207                    // Create our single field input after our list.
208                    this.options.singleFieldNode = $('<input type="hidden" style="display:none;" value="" name="' + this.options.fieldName + '" />');
209                    this.tagList.after(this.options.singleFieldNode);
210                }
211            }
212
213            // Add existing tags from the list, if any.
214            if (!addedExistingFromSingleFieldNode) {
215                this.tagList.children('li').each(function() {
216                    if (!$(this).hasClass('tagit-new')) {
217                        that.createTag($(this).text(), $(this).attr('class'), true);
218                        $(this).remove();
219                    }
220                });
221            }
222
223            // Events.
224            this.tagInput
225                .keydown(function(event) {
226                    // Backspace is not detected within a keypress, so it must use keydown.
227                    if (event.which == $.ui.keyCode.BACKSPACE && that.tagInput.val() === '') {
228                        var tag = that._lastTag();
229                        if (!that.options.removeConfirmation || tag.hasClass('remove')) {
230                            // When backspace is pressed, the last tag is deleted.
231                            that.removeTag(tag);
232                        } else if (that.options.removeConfirmation) {
233                            tag.addClass('remove ui-state-highlight');
234                        }
235                    } else if (that.options.removeConfirmation) {
236                        that._lastTag().removeClass('remove ui-state-highlight');
237                    }
238
239                    // Comma/Space/Enter are all valid delimiters for new tags,
240                    // except when there is an open quote or if setting allowSpaces = true.
241                    // Tab will also create a tag, unless the tag input is empty,
242                    // in which case it isn't caught.
243                    if (
244                        (event.which === $.ui.keyCode.COMMA && event.shiftKey === false) ||
245                        event.which === $.ui.keyCode.ENTER ||
246                        (
247                            event.which == $.ui.keyCode.TAB &&
248                            that.tagInput.val() !== ''
249                        ) ||
250                        (
251                            event.which == $.ui.keyCode.SPACE &&
252                            that.options.allowSpaces !== true &&
253                            (
254                                $.trim(that.tagInput.val()).replace( /^s*/, '' ).charAt(0) != '"' ||
255                                (
256                                    $.trim(that.tagInput.val()).charAt(0) == '"' &&
257                                    $.trim(that.tagInput.val()).charAt($.trim(that.tagInput.val()).length - 1) == '"' &&
258                                    $.trim(that.tagInput.val()).length - 1 !== 0
259                                )
260                            )
261                        )
262                    ) {
263                        // Enter submits the form if there's no text in the input.
264                        if (!(event.which === $.ui.keyCode.ENTER && that.tagInput.val() === '')) {
265                            event.preventDefault();
266                        }
267
268                        // Autocomplete will create its own tag from a selection and close automatically.
269                        if (!(that.options.autocomplete.autoFocus && that.tagInput.data('autocomplete-open'))) {
270                            that.tagInput.autocomplete('close');
271                            that.createTag(that._cleanedInput());
272                        }
273                    }
274                }).blur(function(e){
275                    // Create a tag when the element loses focus.
276                    // If autocomplete is enabled and suggestion was clicked, don't add it.
277                    if (!that.tagInput.data('autocomplete-open')) {
278                        that.createTag(that._cleanedInput());
279                    }
280                });
281
282            // Autocomplete.
283            if (this.options.availableTags || this.options.tagSource || this.options.autocomplete.source) {
284                var autocompleteOptions = {
285                    select: function(event, ui) {
286                        that.createTag(ui.item.value);
287                        // Preventing the tag input to be updated with the chosen value.
288                        return false;
289                    }
290                };
291                $.extend(autocompleteOptions, this.options.autocomplete);
292
293                // tagSource is deprecated, but takes precedence here since autocomplete.source is set by default,
294                // while tagSource is left null by default.
295                autocompleteOptions.source = this.options.tagSource || autocompleteOptions.source;
296
297                this.tagInput.autocomplete(autocompleteOptions).bind('autocompleteopen.tagit', function(event, ui) {
298                    that.tagInput.data('autocomplete-open', true);
299                }).bind('autocompleteclose.tagit', function(event, ui) {
300                    that.tagInput.data('autocomplete-open', false);
301                });
302
303                this.tagInput.autocomplete('widget').addClass('tagit-autocomplete');
304            }
305        },
306
307        destroy: function() {
308            $.Widget.prototype.destroy.call(this);
309
310            this.element.unbind('.tagit');
311            this.tagList.unbind('.tagit');
312
313            this.tagInput.removeData('autocomplete-open');
314
315            this.tagList.removeClass([
316                'tagit',
317                'ui-widget',
318                'ui-widget-content',
319                'ui-corner-all',
320                'tagit-hidden-field'
321            ].join(' '));
322
323            if (this.element.is('input')) {
324                this.element.removeClass('tagit-hidden-field');
325                this.tagList.remove();
326            } else {
327                this.element.children('li').each(function() {
328                    if ($(this).hasClass('tagit-new')) {
329                        $(this).remove();
330                    } else {
331                        $(this).removeClass([
332                            'tagit-choice',
333                            'ui-widget-content',
334                            'ui-state-default',
335                            'ui-state-highlight',
336                            'ui-corner-all',
337                            'remove',
338                            'tagit-choice-editable',
339                            'tagit-choice-read-only'
340                        ].join(' '));
341
342                        $(this).text($(this).children('.tagit-label').text());
343                    }
344                });
345
346                if (this.singleFieldNode) {
347                    this.singleFieldNode.remove();
348                }
349            }
350
351            return this;
352        },
353
354        _cleanedInput: function() {
355            // Returns the contents of the tag input, cleaned and ready to be passed to createTag
356            return $.trim(this.tagInput.val().replace(/^"(.*)"$/, '$1'));
357        },
358
359        _lastTag: function() {
360            return this.tagList.find('.tagit-choice:last:not(.removed)');
361        },
362
363        _tags: function() {
364            return this.tagList.find('.tagit-choice:not(.removed)');
365        },
366
367        assignedTags: function() {
368            // Returns an array of tag string values
369            var that = this;
370            var tags = [];
371            if (this.options.singleField) {
372                tags = $(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter);
373                if (tags[0] === '') {
374                    tags = [];
375                }
376            } else {
377                this._tags().each(function() {
378                    tags.push(that.tagLabel(this));
379                });
380            }
381            return tags;
382        },
383
384        _updateSingleTagsField: function(tags) {
385            // Takes a list of tag string values, updates this.options.singleFieldNode.val to the tags delimited by this.options.singleFieldDelimiter
386            $(this.options.singleFieldNode).val(tags.join(this.options.singleFieldDelimiter)).trigger('change');
387        },
388
389        _subtractArray: function(a1, a2) {
390            var result = [];
391            for (var i = 0; i < a1.length; i++) {
392                if ($.inArray(a1[i], a2) == -1) {
393                    result.push(a1[i]);
394                }
395            }
396            return result;
397        },
398
399        tagLabel: function(tag) {
400            // Returns the tag's string label.
401            if (this.options.singleField) {
402                return $(tag).find('.tagit-label:first').text();
403            } else {
404                return $(tag).find('input:first').val();
405            }
406        },
407
408        _showAutocomplete: function() {
409            this.tagInput.autocomplete('search', '');
410        },
411
412        _findTagByLabel: function(name) {
413            var that = this;
414            var tag = null;
415            this._tags().each(function(i) {
416                if (that._formatStr(name) == that._formatStr(that.tagLabel(this))) {
417                    tag = $(this);
418                    return false;
419                }
420            });
421            return tag;
422        },
423
424        _isNew: function(name) {
425            return !this._findTagByLabel(name);
426        },
427
428        _formatStr: function(str) {
429            if (this.options.caseSensitive) {
430                return str;
431            }
432            return $.trim(str.toLowerCase());
433        },
434
435        _effectExists: function(name) {
436            return Boolean($.effects && ($.effects[name] || ($.effects.effect && $.effects.effect[name])));
437        },
438
439        createTag: function(value, additionalClass, duringInitialization) {
440            var that = this;
441
442            value = $.trim(value);
443
444            if(this.options.preprocessTag) {
445                value = this.options.preprocessTag(value);
446            }
447
448            if (value === '') {
449                return false;
450            }
451
452            if (!this.options.allowDuplicates && !this._isNew(value)) {
453                var existingTag = this._findTagByLabel(value);
454                if (this._trigger('onTagExists', null, {
455                    existingTag: existingTag,
456                    duringInitialization: duringInitialization
457                }) !== false) {
458                    if (this._effectExists('highlight')) {
459                        existingTag.effect('highlight');
460                    }
461                }
462                return false;
463            }
464
465            if (this.options.tagLimit && this._tags().length >= this.options.tagLimit) {
466                this._trigger('onTagLimitExceeded', null, {duringInitialization: duringInitialization});
467                return false;
468            }
469
470            var label = $(this.options.onTagClicked ? '<a class="tagit-label"></a>' : '<span class="tagit-label"></span>').text(value);
471
472            // Create tag.
473            var tag = $('<li></li>')
474                .addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
475                .addClass(additionalClass)
476                .append(label);
477
478            if (this.options.readOnly){
479                tag.addClass('tagit-choice-read-only');
480            } else {
481                tag.addClass('tagit-choice-editable');
482                // Button for removing the tag.
483                var removeTagIcon = $('<span></span>')
484                    .addClass('ui-icon ui-icon-close');
485                var removeTag = $('<a><span class="text-icon">\xd7</span></a>') // \xd7 is an X
486                    .addClass('tagit-close')
487                    .append(removeTagIcon)
488                    .click(function(e) {
489                        // Removes a tag when the little 'x' is clicked.
490                        that.removeTag(tag);
491                    });
492                tag.append(removeTag);
493            }
494
495            // Unless options.singleField is set, each tag has a hidden input field inline.
496            if (!this.options.singleField) {
497                var escapedValue = label.html();
498                tag.append('<input type="hidden" value="' + escapedValue + '" name="' + this.options.fieldName + '" class="tagit-hidden-field" />');
499            }
500
501            if (this._trigger('beforeTagAdded', null, {
502                tag: tag,
503                tagLabel: this.tagLabel(tag),
504                duringInitialization: duringInitialization
505            }) === false) {
506                return;
507            }
508
509            if (this.options.singleField) {
510                var tags = this.assignedTags();
511                tags.push(value);
512                this._updateSingleTagsField(tags);
513            }
514
515            // DEPRECATED.
516            this._trigger('onTagAdded', null, tag);
517
518            this.tagInput.val('');
519
520            // Insert tag.
521            this.tagInput.parent().before(tag);
522
523            this._trigger('afterTagAdded', null, {
524                tag: tag,
525                tagLabel: this.tagLabel(tag),
526                duringInitialization: duringInitialization
527            });
528
529            if (this.options.showAutocompleteOnFocus && !duringInitialization) {
530                setTimeout(function () { that._showAutocomplete(); }, 0);
531            }
532        },
533
534        removeTag: function(tag, animate) {
535            animate = typeof animate === 'undefined' ? this.options.animate : animate;
536
537            tag = $(tag);
538
539            // DEPRECATED.
540            this._trigger('onTagRemoved', null, tag);
541
542            if (this._trigger('beforeTagRemoved', null, {tag: tag, tagLabel: this.tagLabel(tag)}) === false) {
543                return;
544            }
545
546            if (this.options.singleField) {
547                var tags = this.assignedTags();
548                var removedTagLabel = this.tagLabel(tag);
549                tags = $.grep(tags, function(el){
550                    return el != removedTagLabel;
551                });
552                this._updateSingleTagsField(tags);
553            }
554
555            if (animate) {
556                tag.addClass('removed'); // Excludes this tag from _tags.
557                var hide_args = this._effectExists('blind') ? ['blind', {direction: 'horizontal'}, 'fast'] : ['fast'];
558
559                var thisTag = this;
560                hide_args.push(function() {
561                    tag.remove();
562                    thisTag._trigger('afterTagRemoved', null, {tag: tag, tagLabel: thisTag.tagLabel(tag)});
563                });
564
565                tag.fadeOut('fast').hide.apply(tag, hide_args).dequeue();
566            } else {
567                tag.remove();
568                this._trigger('afterTagRemoved', null, {tag: tag, tagLabel: this.tagLabel(tag)});
569            }
570
571        },
572
573        removeTagByLabel: function(tagLabel, animate) {
574            var toRemove = this._findTagByLabel(tagLabel);
575            if (!toRemove) {
576                throw "No such tag exists with the name '" + tagLabel + "'";
577            }
578            this.removeTag(toRemove, animate);
579        },
580
581        removeAll: function() {
582            // Removes all tags.
583            var that = this;
584            this._tags().each(function(index, tag) {
585                that.removeTag(tag, false);
586            });
587        }
588
589    });
590})(jQuery);
591

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.