PageSourceSearch

https://hiking.fo/ease/3rdparty/js/Qodio/q.tagator.jquery.js?v=3.16.2.4_0

js hiking.fo collected 2026-10-08 09:19:41 UTC 17,428 bytes, 556 lines download raw bytes

1/*
2 Tagator jQuery Plugin
3 A plugin to make input elements, tag holders
4 version 1.4, Apr 25th, 2017
5 by Ingi á Steinamørk
6
7 The MIT License (MIT)
8
9 Copyright (c) 2014 QODIO
10
11 Permission is hereby granted, free of charge, to any person obtaining a copy of
12 this software and associated documentation files (the "Software"), to deal in
13 the Software without restriction, including without limitation the rights to
14 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
15 the Software, and to permit persons to whom the Software is furnished to do so,
16 subject to the following conditions:
17
18 The above copyright notice and this permission notice shall be included in all
19 copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
23 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
24 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
25 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 */
28
29(function ($) {
30	$.tagator = function (source_element, options) {
31		var defaults = {
32			prefix:                'tagator_',
33			height:                'auto',
34			useDimmer:             false,
35			showAllOptionsOnFocus: false,
36			allowAutocompleteOnly: false,
37			autocomplete:          []
38		};
39
40		var self = this;
41		var selected_index = -1;
42		var $source_element = $(source_element);
43		var $tagator_element = null;
44		var $tags_element = null;
45		var $placeholder_element = null;
46		var $input_element = null;
47		var $textlength_element = null;
48		var $options_element = null;
49		var key = {
50			backspace: 8,
51			enter:     13,
52			escape:    27,
53			left:      37,
54			up:        38,
55			right:     39,
56			down:      40,
57			comma:     188
58		};
59		self.settings = {};
60
61
62		// INITIALIZE PLUGIN
63		self.init = function () {
64			self.settings = $.extend({}, defaults, options);
65
66			//// ================== CREATE ELEMENTS ================== ////
67			// dimmer
68			if (self.settings.useDimmer) {
69				if ($('#' + self.settings.prefix + 'dimmer').length === 0) {
70					var $dimmer_element = $(document.createElement('div'));
71					$dimmer_element.attr('id', self.settings.prefix + 'dimmer');
72					$dimmer_element.hide();
73					$(document.body).prepend($dimmer_element);
74				}
75			}
76			// box element
77			$tagator_element = $(document.createElement('div'));
78			if ($source_element[0].id !== undefined) {
79				$tagator_element.attr('id', self.settings.prefix + $source_element[0].id);
80			}
81			$tagator_element.addClass(self.settings.prefix + 'element options-hidden');
82			$tagator_element.css({
83				padding:     $source_element.css('padding'),
84				'flex-grow': $source_element.css('flex-grow'),
85				position:    'relative'
86			});
87			if (parseInt($source_element.css('width')) !== 0) {
88				$tagator_element.css({
89					width: $source_element.css('width')
90				});
91			}
92			if (self.settings.height === 'element') {
93				$tagator_element.css({
94					height: $source_element.outerHeight + 'px'
95				});
96			}
97			$source_element.after($tagator_element);
98			$source_element.hide();
99			// textlength element
100			$textlength_element = $(document.createElement('span'));
101			$textlength_element.addClass(self.settings.prefix + 'textlength');
102			$textlength_element.css({
103				position:   'absolute',
104				visibility: 'hidden'
105			});
106			$tagator_element.append($textlength_element);
107			// tags element
108			$tags_element = $(document.createElement('div'));
109			$tags_element.addClass(self.settings.prefix + 'tags');
110			$tagator_element.append($tags_element);
111			// placeholder element
112			$placeholder_element = $(document.createElement('div'));
113			$placeholder_element.addClass(self.settings.prefix + 'placeholder');
114			$tagator_element.append($placeholder_element);
115			// input element
116			$input_element = $(document.createElement('input'));
117			$input_element.addClass(self.settings.prefix + 'input');
118			$input_element.width(20);
119			$input_element.attr('autocomplete', 'false');
120			$tagator_element.append($input_element);
121			// options element
122			$options_element = $(document.createElement('ul'));
123			$options_element.addClass(self.settings.prefix + 'options');
124
125			$tagator_element.append($options_element);
126
127			//// ================== BIND ELEMENTS EVENTS ================== ////
128			// source element
129			$source_element.change(function () {
130				refreshTags();
131			});
132			// box element
133			$tagator_element.on('focus', function (e) {
134				e.preventDefault();
135				e.stopPropagation();
136				showOptions();
137				$input_element.focus();
138			});
139			$tagator_element.on('mousedown', function (e) {
140				e.preventDefault();
141				e.stopPropagation();
142				$input_element.focus();
143				if ($input_element[0].setSelectionRange) {
144					$input_element.focus();
145					$input_element[0].setSelectionRange($input_element.val().length, $input_element.val().length);
146				} else if ($input_element[0].createTextRange) {
147					var range = $input_element[0].createTextRange();
148					range.collapse(true);
149					range.moveEnd('character', $input_element.val().length);
150					range.moveStart('character', $input_element.val().length);
151					range.select();
152				}
153			});
154			$tagator_element.on('mouseup', function (e) {
155				e.preventDefault();
156				e.stopPropagation();
157			});
158			$tagator_element.on('click', function (e) {
159				e.preventDefault();
160				e.stopPropagation();
161				if (self.settings.showAllOptionsOnFocus) {
162					//showOptions();
163					searchOptions();
164				}
165				$input_element.focus();
166			});
167			$tagator_element.on('dblclick', function (e) {
168				e.preventDefault();
169				e.stopPropagation();
170				$input_element.focus();
171				$input_element.select();
172			});
173			// input element
174			$input_element.on('click', function (e) {
175				e.preventDefault();
176				e.stopPropagation();
177			});
178			$input_element.on('dblclick', function (e) {
179				e.preventDefault();
180				e.stopPropagation();
181			});
182			$input_element.on('keydown', function (e) {
183				e.stopPropagation();
184				var keyCode = e.keyCode || e.which;
185				switch (keyCode) {
186					case key.up:
187						e.preventDefault();
188						if (selected_index > -1) {
189							selected_index = selected_index - 1;
190						} else {
191							selected_index = $options_element.find('.' + self.settings.prefix + 'option').length - 1;
192						}
193						refreshActiveOption();
194						scrollToActiveOption();
195						break;
196					case key.down:
197						e.preventDefault();
198						if (selected_index < $options_element.find('.' + self.settings.prefix + 'option').length - 1) {
199							selected_index = selected_index + 1;
200						} else {
201							selected_index = -1;
202						}
203						refreshActiveOption();
204						scrollToActiveOption();
205						break;
206					case key.escape:
207						e.preventDefault();
208						break;
209					case key.comma:
210						e.preventDefault();
211						if (selected_index === -1) {
212							if ($input_element.val() !== '') {
213								addTag($input_element.val());
214							}
215						}
216						resizeInput();
217						break;
218					case key.enter:
219						e.preventDefault();
220						if (selected_index !== -1) {
221							selectOption();
222						} else {
223							if ($input_element.val() !== '') {
224								addTag($input_element.val());
225							}
226						}
227						resizeInput();
228						break;
229					case key.backspace:
230						if ($input_element.val() === '') {
231							$source_element.val($source_element.val().substring(0, $source_element.val().lastIndexOf(',')));
232							$source_element.trigger('change');
233							searchOptions();
234						}
235						resizeInput();
236						break;
237					default:
238						resizeInput();
239						break;
240				}
241				refreshPlaceholder();
242			});
243			$input_element.on('keyup', function (e) {
244				e.preventDefault();
245				e.stopPropagation();
246				var keyCode = e.keyCode || e.which;
247				if (keyCode === key.escape || keyCode === key.enter) {
248					hideOptions();
249				} else if (keyCode < 37 || keyCode > 40) {
250					searchOptions();
251				}
252				if ($tagator_element.hasClass('options-hidden') && (keyCode === key.left || keyCode === key.right || keyCode === key.up || keyCode === key.down)) {
253					searchOptions();
254				}
255				resizeInput();
256				refreshPlaceholder();
257			});
258			$input_element.on('focus', function (e) {
259				e.preventDefault();
260				e.stopPropagation();
261				$tagator_element.addClass('focused');
262				if (!$options_element.is(':empty') || self.settings.showAllOptionsOnFocus) {
263					searchOptions();
264					showOptions();
265				}
266			});
267			$input_element.on('blur', function (e) {
268				e.preventDefault();
269				e.stopPropagation();
270				$tagator_element.removeClass('focused');
271				if ($input_element.val() !== '') {
272					addTag($input_element.val());
273				}
274				hideOptions();
275			});
276			refreshTags();
277		};
278
279
280		// RESIZE INPUT
281		var resizeInput = function () {
282			$textlength_element.html($input_element.val());
283			$input_element.css({ width: ($textlength_element.width() + 30) + 'px' });
284		};
285
286
287		// SET AUTOCOMPLETE LIST
288		self.autocomplete = function (autocomplete) {
289			self.settings.autocomplete = autocomplete !== undefined ? autocomplete : [];
290		};
291
292
293		// REFRESH TAGS
294		self.refresh = function () {
295			refreshTags();
296		};
297		var refreshTags = function () {
298			$tags_element.empty();
299			var tags = $source_element.val().split(',');
300			$.each(tags, function (key, value) {
301				if (value !== '' && checkAllowedTag(value)) {
302					var $tag_element = $(document.createElement('div'));
303					$tag_element.addClass(self.settings.prefix + 'tag');
304					$tag_element.html(value);
305					// remove button
306					var $button_remove_element = $(document.createElement('div'));
307					$button_remove_element.data('text', value);
308					$button_remove_element.addClass(self.settings.prefix + 'tag_remove');
309					$button_remove_element.on('mousedown', function (e) {
310						e.preventDefault();
311						e.stopPropagation();
312					});
313					$button_remove_element.on('mouseup', function (e) {
314						e.preventDefault();
315						e.stopPropagation();
316						removeTag($(this).data('text'));
317						$source_element.trigger('change');
318					});
319					$button_remove_element.html('X');
320					$tag_element.append($button_remove_element);
321					// clear
322					var $clear_element = $(document.createElement('div'));
323					$clear_element.css('clear', 'both');
324					$tag_element.append($clear_element);
325
326					$tags_element.append($tag_element);
327				}
328			});
329			refreshPlaceholder();
330			searchOptions();
331		};
332		
333		// REFRESH PLACEHOLDER
334		var refreshPlaceholder = function () {
335			if ($tags_element.is(':empty') && !$input_element.val() && $source_element.attr('placeholder')) {
336				$placeholder_element.html($source_element.attr('placeholder'));
337				$placeholder_element.show();
338			} else {
339				$placeholder_element.hide();
340			}
341		};
342
343		// REMOVE TAG FROM ORIGINAL ELEMENT
344		var removeTag = function (text) {
345			var tagsBefore = $source_element.val().split(',');
346			var tagsAfter = [];
347			$.each(tagsBefore, function (key, value) {
348				if (value !== text && value !== '') {
349					tagsAfter.push(value);
350				}
351			});
352			$source_element.val(tagsAfter.join(','));
353		};
354
355		// CHECK IF TAG IS PRESENT
356		var hasTag = function (text) {
357			var tags = $source_element.val().split(',');
358			var hasTag = false;
359			$.each(tags, function (key, value) {
360				if ($.trim(value) === $.trim(text)) {
361					hasTag = true;
362				}
363			});
364			return hasTag;
365		};
366
367		// CHECK IF TAG IS ALLOWED
368		var checkAllowedTag = function (text) {
369			if (!self.settings.allowAutocompleteOnly) {
370				return true;
371			}
372
373			var checkAllowedTag = false;
374			$.each(self.settings.autocomplete, function (key, value) {
375				if ($.trim(value) === $.trim(text)) {
376					checkAllowedTag = true;
377				}
378			});
379			return checkAllowedTag;
380		};
381
382		// ADD TAG TO ORIGINAL ELEMENT
383		var addTag = function (text) {
384			if (!hasTag(text) && checkAllowedTag(text)) {
385				$source_element.val($source_element.val() + ($source_element.val() !== '' ? ',' : '') + text);
386				$source_element.trigger('change');
387			}
388			$input_element.val('');
389			$tagator_element.focus();
390			hideOptions();
391		};
392
393		// OPTIONS SEARCH METHOD
394		var searchOptions = function () {
395			$options_element.empty();
396			if ($input_element.val().replace(/\s/g, '') !== '' || self.settings.showAllOptionsOnFocus) {
397				var optionsArray = [];
398				$.each(self.settings.autocomplete, function (key, value) {
399					if (value.toLowerCase().indexOf($input_element.val().toLowerCase()) !== -1) {
400						if (!hasTag(value)) {
401							optionsArray.push(value);
402						}
403					}
404				});
405				generateOptions(optionsArray);
406			}
407			if ($input_element.is(':focus')) {
408				if (!$options_element.is(':empty')) {
409					showOptions();
410				} else {
411					hideOptions();
412				}
413			} else {
414				hideOptions();
415			}
416			selected_index = -1;
417		};
418
419		// GENERATE OPTIONS
420		var generateOptions = function (optionsArray) {
421			var index = -1;
422			$(optionsArray).each(function (key, value) {
423				index++;
424				var option = createOption(value, index);
425				$options_element.append(option);
426			});
427			refreshActiveOption();
428		};
429
430		// CREATE RESULT OPTION
431		var createOption = function (text, index) {
432			// holder li
433			var option = document.createElement('li');
434			$(option).data('index', index);
435			$(option).data('text', text);
436			$(option).html(text);
437			$(option).addClass(self.settings.prefix + 'option');
438			if (this.selected) {
439				$(option).addClass('active');
440			}
441
442			// BIND EVENTS
443			$(option).on('mouseover', function (e) {
444				e.stopPropagation();
445				e.preventDefault();
446				selected_index = index;
447				refreshActiveOption();
448			});
449			$(option).on('mousedown', function (e) {
450				e.stopPropagation();
451				e.preventDefault();
452			});
453			$(option).on('click', function (e) {
454				e.preventDefault();
455				e.stopPropagation();
456				selectOption();
457			});
458
459
460			return option;
461		};
462
463		// SHOW OPTIONS AND DIMMER
464		var showOptions = function () {
465			$tagator_element.removeClass('options-hidden').addClass('options-visible');
466			if (self.settings.useDimmer) {
467				$('#' + self.settings.prefix + 'dimmer').show();
468			}
469			$options_element.css('top', ($tagator_element.outerHeight - 2) + 'px');
470			if ($tagator_element.hasClass('single')) {
471				selected_index = $options_element.find('.' + self.settings.prefix + 'option').index($options_element.find('.' + self.settings.prefix + 'option.active'));
472			}
473			scrollToActiveOption();
474		};
475
476		// HIDE OPTIONS AND DIMMER
477		var hideOptions = function () {
478			$tagator_element.removeClass('options-visible').addClass('options-hidden');
479			if (self.settings.useDimmer) {
480				$('#' + self.settings.prefix + 'dimmer').hide();
481			}
482		};
483
484		// REFRESH ACTIVE IN OPTIONS METHOD
485		var refreshActiveOption = function () {
486			$options_element.find('.active').removeClass('active');
487			if (selected_index !== -1) {
488				$options_element.find('.' + self.settings.prefix + 'option').eq(selected_index).addClass('active');
489			}
490		};
491
492		// SCROLL TO ACTIVE OPTION IN OPTIONS LIST
493		var scrollToActiveOption = function () {
494			var $active_element = $options_element.find('.' + self.settings.prefix + 'option.active');
495			if ($active_element.length > 0) {
496				$options_element.scrollTop($options_element.scrollTop() + $active_element.position().top - $options_element.height()/2 + $active_element.height()/2);
497			}
498
499		};
500
501		// SELECT ACTIVE OPTION
502		var selectOption = function () {
503			addTag($options_element.find('.' + self.settings.prefix + 'option').eq(selected_index).data('text'));
504		};
505
506
507		// REMOVE PLUGIN AND REVERT INPUT ELEMENT TO ORIGINAL STATE
508		self.destroy = function () {
509			$tagator_element.remove();
510			$source_element.removeData('tagator');
511			$source_element.attr('style', '').show();
512			if ($('.tagator').length === 0) {
513				$('#' + self.settings.prefix + 'dimmer').remove();
514			}
515		};
516
517		// Initialize plugin
518		self.init();
519	};
520
521	//// ================== INSTANTIATE PLUGIN METHOD ================== ////
522	$.fn.tagator = function(options) {
523		options = options !== undefined ? options : {};
524		return this.each(function () {
525			if (typeof(options) === 'object') {
526				if ($(this).data('tagator') === undefined ) {
527					var self = new $.tagator(this, options);
528					$(this).data('tagator', self);
529				}
530			} else if (typeof $(this).data('tagator') !== 'undefined' && $(this).data('tagator')[options]) {
531				$(this).data('tagator')[options].apply(this, Array.prototype.slice.call(arguments, 1));
532			} else {
533				//$.error('Method ' + options + ' does not exist in $.tagator');
534			}
535		});
536	};
537}(jQuery));
538
539
540$(function () {
541	$('.tagator').each(function () {
542		var $this = $(this);
543		var options = {};
544		$.each($this.data(), function (key, value) {
545			if (key.substring(0, 7) === 'tagator') {
546				var value_temp = value.toString().replace(/'/g, '"');
547				value_temp = $.parseJSON(value_temp);
548				if (typeof value_temp === 'object') {
549					value = value_temp;
550				}
551				options[key.substring(7, 8).toLowerCase() + key.substring(8)] = value;
552			}
553		});
554		$this.tagator(options);
555	});
556});

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.