[med-svn] r21615 - in trunk/packages/R/r-cran-shiny/trunk/debian: . missing-sources
Andreas Tille
tille at moszumanska.debian.org
Sun Mar 27 14:21:58 UTC 2016
Author: tille
Date: 2016-03-27 14:21:57 +0000 (Sun, 27 Mar 2016)
New Revision: 21615
Added:
trunk/packages/R/r-cran-shiny/trunk/debian/missing-sources/selectize.js
Modified:
trunk/packages/R/r-cran-shiny/trunk/debian/copyright
trunk/packages/R/r-cran-shiny/trunk/debian/rules
Log:
Get source of selectize.min.js
Modified: trunk/packages/R/r-cran-shiny/trunk/debian/copyright
===================================================================
--- trunk/packages/R/r-cran-shiny/trunk/debian/copyright 2016-03-27 13:54:13 UTC (rev 21614)
+++ trunk/packages/R/r-cran-shiny/trunk/debian/copyright 2016-03-27 14:21:57 UTC (rev 21615)
@@ -21,6 +21,7 @@
*/font-awesome
*/datepicker
*/respond.min.js
+ */selectize.min.js
Files: *
Copyright: 2013-2016 Winston Chang <winston at rstudio.com>
@@ -58,6 +59,22 @@
downloaded from <https://github.com/scottjehl/Respond>
using the version from the 1.4.2 tag.
+Files: inst/www/shared/ionrangeslider/*
+Copyright: 2015 Denis Ineshin
+License: MIT
+Comment: The source for this code can be found at
+ https://github.com/IonDen/ion.rangeSlider
+
+Files: inst/www/shared/jqueryui/*
+Copyright: 2008-2015 jQuery Foundation and other contributors
+License: MIT
+
+Files: debian/missing-sources/selectize.js
+Copyright: 2013-2015 Brian Reavis
+License: Apache-2.0
+ On Debian systems you can find the full text of the Apache-2.0 license
+ at /usr/share/common-licenses/Apache-2.0
+
Files: debian/*
Copyright: 2016 Andreas Tille <tille at debian.org>
License: MIT
Added: trunk/packages/R/r-cran-shiny/trunk/debian/missing-sources/selectize.js
===================================================================
--- trunk/packages/R/r-cran-shiny/trunk/debian/missing-sources/selectize.js (rev 0)
+++ trunk/packages/R/r-cran-shiny/trunk/debian/missing-sources/selectize.js 2016-03-27 14:21:57 UTC (rev 21615)
@@ -0,0 +1,2102 @@
+var Selectize = function($input, settings) {
+ var key, i, n, dir, input, self = this;
+ input = $input[0];
+ input.selectize = self;
+
+ // detect rtl environment
+ var computedStyle = window.getComputedStyle && window.getComputedStyle(input, null);
+ dir = computedStyle ? computedStyle.getPropertyValue('direction') : input.currentStyle && input.currentStyle.direction;
+ dir = dir || $input.parents('[dir]:first').attr('dir') || '';
+
+ // setup default state
+ $.extend(self, {
+ order : 0,
+ settings : settings,
+ $input : $input,
+ tabIndex : $input.attr('tabindex') || '',
+ tagType : input.tagName.toLowerCase() === 'select' ? TAG_SELECT : TAG_INPUT,
+ rtl : /rtl/i.test(dir),
+
+ eventNS : '.selectize' + (++Selectize.count),
+ highlightedValue : null,
+ isOpen : false,
+ isDisabled : false,
+ isRequired : $input.is('[required]'),
+ isInvalid : false,
+ isLocked : false,
+ isFocused : false,
+ isInputHidden : false,
+ isSetup : false,
+ isShiftDown : false,
+ isCmdDown : false,
+ isCtrlDown : false,
+ ignoreFocus : false,
+ ignoreBlur : false,
+ ignoreHover : false,
+ hasOptions : false,
+ currentResults : null,
+ lastValue : '',
+ caretPos : 0,
+ loading : 0,
+ loadedSearches : {},
+
+ $activeOption : null,
+ $activeItems : [],
+
+ optgroups : {},
+ options : {},
+ userOptions : {},
+ items : [],
+ renderCache : {},
+ onSearchChange : settings.loadThrottle === null ? self.onSearchChange : debounce(self.onSearchChange, settings.loadThrottle)
+ });
+
+ // search system
+ self.sifter = new Sifter(this.options, {diacritics: settings.diacritics});
+
+ // build options table
+ if (self.settings.options) {
+ for (i = 0, n = self.settings.options.length; i < n; i++) {
+ self.registerOption(self.settings.options[i]);
+ }
+ delete self.settings.options;
+ }
+
+ // build optgroup table
+ if (self.settings.optgroups) {
+ for (i = 0, n = self.settings.optgroups.length; i < n; i++) {
+ self.registerOptionGroup(self.settings.optgroups[i]);
+ }
+ delete self.settings.optgroups;
+ }
+
+ // option-dependent defaults
+ self.settings.mode = self.settings.mode || (self.settings.maxItems === 1 ? 'single' : 'multi');
+ if (typeof self.settings.hideSelected !== 'boolean') {
+ self.settings.hideSelected = self.settings.mode === 'multi';
+ }
+
+ self.initializePlugins(self.settings.plugins);
+ self.setupCallbacks();
+ self.setupTemplates();
+ self.setup();
+};
+
+// mixins
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+MicroEvent.mixin(Selectize);
+MicroPlugin.mixin(Selectize);
+
+// methods
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+$.extend(Selectize.prototype, {
+
+ /**
+ * Creates all elements and sets up event bindings.
+ */
+ setup: function() {
+ var self = this;
+ var settings = self.settings;
+ var eventNS = self.eventNS;
+ var $window = $(window);
+ var $document = $(document);
+ var $input = self.$input;
+
+ var $wrapper;
+ var $control;
+ var $control_input;
+ var $dropdown;
+ var $dropdown_content;
+ var $dropdown_parent;
+ var inputMode;
+ var timeout_blur;
+ var timeout_focus;
+ var classes;
+ var classes_plugins;
+
+ inputMode = self.settings.mode;
+ classes = $input.attr('class') || '';
+
+ $wrapper = $('<div>').addClass(settings.wrapperClass).addClass(classes).addClass(inputMode);
+ $control = $('<div>').addClass(settings.inputClass).addClass('items').appendTo($wrapper);
+ $control_input = $('<input type="text" autocomplete="off" />').appendTo($control).attr('tabindex', $input.is(':disabled') ? '-1' : self.tabIndex);
+ $dropdown_parent = $(settings.dropdownParent || $wrapper);
+ $dropdown = $('<div>').addClass(settings.dropdownClass).addClass(inputMode).hide().appendTo($dropdown_parent);
+ $dropdown_content = $('<div>').addClass(settings.dropdownContentClass).appendTo($dropdown);
+
+ if(self.settings.copyClassesToDropdown) {
+ $dropdown.addClass(classes);
+ }
+
+ $wrapper.css({
+ width: $input[0].style.width
+ });
+
+ if (self.plugins.names.length) {
+ classes_plugins = 'plugin-' + self.plugins.names.join(' plugin-');
+ $wrapper.addClass(classes_plugins);
+ $dropdown.addClass(classes_plugins);
+ }
+
+ if ((settings.maxItems === null || settings.maxItems > 1) && self.tagType === TAG_SELECT) {
+ $input.attr('multiple', 'multiple');
+ }
+
+ if (self.settings.placeholder) {
+ $control_input.attr('placeholder', settings.placeholder);
+ }
+
+ // if splitOn was not passed in, construct it from the delimiter to allow pasting universally
+ if (!self.settings.splitOn && self.settings.delimiter) {
+ var delimiterEscaped = self.settings.delimiter.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+ self.settings.splitOn = new RegExp('\\s*' + delimiterEscaped + '+\\s*');
+ }
+
+ if ($input.attr('autocorrect')) {
+ $control_input.attr('autocorrect', $input.attr('autocorrect'));
+ }
+
+ if ($input.attr('autocapitalize')) {
+ $control_input.attr('autocapitalize', $input.attr('autocapitalize'));
+ }
+
+ self.$wrapper = $wrapper;
+ self.$control = $control;
+ self.$control_input = $control_input;
+ self.$dropdown = $dropdown;
+ self.$dropdown_content = $dropdown_content;
+
+ $dropdown.on('mouseenter', '[data-selectable]', function() { return self.onOptionHover.apply(self, arguments); });
+ $dropdown.on('mousedown click', '[data-selectable]', function() { return self.onOptionSelect.apply(self, arguments); });
+ watchChildEvent($control, 'mousedown', '*:not(input)', function() { return self.onItemSelect.apply(self, arguments); });
+ autoGrow($control_input);
+
+ $control.on({
+ mousedown : function() { return self.onMouseDown.apply(self, arguments); },
+ click : function() { return self.onClick.apply(self, arguments); }
+ });
+
+ $control_input.on({
+ mousedown : function(e) { e.stopPropagation(); },
+ keydown : function() { return self.onKeyDown.apply(self, arguments); },
+ keyup : function() { return self.onKeyUp.apply(self, arguments); },
+ keypress : function() { return self.onKeyPress.apply(self, arguments); },
+ resize : function() { self.positionDropdown.apply(self, []); },
+ blur : function() { return self.onBlur.apply(self, arguments); },
+ focus : function() { self.ignoreBlur = false; return self.onFocus.apply(self, arguments); },
+ paste : function() { return self.onPaste.apply(self, arguments); }
+ });
+
+ $document.on('keydown' + eventNS, function(e) {
+ self.isCmdDown = e[IS_MAC ? 'metaKey' : 'ctrlKey'];
+ self.isCtrlDown = e[IS_MAC ? 'altKey' : 'ctrlKey'];
+ self.isShiftDown = e.shiftKey;
+ });
+
+ $document.on('keyup' + eventNS, function(e) {
+ if (e.keyCode === KEY_CTRL) self.isCtrlDown = false;
+ if (e.keyCode === KEY_SHIFT) self.isShiftDown = false;
+ if (e.keyCode === KEY_CMD) self.isCmdDown = false;
+ });
+
+ $document.on('mousedown' + eventNS, function(e) {
+ if (self.isFocused) {
+ // prevent events on the dropdown scrollbar from causing the control to blur
+ if (e.target === self.$dropdown[0] || e.target.parentNode === self.$dropdown[0]) {
+ return false;
+ }
+ // blur on click outside
+ if (!self.$control.has(e.target).length && e.target !== self.$control[0]) {
+ self.blur(e.target);
+ }
+ }
+ });
+
+ $window.on(['scroll' + eventNS, 'resize' + eventNS].join(' '), function() {
+ if (self.isOpen) {
+ self.positionDropdown.apply(self, arguments);
+ }
+ });
+ $window.on('mousemove' + eventNS, function() {
+ self.ignoreHover = false;
+ });
+
+ // store original children and tab index so that they can be
+ // restored when the destroy() method is called.
+ this.revertSettings = {
+ $children : $input.children().detach(),
+ tabindex : $input.attr('tabindex')
+ };
+
+ $input.attr('tabindex', -1).hide().after(self.$wrapper);
+
+ if ($.isArray(settings.items)) {
+ self.setValue(settings.items);
+ delete settings.items;
+ }
+
+ // feature detect for the validation API
+ if (SUPPORTS_VALIDITY_API) {
+ $input.on('invalid' + eventNS, function(e) {
+ e.preventDefault();
+ self.isInvalid = true;
+ self.refreshState();
+ });
+ }
+
+ self.updateOriginalInput();
+ self.refreshItems();
+ self.refreshState();
+ self.updatePlaceholder();
+ self.isSetup = true;
+
+ if ($input.is(':disabled')) {
+ self.disable();
+ }
+
+ self.on('change', this.onChange);
+
+ $input.data('selectize', self);
+ $input.addClass('selectized');
+ self.trigger('initialize');
+
+ // preload options
+ if (settings.preload === true) {
+ self.onSearchChange('');
+ }
+
+ },
+
+ /**
+ * Sets up default rendering functions.
+ */
+ setupTemplates: function() {
+ var self = this;
+ var field_label = self.settings.labelField;
+ var field_optgroup = self.settings.optgroupLabelField;
+
+ var templates = {
+ 'optgroup': function(data) {
+ return '<div class="optgroup">' + data.html + '</div>';
+ },
+ 'optgroup_header': function(data, escape) {
+ return '<div class="optgroup-header">' + escape(data[field_optgroup]) + '</div>';
+ },
+ 'option': function(data, escape) {
+ return '<div class="option">' + escape(data[field_label]) + '</div>';
+ },
+ 'item': function(data, escape) {
+ return '<div class="item">' + escape(data[field_label]) + '</div>';
+ },
+ 'option_create': function(data, escape) {
+ return '<div class="create">Add <strong>' + escape(data.input) + '</strong>…</div>';
+ }
+ };
+
+ self.settings.render = $.extend({}, templates, self.settings.render);
+ },
+
+ /**
+ * Maps fired events to callbacks provided
+ * in the settings used when creating the control.
+ */
+ setupCallbacks: function() {
+ var key, fn, callbacks = {
+ 'initialize' : 'onInitialize',
+ 'change' : 'onChange',
+ 'item_add' : 'onItemAdd',
+ 'item_remove' : 'onItemRemove',
+ 'clear' : 'onClear',
+ 'option_add' : 'onOptionAdd',
+ 'option_remove' : 'onOptionRemove',
+ 'option_clear' : 'onOptionClear',
+ 'optgroup_add' : 'onOptionGroupAdd',
+ 'optgroup_remove' : 'onOptionGroupRemove',
+ 'optgroup_clear' : 'onOptionGroupClear',
+ 'dropdown_open' : 'onDropdownOpen',
+ 'dropdown_close' : 'onDropdownClose',
+ 'type' : 'onType',
+ 'load' : 'onLoad',
+ 'focus' : 'onFocus',
+ 'blur' : 'onBlur'
+ };
+
+ for (key in callbacks) {
+ if (callbacks.hasOwnProperty(key)) {
+ fn = this.settings[callbacks[key]];
+ if (fn) this.on(key, fn);
+ }
+ }
+ },
+
+ /**
+ * Triggered when the main control element
+ * has a click event.
+ *
+ * @param {object} e
+ * @return {boolean}
+ */
+ onClick: function(e) {
+ var self = this;
+
+ // necessary for mobile webkit devices (manual focus triggering
+ // is ignored unless invoked within a click event)
+ if (!self.isFocused) {
+ self.focus();
+ e.preventDefault();
+ }
+ },
+
+ /**
+ * Triggered when the main control element
+ * has a mouse down event.
+ *
+ * @param {object} e
+ * @return {boolean}
+ */
+ onMouseDown: function(e) {
+ var self = this;
+ var defaultPrevented = e.isDefaultPrevented();
+ var $target = $(e.target);
+
+ if (self.isFocused) {
+ // retain focus by preventing native handling. if the
+ // event target is the input it should not be modified.
+ // otherwise, text selection within the input won't work.
+ if (e.target !== self.$control_input[0]) {
+ if (self.settings.mode === 'single') {
+ // toggle dropdown
+ self.isOpen ? self.close() : self.open();
+ } else if (!defaultPrevented) {
+ self.setActiveItem(null);
+ }
+ return false;
+ }
+ } else {
+ // give control focus
+ if (!defaultPrevented) {
+ window.setTimeout(function() {
+ self.focus();
+ }, 0);
+ }
+ }
+ },
+
+ /**
+ * Triggered when the value of the control has been changed.
+ * This should propagate the event to the original DOM
+ * input / select element.
+ */
+ onChange: function() {
+ this.$input.trigger('change');
+ },
+
+ /**
+ * Triggered on <input> paste.
+ *
+ * @param {object} e
+ * @returns {boolean}
+ */
+ onPaste: function(e) {
+ var self = this;
+ if (self.isFull() || self.isInputHidden || self.isLocked) {
+ e.preventDefault();
+ } else {
+ // If a regex or string is included, this will split the pasted
+ // input and create Items for each separate value
+ if (self.settings.splitOn) {
+ setTimeout(function() {
+ var splitInput = $.trim(self.$control_input.val() || '').split(self.settings.splitOn);
+ for (var i = 0, n = splitInput.length; i < n; i++) {
+ self.createItem(splitInput[i]);
+ }
+ }, 0);
+ }
+ }
+ },
+
+ /**
+ * Triggered on <input> keypress.
+ *
+ * @param {object} e
+ * @returns {boolean}
+ */
+ onKeyPress: function(e) {
+ if (this.isLocked) return e && e.preventDefault();
+ var character = String.fromCharCode(e.keyCode || e.which);
+ if (this.settings.create && this.settings.mode === 'multi' && character === this.settings.delimiter) {
+ this.createItem();
+ e.preventDefault();
+ return false;
+ }
+ },
+
+ /**
+ * Triggered on <input> keydown.
+ *
+ * @param {object} e
+ * @returns {boolean}
+ */
+ onKeyDown: function(e) {
+ var isInput = e.target === this.$control_input[0];
+ var self = this;
+
+ if (self.isLocked) {
+ if (e.keyCode !== KEY_TAB) {
+ e.preventDefault();
+ }
+ return;
+ }
+
+ switch (e.keyCode) {
+ case KEY_A:
+ if (self.isCmdDown) {
+ self.selectAll();
+ return;
+ }
+ break;
+ case KEY_ESC:
+ if (self.isOpen) {
+ e.preventDefault();
+ e.stopPropagation();
+ self.close();
+ }
+ return;
+ case KEY_N:
+ if (!e.ctrlKey || e.altKey) break;
+ case KEY_DOWN:
+ if (!self.isOpen && self.hasOptions) {
+ self.open();
+ } else if (self.$activeOption) {
+ self.ignoreHover = true;
+ var $next = self.getAdjacentOption(self.$activeOption, 1);
+ if ($next.length) self.setActiveOption($next, true, true);
+ }
+ e.preventDefault();
+ return;
+ case KEY_P:
+ if (!e.ctrlKey || e.altKey) break;
+ case KEY_UP:
+ if (self.$activeOption) {
+ self.ignoreHover = true;
+ var $prev = self.getAdjacentOption(self.$activeOption, -1);
+ if ($prev.length) self.setActiveOption($prev, true, true);
+ }
+ e.preventDefault();
+ return;
+ case KEY_RETURN:
+ if (self.isOpen && self.$activeOption) {
+ self.onOptionSelect({currentTarget: self.$activeOption});
+ e.preventDefault();
+ }
+ return;
+ case KEY_LEFT:
+ self.advanceSelection(-1, e);
+ return;
+ case KEY_RIGHT:
+ self.advanceSelection(1, e);
+ return;
+ case KEY_TAB:
+ if (self.settings.selectOnTab && self.isOpen && self.$activeOption) {
+ self.onOptionSelect({currentTarget: self.$activeOption});
+
+ // Default behaviour is to jump to the next field, we only want this
+ // if the current field doesn't accept any more entries
+ if (!self.isFull()) {
+ e.preventDefault();
+ }
+ }
+ if (self.settings.create && self.createItem()) {
+ e.preventDefault();
+ }
+ return;
+ case KEY_BACKSPACE:
+ case KEY_DELETE:
+ self.deleteSelection(e);
+ return;
+ }
+
+ if ((self.isFull() || self.isInputHidden) && !(IS_MAC ? e.metaKey : e.ctrlKey)) {
+ e.preventDefault();
+ return;
+ }
+ },
+
+ /**
+ * Triggered on <input> keyup.
+ *
+ * @param {object} e
+ * @returns {boolean}
+ */
+ onKeyUp: function(e) {
+ var self = this;
+
+ if (self.isLocked) return e && e.preventDefault();
+ var value = self.$control_input.val() || '';
+ if (self.lastValue !== value) {
+ self.lastValue = value;
+ self.onSearchChange(value);
+ self.refreshOptions();
+ self.trigger('type', value);
+ }
+ },
+
+ /**
+ * Invokes the user-provide option provider / loader.
+ *
+ * Note: this function is debounced in the Selectize
+ * constructor (by `settings.loadDelay` milliseconds)
+ *
+ * @param {string} value
+ */
+ onSearchChange: function(value) {
+ var self = this;
+ var fn = self.settings.load;
+ if (!fn) return;
+ if (self.loadedSearches.hasOwnProperty(value)) return;
+ self.loadedSearches[value] = true;
+ self.load(function(callback) {
+ fn.apply(self, [value, callback]);
+ });
+ },
+
+ /**
+ * Triggered on <input> focus.
+ *
+ * @param {object} e (optional)
+ * @returns {boolean}
+ */
+ onFocus: function(e) {
+ var self = this;
+ var wasFocused = self.isFocused;
+
+ if (self.isDisabled) {
+ self.blur();
+ e && e.preventDefault();
+ return false;
+ }
+
+ if (self.ignoreFocus) return;
+ self.isFocused = true;
+ if (self.settings.preload === 'focus') self.onSearchChange('');
+
+ if (!wasFocused) self.trigger('focus');
+
+ if (!self.$activeItems.length) {
+ self.showInput();
+ self.setActiveItem(null);
+ self.refreshOptions(!!self.settings.openOnFocus);
+ }
+
+ self.refreshState();
+ },
+
+ /**
+ * Triggered on <input> blur.
+ *
+ * @param {object} e
+ * @param {Element} dest
+ */
+ onBlur: function(e, dest) {
+ var self = this;
+ if (!self.isFocused) return;
+ self.isFocused = false;
+
+ if (self.ignoreFocus) {
+ return;
+ } else if (!self.ignoreBlur && document.activeElement === self.$dropdown_content[0]) {
+ // necessary to prevent IE closing the dropdown when the scrollbar is clicked
+ self.ignoreBlur = true;
+ self.onFocus(e);
+ return;
+ }
+
+ var deactivate = function() {
+ self.close();
+ self.setTextboxValue('');
+ self.setActiveItem(null);
+ self.setActiveOption(null);
+ self.setCaret(self.items.length);
+ self.refreshState();
+
+ // IE11 bug: element still marked as active
+ (dest || document.body).focus();
+
+ self.ignoreFocus = false;
+ self.trigger('blur');
+ };
+
+ self.ignoreFocus = true;
+ if (self.settings.create && self.settings.createOnBlur) {
+ self.createItem(null, false, deactivate);
+ } else {
+ deactivate();
+ }
+ },
+
+ /**
+ * Triggered when the user rolls over
+ * an option in the autocomplete dropdown menu.
+ *
+ * @param {object} e
+ * @returns {boolean}
+ */
+ onOptionHover: function(e) {
+ if (this.ignoreHover) return;
+ this.setActiveOption(e.currentTarget, false);
+ },
+
+ /**
+ * Triggered when the user clicks on an option
+ * in the autocomplete dropdown menu.
+ *
+ * @param {object} e
+ * @returns {boolean}
+ */
+ onOptionSelect: function(e) {
+ var value, $target, $option, self = this;
+
+ if (e.preventDefault) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+ $target = $(e.currentTarget);
+ if ($target.hasClass('create')) {
+ self.createItem(null, function() {
+ if (self.settings.closeAfterSelect) {
+ self.close();
+ }
+ });
+ } else {
+ value = $target.attr('data-value');
+ if (typeof value !== 'undefined') {
+ self.lastQuery = null;
+ self.setTextboxValue('');
+ self.addItem(value);
+ if (self.settings.closeAfterSelect) {
+ self.close();
+ } else if (!self.settings.hideSelected && e.type && /mouse/.test(e.type)) {
+ self.setActiveOption(self.getOption(value));
+ }
+ }
+ }
+ },
+
+ /**
+ * Triggered when the user clicks on an item
+ * that has been selected.
+ *
+ * @param {object} e
+ * @returns {boolean}
+ */
+ onItemSelect: function(e) {
+ var self = this;
+
+ if (self.isLocked) return;
+ if (self.settings.mode === 'multi') {
+ e.preventDefault();
+ self.setActiveItem(e.currentTarget, e);
+ }
+ },
+
+ /**
+ * Invokes the provided method that provides
+ * results to a callback---which are then added
+ * as options to the control.
+ *
+ * @param {function} fn
+ */
+ load: function(fn) {
+ var self = this;
+ var $wrapper = self.$wrapper.addClass(self.settings.loadingClass);
+
+ self.loading++;
+ fn.apply(self, [function(results) {
+ self.loading = Math.max(self.loading - 1, 0);
+ if (results && results.length) {
+ self.addOption(results);
+ self.refreshOptions(self.isFocused && !self.isInputHidden);
+ }
+ if (!self.loading) {
+ $wrapper.removeClass(self.settings.loadingClass);
+ }
+ self.trigger('load', results);
+ }]);
+ },
+
+ /**
+ * Sets the input field of the control to the specified value.
+ *
+ * @param {string} value
+ */
+ setTextboxValue: function(value) {
+ var $input = this.$control_input;
+ var changed = $input.val() !== value;
+ if (changed) {
+ $input.val(value).triggerHandler('update');
+ this.lastValue = value;
+ }
+ },
+
+ /**
+ * Returns the value of the control. If multiple items
+ * can be selected (e.g. <select multiple>), this returns
+ * an array. If only one item can be selected, this
+ * returns a string.
+ *
+ * @returns {mixed}
+ */
+ getValue: function() {
+ if (this.tagType === TAG_SELECT && this.$input.attr('multiple')) {
+ return this.items;
+ } else {
+ return this.items.join(this.settings.delimiter);
+ }
+ },
+
+ /**
+ * Resets the selected items to the given value.
+ *
+ * @param {mixed} value
+ */
+ setValue: function(value, silent) {
+ var events = silent ? [] : ['change'];
+
+ debounce_events(this, events, function() {
+ this.clear(silent);
+ this.addItems(value, silent);
+ });
+ },
+
+ /**
+ * Sets the selected item.
+ *
+ * @param {object} $item
+ * @param {object} e (optional)
+ */
+ setActiveItem: function($item, e) {
+ var self = this;
+ var eventName;
+ var i, idx, begin, end, item, swap;
+ var $last;
+
+ if (self.settings.mode === 'single') return;
+ $item = $($item);
+
+ // clear the active selection
+ if (!$item.length) {
+ $(self.$activeItems).removeClass('active');
+ self.$activeItems = [];
+ if (self.isFocused) {
+ self.showInput();
+ }
+ return;
+ }
+
+ // modify selection
+ eventName = e && e.type.toLowerCase();
+
+ if (eventName === 'mousedown' && self.isShiftDown && self.$activeItems.length) {
+ $last = self.$control.children('.active:last');
+ begin = Array.prototype.indexOf.apply(self.$control[0].childNodes, [$last[0]]);
+ end = Array.prototype.indexOf.apply(self.$control[0].childNodes, [$item[0]]);
+ if (begin > end) {
+ swap = begin;
+ begin = end;
+ end = swap;
+ }
+ for (i = begin; i <= end; i++) {
+ item = self.$control[0].childNodes[i];
+ if (self.$activeItems.indexOf(item) === -1) {
+ $(item).addClass('active');
+ self.$activeItems.push(item);
+ }
+ }
+ e.preventDefault();
+ } else if ((eventName === 'mousedown' && self.isCtrlDown) || (eventName === 'keydown' && this.isShiftDown)) {
+ if ($item.hasClass('active')) {
+ idx = self.$activeItems.indexOf($item[0]);
+ self.$activeItems.splice(idx, 1);
+ $item.removeClass('active');
+ } else {
+ self.$activeItems.push($item.addClass('active')[0]);
+ }
+ } else {
+ $(self.$activeItems).removeClass('active');
+ self.$activeItems = [$item.addClass('active')[0]];
+ }
+
+ // ensure control has focus
+ self.hideInput();
+ if (!this.isFocused) {
+ self.focus();
+ }
+ },
+
+ /**
+ * Sets the selected item in the dropdown menu
+ * of available options.
+ *
+ * @param {object} $object
+ * @param {boolean} scroll
+ * @param {boolean} animate
+ */
+ setActiveOption: function($option, scroll, animate) {
+ var height_menu, height_item, y;
+ var scroll_top, scroll_bottom;
+ var self = this;
+
+ if (self.$activeOption) self.$activeOption.removeClass('active');
+ self.$activeOption = null;
+
+ $option = $($option);
+ if (!$option.length) return;
+
+ self.$activeOption = $option.addClass('active');
+
+ if (scroll || !isset(scroll)) {
+
+ height_menu = self.$dropdown_content.height();
+ height_item = self.$activeOption.outerHeight(true);
+ scroll = self.$dropdown_content.scrollTop() || 0;
+ y = self.$activeOption.offset().top - self.$dropdown_content.offset().top + scroll;
+ scroll_top = y;
+ scroll_bottom = y - height_menu + height_item;
+
+ if (y + height_item > height_menu + scroll) {
+ self.$dropdown_content.stop().animate({scrollTop: scroll_bottom}, animate ? self.settings.scrollDuration : 0);
+ } else if (y < scroll) {
+ self.$dropdown_content.stop().animate({scrollTop: scroll_top}, animate ? self.settings.scrollDuration : 0);
+ }
+
+ }
+ },
+
+ /**
+ * Selects all items (CTRL + A).
+ */
+ selectAll: function() {
+ var self = this;
+ if (self.settings.mode === 'single') return;
+
+ self.$activeItems = Array.prototype.slice.apply(self.$control.children(':not(input)').addClass('active'));
+ if (self.$activeItems.length) {
+ self.hideInput();
+ self.close();
+ }
+ self.focus();
+ },
+
+ /**
+ * Hides the input element out of view, while
+ * retaining its focus.
+ */
+ hideInput: function() {
+ var self = this;
+
+ self.setTextboxValue('');
+ self.$control_input.css({opacity: 0, position: 'absolute', left: self.rtl ? 10000 : -10000});
+ self.isInputHidden = true;
+ },
+
+ /**
+ * Restores input visibility.
+ */
+ showInput: function() {
+ this.$control_input.css({opacity: 1, position: 'relative', left: 0});
+ this.isInputHidden = false;
+ },
+
+ /**
+ * Gives the control focus.
+ */
+ focus: function() {
+ var self = this;
+ if (self.isDisabled) return;
+
+ self.ignoreFocus = true;
+ self.$control_input[0].focus();
+ window.setTimeout(function() {
+ self.ignoreFocus = false;
+ self.onFocus();
+ }, 0);
+ },
+
+ /**
+ * Forces the control out of focus.
+ *
+ * @param {Element} dest
+ */
+ blur: function(dest) {
+ this.$control_input[0].blur();
+ this.onBlur(null, dest);
+ },
+
+ /**
+ * Returns a function that scores an object
+ * to show how good of a match it is to the
+ * provided query.
+ *
+ * @param {string} query
+ * @param {object} options
+ * @return {function}
+ */
+ getScoreFunction: function(query) {
+ return this.sifter.getScoreFunction(query, this.getSearchOptions());
+ },
+
+ /**
+ * Returns search options for sifter (the system
+ * for scoring and sorting results).
+ *
+ * @see https://github.com/brianreavis/sifter.js
+ * @return {object}
+ */
+ getSearchOptions: function() {
+ var settings = this.settings;
+ var sort = settings.sortField;
+ if (typeof sort === 'string') {
+ sort = [{field: sort}];
+ }
+
+ return {
+ fields : settings.searchField,
+ conjunction : settings.searchConjunction,
+ sort : sort
+ };
+ },
+
+ /**
+ * Searches through available options and returns
+ * a sorted array of matches.
+ *
+ * Returns an object containing:
+ *
+ * - query {string}
+ * - tokens {array}
+ * - total {int}
+ * - items {array}
+ *
+ * @param {string} query
+ * @returns {object}
+ */
+ search: function(query) {
+ var i, value, score, result, calculateScore;
+ var self = this;
+ var settings = self.settings;
+ var options = this.getSearchOptions();
+
+ // validate user-provided result scoring function
+ if (settings.score) {
+ calculateScore = self.settings.score.apply(this, [query]);
+ if (typeof calculateScore !== 'function') {
+ throw new Error('Selectize "score" setting must be a function that returns a function');
+ }
+ }
+
+ // perform search
+ if (query !== self.lastQuery) {
+ self.lastQuery = query;
+ result = self.sifter.search(query, $.extend(options, {score: calculateScore}));
+ self.currentResults = result;
+ } else {
+ result = $.extend(true, {}, self.currentResults);
+ }
+
+ // filter out selected items
+ if (settings.hideSelected) {
+ for (i = result.items.length - 1; i >= 0; i--) {
+ if (self.items.indexOf(hash_key(result.items[i].id)) !== -1) {
+ result.items.splice(i, 1);
+ }
+ }
+ }
+
+ return result;
+ },
+
+ /**
+ * Refreshes the list of available options shown
+ * in the autocomplete dropdown menu.
+ *
+ * @param {boolean} triggerDropdown
+ */
+ refreshOptions: function(triggerDropdown) {
+ var i, j, k, n, groups, groups_order, option, option_html, optgroup, optgroups, html, html_children, has_create_option;
+ var $active, $active_before, $create;
+
+ if (typeof triggerDropdown === 'undefined') {
+ triggerDropdown = true;
+ }
+
+ var self = this;
+ var query = $.trim(self.$control_input.val());
+ var results = self.search(query);
+ var $dropdown_content = self.$dropdown_content;
+ var active_before = self.$activeOption && hash_key(self.$activeOption.attr('data-value'));
+
+ // build markup
+ n = results.items.length;
+ if (typeof self.settings.maxOptions === 'number') {
+ n = Math.min(n, self.settings.maxOptions);
+ }
+
+ // render and group available options individually
+ groups = {};
+ groups_order = [];
+
+ for (i = 0; i < n; i++) {
+ option = self.options[results.items[i].id];
+ option_html = self.render('option', option);
+ optgroup = option[self.settings.optgroupField] || '';
+ optgroups = $.isArray(optgroup) ? optgroup : [optgroup];
+
+ for (j = 0, k = optgroups && optgroups.length; j < k; j++) {
+ optgroup = optgroups[j];
+ if (!self.optgroups.hasOwnProperty(optgroup)) {
+ optgroup = '';
+ }
+ if (!groups.hasOwnProperty(optgroup)) {
+ groups[optgroup] = [];
+ groups_order.push(optgroup);
+ }
+ groups[optgroup].push(option_html);
+ }
+ }
+
+ // sort optgroups
+ if (this.settings.lockOptgroupOrder) {
+ groups_order.sort(function(a, b) {
+ var a_order = self.optgroups[a].$order || 0;
+ var b_order = self.optgroups[b].$order || 0;
+ return a_order - b_order;
+ });
+ }
+
+ // render optgroup headers & join groups
+ html = [];
+ for (i = 0, n = groups_order.length; i < n; i++) {
+ optgroup = groups_order[i];
+ if (self.optgroups.hasOwnProperty(optgroup) && groups[optgroup].length) {
+ // render the optgroup header and options within it,
+ // then pass it to the wrapper template
+ html_children = self.render('optgroup_header', self.optgroups[optgroup]) || '';
+ html_children += groups[optgroup].join('');
+ html.push(self.render('optgroup', $.extend({}, self.optgroups[optgroup], {
+ html: html_children
+ })));
+ } else {
+ html.push(groups[optgroup].join(''));
+ }
+ }
+
+ $dropdown_content.html(html.join(''));
+
+ // highlight matching terms inline
+ if (self.settings.highlight && results.query.length && results.tokens.length) {
+ for (i = 0, n = results.tokens.length; i < n; i++) {
+ highlight($dropdown_content, results.tokens[i].regex);
+ }
+ }
+
+ // add "selected" class to selected options
+ if (!self.settings.hideSelected) {
+ for (i = 0, n = self.items.length; i < n; i++) {
+ self.getOption(self.items[i]).addClass('selected');
+ }
+ }
+
+ // add create option
+ has_create_option = self.canCreate(query);
+ if (has_create_option) {
+ $dropdown_content.prepend(self.render('option_create', {input: query}));
+ $create = $($dropdown_content[0].childNodes[0]);
+ }
+
+ // activate
+ self.hasOptions = results.items.length > 0 || has_create_option;
+ if (self.hasOptions) {
+ if (results.items.length > 0) {
+ $active_before = active_before && self.getOption(active_before);
+ if ($active_before && $active_before.length) {
+ $active = $active_before;
+ } else if (self.settings.mode === 'single' && self.items.length) {
+ $active = self.getOption(self.items[0]);
+ }
+ if (!$active || !$active.length) {
+ if ($create && !self.settings.addPrecedence) {
+ $active = self.getAdjacentOption($create, 1);
+ } else {
+ $active = $dropdown_content.find('[data-selectable]:first');
+ }
+ }
+ } else {
+ $active = $create;
+ }
+ self.setActiveOption($active);
+ if (triggerDropdown && !self.isOpen) { self.open(); }
+ } else {
+ self.setActiveOption(null);
+ if (triggerDropdown && self.isOpen) { self.close(); }
+ }
+ },
+
+ /**
+ * Adds an available option. If it already exists,
+ * nothing will happen. Note: this does not refresh
+ * the options list dropdown (use `refreshOptions`
+ * for that).
+ *
+ * Usage:
+ *
+ * this.addOption(data)
+ *
+ * @param {object|array} data
+ */
+ addOption: function(data) {
+ var i, n, value, self = this;
+
+ if ($.isArray(data)) {
+ for (i = 0, n = data.length; i < n; i++) {
+ self.addOption(data[i]);
+ }
+ return;
+ }
+
+ if (value = self.registerOption(data)) {
+ self.userOptions[value] = true;
+ self.lastQuery = null;
+ self.trigger('option_add', value, data);
+ }
+ },
+
+ /**
+ * Registers an option to the pool of options.
+ *
+ * @param {object} data
+ * @return {boolean|string}
+ */
+ registerOption: function(data) {
+ var key = hash_key(data[this.settings.valueField]);
+ if (!key || this.options.hasOwnProperty(key)) return false;
+ data.$order = data.$order || ++this.order;
+ this.options[key] = data;
+ return key;
+ },
+
+ /**
+ * Registers an option group to the pool of option groups.
+ *
+ * @param {object} data
+ * @return {boolean|string}
+ */
+ registerOptionGroup: function(data) {
+ var key = hash_key(data[this.settings.optgroupValueField]);
+ if (!key) return false;
+
+ data.$order = data.$order || ++this.order;
+ this.optgroups[key] = data;
+ return key;
+ },
+
+ /**
+ * Registers a new optgroup for options
+ * to be bucketed into.
+ *
+ * @param {string} id
+ * @param {object} data
+ */
+ addOptionGroup: function(id, data) {
+ data[this.settings.optgroupValueField] = id;
+ if (id = this.registerOptionGroup(data)) {
+ this.trigger('optgroup_add', id, data);
+ }
+ },
+
+ /**
+ * Removes an existing option group.
+ *
+ * @param {string} id
+ */
+ removeOptionGroup: function(id) {
+ if (this.optgroups.hasOwnProperty(id)) {
+ delete this.optgroups[id];
+ this.renderCache = {};
+ this.trigger('optgroup_remove', id);
+ }
+ },
+
+ /**
+ * Clears all existing option groups.
+ */
+ clearOptionGroups: function() {
+ this.optgroups = {};
+ this.renderCache = {};
+ this.trigger('optgroup_clear');
+ },
+
+ /**
+ * Updates an option available for selection. If
+ * it is visible in the selected items or options
+ * dropdown, it will be re-rendered automatically.
+ *
+ * @param {string} value
+ * @param {object} data
+ */
+ updateOption: function(value, data) {
+ var self = this;
+ var $item, $item_new;
+ var value_new, index_item, cache_items, cache_options, order_old;
+
+ value = hash_key(value);
+ value_new = hash_key(data[self.settings.valueField]);
+
+ // sanity checks
+ if (value === null) return;
+ if (!self.options.hasOwnProperty(value)) return;
+ if (typeof value_new !== 'string') throw new Error('Value must be set in option data');
+
+ order_old = self.options[value].$order;
+
+ // update references
+ if (value_new !== value) {
+ delete self.options[value];
+ index_item = self.items.indexOf(value);
+ if (index_item !== -1) {
+ self.items.splice(index_item, 1, value_new);
+ }
+ }
+ data.$order = data.$order || order_old;
+ self.options[value_new] = data;
+
+ // invalidate render cache
+ cache_items = self.renderCache['item'];
+ cache_options = self.renderCache['option'];
+
+ if (cache_items) {
+ delete cache_items[value];
+ delete cache_items[value_new];
+ }
+ if (cache_options) {
+ delete cache_options[value];
+ delete cache_options[value_new];
+ }
+
+ // update the item if it's selected
+ if (self.items.indexOf(value_new) !== -1) {
+ $item = self.getItem(value);
+ $item_new = $(self.render('item', data));
+ if ($item.hasClass('active')) $item_new.addClass('active');
+ $item.replaceWith($item_new);
+ }
+
+ // invalidate last query because we might have updated the sortField
+ self.lastQuery = null;
+
+ // update dropdown contents
+ if (self.isOpen) {
+ self.refreshOptions(false);
+ }
+ },
+
+ /**
+ * Removes a single option.
+ *
+ * @param {string} value
+ * @param {boolean} silent
+ */
+ removeOption: function(value, silent) {
+ var self = this;
+ value = hash_key(value);
+
+ var cache_items = self.renderCache['item'];
+ var cache_options = self.renderCache['option'];
+ if (cache_items) delete cache_items[value];
+ if (cache_options) delete cache_options[value];
+
+ delete self.userOptions[value];
+ delete self.options[value];
+ self.lastQuery = null;
+ self.trigger('option_remove', value);
+ self.removeItem(value, silent);
+ },
+
+ /**
+ * Clears all options.
+ */
+ clearOptions: function() {
+ var self = this;
+
+ self.loadedSearches = {};
+ self.userOptions = {};
+ self.renderCache = {};
+ self.options = self.sifter.items = {};
+ self.lastQuery = null;
+ self.trigger('option_clear');
+ self.clear();
+ },
+
+ /**
+ * Returns the jQuery element of the option
+ * matching the given value.
+ *
+ * @param {string} value
+ * @returns {object}
+ */
+ getOption: function(value) {
+ return this.getElementWithValue(value, this.$dropdown_content.find('[data-selectable]'));
+ },
+
+ /**
+ * Returns the jQuery element of the next or
+ * previous selectable option.
+ *
+ * @param {object} $option
+ * @param {int} direction can be 1 for next or -1 for previous
+ * @return {object}
+ */
+ getAdjacentOption: function($option, direction) {
+ var $options = this.$dropdown.find('[data-selectable]');
+ var index = $options.index($option) + direction;
+
+ return index >= 0 && index < $options.length ? $options.eq(index) : $();
+ },
+
+ /**
+ * Finds the first element with a "data-value" attribute
+ * that matches the given value.
+ *
+ * @param {mixed} value
+ * @param {object} $els
+ * @return {object}
+ */
+ getElementWithValue: function(value, $els) {
+ value = hash_key(value);
+
+ if (typeof value !== 'undefined' && value !== null) {
+ for (var i = 0, n = $els.length; i < n; i++) {
+ if ($els[i].getAttribute('data-value') === value) {
+ return $($els[i]);
+ }
+ }
+ }
+
+ return $();
+ },
+
+ /**
+ * Returns the jQuery element of the item
+ * matching the given value.
+ *
+ * @param {string} value
+ * @returns {object}
+ */
+ getItem: function(value) {
+ return this.getElementWithValue(value, this.$control.children());
+ },
+
+ /**
+ * "Selects" multiple items at once. Adds them to the list
+ * at the current caret position.
+ *
+ * @param {string} value
+ * @param {boolean} silent
+ */
+ addItems: function(values, silent) {
+ var items = $.isArray(values) ? values : [values];
+ for (var i = 0, n = items.length; i < n; i++) {
+ this.isPending = (i < n - 1);
+ this.addItem(items[i], silent);
+ }
+ },
+
+ /**
+ * "Selects" an item. Adds it to the list
+ * at the current caret position.
+ *
+ * @param {string} value
+ * @param {boolean} silent
+ */
+ addItem: function(value, silent) {
+ var events = silent ? [] : ['change'];
+
+ debounce_events(this, events, function() {
+ var $item, $option, $options;
+ var self = this;
+ var inputMode = self.settings.mode;
+ var i, active, value_next, wasFull;
+ value = hash_key(value);
+
+ if (self.items.indexOf(value) !== -1) {
+ if (inputMode === 'single') self.close();
+ return;
+ }
+
+ if (!self.options.hasOwnProperty(value)) return;
+ if (inputMode === 'single') self.clear(silent);
+ if (inputMode === 'multi' && self.isFull()) return;
+
+ $item = $(self.render('item', self.options[value]));
+ wasFull = self.isFull();
+ self.items.splice(self.caretPos, 0, value);
+ self.insertAtCaret($item);
+ if (!self.isPending || (!wasFull && self.isFull())) {
+ self.refreshState();
+ }
+
+ if (self.isSetup) {
+ $options = self.$dropdown_content.find('[data-selectable]');
+
+ // update menu / remove the option (if this is not one item being added as part of series)
+ if (!self.isPending) {
+ $option = self.getOption(value);
+ value_next = self.getAdjacentOption($option, 1).attr('data-value');
+ self.refreshOptions(self.isFocused && inputMode !== 'single');
+ if (value_next) {
+ self.setActiveOption(self.getOption(value_next));
+ }
+ }
+
+ // hide the menu if the maximum number of items have been selected or no options are left
+ if (!$options.length || self.isFull()) {
+ self.close();
+ } else {
+ self.positionDropdown();
+ }
+
+ self.updatePlaceholder();
+ self.trigger('item_add', value, $item);
+ self.updateOriginalInput({silent: silent});
+ }
+ });
+ },
+
+ /**
+ * Removes the selected item matching
+ * the provided value.
+ *
+ * @param {string} value
+ */
+ removeItem: function(value, silent) {
+ var self = this;
+ var $item, i, idx;
+
+ $item = (typeof value === 'object') ? value : self.getItem(value);
+ value = hash_key($item.attr('data-value'));
+ i = self.items.indexOf(value);
+
+ if (i !== -1) {
+ $item.remove();
+ if ($item.hasClass('active')) {
+ idx = self.$activeItems.indexOf($item[0]);
+ self.$activeItems.splice(idx, 1);
+ }
+
+ self.items.splice(i, 1);
+ self.lastQuery = null;
+ if (!self.settings.persist && self.userOptions.hasOwnProperty(value)) {
+ self.removeOption(value, silent);
+ }
+
+ if (i < self.caretPos) {
+ self.setCaret(self.caretPos - 1);
+ }
+
+ self.refreshState();
+ self.updatePlaceholder();
+ self.updateOriginalInput({silent: silent});
+ self.positionDropdown();
+ self.trigger('item_remove', value, $item);
+ }
+ },
+
+ /**
+ * Invokes the `create` method provided in the
+ * selectize options that should provide the data
+ * for the new item, given the user input.
+ *
+ * Once this completes, it will be added
+ * to the item list.
+ *
+ * @param {string} value
+ * @param {boolean} [triggerDropdown]
+ * @param {function} [callback]
+ * @return {boolean}
+ */
+ createItem: function(input, triggerDropdown) {
+ var self = this;
+ var caret = self.caretPos;
+ input = input || $.trim(self.$control_input.val() || '');
+
+ var callback = arguments[arguments.length - 1];
+ if (typeof callback !== 'function') callback = function() {};
+
+ if (typeof triggerDropdown !== 'boolean') {
+ triggerDropdown = true;
+ }
+
+ if (!self.canCreate(input)) {
+ callback();
+ return false;
+ }
+
+ self.lock();
+
+ var setup = (typeof self.settings.create === 'function') ? this.settings.create : function(input) {
+ var data = {};
+ data[self.settings.labelField] = input;
+ data[self.settings.valueField] = input;
+ return data;
+ };
+
+ var create = once(function(data) {
+ self.unlock();
+
+ if (!data || typeof data !== 'object') return callback();
+ var value = hash_key(data[self.settings.valueField]);
+ if (typeof value !== 'string') return callback();
+
+ self.setTextboxValue('');
+ self.addOption(data);
+ self.setCaret(caret);
+ self.addItem(value);
+ self.refreshOptions(triggerDropdown && self.settings.mode !== 'single');
+ callback(data);
+ });
+
+ var output = setup.apply(this, [input, create]);
+ if (typeof output !== 'undefined') {
+ create(output);
+ }
+
+ return true;
+ },
+
+ /**
+ * Re-renders the selected item lists.
+ */
+ refreshItems: function() {
+ this.lastQuery = null;
+
+ if (this.isSetup) {
+ this.addItem(this.items);
+ }
+
+ this.refreshState();
+ this.updateOriginalInput();
+ },
+
+ /**
+ * Updates all state-dependent attributes
+ * and CSS classes.
+ */
+ refreshState: function() {
+ var invalid, self = this;
+ if (self.isRequired) {
+ if (self.items.length) self.isInvalid = false;
+ self.$control_input.prop('required', invalid);
+ }
+ self.refreshClasses();
+ },
+
+ /**
+ * Updates all state-dependent CSS classes.
+ */
+ refreshClasses: function() {
+ var self = this;
+ var isFull = self.isFull();
+ var isLocked = self.isLocked;
+
+ self.$wrapper
+ .toggleClass('rtl', self.rtl);
+
+ self.$control
+ .toggleClass('focus', self.isFocused)
+ .toggleClass('disabled', self.isDisabled)
+ .toggleClass('required', self.isRequired)
+ .toggleClass('invalid', self.isInvalid)
+ .toggleClass('locked', isLocked)
+ .toggleClass('full', isFull).toggleClass('not-full', !isFull)
+ .toggleClass('input-active', self.isFocused && !self.isInputHidden)
+ .toggleClass('dropdown-active', self.isOpen)
+ .toggleClass('has-options', !$.isEmptyObject(self.options))
+ .toggleClass('has-items', self.items.length > 0);
+
+ self.$control_input.data('grow', !isFull && !isLocked);
+ },
+
+ /**
+ * Determines whether or not more items can be added
+ * to the control without exceeding the user-defined maximum.
+ *
+ * @returns {boolean}
+ */
+ isFull: function() {
+ return this.settings.maxItems !== null && this.items.length >= this.settings.maxItems;
+ },
+
+ /**
+ * Refreshes the original <select> or <input>
+ * element to reflect the current state.
+ */
+ updateOriginalInput: function(opts) {
+ var i, n, options, label, self = this;
+ opts = opts || {};
+
+ if (self.tagType === TAG_SELECT) {
+ options = [];
+ for (i = 0, n = self.items.length; i < n; i++) {
+ label = self.options[self.items[i]][self.settings.labelField] || '';
+ options.push('<option value="' + escape_html(self.items[i]) + '" selected="selected">' + escape_html(label) + '</option>');
+ }
+ if (!options.length && !this.$input.attr('multiple')) {
+ options.push('<option value="" selected="selected"></option>');
+ }
+ self.$input.html(options.join(''));
+ } else {
+ self.$input.val(self.getValue());
+ self.$input.attr('value',self.$input.val());
+ }
+
+ if (self.isSetup) {
+ if (!opts.silent) {
+ self.trigger('change', self.$input.val());
+ }
+ }
+ },
+
+ /**
+ * Shows/hide the input placeholder depending
+ * on if there items in the list already.
+ */
+ updatePlaceholder: function() {
+ if (!this.settings.placeholder) return;
+ var $input = this.$control_input;
+
+ if (this.items.length) {
+ $input.removeAttr('placeholder');
+ } else {
+ $input.attr('placeholder', this.settings.placeholder);
+ }
+ $input.triggerHandler('update', {force: true});
+ },
+
+ /**
+ * Shows the autocomplete dropdown containing
+ * the available options.
+ */
+ open: function() {
+ var self = this;
+
+ if (self.isLocked || self.isOpen || (self.settings.mode === 'multi' && self.isFull())) return;
+ self.focus();
+ self.isOpen = true;
+ self.refreshState();
+ self.$dropdown.css({visibility: 'hidden', display: 'block'});
+ self.positionDropdown();
+ self.$dropdown.css({visibility: 'visible'});
+ self.trigger('dropdown_open', self.$dropdown);
+ },
+
+ /**
+ * Closes the autocomplete dropdown menu.
+ */
+ close: function() {
+ var self = this;
+ var trigger = self.isOpen;
+
+ if (self.settings.mode === 'single' && self.items.length) {
+ self.hideInput();
+ }
+
+ self.isOpen = false;
+ self.$dropdown.hide();
+ self.setActiveOption(null);
+ self.refreshState();
+
+ if (trigger) self.trigger('dropdown_close', self.$dropdown);
+ },
+
+ /**
+ * Calculates and applies the appropriate
+ * position of the dropdown.
+ */
+ positionDropdown: function() {
+ var $control = this.$control;
+ var offset = this.settings.dropdownParent === 'body' ? $control.offset() : $control.position();
+ offset.top += $control.outerHeight(true);
+
+ this.$dropdown.css({
+ width : $control.outerWidth(),
+ top : offset.top,
+ left : offset.left
+ });
+ },
+
+ /**
+ * Resets / clears all selected items
+ * from the control.
+ *
+ * @param {boolean} silent
+ */
+ clear: function(silent) {
+ var self = this;
+
+ if (!self.items.length) return;
+ self.$control.children(':not(input)').remove();
+ self.items = [];
+ self.lastQuery = null;
+ self.setCaret(0);
+ self.setActiveItem(null);
+ self.updatePlaceholder();
+ self.updateOriginalInput({silent: silent});
+ self.refreshState();
+ self.showInput();
+ self.trigger('clear');
+ },
+
+ /**
+ * A helper method for inserting an element
+ * at the current caret position.
+ *
+ * @param {object} $el
+ */
+ insertAtCaret: function($el) {
+ var caret = Math.min(this.caretPos, this.items.length);
+ if (caret === 0) {
+ this.$control.prepend($el);
+ } else {
+ $(this.$control[0].childNodes[caret]).before($el);
+ }
+ this.setCaret(caret + 1);
+ },
+
+ /**
+ * Removes the current selected item(s).
+ *
+ * @param {object} e (optional)
+ * @returns {boolean}
+ */
+ deleteSelection: function(e) {
+ var i, n, direction, selection, values, caret, option_select, $option_select, $tail;
+ var self = this;
+
+ direction = (e && e.keyCode === KEY_BACKSPACE) ? -1 : 1;
+ selection = getSelection(self.$control_input[0]);
+
+ if (self.$activeOption && !self.settings.hideSelected) {
+ option_select = self.getAdjacentOption(self.$activeOption, -1).attr('data-value');
+ }
+
+ // determine items that will be removed
+ values = [];
+
+ if (self.$activeItems.length) {
+ $tail = self.$control.children('.active:' + (direction > 0 ? 'last' : 'first'));
+ caret = self.$control.children(':not(input)').index($tail);
+ if (direction > 0) { caret++; }
+
+ for (i = 0, n = self.$activeItems.length; i < n; i++) {
+ values.push($(self.$activeItems[i]).attr('data-value'));
+ }
+ if (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ } else if ((self.isFocused || self.settings.mode === 'single') && self.items.length) {
+ if (direction < 0 && selection.start === 0 && selection.length === 0) {
+ values.push(self.items[self.caretPos - 1]);
+ } else if (direction > 0 && selection.start === self.$control_input.val().length) {
+ values.push(self.items[self.caretPos]);
+ }
+ }
+
+ // allow the callback to abort
+ if (!values.length || (typeof self.settings.onDelete === 'function' && self.settings.onDelete.apply(self, [values]) === false)) {
+ return false;
+ }
+
+ // perform removal
+ if (typeof caret !== 'undefined') {
+ self.setCaret(caret);
+ }
+ while (values.length) {
+ self.removeItem(values.pop());
+ }
+
+ self.showInput();
+ self.positionDropdown();
+ self.refreshOptions(true);
+
+ // select previous option
+ if (option_select) {
+ $option_select = self.getOption(option_select);
+ if ($option_select.length) {
+ self.setActiveOption($option_select);
+ }
+ }
+
+ return true;
+ },
+
+ /**
+ * Selects the previous / next item (depending
+ * on the `direction` argument).
+ *
+ * > 0 - right
+ * < 0 - left
+ *
+ * @param {int} direction
+ * @param {object} e (optional)
+ */
+ advanceSelection: function(direction, e) {
+ var tail, selection, idx, valueLength, cursorAtEdge, $tail;
+ var self = this;
+
+ if (direction === 0) return;
+ if (self.rtl) direction *= -1;
+
+ tail = direction > 0 ? 'last' : 'first';
+ selection = getSelection(self.$control_input[0]);
+
+ if (self.isFocused && !self.isInputHidden) {
+ valueLength = self.$control_input.val().length;
+ cursorAtEdge = direction < 0
+ ? selection.start === 0 && selection.length === 0
+ : selection.start === valueLength;
+
+ if (cursorAtEdge && !valueLength) {
+ self.advanceCaret(direction, e);
+ }
+ } else {
+ $tail = self.$control.children('.active:' + tail);
+ if ($tail.length) {
+ idx = self.$control.children(':not(input)').index($tail);
+ self.setActiveItem(null);
+ self.setCaret(direction > 0 ? idx + 1 : idx);
+ }
+ }
+ },
+
+ /**
+ * Moves the caret left / right.
+ *
+ * @param {int} direction
+ * @param {object} e (optional)
+ */
+ advanceCaret: function(direction, e) {
+ var self = this, fn, $adj;
+
+ if (direction === 0) return;
+
+ fn = direction > 0 ? 'next' : 'prev';
+ if (self.isShiftDown) {
+ $adj = self.$control_input[fn]();
+ if ($adj.length) {
+ self.hideInput();
+ self.setActiveItem($adj);
+ e && e.preventDefault();
+ }
+ } else {
+ self.setCaret(self.caretPos + direction);
+ }
+ },
+
+ /**
+ * Moves the caret to the specified index.
+ *
+ * @param {int} i
+ */
+ setCaret: function(i) {
+ var self = this;
+
+ if (self.settings.mode === 'single') {
+ i = self.items.length;
+ } else {
+ i = Math.max(0, Math.min(self.items.length, i));
+ }
+
+ if(!self.isPending) {
+ // the input must be moved by leaving it in place and moving the
+ // siblings, due to the fact that focus cannot be restored once lost
+ // on mobile webkit devices
+ var j, n, fn, $children, $child;
+ $children = self.$control.children(':not(input)');
+ for (j = 0, n = $children.length; j < n; j++) {
+ $child = $($children[j]).detach();
+ if (j < i) {
+ self.$control_input.before($child);
+ } else {
+ self.$control.append($child);
+ }
+ }
+ }
+
+ self.caretPos = i;
+ },
+
+ /**
+ * Disables user input on the control. Used while
+ * items are being asynchronously created.
+ */
+ lock: function() {
+ this.close();
+ this.isLocked = true;
+ this.refreshState();
+ },
+
+ /**
+ * Re-enables user input on the control.
+ */
+ unlock: function() {
+ this.isLocked = false;
+ this.refreshState();
+ },
+
+ /**
+ * Disables user input on the control completely.
+ * While disabled, it cannot receive focus.
+ */
+ disable: function() {
+ var self = this;
+ self.$input.prop('disabled', true);
+ self.$control_input.prop('disabled', true).prop('tabindex', -1);
+ self.isDisabled = true;
+ self.lock();
+ },
+
+ /**
+ * Enables the control so that it can respond
+ * to focus and user input.
+ */
+ enable: function() {
+ var self = this;
+ self.$input.prop('disabled', false);
+ self.$control_input.prop('disabled', false).prop('tabindex', self.tabIndex);
+ self.isDisabled = false;
+ self.unlock();
+ },
+
+ /**
+ * Completely destroys the control and
+ * unbinds all event listeners so that it can
+ * be garbage collected.
+ */
+ destroy: function() {
+ var self = this;
+ var eventNS = self.eventNS;
+ var revertSettings = self.revertSettings;
+
+ self.trigger('destroy');
+ self.off();
+ self.$wrapper.remove();
+ self.$dropdown.remove();
+
+ self.$input
+ .html('')
+ .append(revertSettings.$children)
+ .removeAttr('tabindex')
+ .removeClass('selectized')
+ .attr({tabindex: revertSettings.tabindex})
+ .show();
+
+ self.$control_input.removeData('grow');
+ self.$input.removeData('selectize');
+
+ $(window).off(eventNS);
+ $(document).off(eventNS);
+ $(document.body).off(eventNS);
+
+ delete self.$input[0].selectize;
+ },
+
+ /**
+ * A helper method for rendering "item" and
+ * "option" templates, given the data.
+ *
+ * @param {string} templateName
+ * @param {object} data
+ * @returns {string}
+ */
+ render: function(templateName, data) {
+ var value, id, label;
+ var html = '';
+ var cache = false;
+ var self = this;
+ var regex_tag = /^[\t \r\n]*<([a-z][a-z0-9\-_]*(?:\:[a-z][a-z0-9\-_]*)?)/i;
+
+ if (templateName === 'option' || templateName === 'item') {
+ value = hash_key(data[self.settings.valueField]);
+ cache = !!value;
+ }
+
+ // pull markup from cache if it exists
+ if (cache) {
+ if (!isset(self.renderCache[templateName])) {
+ self.renderCache[templateName] = {};
+ }
+ if (self.renderCache[templateName].hasOwnProperty(value)) {
+ return self.renderCache[templateName][value];
+ }
+ }
+
+ // render markup
+ html = self.settings.render[templateName].apply(this, [data, escape_html]);
+
+ // add mandatory attributes
+ if (templateName === 'option' || templateName === 'option_create') {
+ html = html.replace(regex_tag, '<$1 data-selectable');
+ }
+ if (templateName === 'optgroup') {
+ id = data[self.settings.optgroupValueField] || '';
+ html = html.replace(regex_tag, '<$1 data-group="' + escape_replace(escape_html(id)) + '"');
+ }
+ if (templateName === 'option' || templateName === 'item') {
+ html = html.replace(regex_tag, '<$1 data-value="' + escape_replace(escape_html(value || '')) + '"');
+ }
+
+ // update cache
+ if (cache) {
+ self.renderCache[templateName][value] = html;
+ }
+
+ return html;
+ },
+
+ /**
+ * Clears the render cache for a template. If
+ * no template is given, clears all render
+ * caches.
+ *
+ * @param {string} templateName
+ */
+ clearCache: function(templateName) {
+ var self = this;
+ if (typeof templateName === 'undefined') {
+ self.renderCache = {};
+ } else {
+ delete self.renderCache[templateName];
+ }
+ },
+
+ /**
+ * Determines whether or not to display the
+ * create item prompt, given a user input.
+ *
+ * @param {string} input
+ * @return {boolean}
+ */
+ canCreate: function(input) {
+ var self = this;
+ if (!self.settings.create) return false;
+ var filter = self.settings.createFilter;
+ return input.length
+ && (typeof filter !== 'function' || filter.apply(self, [input]))
+ && (typeof filter !== 'string' || new RegExp(filter).test(input))
+ && (!(filter instanceof RegExp) || filter.test(input));
+ }
+
+});
Modified: trunk/packages/R/r-cran-shiny/trunk/debian/rules
===================================================================
--- trunk/packages/R/r-cran-shiny/trunk/debian/rules 2016-03-27 13:54:13 UTC (rev 21614)
+++ trunk/packages/R/r-cran-shiny/trunk/debian/rules 2016-03-27 14:21:57 UTC (rev 21615)
@@ -10,3 +10,5 @@
rm -rf $(debRlib)/$(cranName)/www/shared/showdown/license.txt
mkdir -p $(debRlib)/$(cranName)/www/shared/bootstrap/shim
yui-compressor debian/missing-sources/respond.src.js > $(debRlib)/$(cranName)/www/shared/bootstrap/shim/respond.min.js
+ mkdir -p $(debRlib)/$(cranName)/www/shared/selectize/js
+ yui-compressor debian/missing-sources/selectize.js > $(debRlib)/$(cranName)/www/shared/selectize/js/selectize.min.js
More information about the debian-med-commit
mailing list