");
$input.on("blur.tt", function($e) {
var active, isActive, hasActive;
active = document.activeElement;
isActive = $menu.is(active);
hasActive = $menu.has(active).length > 0;
if (_.isMsie() && (isActive || hasActive)) {
$e.preventDefault();
$e.stopImmediatePropagation();
_.defer(function() {
$input.focus();
});
}
});
$menu.on("mousedown.tt", function($e) {
$e.preventDefault();
});
},
_onSelectableClicked: function onSelectableClicked(type, $el) {
this.select($el);
},
_onDatasetCleared: function onDatasetCleared() {
this._updateHint();
},
_onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) {
this._updateHint();
this.eventBus.trigger("render", suggestions, async, dataset);
},
_onAsyncRequested: function onAsyncRequested(type, dataset, query) {
this.eventBus.trigger("asyncrequest", query, dataset);
},
_onAsyncCanceled: function onAsyncCanceled(type, dataset, query) {
this.eventBus.trigger("asynccancel", query, dataset);
},
_onAsyncReceived: function onAsyncReceived(type, dataset, query) {
this.eventBus.trigger("asyncreceive", query, dataset);
},
_onFocused: function onFocused() {
this._minLengthMet() && this.menu.update(this.input.getQuery());
},
_onBlurred: function onBlurred() {
if (this.input.hasQueryChangedSinceLastFocus()) {
this.eventBus.trigger("change", this.input.getQuery());
}
},
_onEnterKeyed: function onEnterKeyed(type, $e) {
var $selectable;
if ($selectable = this.menu.getActiveSelectable()) {
this.select($selectable) && $e.preventDefault();
}
},
_onTabKeyed: function onTabKeyed(type, $e) {
var $selectable;
if ($selectable = this.menu.getActiveSelectable()) {
this.select($selectable) && $e.preventDefault();
} else if ($selectable = this.menu.getTopSelectable()) {
this.autocomplete($selectable) && $e.preventDefault();
}
},
_onEscKeyed: function onEscKeyed() {
this.close();
},
_onUpKeyed: function onUpKeyed() {
this.moveCursor(-1);
},
_onDownKeyed: function onDownKeyed() {
this.moveCursor(+1);
},
_onLeftKeyed: function onLeftKeyed() {
if (this.dir === "rtl" && this.input.isCursorAtEnd()) {
this.autocomplete(this.menu.getTopSelectable());
}
},
_onRightKeyed: function onRightKeyed() {
if (this.dir === "ltr" && this.input.isCursorAtEnd()) {
this.autocomplete(this.menu.getTopSelectable());
}
},
_onQueryChanged: function onQueryChanged(e, query) {
this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty();
},
_onWhitespaceChanged: function onWhitespaceChanged() {
this._updateHint();
},
_onLangDirChanged: function onLangDirChanged(e, dir) {
if (this.dir !== dir) {
this.dir = dir;
this.menu.setLanguageDirection(dir);
}
},
_openIfActive: function openIfActive() {
this.isActive() && this.open();
},
_minLengthMet: function minLengthMet(query) {
query = _.isString(query) ? query : this.input.getQuery() || "";
return query.length >= this.minLength;
},
_updateHint: function updateHint() {
var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match;
$selectable = this.menu.getTopSelectable();
data = this.menu.getSelectableData($selectable);
val = this.input.getInputValue();
if (data && !_.isBlankString(val) && !this.input.hasOverflow()) {
query = Input.normalizeQuery(val);
escapedQuery = _.escapeRegExChars(query);
frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i");
match = frontMatchRegEx.exec(data.val);
match && this.input.setHint(val + match[1]);
} else {
this.input.clearHint();
}
},
isEnabled: function isEnabled() {
return this.enabled;
},
enable: function enable() {
this.enabled = true;
},
disable: function disable() {
this.enabled = false;
},
isActive: function isActive() {
return this.active;
},
activate: function activate() {
if (this.isActive()) {
return true;
} else if (!this.isEnabled() || this.eventBus.before("active")) {
return false;
} else {
this.active = true;
this.eventBus.trigger("active");
return true;
}
},
deactivate: function deactivate() {
if (!this.isActive()) {
return true;
} else if (this.eventBus.before("idle")) {
return false;
} else {
this.active = false;
this.close();
this.eventBus.trigger("idle");
return true;
}
},
isOpen: function isOpen() {
return this.menu.isOpen();
},
open: function open() {
if (!this.isOpen() && !this.eventBus.before("open")) {
this.menu.open();
this._updateHint();
this.eventBus.trigger("open");
}
return this.isOpen();
},
close: function close() {
if (this.isOpen() && !this.eventBus.before("close")) {
this.menu.close();
this.input.clearHint();
this.input.resetInputValue();
this.eventBus.trigger("close");
}
return !this.isOpen();
},
setVal: function setVal(val) {
this.input.setQuery(_.toStr(val));
},
getVal: function getVal() {
return this.input.getQuery();
},
select: function select($selectable) {
var data = this.menu.getSelectableData($selectable);
if (data && !this.eventBus.before("select", data.obj)) {
this.input.setQuery(data.val, true);
this.eventBus.trigger("select", data.obj);
this.close();
return true;
}
return false;
},
autocomplete: function autocomplete($selectable) {
var query, data, isValid;
query = this.input.getQuery();
data = this.menu.getSelectableData($selectable);
isValid = data && query !== data.val;
if (isValid && !this.eventBus.before("autocomplete", data.obj)) {
this.input.setQuery(data.val);
this.eventBus.trigger("autocomplete", data.obj);
return true;
}
return false;
},
moveCursor: function moveCursor(delta) {
var query, $candidate, data, payload, cancelMove;
query = this.input.getQuery();
$candidate = this.menu.selectableRelativeToCursor(delta);
data = this.menu.getSelectableData($candidate);
payload = data ? data.obj : null;
cancelMove = this._minLengthMet() && this.menu.update(query);
if (!cancelMove && !this.eventBus.before("cursorchange", payload)) {
this.menu.setCursor($candidate);
if (data) {
this.input.setInputValue(data.val);
} else {
this.input.resetInputValue();
this._updateHint();
}
this.eventBus.trigger("cursorchange", payload);
return true;
}
return false;
},
destroy: function destroy() {
this.input.destroy();
this.menu.destroy();
}
});
return Typeahead;
function c(ctx) {
var methods = [].slice.call(arguments, 1);
return function() {
var args = [].slice.call(arguments);
_.each(methods, function(method) {
return ctx[method].apply(ctx, args);
});
};
}
}();
(function() {
"use strict";
var old, keys, methods;
old = $.fn.typeahead;
keys = {
www: "tt-www",
attrs: "tt-attrs",
typeahead: "tt-typeahead"
};
methods = {
initialize: function initialize(o, datasets) {
var www;
datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1);
o = o || {};
www = WWW(o.classNames);
return this.each(attach);
function attach() {
var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor;
_.each(datasets, function(d) {
d.highlight = !!o.highlight;
});
$input = $(this);
$wrapper = $(www.html.wrapper);
$hint = $elOrNull(o.hint);
$menu = $elOrNull(o.menu);
defaultHint = o.hint !== false && !$hint;
defaultMenu = o.menu !== false && !$menu;
defaultHint && ($hint = buildHintFromInput($input, www));
defaultMenu && ($menu = $(www.html.menu).css(www.css.menu));
$hint && $hint.val("");
$input = prepInput($input, www);
if (defaultHint || defaultMenu) {
$wrapper.css(www.css.wrapper);
$input.css(defaultHint ? www.css.input : www.css.inputWithNoHint);
$input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null);
}
MenuConstructor = defaultMenu ? DefaultMenu : Menu;
eventBus = new EventBus({
el: $input
});
input = new Input({
hint: $hint,
input: $input
}, www);
menu = new MenuConstructor({
node: $menu,
datasets: datasets
}, www);
typeahead = new Typeahead({
input: input,
menu: menu,
eventBus: eventBus,
minLength: o.minLength
}, www);
$input.data(keys.www, www);
$input.data(keys.typeahead, typeahead);
}
},
isEnabled: function isEnabled() {
var enabled;
ttEach(this.first(), function(t) {
enabled = t.isEnabled();
});
return enabled;
},
enable: function enable() {
ttEach(this, function(t) {
t.enable();
});
return this;
},
disable: function disable() {
ttEach(this, function(t) {
t.disable();
});
return this;
},
isActive: function isActive() {
var active;
ttEach(this.first(), function(t) {
active = t.isActive();
});
return active;
},
activate: function activate() {
ttEach(this, function(t) {
t.activate();
});
return this;
},
deactivate: function deactivate() {
ttEach(this, function(t) {
t.deactivate();
});
return this;
},
isOpen: function isOpen() {
var open;
ttEach(this.first(), function(t) {
open = t.isOpen();
});
return open;
},
open: function open() {
ttEach(this, function(t) {
t.open();
});
return this;
},
close: function close() {
ttEach(this, function(t) {
t.close();
});
return this;
},
select: function select(el) {
var success = false, $el = $(el);
ttEach(this.first(), function(t) {
success = t.select($el);
});
return success;
},
autocomplete: function autocomplete(el) {
var success = false, $el = $(el);
ttEach(this.first(), function(t) {
success = t.autocomplete($el);
});
return success;
},
moveCursor: function moveCursoe(delta) {
var success = false;
ttEach(this.first(), function(t) {
success = t.moveCursor(delta);
});
return success;
},
val: function val(newVal) {
var query;
if (!arguments.length) {
ttEach(this.first(), function(t) {
query = t.getVal();
});
return query;
} else {
ttEach(this, function(t) {
t.setVal(newVal);
});
return this;
}
},
destroy: function destroy() {
ttEach(this, function(typeahead, $input) {
revert($input);
typeahead.destroy();
});
return this;
}
};
$.fn.typeahead = function(method) {
if (methods[method]) {
return methods[method].apply(this, [].slice.call(arguments, 1));
} else {
return methods.initialize.apply(this, arguments);
}
};
$.fn.typeahead.noConflict = function noConflict() {
$.fn.typeahead = old;
return this;
};
function ttEach($els, fn) {
$els.each(function() {
var $input = $(this), typeahead;
(typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input);
});
}
function buildHintFromInput($input, www) {
return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop("readonly", true).removeAttr("id name placeholder required").attr({
autocomplete: "off",
spellcheck: "false",
tabindex: -1
});
}
function prepInput($input, www) {
$input.data(keys.attrs, {
dir: $input.attr("dir"),
autocomplete: $input.attr("autocomplete"),
spellcheck: $input.attr("spellcheck"),
style: $input.attr("style")
});
$input.addClass(www.classes.input).attr({
autocomplete: "off",
spellcheck: false
});
try {
!$input.attr("dir") && $input.attr("dir", "auto");
} catch (e) {}
return $input;
}
function getBackgroundStyles($el) {
return {
backgroundAttachment: $el.css("background-attachment"),
backgroundClip: $el.css("background-clip"),
backgroundColor: $el.css("background-color"),
backgroundImage: $el.css("background-image"),
backgroundOrigin: $el.css("background-origin"),
backgroundPosition: $el.css("background-position"),
backgroundRepeat: $el.css("background-repeat"),
backgroundSize: $el.css("background-size")
};
}
function revert($input) {
var www, $wrapper;
www = $input.data(keys.www);
$wrapper = $input.parent().filter(www.selectors.wrapper);
_.each($input.data(keys.attrs), function(val, key) {
_.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
});
$input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input);
if ($wrapper.length) {
$input.detach().insertAfter($wrapper);
$wrapper.remove();
}
}
function $elOrNull(obj) {
var isValid, $el;
isValid = _.isJQuery(obj) || _.isElement(obj);
$el = isValid ? $(obj).first() : [];
return $el.length ? $el : null;
}
})();
});
;/*})'"*/
;/*})'"*/
Drupal.wysiwyg = Drupal.wysiwyg || { 'instances': {}, 'excludeIdSelectors': { 'tokens': ['[id^="token-"]'] } };
Drupal.wysiwyg.editor = Drupal.wysiwyg.editor || { 'init': {}, 'update': {}, 'attach': {}, 'detach': {}, 'instance': {} };
Drupal.wysiwyg.plugins = Drupal.wysiwyg.plugins || {};
(function ($) {
// Determine support for queryCommandEnabled().
// An exception should be thrown for non-existing commands.
// Safari and Chrome (WebKit based) return -1 instead.
try {
document.queryCommandEnabled('__wysiwygTestCommand');
$.support.queryCommandEnabled = false;
}
catch (error) {
$.support.queryCommandEnabled = true;
}
})(jQuery);
;/*})'"*/
;/*})'"*/
/*!
Chosen, a Select Box Enhancer for jQuery and Prototype
by Patrick Filler for Harvest, http://getharvest.com
Version 1.8.7
Full source at https://github.com/harvesthq/chosen
Copyright (c) 2011-2018 Harvest http://getharvest.com
MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
This file is generated by `grunt build`, do not edit it by hand.
*/
(function() {
var $, AbstractChosen, Chosen, SelectParser,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
SelectParser = (function() {
function SelectParser() {
this.options_index = 0;
this.parsed = [];
}
SelectParser.prototype.add_node = function(child) {
if (child.nodeName.toUpperCase() === "OPTGROUP") {
return this.add_group(child);
} else {
return this.add_option(child);
}
};
SelectParser.prototype.add_group = function(group) {
var group_position, i, len, option, ref, results1;
group_position = this.parsed.length;
this.parsed.push({
array_index: group_position,
group: true,
label: group.label,
title: group.title ? group.title : void 0,
children: 0,
disabled: group.disabled,
classes: group.className
});
ref = group.childNodes;
results1 = [];
for (i = 0, len = ref.length; i < len; i++) {
option = ref[i];
results1.push(this.add_option(option, group_position, group.disabled));
}
return results1;
};
SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
if (option.nodeName.toUpperCase() === "OPTION") {
if (option.text !== "") {
if (group_position != null) {
this.parsed[group_position].children += 1;
}
this.parsed.push({
array_index: this.parsed.length,
options_index: this.options_index,
value: option.value,
text: option.text,
html: option.innerHTML,
title: option.title ? option.title : void 0,
selected: option.selected,
disabled: group_disabled === true ? group_disabled : option.disabled,
group_array_index: group_position,
group_label: group_position != null ? this.parsed[group_position].label : null,
classes: option.className,
style: option.style.cssText
});
} else {
this.parsed.push({
array_index: this.parsed.length,
options_index: this.options_index,
empty: true
});
}
return this.options_index += 1;
}
};
return SelectParser;
})();
SelectParser.select_to_array = function(select) {
var child, i, len, parser, ref;
parser = new SelectParser();
ref = select.childNodes;
for (i = 0, len = ref.length; i < len; i++) {
child = ref[i];
parser.add_node(child);
}
return parser.parsed;
};
AbstractChosen = (function() {
function AbstractChosen(form_field, options1) {
this.form_field = form_field;
this.options = options1 != null ? options1 : {};
this.label_click_handler = bind(this.label_click_handler, this);
if (!AbstractChosen.browser_is_supported()) {
return;
}
this.is_multiple = this.form_field.multiple;
this.set_default_text();
this.set_default_values();
this.setup();
this.set_up_html();
this.register_observers();
this.on_ready();
}
AbstractChosen.prototype.set_default_values = function() {
this.click_test_action = (function(_this) {
return function(evt) {
return _this.test_active_click(evt);
};
})(this);
this.activate_action = (function(_this) {
return function(evt) {
return _this.activate_field(evt);
};
})(this);
this.active_field = false;
this.mouse_on_container = false;
this.results_showing = false;
this.result_highlighted = null;
this.is_rtl = this.options.rtl || /\bchosen-rtl\b/.test(this.form_field.className);
this.allow_single_deselect = (this.options.allow_single_deselect != null) && (this.form_field.options[0] != null) && this.form_field.options[0].text === "" ? this.options.allow_single_deselect : false;
this.disable_search_threshold = this.options.disable_search_threshold || 0;
this.disable_search = this.options.disable_search || false;
this.enable_split_word_search = this.options.enable_split_word_search != null ? this.options.enable_split_word_search : true;
this.group_search = this.options.group_search != null ? this.options.group_search : true;
this.search_contains = this.options.search_contains || false;
this.single_backstroke_delete = this.options.single_backstroke_delete != null ? this.options.single_backstroke_delete : true;
this.max_selected_options = this.options.max_selected_options || Infinity;
this.inherit_select_classes = this.options.inherit_select_classes || false;
this.display_selected_options = this.options.display_selected_options != null ? this.options.display_selected_options : true;
this.display_disabled_options = this.options.display_disabled_options != null ? this.options.display_disabled_options : true;
this.include_group_label_in_selected = this.options.include_group_label_in_selected || false;
this.max_shown_results = this.options.max_shown_results || Number.POSITIVE_INFINITY;
this.case_sensitive_search = this.options.case_sensitive_search || false;
return this.hide_results_on_select = this.options.hide_results_on_select != null ? this.options.hide_results_on_select : false;
};
AbstractChosen.prototype.set_default_text = function() {
if (this.form_field.getAttribute("data-placeholder")) {
this.default_text = this.form_field.getAttribute("data-placeholder");
} else if (this.is_multiple) {
this.default_text = this.options.placeholder_text_multiple || this.options.placeholder_text || AbstractChosen.default_multiple_text;
if(this.form_field.id=='edit-field-tag-und') this.default_text = this.options.placeholder_text_multiple_group;
if(this.form_field.id=='edit-field-tags-und') this.default_text = this.options.placeholder_text_multiple_tags;
} else {
this.default_text = this.options.placeholder_text_single || this.options.placeholder_text || AbstractChosen.default_single_text;
}
this.default_text = this.escape_html(this.default_text);
return this.results_none_found = this.form_field.getAttribute("data-no_results_text") || this.options.no_results_text || AbstractChosen.default_no_result_text;
};
AbstractChosen.prototype.choice_label = function(item) {
if (this.include_group_label_in_selected && (item.group_label != null)) {
return "
" + (this.escape_html(item.group_label)) + "" + item.html;
} else {
return item.html;
}
};
AbstractChosen.prototype.mouse_enter = function() {
return this.mouse_on_container = true;
};
AbstractChosen.prototype.mouse_leave = function() {
return this.mouse_on_container = false;
};
AbstractChosen.prototype.input_focus = function(evt) {
if (this.is_multiple) {
if (!this.active_field) {
return setTimeout(((function(_this) {
return function() {
return _this.container_mousedown();
};
})(this)), 50);
}
} else {
if (!this.active_field) {
return this.activate_field();
}
}
};
AbstractChosen.prototype.input_blur = function(evt) {
if (!this.mouse_on_container) {
this.active_field = false;
return setTimeout(((function(_this) {
return function() {
return _this.blur_test();
};
})(this)), 100);
}
};
AbstractChosen.prototype.label_click_handler = function(evt) {
if (this.is_multiple) {
return this.container_mousedown(evt);
} else {
return this.activate_field();
}
};
AbstractChosen.prototype.results_option_build = function(options) {
var content, data, data_content, i, len, ref, shown_results;
content = '';
shown_results = 0;
ref = this.results_data;
for (i = 0, len = ref.length; i < len; i++) {
data = ref[i];
data_content = '';
if (data.group) {
data_content = this.result_add_group(data);
} else {
data_content = this.result_add_option(data);
}
if (data_content !== '') {
shown_results++;
content += data_content;
}
if (options != null ? options.first : void 0) {
if (data.selected && this.is_multiple) {
this.choice_build(data);
} else if (data.selected && !this.is_multiple) {
this.single_set_selected_text(this.choice_label(data));
}
}
if (shown_results >= this.max_shown_results) {
break;
}
}
return content;
};
AbstractChosen.prototype.result_add_option = function(option) {
var classes, option_el;
if (!option.search_match) {
return '';
}
if (!this.include_option_in_results(option)) {
return '';
}
classes = [];
if (!option.disabled && !(option.selected && this.is_multiple)) {
classes.push("active-result");
}
if (option.disabled && !(option.selected && this.is_multiple)) {
classes.push("disabled-result");
}
if (option.selected) {
classes.push("result-selected");
}
if (option.group_array_index != null) {
classes.push("group-option");
}
if (option.classes !== "") {
classes.push(option.classes);
}
option_el = document.createElement("li");
option_el.className = classes.join(" ");
if (option.style) {
option_el.style.cssText = option.style;
}
option_el.setAttribute("data-option-array-index", option.array_index);
option_el.innerHTML = option.highlighted_html || option.html;
if (option.title) {
option_el.title = option.title;
}
return this.outerHTML(option_el);
};
AbstractChosen.prototype.result_add_group = function(group) {
var classes, group_el;
if (!(group.search_match || group.group_match)) {
return '';
}
if (!(group.active_options > 0)) {
return '';
}
classes = [];
classes.push("group-result");
if (group.classes) {
classes.push(group.classes);
}
group_el = document.createElement("li");
group_el.className = classes.join(" ");
group_el.innerHTML = group.highlighted_html || this.escape_html(group.label);
if (group.title) {
group_el.title = group.title;
}
return this.outerHTML(group_el);
};
AbstractChosen.prototype.results_update_field = function() {
this.set_default_text();
if (!this.is_multiple) {
this.results_reset_cleanup();
}
this.result_clear_highlight();
this.results_build();
if (this.results_showing) {
return this.winnow_results();
}
};
AbstractChosen.prototype.reset_single_select_options = function() {
var i, len, ref, result, results1;
ref = this.results_data;
results1 = [];
for (i = 0, len = ref.length; i < len; i++) {
result = ref[i];
if (result.selected) {
results1.push(result.selected = false);
} else {
results1.push(void 0);
}
}
return results1;
};
AbstractChosen.prototype.results_toggle = function() {
if (this.results_showing) {
return this.results_hide();
} else {
return this.results_show();
}
};
AbstractChosen.prototype.results_search = function(evt) {
if (this.results_showing) {
return this.winnow_results();
} else {
return this.results_show();
}
};
AbstractChosen.prototype.winnow_results = function(options) {
var escapedQuery, fix, i, len, option, prefix, query, ref, regex, results, results_group, search_match, startpos, suffix, text;
this.no_results_clear();
results = 0;
query = this.get_search_text();
escapedQuery = query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
regex = this.get_search_regex(escapedQuery);
ref = this.results_data;
for (i = 0, len = ref.length; i < len; i++) {
option = ref[i];
option.search_match = false;
results_group = null;
search_match = null;
option.highlighted_html = '';
if (this.include_option_in_results(option)) {
if (option.group) {
option.group_match = false;
option.active_options = 0;
}
if ((option.group_array_index != null) && this.results_data[option.group_array_index]) {
results_group = this.results_data[option.group_array_index];
if (results_group.active_options === 0 && results_group.search_match) {
results += 1;
}
results_group.active_options += 1;
}
text = option.group ? option.label : option.text;
if (!(option.group && !this.group_search)) {
search_match = this.search_string_match(text, regex);
option.search_match = search_match != null;
if (option.search_match && !option.group) {
results += 1;
}
if (option.search_match) {
if (query.length) {
startpos = search_match.index;
prefix = text.slice(0, startpos);
fix = text.slice(startpos, startpos + query.length);
suffix = text.slice(startpos + query.length);
option.highlighted_html = (this.escape_html(prefix)) + "
" + (this.escape_html(fix)) + "" + (this.escape_html(suffix));
}
if (results_group != null) {
results_group.group_match = true;
}
} else if ((option.group_array_index != null) && this.results_data[option.group_array_index].search_match) {
option.search_match = true;
}
}
}
}
this.result_clear_highlight();
if (results < 1 && query.length) {
this.update_results_content("");
return this.no_results(query);
} else {
this.update_results_content(this.results_option_build());
if (!(options != null ? options.skip_highlight : void 0)) {
return this.winnow_results_set_highlight();
}
}
};
AbstractChosen.prototype.get_search_regex = function(escaped_search_string) {
var regex_flag, regex_string;
regex_string = this.search_contains ? escaped_search_string : "(^|\\s|\\b)" + escaped_search_string + "[^\\s]*";
if (!(this.enable_split_word_search || this.search_contains)) {
regex_string = "^" + regex_string;
}
regex_flag = this.case_sensitive_search ? "" : "i";
return new RegExp(regex_string, regex_flag);
};
AbstractChosen.prototype.search_string_match = function(search_string, regex) {
var match;
match = regex.exec(search_string);
if (!this.search_contains && (match != null ? match[1] : void 0)) {
match.index += 1;
}
return match;
};
AbstractChosen.prototype.choices_count = function() {
var i, len, option, ref;
if (this.selected_option_count != null) {
return this.selected_option_count;
}
this.selected_option_count = 0;
ref = this.form_field.options;
for (i = 0, len = ref.length; i < len; i++) {
option = ref[i];
if (option.selected) {
this.selected_option_count += 1;
}
}
return this.selected_option_count;
};
AbstractChosen.prototype.choices_click = function(evt) {
evt.preventDefault();
this.activate_field();
if (!(this.results_showing || this.is_disabled)) {
return this.results_show();
}
};
AbstractChosen.prototype.keydown_checker = function(evt) {
var ref, stroke;
stroke = (ref = evt.which) != null ? ref : evt.keyCode;
this.search_field_scale();
if (stroke !== 8 && this.pending_backstroke) {
this.clear_backstroke();
}
switch (stroke) {
case 8:
this.backstroke_length = this.get_search_field_value().length;
break;
case 9:
if (this.results_showing && !this.is_multiple) {
this.result_select(evt);
}
this.mouse_on_container = false;
break;
case 13:
if (this.results_showing) {
evt.preventDefault();
}
break;
case 27:
if (this.results_showing) {
evt.preventDefault();
}
break;
case 32:
if (this.disable_search) {
evt.preventDefault();
}
break;
case 38:
evt.preventDefault();
this.keyup_arrow();
break;
case 40:
evt.preventDefault();
this.keydown_arrow();
break;
}
};
AbstractChosen.prototype.keyup_checker = function(evt) {
var ref, stroke;
stroke = (ref = evt.which) != null ? ref : evt.keyCode;
this.search_field_scale();
switch (stroke) {
case 8:
if (this.is_multiple && this.backstroke_length < 1 && this.choices_count() > 0) {
this.keydown_backstroke();
} else if (!this.pending_backstroke) {
this.result_clear_highlight();
this.results_search();
}
break;
case 13:
evt.preventDefault();
if (this.results_showing) {
this.result_select(evt);
}
break;
case 27:
if (this.results_showing) {
this.results_hide();
}
break;
case 9:
case 16:
case 17:
case 18:
case 38:
case 40:
case 91:
break;
default:
this.results_search();
break;
}
};
AbstractChosen.prototype.clipboard_event_checker = function(evt) {
if (this.is_disabled) {
return;
}
return setTimeout(((function(_this) {
return function() {
return _this.results_search();
};
})(this)), 50);
};
AbstractChosen.prototype.container_width = function() {
if (this.options.width != null) {
return this.options.width;
} else {
return this.form_field.offsetWidth + "px";
}
};
AbstractChosen.prototype.include_option_in_results = function(option) {
if (this.is_multiple && (!this.display_selected_options && option.selected)) {
return false;
}
if (!this.display_disabled_options && option.disabled) {
return false;
}
if (option.empty) {
return false;
}
return true;
};
AbstractChosen.prototype.search_results_touchstart = function(evt) {
this.touch_started = true;
return this.search_results_mouseover(evt);
};
AbstractChosen.prototype.search_results_touchmove = function(evt) {
this.touch_started = false;
return this.search_results_mouseout(evt);
};
AbstractChosen.prototype.search_results_touchend = function(evt) {
if (this.touch_started) {
return this.search_results_mouseup(evt);
}
};
AbstractChosen.prototype.outerHTML = function(element) {
var tmp;
if (element.outerHTML) {
return element.outerHTML;
}
tmp = document.createElement("div");
tmp.appendChild(element);
return tmp.innerHTML;
};
AbstractChosen.prototype.get_single_html = function() {
return "
\n " + this.default_text + "\n
\n\n
";
};
AbstractChosen.prototype.get_multi_html = function() {
return "
\n
";
};
AbstractChosen.prototype.get_no_results_html = function(terms) {
return "
\n " + this.results_none_found + " " + (this.escape_html(terms)) + "\n";
};
AbstractChosen.browser_is_supported = function() {
if ("Microsoft Internet Explorer" === window.navigator.appName) {
return document.documentMode >= 8;
}
return true;
if (/iP(od|hone)/i.test(window.navigator.userAgent) || /IEMobile/i.test(window.navigator.userAgent) || /Windows Phone/i.test(window.navigator.userAgent) || /BlackBerry/i.test(window.navigator.userAgent) || /BB10/i.test(window.navigator.userAgent) || /Android.*Mobile/i.test(window.navigator.userAgent)) {
return false;
}
return true;
};
AbstractChosen.default_multiple_text = "Select Some Options";
AbstractChosen.default_single_text = "Select an Option";
AbstractChosen.default_no_result_text = "No results match";
return AbstractChosen;
})();
$ = jQuery;
$.fn.extend({
chosen: function(options) {
if (!AbstractChosen.browser_is_supported()) {
return this;
}
return this.each(function(input_field) {
var $this, chosen;
$this = $(this);
chosen = $this.data('chosen');
if (options === 'destroy') {
if (chosen instanceof Chosen) {
chosen.destroy();
}
return;
}
if (!(chosen instanceof Chosen)) {
$this.data('chosen', new Chosen(this, options));
}
});
}
});
Chosen = (function(superClass) {
extend(Chosen, superClass);
function Chosen() {
return Chosen.__super__.constructor.apply(this, arguments);
}
Chosen.prototype.setup = function() {
this.form_field_jq = $(this.form_field);
return this.current_selectedIndex = this.form_field.selectedIndex;
};
Chosen.prototype.set_up_html = function() {
var container_classes, container_props;
container_classes = ["chosen-container"];
container_classes.push("chosen-container-" + (this.is_multiple ? "multi" : "single"));
if (this.inherit_select_classes && this.form_field.className) {
container_classes.push(this.form_field.className);
}
if (this.is_rtl) {
container_classes.push("chosen-rtl");
}
container_props = {
'class': container_classes.join(' '),
'title': this.form_field.title
};
if (this.form_field.id.length) {
container_props.id = this.form_field.id.replace(/[^\w]/g, '_') + "_chosen";
}
this.container = $("
", container_props);
this.container.width(this.container_width());
if (this.is_multiple) {
this.container.html(this.get_multi_html());
} else {
this.container.html(this.get_single_html());
}
this.form_field_jq.hide().after(this.container);
this.dropdown = this.container.find('div.chosen-drop').first();
this.search_field = this.container.find('input').first();
this.search_results = this.container.find('ul.chosen-results').first();
this.search_field_scale();
this.search_no_results = this.container.find('li.no-results').first();
if (this.is_multiple) {
this.search_choices = this.container.find('ul.chosen-choices').first();
this.search_container = this.container.find('li.search-field').first();
} else {
this.search_container = this.container.find('div.chosen-search').first();
this.selected_item = this.container.find('.chosen-single').first();
}
this.results_build();
this.set_tab_index();
return this.set_label_behavior();
};
Chosen.prototype.on_ready = function() {
return this.form_field_jq.trigger("chosen:ready", {
chosen: this
});
};
Chosen.prototype.register_observers = function() {
this.container.on('touchstart.chosen', (function(_this) {
return function(evt) {
_this.container_mousedown(evt);
};
})(this));
this.container.on('touchend.chosen', (function(_this) {
return function(evt) {
_this.container_mouseup(evt);
};
})(this));
this.container.on('mousedown.chosen', (function(_this) {
return function(evt) {
_this.container_mousedown(evt);
};
})(this));
this.container.on('mouseup.chosen', (function(_this) {
return function(evt) {
_this.container_mouseup(evt);
};
})(this));
this.container.on('mouseenter.chosen', (function(_this) {
return function(evt) {
_this.mouse_enter(evt);
};
})(this));
this.container.on('mouseleave.chosen', (function(_this) {
return function(evt) {
_this.mouse_leave(evt);
};
})(this));
this.search_results.on('mouseup.chosen', (function(_this) {
return function(evt) {
_this.search_results_mouseup(evt);
};
})(this));
this.search_results.on('mouseover.chosen', (function(_this) {
return function(evt) {
_this.search_results_mouseover(evt);
};
})(this));
this.search_results.on('mouseout.chosen', (function(_this) {
return function(evt) {
_this.search_results_mouseout(evt);
};
})(this));
this.search_results.on('mousewheel.chosen DOMMouseScroll.chosen', (function(_this) {
return function(evt) {
_this.search_results_mousewheel(evt);
};
})(this));
this.search_results.on('touchstart.chosen', (function(_this) {
return function(evt) {
_this.search_results_touchstart(evt);
};
})(this));
this.search_results.on('touchmove.chosen', (function(_this) {
return function(evt) {
_this.search_results_touchmove(evt);
};
})(this));
this.search_results.on('touchend.chosen', (function(_this) {
return function(evt) {
_this.search_results_touchend(evt);
};
})(this));
this.form_field_jq.on("chosen:updated.chosen", (function(_this) {
return function(evt) {
_this.results_update_field(evt);
};
})(this));
this.form_field_jq.on("chosen:activate.chosen", (function(_this) {
return function(evt) {
_this.activate_field(evt);
};
})(this));
this.form_field_jq.on("chosen:open.chosen", (function(_this) {
return function(evt) {
_this.container_mousedown(evt);
};
})(this));
this.form_field_jq.on("chosen:close.chosen", (function(_this) {
return function(evt) {
_this.close_field(evt);
};
})(this));
this.search_field.on('blur.chosen', (function(_this) {
return function(evt) {
_this.input_blur(evt);
};
})(this));
this.search_field.on('keyup.chosen', (function(_this) {
return function(evt) {
_this.keyup_checker(evt);
};
})(this));
this.search_field.on('keydown.chosen', (function(_this) {
return function(evt) {
_this.keydown_checker(evt);
};
})(this));
this.search_field.on('focus.chosen', (function(_this) {
return function(evt) {
_this.input_focus(evt);
};
})(this));
this.search_field.on('cut.chosen', (function(_this) {
return function(evt) {
_this.clipboard_event_checker(evt);
};
})(this));
this.search_field.on('paste.chosen', (function(_this) {
return function(evt) {
_this.clipboard_event_checker(evt);
};
})(this));
if (this.is_multiple) {
return this.search_choices.on('click.chosen', (function(_this) {
return function(evt) {
_this.choices_click(evt);
};
})(this));
} else {
return this.container.on('click.chosen', function(evt) {
evt.preventDefault();
});
}
};
Chosen.prototype.destroy = function() {
$(this.container[0].ownerDocument).off('click.chosen', this.click_test_action);
if (this.form_field_label.length > 0) {
this.form_field_label.off('click.chosen');
}
if (this.search_field[0].tabIndex) {
this.form_field_jq[0].tabIndex = this.search_field[0].tabIndex;
}
this.container.remove();
this.form_field_jq.removeData('chosen');
return this.form_field_jq.show();
};
Chosen.prototype.search_field_disabled = function() {
this.is_disabled = this.form_field.disabled || this.form_field_jq.parents('fieldset').is(':disabled');
this.container.toggleClass('chosen-disabled', this.is_disabled);
this.search_field[0].disabled = this.is_disabled;
if (!this.is_multiple) {
this.selected_item.off('focus.chosen', this.activate_field);
}
if (this.is_disabled) {
return this.close_field();
} else if (!this.is_multiple) {
return this.selected_item.on('focus.chosen', this.activate_field);
}
};
Chosen.prototype.container_mousedown = function(evt) {
var ref;
if (this.is_disabled) {
return;
}
if (evt && ((ref = evt.type) === 'mousedown' || ref === 'touchstart') && !this.results_showing) {
evt.preventDefault();
}
if (!((evt != null) && ($(evt.target)).hasClass("search-choice-close"))) {
if (!this.active_field) {
if (this.is_multiple) {
this.search_field.val("");
}
$(this.container[0].ownerDocument).on('click.chosen', this.click_test_action);
this.results_show();
} else if (!this.is_multiple && evt && (($(evt.target)[0] === this.selected_item[0]) || $(evt.target).parents("a.chosen-single").length)) {
evt.preventDefault();
this.results_toggle();
}
return this.activate_field();
}
};
Chosen.prototype.container_mouseup = function(evt) {
if (evt.target.nodeName === "ABBR" && !this.is_disabled) {
return this.results_reset(evt);
}
};
Chosen.prototype.search_results_mousewheel = function(evt) {
var delta;
if (evt.originalEvent) {
delta = evt.originalEvent.deltaY || -evt.originalEvent.wheelDelta || evt.originalEvent.detail;
}
if (delta != null) {
evt.preventDefault();
if (evt.type === 'DOMMouseScroll') {
delta = delta * 40;
}
return this.search_results.scrollTop(delta + this.search_results.scrollTop());
}
};
Chosen.prototype.blur_test = function(evt) {
if (!this.active_field && this.container.hasClass("chosen-container-active")) {
return this.close_field();
}
};
Chosen.prototype.close_field = function() {
$(this.container[0].ownerDocument).off("click.chosen", this.click_test_action);
this.active_field = false;
this.results_hide();
this.container.removeClass("chosen-container-active");
this.clear_backstroke();
this.show_search_field_default();
this.search_field_scale();
return this.search_field.blur();
};
Chosen.prototype.activate_field = function() {
if (this.is_disabled) {
return;
}
this.container.addClass("chosen-container-active");
this.active_field = true;
this.search_field.val(this.search_field.val());
return this.search_field.focus();
};
Chosen.prototype.test_active_click = function(evt) {
var active_container;
active_container = $(evt.target).closest('.chosen-container');
if (active_container.length && this.container[0] === active_container[0]) {
return this.active_field = true;
} else {
return this.close_field();
}
};
Chosen.prototype.results_build = function() {
this.parsing = true;
this.selected_option_count = null;
this.results_data = SelectParser.select_to_array(this.form_field);
if (this.is_multiple) {
this.search_choices.find("li.search-choice").remove();
} else {
this.single_set_selected_text();
if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold) {
this.search_field[0].readOnly = true;
this.container.addClass("chosen-container-single-nosearch");
} else {
this.search_field[0].readOnly = false;
this.container.removeClass("chosen-container-single-nosearch");
}
}
this.update_results_content(this.results_option_build({
first: true
}));
this.search_field_disabled();
this.show_search_field_default();
this.search_field_scale();
return this.parsing = false;
};
Chosen.prototype.result_do_highlight = function(el) {
var high_bottom, high_top, maxHeight, visible_bottom, visible_top;
if (el.length) {
this.result_clear_highlight();
this.result_highlight = el;
this.result_highlight.addClass("highlighted");
maxHeight = parseInt(this.search_results.css("maxHeight"), 10);
visible_top = this.search_results.scrollTop();
visible_bottom = maxHeight + visible_top;
high_top = this.result_highlight.position().top + this.search_results.scrollTop();
high_bottom = high_top + this.result_highlight.outerHeight();
if (high_bottom >= visible_bottom) {
return this.search_results.scrollTop((high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0);
} else if (high_top < visible_top) {
return this.search_results.scrollTop(high_top);
}
}
};
Chosen.prototype.result_clear_highlight = function() {
if (this.result_highlight) {
this.result_highlight.removeClass("highlighted");
}
return this.result_highlight = null;
};
Chosen.prototype.results_show = function() {
if (this.is_multiple && this.max_selected_options <= this.choices_count()) {
this.form_field_jq.trigger("chosen:maxselected", {
chosen: this
});
return false;
}
this.container.addClass("chosen-with-drop");
this.results_showing = true;
this.search_field.focus();
this.search_field.val(this.get_search_field_value());
this.winnow_results();
return this.form_field_jq.trigger("chosen:showing_dropdown", {
chosen: this
});
};
Chosen.prototype.update_results_content = function(content) {
return this.search_results.html(content);
};
Chosen.prototype.results_hide = function() {
if (this.results_showing) {
this.result_clear_highlight();
this.container.removeClass("chosen-with-drop");
this.form_field_jq.trigger("chosen:hiding_dropdown", {
chosen: this
});
}
return this.results_showing = false;
};
Chosen.prototype.set_tab_index = function(el) {
var ti;
if (this.form_field.tabIndex) {
ti = this.form_field.tabIndex;
this.form_field.tabIndex = -1;
return this.search_field[0].tabIndex = ti;
}
};
Chosen.prototype.set_label_behavior = function() {
this.form_field_label = this.form_field_jq.parents("label");
if (!this.form_field_label.length && this.form_field.id.length) {
this.form_field_label = $("label[for='" + this.form_field.id + "']");
}
if (this.form_field_label.length > 0) {
return this.form_field_label.on('click.chosen', this.label_click_handler);
}
};
Chosen.prototype.show_search_field_default = function() {
if (this.is_multiple && this.choices_count() < 1 && !this.active_field) {
this.search_field.val(this.default_text);
return this.search_field.addClass("default");
} else {
this.search_field.val("");
return this.search_field.removeClass("default");
}
};
Chosen.prototype.search_results_mouseup = function(evt) {
var target;
target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
if (target.length) {
this.result_highlight = target;
this.result_select(evt);
return this.search_field.focus();
}
};
Chosen.prototype.search_results_mouseover = function(evt) {
var target;
target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
if (target) {
return this.result_do_highlight(target);
}
};
Chosen.prototype.search_results_mouseout = function(evt) {
if ($(evt.target).hasClass("active-result") || $(evt.target).parents('.active-result').first()) {
return this.result_clear_highlight();
}
};
Chosen.prototype.choice_build = function(item) {
var choice, close_link;
choice = $('
', {
"class": "search-choice"
}).html("
" + (this.choice_label(item)) + "");
if (item.disabled) {
choice.addClass('search-choice-disabled');
} else {
close_link = $('
', {
"class": 'search-choice-close',
'data-option-array-index': item.array_index
});
close_link.on('click.chosen', (function(_this) {
return function(evt) {
return _this.choice_destroy_link_click(evt);
};
})(this));
close_link.on('touchstart', (function(_this) {
return function(evt) {
return _this.choice_destroy_link_click(evt);
};
})(this));
choice.append(close_link);
}
return this.search_container.before(choice);
};
Chosen.prototype.choice_destroy_link_click = function(evt) {
evt.preventDefault();
evt.stopPropagation();
if (!this.is_disabled) {
return this.choice_destroy($(evt.target));
}
};
Chosen.prototype.choice_destroy = function(link) {
if (this.result_deselect(link[0].getAttribute("data-option-array-index"))) {
if (this.active_field) {
this.search_field.focus();
} else {
this.show_search_field_default();
}
if (this.is_multiple && this.choices_count() > 0 && this.get_search_field_value().length < 1) {
this.results_hide();
}
link.parents('li').first().remove();
return this.search_field_scale();
}
};
Chosen.prototype.results_reset = function() {
this.reset_single_select_options();
this.form_field.options[0].selected = true;
this.single_set_selected_text();
this.show_search_field_default();
this.results_reset_cleanup();
this.trigger_form_field_change();
if (this.active_field) {
return this.results_hide();
}
};
Chosen.prototype.results_reset_cleanup = function() {
this.current_selectedIndex = this.form_field.selectedIndex;
return this.selected_item.find("abbr").remove();
};
Chosen.prototype.result_select = function(evt) {
var high, item;
if (this.result_highlight) {
high = this.result_highlight;
this.result_clear_highlight();
if (this.is_multiple && this.max_selected_options <= this.choices_count()) {
this.form_field_jq.trigger("chosen:maxselected", {
chosen: this
});
return false;
}
if (this.is_multiple) {
high.removeClass("active-result");
} else {
this.reset_single_select_options();
}
high.addClass("result-selected");
item = this.results_data[high[0].getAttribute("data-option-array-index")];
item.selected = true;
this.form_field.options[item.options_index].selected = true;
this.selected_option_count = null;
if (this.is_multiple) {
this.choice_build(item);
} else {
this.single_set_selected_text(this.choice_label(item));
}
if (this.is_multiple && (!this.hide_results_on_select || (evt.metaKey || evt.ctrlKey))) {
if (evt.metaKey || evt.ctrlKey) {
this.winnow_results({
skip_highlight: true
});
} else {
this.search_field.val("");
this.winnow_results();
}
} else {
this.results_hide();
this.show_search_field_default();
}
if (this.is_multiple || this.form_field.selectedIndex !== this.current_selectedIndex) {
this.trigger_form_field_change({
selected: this.form_field.options[item.options_index].value
});
}
this.current_selectedIndex = this.form_field.selectedIndex;
evt.preventDefault();
return this.search_field_scale();
}
};
Chosen.prototype.single_set_selected_text = function(text) {
if (text == null) {
text = this.default_text;
}
if (text === this.default_text) {
this.selected_item.addClass("chosen-default");
} else {
this.single_deselect_control_build();
this.selected_item.removeClass("chosen-default");
}
return this.selected_item.find("span").html(text);
};
Chosen.prototype.result_deselect = function(pos) {
var result_data;
result_data = this.results_data[pos];
if (!this.form_field.options[result_data.options_index].disabled) {
result_data.selected = false;
this.form_field.options[result_data.options_index].selected = false;
this.selected_option_count = null;
this.result_clear_highlight();
if (this.results_showing) {
this.winnow_results();
}
this.trigger_form_field_change({
deselected: this.form_field.options[result_data.options_index].value
});
this.search_field_scale();
return true;
} else {
return false;
}
};
Chosen.prototype.single_deselect_control_build = function() {
if (!this.allow_single_deselect) {
return;
}
if (!this.selected_item.find("abbr").length) {
this.selected_item.find("span").first().after("
");
}
return this.selected_item.addClass("chosen-single-with-deselect");
};
Chosen.prototype.get_search_field_value = function() {
return this.search_field.val();
};
Chosen.prototype.get_search_text = function() {
return $.trim(this.get_search_field_value());
};
Chosen.prototype.escape_html = function(text) {
return $('
').text(text).html();
};
Chosen.prototype.winnow_results_set_highlight = function() {
var do_high, selected_results;
selected_results = !this.is_multiple ? this.search_results.find(".result-selected.active-result") : [];
do_high = selected_results.length ? selected_results.first() : this.search_results.find(".active-result").first();
if (do_high != null) {
return this.result_do_highlight(do_high);
}
};
Chosen.prototype.no_results = function(terms) {
var no_results_html;
no_results_html = this.get_no_results_html(terms);
this.search_results.append(no_results_html);
return this.form_field_jq.trigger("chosen:no_results", {
chosen: this
});
};
Chosen.prototype.no_results_clear = function() {
return this.search_results.find(".no-results").remove();
};
Chosen.prototype.keydown_arrow = function() {
var next_sib;
if (this.results_showing && this.result_highlight) {
next_sib = this.result_highlight.nextAll("li.active-result").first();
if (next_sib) {
return this.result_do_highlight(next_sib);
}
} else {
return this.results_show();
}
};
Chosen.prototype.keyup_arrow = function() {
var prev_sibs;
if (!this.results_showing && !this.is_multiple) {
return this.results_show();
} else if (this.result_highlight) {
prev_sibs = this.result_highlight.prevAll("li.active-result");
if (prev_sibs.length) {
return this.result_do_highlight(prev_sibs.first());
} else {
if (this.choices_count() > 0) {
this.results_hide();
}
return this.result_clear_highlight();
}
}
};
Chosen.prototype.keydown_backstroke = function() {
var next_available_destroy;
if (this.pending_backstroke) {
this.choice_destroy(this.pending_backstroke.find("a").first());
return this.clear_backstroke();
} else {
next_available_destroy = this.search_container.siblings("li.search-choice").last();
if (next_available_destroy.length && !next_available_destroy.hasClass("search-choice-disabled")) {
this.pending_backstroke = next_available_destroy;
if (this.single_backstroke_delete) {
return this.keydown_backstroke();
} else {
return this.pending_backstroke.addClass("search-choice-focus");
}
}
}
};
Chosen.prototype.clear_backstroke = function() {
if (this.pending_backstroke) {
this.pending_backstroke.removeClass("search-choice-focus");
}
return this.pending_backstroke = null;
};
Chosen.prototype.search_field_scale = function() {
var div, i, len, style, style_block, styles, width;
if (!this.is_multiple) {
return;
}
style_block = {
position: 'absolute',
left: '-1000px',
top: '-1000px',
display: 'none',
whiteSpace: 'pre'
};
styles = ['fontSize', 'fontStyle', 'fontWeight', 'fontFamily', 'lineHeight', 'textTransform', 'letterSpacing'];
for (i = 0, len = styles.length; i < len; i++) {
style = styles[i];
style_block[style] = this.search_field.css(style);
}
div = $('
').css(style_block);
div.text(this.get_search_field_value());
$('body').append(div);
width = div.width() + 25;
div.remove();
if (this.container.is(':visible')) {
width = Math.min(this.container.outerWidth() - 10, width);
}
return this.search_field.width(width);
};
Chosen.prototype.trigger_form_field_change = function(extra) {
this.form_field_jq.trigger("input", extra);
return this.form_field_jq.trigger("change", extra);
};
return Chosen;
})(AbstractChosen);
}).call(this);
;/*})'"*/
;/*})'"*/
/**
* @license MIT
*/
(function(window, document, undefined) {'use strict';
// ie10+
var ie10plus = window.navigator.msPointerEnabled;
/**
* Flow.js is a library providing multiple simultaneous, stable and
* resumable uploads via the HTML5 File API.
* @param [opts]
* @param {number} [opts.chunkSize]
* @param {bool} [opts.forceChunkSize]
* @param {number} [opts.simultaneousUploads]
* @param {bool} [opts.singleFile]
* @param {string} [opts.fileParameterName]
* @param {number} [opts.progressCallbacksInterval]
* @param {number} [opts.speedSmoothingFactor]
* @param {Object|Function} [opts.query]
* @param {Object|Function} [opts.headers]
* @param {bool} [opts.withCredentials]
* @param {Function} [opts.preprocess]
* @param {string} [opts.method]
* @param {string|Function} [opts.testMethod]
* @param {string|Function} [opts.uploadMethod]
* @param {bool} [opts.prioritizeFirstAndLastChunk]
* @param {bool} [opts.allowDuplicateUploads]
* @param {string|Function} [opts.target]
* @param {number} [opts.maxChunkRetries]
* @param {number} [opts.chunkRetryInterval]
* @param {Array.
} [opts.permanentErrors]
* @param {Array.} [opts.successStatuses]
* @param {Function} [opts.initFileFn]
* @param {Function} [opts.readFileFn]
* @param {Function} [opts.generateUniqueIdentifier]
* @constructor
*/
function Flow(opts) {
/**
* Supported by browser?
* @type {boolean}
*/
this.support = (
typeof File !== 'undefined' &&
typeof Blob !== 'undefined' &&
typeof FileList !== 'undefined' &&
(
!!Blob.prototype.slice || !!Blob.prototype.webkitSlice || !!Blob.prototype.mozSlice ||
false
) // slicing files support
);
if (!this.support) {
return ;
}
/**
* Check if directory upload is supported
* @type {boolean}
*/
this.supportDirectory = /Chrome/.test(window.navigator.userAgent);
/**
* List of FlowFile objects
* @type {Array.}
*/
this.files = [];
/**
* Default options for flow.js
* @type {Object}
*/
this.defaults = {
chunkSize: 1024 * 1024,
forceChunkSize: false,
simultaneousUploads: 3,
singleFile: false,
fileParameterName: 'file',
progressCallbacksInterval: 500,
speedSmoothingFactor: 0.1,
query: {},
headers: {},
withCredentials: false,
preprocess: null,
method: 'multipart',
testMethod: 'GET',
uploadMethod: 'POST',
prioritizeFirstAndLastChunk: false,
allowDuplicateUploads: false,
target: '/',
testChunks: true,
generateUniqueIdentifier: null,
maxChunkRetries: 0,
chunkRetryInterval: null,
permanentErrors: [404, 413, 415, 500, 501],
successStatuses: [200, 201, 202],
onDropStopPropagation: false,
initFileFn: null,
readFileFn: webAPIFileRead
};
/**
* Current options
* @type {Object}
*/
this.opts = {};
/**
* List of events:
* key stands for event name
* value array list of callbacks
* @type {}
*/
this.events = {};
var $ = this;
/**
* On drop event
* @function
* @param {MouseEvent} event
*/
this.onDrop = function (event) {
if ($.opts.onDropStopPropagation) {
event.stopPropagation();
}
event.preventDefault();
var dataTransfer = event.dataTransfer;
if (dataTransfer.items && dataTransfer.items[0] &&
dataTransfer.items[0].webkitGetAsEntry) {
$.webkitReadDataTransfer(event);
} else {
$.addFiles(dataTransfer.files, event);
}
};
/**
* Prevent default
* @function
* @param {MouseEvent} event
*/
this.preventEvent = function (event) {
event.preventDefault();
};
/**
* Current options
* @type {Object}
*/
this.opts = Flow.extend({}, this.defaults, opts || {});
}
Flow.prototype = {
/**
* Set a callback for an event, possible events:
* fileSuccess(file), fileProgress(file), fileAdded(file, event),
* fileRemoved(file), fileRetry(file), fileError(file, message),
* complete(), progress(), error(message, file), pause()
* @function
* @param {string} event
* @param {Function} callback
*/
on: function (event, callback) {
event = event.toLowerCase();
if (!this.events.hasOwnProperty(event)) {
this.events[event] = [];
}
this.events[event].push(callback);
},
/**
* Remove event callback
* @function
* @param {string} [event] removes all events if not specified
* @param {Function} [fn] removes all callbacks of event if not specified
*/
off: function (event, fn) {
if (event !== undefined) {
event = event.toLowerCase();
if (fn !== undefined) {
if (this.events.hasOwnProperty(event)) {
arrayRemove(this.events[event], fn);
}
} else {
delete this.events[event];
}
} else {
this.events = {};
}
},
/**
* Fire an event
* @function
* @param {string} event event name
* @param {...} args arguments of a callback
* @return {bool} value is false if at least one of the event handlers which handled this event
* returned false. Otherwise it returns true.
*/
fire: function (event, args) {
// `arguments` is an object, not array, in FF, so:
args = Array.prototype.slice.call(arguments);
event = event.toLowerCase();
var preventDefault = false;
if (this.events.hasOwnProperty(event)) {
each(this.events[event], function (callback) {
preventDefault = callback.apply(this, args.slice(1)) === false || preventDefault;
}, this);
}
if (event != 'catchall') {
args.unshift('catchAll');
preventDefault = this.fire.apply(this, args) === false || preventDefault;
}
return !preventDefault;
},
/**
* Read webkit dataTransfer object
* @param event
*/
webkitReadDataTransfer: function (event) {
var $ = this;
var queue = event.dataTransfer.items.length;
var files = [];
each(event.dataTransfer.items, function (item) {
var entry = item.webkitGetAsEntry();
if (!entry) {
decrement();
return ;
}
if (entry.isFile) {
// due to a bug in Chrome's File System API impl - #149735
fileReadSuccess(item.getAsFile(), entry.fullPath);
} else {
readDirectory(entry.createReader());
}
});
function readDirectory(reader) {
reader.readEntries(function (entries) {
if (entries.length) {
queue += entries.length;
each(entries, function(entry) {
if (entry.isFile) {
var fullPath = entry.fullPath;
entry.file(function (file) {
fileReadSuccess(file, fullPath);
}, readError);
} else if (entry.isDirectory) {
readDirectory(entry.createReader());
}
});
readDirectory(reader);
} else {
decrement();
}
}, readError);
}
function fileReadSuccess(file, fullPath) {
// relative path should not start with "/"
file.relativePath = fullPath.substring(1);
files.push(file);
decrement();
}
function readError(fileError) {
throw fileError;
}
function decrement() {
if (--queue == 0) {
$.addFiles(files, event);
}
}
},
/**
* Generate unique identifier for a file
* @function
* @param {FlowFile} file
* @returns {string}
*/
generateUniqueIdentifier: function (file) {
var custom = this.opts.generateUniqueIdentifier;
if (typeof custom === 'function') {
return custom(file);
}
// Some confusion in different versions of Firefox
var relativePath = file.relativePath || file.webkitRelativePath || file.fileName || file.name;
return file.size + '-' + relativePath.replace(/[^0-9a-zA-Z_-]/img, '');
},
/**
* Upload next chunk from the queue
* @function
* @returns {boolean}
* @private
*/
uploadNextChunk: function (preventEvents) {
// In some cases (such as videos) it's really handy to upload the first
// and last chunk of a file quickly; this let's the server check the file's
// metadata and determine if there's even a point in continuing.
var found = false;
if (this.opts.prioritizeFirstAndLastChunk) {
each(this.files, function (file) {
if (!file.paused && file.chunks.length &&
file.chunks[0].status() === 'pending') {
file.chunks[0].send();
found = true;
return false;
}
if (!file.paused && file.chunks.length > 1 &&
file.chunks[file.chunks.length - 1].status() === 'pending') {
file.chunks[file.chunks.length - 1].send();
found = true;
return false;
}
});
if (found) {
return found;
}
}
// Now, simply look for the next, best thing to upload
each(this.files, function (file) {
if (!file.paused) {
each(file.chunks, function (chunk) {
if (chunk.status() === 'pending') {
chunk.send();
found = true;
return false;
}
});
}
if (found) {
return false;
}
});
if (found) {
return true;
}
// The are no more outstanding chunks to upload, check is everything is done
var outstanding = false;
each(this.files, function (file) {
if (!file.isComplete()) {
outstanding = true;
return false;
}
});
if (!outstanding && !preventEvents) {
// All chunks have been uploaded, complete
async(function () {
this.fire('complete');
}, this);
}
return false;
},
/**
* Assign a browse action to one or more DOM nodes.
* @function
* @param {Element|Array.} domNodes
* @param {boolean} isDirectory Pass in true to allow directories to
* @param {boolean} singleFile prevent multi file upload
* @param {Object} attributes set custom attributes:
* http://www.w3.org/TR/html-markup/input.file.html#input.file-attributes
* eg: accept: 'image/*'
* be selected (Chrome only).
*/
assignBrowse: function (domNodes, isDirectory, singleFile, attributes) {
if (domNodes instanceof Element) {
domNodes = [domNodes];
}
each(domNodes, function (domNode) {
var input;
if (domNode.tagName === 'INPUT' && domNode.type === 'file') {
input = domNode;
} else {
input = document.createElement('input');
input.setAttribute('type', 'file');
// display:none - not working in opera 12
extend(input.style, {
visibility: 'hidden',
position: 'absolute',
width: '1px',
height: '1px'
});
// for opera 12 browser, input must be assigned to a document
domNode.appendChild(input);
// https://developer.mozilla.org/en/using_files_from_web_applications)
// event listener is executed two times
// first one - original mouse click event
// second - input.click(), input is inside domNode
domNode.addEventListener('click', function() {
input.click();
}, false);
}
if (!this.opts.singleFile && !singleFile) {
input.setAttribute('multiple', 'multiple');
}
if (isDirectory) {
input.setAttribute('webkitdirectory', 'webkitdirectory');
}
each(attributes, function (value, key) {
input.setAttribute(key, value);
});
// When new files are added, simply append them to the overall list
var $ = this;
input.addEventListener('change', function (e) {
if (e.target.value) {
$.addFiles(e.target.files, e);
e.target.value = '';
}
}, false);
}, this);
},
/**
* Assign one or more DOM nodes as a drop target.
* @function
* @param {Element|Array.} domNodes
*/
assignDrop: function (domNodes) {
if (typeof domNodes.length === 'undefined') {
domNodes = [domNodes];
}
each(domNodes, function (domNode) {
domNode.addEventListener('dragover', this.preventEvent, false);
domNode.addEventListener('dragenter', this.preventEvent, false);
domNode.addEventListener('drop', this.onDrop, false);
}, this);
},
/**
* Un-assign drop event from DOM nodes
* @function
* @param domNodes
*/
unAssignDrop: function (domNodes) {
if (typeof domNodes.length === 'undefined') {
domNodes = [domNodes];
}
each(domNodes, function (domNode) {
domNode.removeEventListener('dragover', this.preventEvent);
domNode.removeEventListener('dragenter', this.preventEvent);
domNode.removeEventListener('drop', this.onDrop);
}, this);
},
/**
* Returns a boolean indicating whether or not the instance is currently
* uploading anything.
* @function
* @returns {boolean}
*/
isUploading: function () {
var uploading = false;
each(this.files, function (file) {
if (file.isUploading()) {
uploading = true;
return false;
}
});
return uploading;
},
/**
* should upload next chunk
* @function
* @returns {boolean|number}
*/
_shouldUploadNext: function () {
var num = 0;
var should = true;
var simultaneousUploads = this.opts.simultaneousUploads;
each(this.files, function (file) {
each(file.chunks, function(chunk) {
if (chunk.status() === 'uploading') {
num++;
if (num >= simultaneousUploads) {
should = false;
return false;
}
}
});
});
// if should is true then return uploading chunks's length
return should && num;
},
/**
* Start or resume uploading.
* @function
*/
upload: function () {
// Make sure we don't start too many uploads at once
var ret = this._shouldUploadNext();
if (ret === false) {
return;
}
// Kick off the queue
this.fire('uploadStart');
var started = false;
for (var num = 1; num <= this.opts.simultaneousUploads - ret; num++) {
started = this.uploadNextChunk(true) || started;
}
if (!started) {
async(function () {
this.fire('complete');
}, this);
}
},
/**
* Resume uploading.
* @function
*/
resume: function () {
each(this.files, function (file) {
file.resume();
});
},
/**
* Pause uploading.
* @function
*/
pause: function () {
each(this.files, function (file) {
file.pause();
});
},
/**
* Cancel upload of all FlowFile objects and remove them from the list.
* @function
*/
cancel: function () {
for (var i = this.files.length - 1; i >= 0; i--) {
this.files[i].cancel();
}
},
/**
* Returns a number between 0 and 1 indicating the current upload progress
* of all files.
* @function
* @returns {number}
*/
progress: function () {
var totalDone = 0;
var totalSize = 0;
// Resume all chunks currently being uploaded
each(this.files, function (file) {
totalDone += file.progress() * file.size;
totalSize += file.size;
});
return totalSize > 0 ? totalDone / totalSize : 0;
},
/**
* Add a HTML5 File object to the list of files.
* @function
* @param {File} file
* @param {Event} [event] event is optional
*/
addFile: function (file, event) {
this.addFiles([file], event);
},
/**
* Add a HTML5 File object to the list of files.
* @function
* @param {FileList|Array} fileList
* @param {Event} [event] event is optional
*/
addFiles: function (fileList, event) {
var files = [];
each(fileList, function (file) {
// https://github.com/flowjs/flow.js/issues/55
if ((!ie10plus || ie10plus && file.size > 0) && !(file.size % 4096 === 0 && (file.name === '.' || file.fileName === '.')) &&
(this.opts.allowDuplicateUploads || !this.getFromUniqueIdentifier(this.generateUniqueIdentifier(file)))) {
var f = new FlowFile(this, file);
if (this.fire('fileAdded', f, event)) {
files.push(f);
}
}
}, this);
if (this.fire('filesAdded', files, event)) {
each(files, function (file) {
if (this.opts.singleFile && this.files.length > 0) {
this.removeFile(this.files[0]);
}
this.files.push(file);
}, this);
this.fire('filesSubmitted', files, event);
}
},
/**
* Cancel upload of a specific FlowFile object from the list.
* @function
* @param {FlowFile} file
*/
removeFile: function (file) {
for (var i = this.files.length - 1; i >= 0; i--) {
if (this.files[i] === file) {
this.files.splice(i, 1);
file.abort();
this.fire('fileRemoved', file);
}
}
},
/**
* Look up a FlowFile object by its unique identifier.
* @function
* @param {string} uniqueIdentifier
* @returns {boolean|FlowFile} false if file was not found
*/
getFromUniqueIdentifier: function (uniqueIdentifier) {
var ret = false;
each(this.files, function (file) {
if (file.uniqueIdentifier === uniqueIdentifier) {
ret = file;
}
});
return ret;
},
/**
* Returns the total size of all files in bytes.
* @function
* @returns {number}
*/
getSize: function () {
var totalSize = 0;
each(this.files, function (file) {
totalSize += file.size;
});
return totalSize;
},
/**
* Returns the total size uploaded of all files in bytes.
* @function
* @returns {number}
*/
sizeUploaded: function () {
var size = 0;
each(this.files, function (file) {
size += file.sizeUploaded();
});
return size;
},
/**
* Returns remaining time to upload all files in seconds. Accuracy is based on average speed.
* If speed is zero, time remaining will be equal to positive infinity `Number.POSITIVE_INFINITY`
* @function
* @returns {number}
*/
timeRemaining: function () {
var sizeDelta = 0;
var averageSpeed = 0;
each(this.files, function (file) {
if (!file.paused && !file.error) {
sizeDelta += file.size - file.sizeUploaded();
averageSpeed += file.averageSpeed;
}
});
if (sizeDelta && !averageSpeed) {
return Number.POSITIVE_INFINITY;
}
if (!sizeDelta && !averageSpeed) {
return 0;
}
return Math.floor(sizeDelta / averageSpeed);
}
};
/**
* FlowFile class
* @name FlowFile
* @param {Flow} flowObj
* @param {File} file
* @constructor
*/
function FlowFile(flowObj, file) {
/**
* Reference to parent Flow instance
* @type {Flow}
*/
this.flowObj = flowObj;
/**
* Used to store the bytes read
* @type {Blob|string}
*/
this.bytes = null;
/**
* Reference to file
* @type {File}
*/
this.file = file;
/**
* File name. Some confusion in different versions of Firefox
* @type {string}
*/
this.name = file.fileName || file.name;
/**
* File size
* @type {number}
*/
this.size = file.size;
/**
* Relative file path
* @type {string}
*/
this.relativePath = file.relativePath || file.webkitRelativePath || this.name;
/**
* File unique identifier
* @type {string}
*/
this.uniqueIdentifier = flowObj.generateUniqueIdentifier(file);
/**
* List of chunks
* @type {Array.}
*/
this.chunks = [];
/**
* Indicated if file is paused
* @type {boolean}
*/
this.paused = false;
/**
* Indicated if file has encountered an error
* @type {boolean}
*/
this.error = false;
/**
* Average upload speed
* @type {number}
*/
this.averageSpeed = 0;
/**
* Current upload speed
* @type {number}
*/
this.currentSpeed = 0;
/**
* Date then progress was called last time
* @type {number}
* @private
*/
this._lastProgressCallback = Date.now();
/**
* Previously uploaded file size
* @type {number}
* @private
*/
this._prevUploadedSize = 0;
/**
* Holds previous progress
* @type {number}
* @private
*/
this._prevProgress = 0;
this.bootstrap();
}
FlowFile.prototype = {
/**
* Update speed parameters
* @link http://stackoverflow.com/questions/2779600/how-to-estimate-download-time-remaining-accurately
* @function
*/
measureSpeed: function () {
var timeSpan = Date.now() - this._lastProgressCallback;
if (!timeSpan) {
return ;
}
var smoothingFactor = this.flowObj.opts.speedSmoothingFactor;
var uploaded = this.sizeUploaded();
// Prevent negative upload speed after file upload resume
this.currentSpeed = Math.max((uploaded - this._prevUploadedSize) / timeSpan * 1000, 0);
this.averageSpeed = smoothingFactor * this.currentSpeed + (1 - smoothingFactor) * this.averageSpeed;
this._prevUploadedSize = uploaded;
},
/**
* For internal usage only.
* Callback when something happens within the chunk.
* @function
* @param {FlowChunk} chunk
* @param {string} event can be 'progress', 'success', 'error' or 'retry'
* @param {string} [message]
*/
chunkEvent: function (chunk, event, message) {
switch (event) {
case 'progress':
if (Date.now() - this._lastProgressCallback <
this.flowObj.opts.progressCallbacksInterval) {
break;
}
this.measureSpeed();
this.flowObj.fire('fileProgress', this, chunk);
this.flowObj.fire('progress');
this._lastProgressCallback = Date.now();
break;
case 'error':
this.error = true;
this.abort(true);
this.flowObj.fire('fileError', this, message, chunk);
this.flowObj.fire('error', message, this, chunk);
break;
case 'success':
if (this.error) {
return;
}
this.measureSpeed();
this.flowObj.fire('fileProgress', this, chunk);
this.flowObj.fire('progress');
this._lastProgressCallback = Date.now();
if (this.isComplete()) {
this.currentSpeed = 0;
this.averageSpeed = 0;
this.flowObj.fire('fileSuccess', this, message, chunk);
}
break;
case 'retry':
this.flowObj.fire('fileRetry', this, chunk);
break;
}
},
/**
* Pause file upload
* @function
*/
pause: function() {
this.paused = true;
this.abort();
},
/**
* Resume file upload
* @function
*/
resume: function() {
this.paused = false;
this.flowObj.upload();
},
/**
* Abort current upload
* @function
*/
abort: function (reset) {
this.currentSpeed = 0;
this.averageSpeed = 0;
var chunks = this.chunks;
if (reset) {
this.chunks = [];
}
each(chunks, function (c) {
if (c.status() === 'uploading') {
c.abort();
this.flowObj.uploadNextChunk();
}
}, this);
},
/**
* Cancel current upload and remove from a list
* @function
*/
cancel: function () {
this.flowObj.removeFile(this);
},
/**
* Retry aborted file upload
* @function
*/
retry: function () {
this.bootstrap();
this.flowObj.upload();
},
/**
* Clear current chunks and slice file again
* @function
*/
bootstrap: function () {
if (typeof this.flowObj.opts.initFileFn === "function") {
this.flowObj.opts.initFileFn(this);
}
this.abort(true);
this.error = false;
// Rebuild stack of chunks from file
this._prevProgress = 0;
var round = this.flowObj.opts.forceChunkSize ? Math.ceil : Math.floor;
var chunks = Math.max(
round(this.size / this.flowObj.opts.chunkSize), 1
);
for (var offset = 0; offset < chunks; offset++) {
this.chunks.push(
new FlowChunk(this.flowObj, this, offset)
);
}
},
/**
* Get current upload progress status
* @function
* @returns {number} from 0 to 1
*/
progress: function () {
if (this.error) {
return 1;
}
if (this.chunks.length === 1) {
this._prevProgress = Math.max(this._prevProgress, this.chunks[0].progress());
return this._prevProgress;
}
// Sum up progress across everything
var bytesLoaded = 0;
each(this.chunks, function (c) {
// get chunk progress relative to entire file
bytesLoaded += c.progress() * (c.endByte - c.startByte);
});
var percent = bytesLoaded / this.size;
// We don't want to lose percentages when an upload is paused
this._prevProgress = Math.max(this._prevProgress, percent > 0.9999 ? 1 : percent);
return this._prevProgress;
},
/**
* Indicates if file is being uploaded at the moment
* @function
* @returns {boolean}
*/
isUploading: function () {
var uploading = false;
each(this.chunks, function (chunk) {
if (chunk.status() === 'uploading') {
uploading = true;
return false;
}
});
return uploading;
},
/**
* Indicates if file is has finished uploading and received a response
* @function
* @returns {boolean}
*/
isComplete: function () {
var outstanding = false;
each(this.chunks, function (chunk) {
var status = chunk.status();
if (status === 'pending' || status === 'uploading' || status === 'reading' || chunk.preprocessState === 1 || chunk.readState === 1) {
outstanding = true;
return false;
}
});
return !outstanding;
},
/**
* Count total size uploaded
* @function
* @returns {number}
*/
sizeUploaded: function () {
var size = 0;
each(this.chunks, function (chunk) {
size += chunk.sizeUploaded();
});
return size;
},
/**
* Returns remaining time to finish upload file in seconds. Accuracy is based on average speed.
* If speed is zero, time remaining will be equal to positive infinity `Number.POSITIVE_INFINITY`
* @function
* @returns {number}
*/
timeRemaining: function () {
if (this.paused || this.error) {
return 0;
}
var delta = this.size - this.sizeUploaded();
if (delta && !this.averageSpeed) {
return Number.POSITIVE_INFINITY;
}
if (!delta && !this.averageSpeed) {
return 0;
}
return Math.floor(delta / this.averageSpeed);
},
/**
* Get file type
* @function
* @returns {string}
*/
getType: function () {
return this.file.type && this.file.type.split('/')[1];
},
/**
* Get file extension
* @function
* @returns {string}
*/
getExtension: function () {
return this.name.substr((~-this.name.lastIndexOf(".") >>> 0) + 2).toLowerCase();
}
};
/**
* Default read function using the webAPI
*
* @function webAPIFileRead(fileObj, startByte, endByte, fileType, chunk)
*
*/
function webAPIFileRead(fileObj, startByte, endByte, fileType, chunk) {
var function_name = 'slice';
if (fileObj.file.slice)
function_name = 'slice';
else if (fileObj.file.mozSlice)
function_name = 'mozSlice';
else if (fileObj.file.webkitSlice)
function_name = 'webkitSlice';
chunk.readFinished(fileObj.file[function_name](startByte, endByte, fileType));
}
/**
* Class for storing a single chunk
* @name FlowChunk
* @param {Flow} flowObj
* @param {FlowFile} fileObj
* @param {number} offset
* @constructor
*/
function FlowChunk(flowObj, fileObj, offset) {
/**
* Reference to parent flow object
* @type {Flow}
*/
this.flowObj = flowObj;
/**
* Reference to parent FlowFile object
* @type {FlowFile}
*/
this.fileObj = fileObj;
/**
* File offset
* @type {number}
*/
this.offset = offset;
/**
* Indicates if chunk existence was checked on the server
* @type {boolean}
*/
this.tested = false;
/**
* Number of retries performed
* @type {number}
*/
this.retries = 0;
/**
* Pending retry
* @type {boolean}
*/
this.pendingRetry = false;
/**
* Preprocess state
* @type {number} 0 = unprocessed, 1 = processing, 2 = finished
*/
this.preprocessState = 0;
/**
* Read state
* @type {number} 0 = not read, 1 = reading, 2 = finished
*/
this.readState = 0;
/**
* Bytes transferred from total request size
* @type {number}
*/
this.loaded = 0;
/**
* Total request size
* @type {number}
*/
this.total = 0;
/**
* Size of a chunk
* @type {number}
*/
this.chunkSize = this.flowObj.opts.chunkSize;
/**
* Chunk start byte in a file
* @type {number}
*/
this.startByte = this.offset * this.chunkSize;
/**
* Compute the endbyte in a file
*
*/
this.computeEndByte = function() {
var endByte = Math.min(this.fileObj.size, (this.offset + 1) * this.chunkSize);
if (this.fileObj.size - endByte < this.chunkSize && !this.flowObj.opts.forceChunkSize) {
// The last chunk will be bigger than the chunk size,
// but less than 2 * this.chunkSize
endByte = this.fileObj.size;
}
return endByte;
}
/**
* Chunk end byte in a file
* @type {number}
*/
this.endByte = this.computeEndByte();
/**
* XMLHttpRequest
* @type {XMLHttpRequest}
*/
this.xhr = null;
var $ = this;
/**
* Send chunk event
* @param event
* @param {...} args arguments of a callback
*/
this.event = function (event, args) {
args = Array.prototype.slice.call(arguments);
args.unshift($);
$.fileObj.chunkEvent.apply($.fileObj, args);
};
/**
* Catch progress event
* @param {ProgressEvent} event
*/
this.progressHandler = function(event) {
if (event.lengthComputable) {
$.loaded = event.loaded ;
$.total = event.total;
}
$.event('progress', event);
};
/**
* Catch test event
* @param {Event} event
*/
this.testHandler = function(event) {
var status = $.status(true);
if (status === 'error') {
$.event(status, $.message());
$.flowObj.uploadNextChunk();
} else if (status === 'success') {
$.tested = true;
$.event(status, $.message());
$.flowObj.uploadNextChunk();
} else if (!$.fileObj.paused) {
// Error might be caused by file pause method
// Chunks does not exist on the server side
$.tested = true;
$.send();
}
};
/**
* Upload has stopped
* @param {Event} event
*/
this.doneHandler = function(event) {
var status = $.status();
if (status === 'success' || status === 'error') {
delete this.data;
$.event(status, $.message());
$.flowObj.uploadNextChunk();
} else {
$.event('retry', $.message());
$.pendingRetry = true;
$.abort();
$.retries++;
var retryInterval = $.flowObj.opts.chunkRetryInterval;
if (retryInterval !== null) {
setTimeout(function () {
$.send();
}, retryInterval);
} else {
$.send();
}
}
};
}
FlowChunk.prototype = {
/**
* Get params for a request
* @function
*/
getParams: function () {
return {
flowChunkNumber: this.offset + 1,
flowChunkSize: this.flowObj.opts.chunkSize,
flowCurrentChunkSize: this.endByte - this.startByte,
flowTotalSize: this.fileObj.size,
flowIdentifier: this.fileObj.uniqueIdentifier,
flowFilename: this.fileObj.name,
flowRelativePath: this.fileObj.relativePath,
flowTotalChunks: this.fileObj.chunks.length
};
},
/**
* Get target option with query params
* @function
* @param params
* @returns {string}
*/
getTarget: function(target, params){
if(target.indexOf('?') < 0) {
target += '?';
} else {
target += '&';
}
return target + params.join('&');
},
/**
* Makes a GET request without any data to see if the chunk has already
* been uploaded in a previous session
* @function
*/
test: function () {
// Set up request and listen for event
this.xhr = new XMLHttpRequest();
this.xhr.addEventListener("load", this.testHandler, false);
this.xhr.addEventListener("error", this.testHandler, false);
var testMethod = evalOpts(this.flowObj.opts.testMethod, this.fileObj, this);
var data = this.prepareXhrRequest(testMethod, true);
this.xhr.send(data);
},
/**
* Finish preprocess state
* @function
*/
preprocessFinished: function () {
// Re-compute the endByte after the preprocess function to allow an
// implementer of preprocess to set the fileObj size
this.endByte = this.computeEndByte();
this.preprocessState = 2;
this.send();
},
/**
* Finish read state
* @function
*/
readFinished: function (bytes) {
this.readState = 2;
this.bytes = bytes;
this.send();
},
/**
* Uploads the actual data in a POST call
* @function
*/
send: function () {
var preprocess = this.flowObj.opts.preprocess;
var read = this.flowObj.opts.readFileFn;
if (typeof preprocess === 'function') {
switch (this.preprocessState) {
case 0:
this.preprocessState = 1;
preprocess(this);
return;
case 1:
return;
}
}
switch (this.readState) {
case 0:
this.readState = 1;
read(this.fileObj, this.startByte, this.endByte, this.fileObj.file.type, this);
return;
case 1:
return;
}
if (this.flowObj.opts.testChunks && !this.tested) {
this.test();
return;
}
this.loaded = 0;
this.total = 0;
this.pendingRetry = false;
// Set up request and listen for event
this.xhr = new XMLHttpRequest();
this.xhr.upload.addEventListener('progress', this.progressHandler, false);
this.xhr.addEventListener("load", this.doneHandler, false);
this.xhr.addEventListener("error", this.doneHandler, false);
var uploadMethod = evalOpts(this.flowObj.opts.uploadMethod, this.fileObj, this);
var data = this.prepareXhrRequest(uploadMethod, false, this.flowObj.opts.method, this.bytes);
this.xhr.send(data);
},
/**
* Abort current xhr request
* @function
*/
abort: function () {
// Abort and reset
var xhr = this.xhr;
this.xhr = null;
if (xhr) {
xhr.abort();
}
},
/**
* Retrieve current chunk upload status
* @function
* @returns {string} 'pending', 'uploading', 'success', 'error'
*/
status: function (isTest) {
if (this.readState === 1) {
return 'reading';
} else if (this.pendingRetry || this.preprocessState === 1) {
// if pending retry then that's effectively the same as actively uploading,
// there might just be a slight delay before the retry starts
return 'uploading';
} else if (!this.xhr) {
return 'pending';
} else if (this.xhr.readyState < 4) {
// Status is really 'OPENED', 'HEADERS_RECEIVED'
// or 'LOADING' - meaning that stuff is happening
return 'uploading';
} else {
if (this.flowObj.opts.successStatuses.indexOf(this.xhr.status) > -1) {
// HTTP 200, perfect
// HTTP 202 Accepted - The request has been accepted for processing, but the processing has not been completed.
return 'success';
} else if (this.flowObj.opts.permanentErrors.indexOf(this.xhr.status) > -1 ||
!isTest && this.retries >= this.flowObj.opts.maxChunkRetries) {
// HTTP 413/415/500/501, permanent error
return 'error';
} else {
// this should never happen, but we'll reset and queue a retry
// a likely case for this would be 503 service unavailable
this.abort();
return 'pending';
}
}
},
/**
* Get response from xhr request
* @function
* @returns {String}
*/
message: function () {
return this.xhr ? this.xhr.responseText : '';
},
/**
* Get upload progress
* @function
* @returns {number}
*/
progress: function () {
if (this.pendingRetry) {
return 0;
}
var s = this.status();
if (s === 'success' || s === 'error') {
return 1;
} else if (s === 'pending') {
return 0;
} else {
return this.total > 0 ? this.loaded / this.total : 0;
}
},
/**
* Count total size uploaded
* @function
* @returns {number}
*/
sizeUploaded: function () {
var size = this.endByte - this.startByte;
// can't return only chunk.loaded value, because it is bigger than chunk size
if (this.status() !== 'success') {
size = this.progress() * size;
}
return size;
},
/**
* Prepare Xhr request. Set query, headers and data
* @param {string} method GET or POST
* @param {bool} isTest is this a test request
* @param {string} [paramsMethod] octet or form
* @param {Blob} [blob] to send
* @returns {FormData|Blob|Null} data to send
*/
prepareXhrRequest: function(method, isTest, paramsMethod, blob) {
// Add data from the query options
var query = evalOpts(this.flowObj.opts.query, this.fileObj, this, isTest);
query = extend(query, this.getParams());
var target = evalOpts(this.flowObj.opts.target, this.fileObj, this, isTest);
var data = null;
if (method === 'GET' || paramsMethod === 'octet') {
// Add data from the query options
var params = [];
each(query, function (v, k) {
params.push([encodeURIComponent(k), encodeURIComponent(v)].join('='));
});
target = this.getTarget(target, params);
data = blob || null;
} else {
// Add data from the query options
data = new FormData();
each(query, function (v, k) {
data.append(k, v);
});
data.append(this.flowObj.opts.fileParameterName, blob, this.fileObj.file.name);
}
this.xhr.open(method, target, true);
this.xhr.withCredentials = this.flowObj.opts.withCredentials;
// Add data from header options
each(evalOpts(this.flowObj.opts.headers, this.fileObj, this, isTest), function (v, k) {
this.xhr.setRequestHeader(k, v);
}, this);
return data;
}
};
/**
* Remove value from array
* @param array
* @param value
*/
function arrayRemove(array, value) {
var index = array.indexOf(value);
if (index > -1) {
array.splice(index, 1);
}
}
/**
* If option is a function, evaluate it with given params
* @param {*} data
* @param {...} args arguments of a callback
* @returns {*}
*/
function evalOpts(data, args) {
if (typeof data === "function") {
// `arguments` is an object, not array, in FF, so:
args = Array.prototype.slice.call(arguments);
data = data.apply(null, args.slice(1));
}
return data;
}
Flow.evalOpts = evalOpts;
/**
* Execute function asynchronously
* @param fn
* @param context
*/
function async(fn, context) {
setTimeout(fn.bind(context), 0);
}
/**
* Extends the destination object `dst` by copying all of the properties from
* the `src` object(s) to `dst`. You can specify multiple `src` objects.
* @function
* @param {Object} dst Destination object.
* @param {...Object} src Source object(s).
* @returns {Object} Reference to `dst`.
*/
function extend(dst, src) {
each(arguments, function(obj) {
if (obj !== dst) {
each(obj, function(value, key){
dst[key] = value;
});
}
});
return dst;
}
Flow.extend = extend;
/**
* Iterate each element of an object
* @function
* @param {Array|Object} obj object or an array to iterate
* @param {Function} callback first argument is a value and second is a key.
* @param {Object=} context Object to become context (`this`) for the iterator function.
*/
function each(obj, callback, context) {
if (!obj) {
return ;
}
var key;
// Is Array?
// Array.isArray won't work, not only arrays can be iterated by index https://github.com/flowjs/ng-flow/issues/236#
if (typeof(obj.length) !== 'undefined') {
for (key = 0; key < obj.length; key++) {
if (callback.call(context, obj[key], key) === false) {
return ;
}
}
} else {
for (key in obj) {
if (obj.hasOwnProperty(key) && callback.call(context, obj[key], key) === false) {
return ;
}
}
}
}
Flow.each = each;
/**
* FlowFile constructor
* @type {FlowFile}
*/
Flow.FlowFile = FlowFile;
/**
* FlowFile constructor
* @type {FlowChunk}
*/
Flow.FlowChunk = FlowChunk;
/**
* Library version
* @type {string}
*/
Flow.version = '2.11.2';
if ( typeof module === "object" && module && typeof module.exports === "object" ) {
// Expose Flow as module.exports in loaders that implement the Node
// module pattern (including browserify). Do not create the global, since
// the user will be storing it themselves locally, and globals are frowned
// upon in the Node module world.
module.exports = Flow;
} else {
// Otherwise expose Flow to the global object as usual
window.Flow = Flow;
// Register as a named AMD module, since Flow can be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase flow is used because AMD module names are
// derived from file names, and Flow is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of Flow, it will work.
if ( typeof define === "function" && define.amd ) {
define( "flow", [], function () { return Flow; } );
}
}
})(window, document);
;/*})'"*/
;/*})'"*/
(function ($) {
Drupal.ocupload = Drupal.ocupload || {};
/**
* Create and configure Flow.js object.
*/
Drupal.ocupload.createFlow = function () {
// Create Flow.js instance
var flow = new Flow({
target: Drupal.settings.basePath + 'ocupload/upload',
testChunks: false,
chunkSize: 5*1024*1024,
simultaneousUploads: 1
});
if (!flow.support) {
return flow;
}
flow.on('fileAdded', Drupal.ocupload.onFileAdded);
flow.on('filesSubmitted', Drupal.ocupload.onFilesSubmitted);
flow.on('fileProgress', Drupal.ocupload.onFileProgress);
flow.on('fileSuccess', Drupal.ocupload.onFileSuccess);
flow.on('error', Drupal.ocupload.onError);
flow.on('complete', Drupal.ocupload.onComplete);
return flow;
};
/**
* Return true if response in JSON format.
*/
Drupal.ocupload.checkResponse = function (response) {
return $.trim(response).substring(0, 1) == '{';
};
/**
* Return target textarea.
*/
Drupal.ocupload.findTextarea = function(element) {
var $parent = $(element).parent();
var $textarea = $parent.find('textarea:first');
return ($textarea.length == 0) ? Drupal.ocupload.findTextarea($parent) : $textarea;
};
/**
* File added handler.
*/
Drupal.ocupload.onFileAdded = function (file, event) {
if ($.inArray(file.getExtension(), Drupal.settings.ocupload.allowedExt) == -1) {
alert(Drupal.t('You can not upload files of type .@file_ext', {'@file_ext':file.getExtension()}));
return false;
}
};
/**
* Files selected handler.
*/
Drupal.ocupload.onFilesSubmitted = function (files, event) {
var flow = this;
var $textarea = Drupal.ocupload.findTextarea(event.target);
var $queue = $('#upload-queue');
if ($queue.length == 0) {
$queue = $('').appendTo('body');
}
$.each(files, function (index, file) {
$queue.prepend('' + file.name + '
');
});
flow.opts.query.fieldName = $textarea.attr('name');
flow.opts.query.formId = $textarea.closest('form').find('input[name="form_id"]').val();
};
/**
* File upload progress handler.
*/
Drupal.ocupload.onFileProgress = function (file, chunk) {
var $fileQueue = $('#queue-' + file.uniqueIdentifier);
$fileQueue.css({
'background': 'url(' + Drupal.settings.basePath + 'misc/progress.gif) repeat-x 0 center',
'color': 'white'
});
};
/**
* File uploaded handler.
*/
Drupal.ocupload.onFileSuccess = function (file, response, chunk) {
var $fileQueue = $('#queue-' + file.uniqueIdentifier);
$fileQueue.hide('fast', function () {
$fileQueue.remove();
});
if (!Drupal.ocupload.checkResponse(response)) {
alert(Drupal.t('Server response came not in JSON format: @response', {'@response':response}));
}
};
/**
* Upload error handler.
*/
Drupal.ocupload.onError = function (message, file, chunk) {
alert(Drupal.t('Upload error: @message', {'@message': message}))
};
/**
* Files uploaded handler.
*/
Drupal.ocupload.onComplete = function () {
var flow = this;
flow.cancel();
};
})(jQuery);
// Translate string because plugin.js not visible in locale_js_alter()
// Drupal.t('Upload file');
// Drupal.t('Your browser not support HTML5 File API');
;/*})'"*/
;/*})'"*/
(function ($) {
Drupal.behaviors.ocuploadTextarea = {
attach: function (context, settings) {
if (!Drupal.settings.ocupload || !Drupal.settings.ocupload.allowedExt) {
return;
}
$('textarea.ocupload-drop', context).once('ocupload-drop').each(function () {
var textarea = this;
// Lazy create and configure Flow.js object
if (!Drupal.ocupload.textareaPlugin.flow) {
Drupal.ocupload.textareaPlugin.createFlow();
}
// Process textarea
if (Drupal.ocupload.textareaPlugin.flow.support) {
Drupal.ocupload.textareaPlugin.flow.assignDrop(textarea);
// Hack for IE. IE loses textarea selection on drag start.
if (Drupal.ocupload.textareaPlugin.isIE) {
$(textarea).bind('blur', Drupal.ocupload.textareaPlugin.saveSelection);
}
}
});
}
};
Drupal.ocupload = Drupal.ocupload || {};
Drupal.ocupload.textareaPlugin = Drupal.ocupload.textareaPlugin || {};
Drupal.ocupload.textareaPlugin.isIE = document.documentMode ? true : false;
/**
* Create and configure Flow.js object.
*/
Drupal.ocupload.textareaPlugin.createFlow = function () {
Drupal.ocupload.textareaPlugin.flow = Drupal.ocupload.createFlow();
if (!Drupal.ocupload.textareaPlugin.flow.support) {
return false;
}
Drupal.ocupload.textareaPlugin.flow.on('filesSubmitted', Drupal.ocupload.textareaPlugin.onFilesSubmitted);
Drupal.ocupload.textareaPlugin.flow.on('fileSuccess', Drupal.ocupload.textareaPlugin.onFileSuccess);
Drupal.ocupload.textareaPlugin.flow.on('complete', Drupal.ocupload.textareaPlugin.onComplete);
return true;
};
/**
* Get selected text in textarea.
*/
Drupal.ocupload.textareaPlugin.getSelectedText = function (element) {
if (element instanceof jQuery) {
element = element[0];
}
return element.value.substring(element.selectionStart, element.selectionEnd);
};
/**
* Save selection info in element data attribute.
*/
Drupal.ocupload.textareaPlugin.saveSelection = function (event) {
var textarea = this;
$(textarea).data('ocuploadSelection', {
selectedText: Drupal.ocupload.textareaPlugin.getSelectedText(textarea),
selectionStart: textarea.selectionStart,
selectionEnd: textarea.selectionEnd,
});
};
/**
* Files selected handler.
*/
Drupal.ocupload.textareaPlugin.onFilesSubmitted = function (files, event) {
var $textarea = $(event.target).closest('.form-item').find('textarea');
var selectedText = Drupal.ocupload.textareaPlugin.getSelectedText($textarea);
// Hack for IE. Restore selection from data
if (Drupal.ocupload.textareaPlugin.isIE) {
selectedText = $textarea.data('ocuploadSelection').selectedText;
}
Drupal.ocupload.textareaPlugin.flow.opts.query.selectedText = selectedText;
Drupal.ocupload.textareaPlugin.flow.upload();
$textarea[0].disabled = true;
// Save textarea id in global var, because event 'complete' not contains this information
Drupal.ocupload.textareaPlugin.activeTextareaId = $textarea.attr('id');
};
/**
* File uploaded handler.
*/
Drupal.ocupload.textareaPlugin.onFileSuccess = function (file, response, chunk) {
if (!Drupal.ocupload.checkResponse(response)) {
return;
}
response = $.parseJSON(response);
if (response.status) {
var $textarea = $('#' + Drupal.ocupload.textareaPlugin.activeTextareaId);
var textarea = $textarea[0];
var selectionStart = textarea.selectionStart;
var selectionEnd = textarea.selectionEnd;
var insertedText = response.data;
// Hack for IE
if (Drupal.ocupload.textareaPlugin.isIE) {
var selection = $textarea.data('ocuploadSelection');
selectionStart = selection.selectionStart;
selectionEnd = selection.selectionEnd;
textarea.disabled = false;
textarea.focus();
}
if (selectionStart == selectionEnd) {
insertedText += "\n";
}
textarea.value = textarea.value.substring(0, selectionStart)
+ insertedText
+ textarea.value.substring(selectionEnd, textarea.value.length);
var cursorPosition = selectionStart + insertedText.length;
textarea.selectionStart = cursorPosition;
textarea.selectionEnd = cursorPosition;
// Hack for IE
if (Drupal.ocupload.textareaPlugin.isIE) {
textarea.disabled = true;
$textarea.data('ocuploadSelection', {
selectionStart: cursorPosition,
selectionEnd: cursorPosition,
})
}
}
else {
alert(response.data);
}
};
/**
* Files uploaded handler.
*/
Drupal.ocupload.textareaPlugin.onComplete = function () {
var $textarea = $('#' + Drupal.ocupload.textareaPlugin.activeTextareaId);
$textarea[0].disabled = false;
$textarea.focus();
};
})(jQuery);
;/*})'"*/
;/*})'"*/
(function ($) {
/**
* Retrieves the summary for the first element.
*/
$.fn.drupalGetSummary = function () {
var callback = this.data('summaryCallback');
return (this[0] && callback) ? $.trim(callback(this[0])) : '';
};
/**
* Sets the summary for all matched elements.
*
* @param callback
* Either a function that will be called each time the summary is
* retrieved or a string (which is returned each time).
*/
$.fn.drupalSetSummary = function (callback) {
var self = this;
// To facilitate things, the callback should always be a function. If it's
// not, we wrap it into an anonymous function which just returns the value.
if (typeof callback != 'function') {
var val = callback;
callback = function () { return val; };
}
return this
.data('summaryCallback', callback)
// To prevent duplicate events, the handlers are first removed and then
// (re-)added.
.unbind('formUpdated.summary')
.bind('formUpdated.summary', function () {
self.trigger('summaryUpdated');
})
// The actual summaryUpdated handler doesn't fire when the callback is
// changed, so we have to do this manually.
.trigger('summaryUpdated');
};
/**
* Sends a 'formUpdated' event each time a form element is modified.
*/
Drupal.behaviors.formUpdated = {
attach: function (context) {
// These events are namespaced so that we can remove them later.
var events = 'change.formUpdated click.formUpdated blur.formUpdated keyup.formUpdated';
$(context)
// Since context could be an input element itself, it's added back to
// the jQuery object and filtered again.
.find(':input').andSelf().filter(':input')
// To prevent duplicate events, the handlers are first removed and then
// (re-)added.
.unbind(events).bind(events, function () {
$(this).trigger('formUpdated');
});
}
};
/**
* Prepopulate form fields with information from the visitor cookie.
*/
Drupal.behaviors.fillUserInfoFromCookie = {
attach: function (context, settings) {
$('form.user-info-from-cookie').once('user-info-from-cookie', function () {
var formContext = this;
$.each(['name', 'mail', 'homepage'], function () {
var $element = $('[name=' + this + ']', formContext);
var cookie = $.cookie('Drupal.visitor.' + this);
if ($element.length && cookie) {
$element.val(cookie);
}
});
});
}
};
})(jQuery);
;/*})'"*/
;/*})'"*/
(function ($) {
/**
* Provides Ajax page updating via jQuery $.ajax (Asynchronous JavaScript and XML).
*
* Ajax is a method of making a request via JavaScript while viewing an HTML
* page. The request returns an array of commands encoded in JSON, which is
* then executed to make any changes that are necessary to the page.
*
* Drupal uses this file to enhance form elements with #ajax['path'] and
* #ajax['wrapper'] properties. If set, this file will automatically be included
* to provide Ajax capabilities.
*/
Drupal.ajax = Drupal.ajax || {};
Drupal.settings.urlIsAjaxTrusted = Drupal.settings.urlIsAjaxTrusted || {};
/**
* Attaches the Ajax behavior to each Ajax form element.
*/
Drupal.behaviors.AJAX = {
attach: function (context, settings) {
// Load all Ajax behaviors specified in the settings.
for (var base in settings.ajax) {
if (!$('#' + base + '.ajax-processed').length) {
var element_settings = settings.ajax[base];
if (typeof element_settings.selector == 'undefined') {
element_settings.selector = '#' + base;
}
$(element_settings.selector).each(function () {
element_settings.element = this;
Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
});
$('#' + base).addClass('ajax-processed');
}
}
// Bind Ajax behaviors to all items showing the class.
$('.use-ajax:not(.ajax-processed)').addClass('ajax-processed').each(function () {
var element_settings = {};
// Clicked links look better with the throbber than the progress bar.
element_settings.progress = { 'type': 'throbber' };
// For anchor tags, these will go to the target of the anchor rather
// than the usual location.
if ($(this).attr('href')) {
element_settings.url = $(this).attr('href');
element_settings.event = 'click';
}
var base = $(this).attr('id');
Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
});
// This class means to submit the form to the action using Ajax.
$('.use-ajax-submit:not(.ajax-processed)').addClass('ajax-processed').each(function () {
var element_settings = {};
// Ajax submits specified in this manner automatically submit to the
// normal form action.
element_settings.url = $(this.form).attr('action');
// Form submit button clicks need to tell the form what was clicked so
// it gets passed in the POST request.
element_settings.setClick = true;
// Form buttons use the 'click' event rather than mousedown.
element_settings.event = 'click';
// Clicked form buttons look better with the throbber than the progress bar.
element_settings.progress = { 'type': 'throbber' };
var base = $(this).attr('id');
Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
});
}
};
/**
* Ajax object.
*
* All Ajax objects on a page are accessible through the global Drupal.ajax
* object and are keyed by the submit button's ID. You can access them from
* your module's JavaScript file to override properties or functions.
*
* For example, if your Ajax enabled button has the ID 'edit-submit', you can
* redefine the function that is called to insert the new content like this
* (inside a Drupal.behaviors attach block):
* @code
* Drupal.behaviors.myCustomAJAXStuff = {
* attach: function (context, settings) {
* Drupal.ajax['edit-submit'].commands.insert = function (ajax, response, status) {
* new_content = $(response.data);
* $('#my-wrapper').append(new_content);
* alert('New content was appended to #my-wrapper');
* }
* }
* };
* @endcode
*/
Drupal.ajax = function (base, element, element_settings) {
var defaults = {
url: 'system/ajax',
event: 'mousedown',
keypress: true,
selector: '#' + base,
effect: 'none',
speed: 'none',
method: 'replaceWith',
progress: {
type: 'throbber',
message: Drupal.t('Please wait...')
},
submit: {
'js': true
}
};
$.extend(this, defaults, element_settings);
this.element = element;
this.element_settings = element_settings;
// Replacing 'nojs' with 'ajax' in the URL allows for an easy method to let
// the server detect when it needs to degrade gracefully.
// There are five scenarios to check for:
// 1. /nojs/
// 2. /nojs$ - The end of a URL string.
// 3. /nojs? - Followed by a query (with clean URLs enabled).
// E.g.: path/nojs?destination=foobar
// 4. /nojs& - Followed by a query (without clean URLs enabled).
// E.g.: ?q=path/nojs&destination=foobar
// 5. /nojs# - Followed by a fragment.
// E.g.: path/nojs#myfragment
this.url = element_settings.url.replace(/\/nojs(\/|$|\?|&|#)/g, '/ajax$1');
// If the 'nojs' version of the URL is trusted, also trust the 'ajax' version.
if (Drupal.settings.urlIsAjaxTrusted[element_settings.url]) {
Drupal.settings.urlIsAjaxTrusted[this.url] = true;
}
this.wrapper = '#' + element_settings.wrapper;
// If there isn't a form, jQuery.ajax() will be used instead, allowing us to
// bind Ajax to links as well.
if (this.element.form) {
this.form = $(this.element.form);
}
// Set the options for the ajaxSubmit function.
// The 'this' variable will not persist inside of the options object.
var ajax = this;
ajax.options = {
url: Drupal.sanitizeAjaxUrl(ajax.url),
data: ajax.submit,
beforeSerialize: function (element_settings, options) {
return ajax.beforeSerialize(element_settings, options);
},
beforeSubmit: function (form_values, element_settings, options) {
ajax.ajaxing = true;
return ajax.beforeSubmit(form_values, element_settings, options);
},
beforeSend: function (xmlhttprequest, options) {
ajax.ajaxing = true;
return ajax.beforeSend(xmlhttprequest, options);
},
success: function (response, status, xmlhttprequest) {
// Sanity check for browser support (object expected).
// When using iFrame uploads, responses must be returned as a string.
if (typeof response == 'string') {
response = $.parseJSON(response);
}
// Prior to invoking the response's commands, verify that they can be
// trusted by checking for a response header. See
// ajax_set_verification_header() for details.
// - Empty responses are harmless so can bypass verification. This avoids
// an alert message for server-generated no-op responses that skip Ajax
// rendering.
// - Ajax objects with trusted URLs (e.g., ones defined server-side via
// #ajax) can bypass header verification. This is especially useful for
// Ajax with multipart forms. Because IFRAME transport is used, the
// response headers cannot be accessed for verification.
if (response !== null && !Drupal.settings.urlIsAjaxTrusted[ajax.url]) {
if (xmlhttprequest.getResponseHeader('X-Drupal-Ajax-Token') !== '1') {
var customMessage = Drupal.t("The response failed verification so will not be processed.");
return ajax.error(xmlhttprequest, ajax.url, customMessage);
}
}
return ajax.success(response, status);
},
complete: function (xmlhttprequest, status) {
ajax.ajaxing = false;
if (status == 'error' || status == 'parsererror') {
return ajax.error(xmlhttprequest, ajax.url);
}
},
dataType: 'json',
jsonp: false,
type: 'POST'
};
// For multipart forms (e.g., file uploads), jQuery Form targets the form
// submission to an iframe instead of using an XHR object. The initial "src"
// of the iframe, prior to the form submission, is set to options.iframeSrc.
// "about:blank" is the semantically correct, standards-compliant, way to
// initialize a blank iframe; however, some old IE versions (possibly only 6)
// incorrectly report a mixed content warning when iframes with an
// "about:blank" src are added to a parent document with an https:// origin.
// jQuery Form works around this by defaulting to "javascript:false" instead,
// but that breaks on Chrome 83, so here we force the semantically correct
// behavior for all browsers except old IE.
// @see https://www.drupal.org/project/drupal/issues/3143016
// @see https://github.com/jquery-form/form/blob/df9cb101b9c9c085c8d75ad980c7ff1cf62063a1/jquery.form.js#L68
// @see https://bugs.chromium.org/p/chromium/issues/detail?id=1084874
// @see https://html.spec.whatwg.org/multipage/browsers.html#creating-browsing-contexts
// @see https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
if (navigator.userAgent.indexOf("MSIE") === -1) {
ajax.options.iframeSrc = 'about:blank';
}
// Bind the ajaxSubmit function to the element event.
$(ajax.element).bind(element_settings.event, function (event) {
if (!Drupal.settings.urlIsAjaxTrusted[ajax.url] && !Drupal.urlIsLocal(ajax.url)) {
throw new Error(Drupal.t('The callback URL is not local and not trusted: !url', {'!url': ajax.url}));
}
return ajax.eventResponse(this, event);
});
// If necessary, enable keyboard submission so that Ajax behaviors
// can be triggered through keyboard input as well as e.g. a mousedown
// action.
if (element_settings.keypress) {
$(ajax.element).keypress(function (event) {
return ajax.keypressResponse(this, event);
});
}
// If necessary, prevent the browser default action of an additional event.
// For example, prevent the browser default action of a click, even if the
// AJAX behavior binds to mousedown.
if (element_settings.prevent) {
$(ajax.element).bind(element_settings.prevent, false);
}
};
/**
* Handle a key press.
*
* The Ajax object will, if instructed, bind to a key press response. This
* will test to see if the key press is valid to trigger this event and
* if it is, trigger it for us and prevent other keypresses from triggering.
* In this case we're handling RETURN and SPACEBAR keypresses (event codes 13
* and 32. RETURN is often used to submit a form when in a textfield, and
* SPACE is often used to activate an element without submitting.
*/
Drupal.ajax.prototype.keypressResponse = function (element, event) {
// Create a synonym for this to reduce code confusion.
var ajax = this;
// Detect enter key and space bar and allow the standard response for them,
// except for form elements of type 'text' and 'textarea', where the
// spacebar activation causes inappropriate activation if #ajax['keypress'] is
// TRUE. On a text-type widget a space should always be a space.
if (event.which == 13 || (event.which == 32 && element.type != 'text' && element.type != 'textarea')) {
$(ajax.element_settings.element).trigger(ajax.element_settings.event);
return false;
}
};
/**
* Handle an event that triggers an Ajax response.
*
* When an event that triggers an Ajax response happens, this method will
* perform the actual Ajax call. It is bound to the event using
* bind() in the constructor, and it uses the options specified on the
* ajax object.
*/
Drupal.ajax.prototype.eventResponse = function (element, event) {
// Create a synonym for this to reduce code confusion.
var ajax = this;
// Do not perform another ajax command if one is already in progress.
if (ajax.ajaxing) {
return false;
}
try {
if (ajax.form) {
// If setClick is set, we must set this to ensure that the button's
// value is passed.
if (ajax.setClick) {
// Mark the clicked button. 'form.clk' is a special variable for
// ajaxSubmit that tells the system which element got clicked to
// trigger the submit. Without it there would be no 'op' or
// equivalent.
element.form.clk = element;
}
ajax.form.ajaxSubmit(ajax.options);
}
else {
ajax.beforeSerialize(ajax.element, ajax.options);
$.ajax(ajax.options);
}
}
catch (e) {
// Unset the ajax.ajaxing flag here because it won't be unset during
// the complete response.
ajax.ajaxing = false;
alert("An error occurred while attempting to process " + ajax.options.url + ": " + e.message);
}
// For radio/checkbox, allow the default event. On IE, this means letting
// it actually check the box.
if (typeof element.type != 'undefined' && (element.type == 'checkbox' || element.type == 'radio')) {
return true;
}
else {
return false;
}
};
/**
* Handler for the form serialization.
*
* Runs before the beforeSend() handler (see below), and unlike that one, runs
* before field data is collected.
*/
Drupal.ajax.prototype.beforeSerialize = function (element, options) {
// Allow detaching behaviors to update field values before collecting them.
// This is only needed when field values are added to the POST data, so only
// when there is a form such that this.form.ajaxSubmit() is used instead of
// $.ajax(). When there is no form and $.ajax() is used, beforeSerialize()
// isn't called, but don't rely on that: explicitly check this.form.
if (this.form) {
var settings = this.settings || Drupal.settings;
Drupal.detachBehaviors(this.form, settings, 'serialize');
}
// Prevent duplicate HTML ids in the returned markup.
// @see drupal_html_id()
options.data['ajax_html_ids[]'] = [];
$('[id]').each(function () {
options.data['ajax_html_ids[]'].push(this.id);
});
// Allow Drupal to return new JavaScript and CSS files to load without
// returning the ones already loaded.
// @see ajax_base_page_theme()
// @see drupal_get_css()
// @see drupal_get_js()
options.data['ajax_page_state[theme]'] = Drupal.settings.ajaxPageState.theme;
options.data['ajax_page_state[theme_token]'] = Drupal.settings.ajaxPageState.theme_token;
for (var key in Drupal.settings.ajaxPageState.css) {
options.data['ajax_page_state[css][' + key + ']'] = 1;
}
for (var key in Drupal.settings.ajaxPageState.js) {
options.data['ajax_page_state[js][' + key + ']'] = 1;
}
};
/**
* Modify form values prior to form submission.
*/
Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) {
// This function is left empty to make it simple to override for modules
// that wish to add functionality here.
};
/**
* Prepare the Ajax request before it is sent.
*/
Drupal.ajax.prototype.beforeSend = function (xmlhttprequest, options) {
// For forms without file inputs, the jQuery Form plugin serializes the form
// values, and then calls jQuery's $.ajax() function, which invokes this
// handler. In this circumstance, options.extraData is never used. For forms
// with file inputs, the jQuery Form plugin uses the browser's normal form
// submission mechanism, but captures the response in a hidden IFRAME. In this
// circumstance, it calls this handler first, and then appends hidden fields
// to the form to submit the values in options.extraData. There is no simple
// way to know which submission mechanism will be used, so we add to extraData
// regardless, and allow it to be ignored in the former case.
if (this.form) {
options.extraData = options.extraData || {};
// Let the server know when the IFRAME submission mechanism is used. The
// server can use this information to wrap the JSON response in a TEXTAREA,
// as per http://jquery.malsup.com/form/#file-upload.
options.extraData.ajax_iframe_upload = '1';
// The triggering element is about to be disabled (see below), but if it
// contains a value (e.g., a checkbox, textfield, select, etc.), ensure that
// value is included in the submission. As per above, submissions that use
// $.ajax() are already serialized prior to the element being disabled, so
// this is only needed for IFRAME submissions.
var v = $.fieldValue(this.element);
if (v !== null) {
options.extraData[this.element.name] = Drupal.checkPlain(v);
}
}
// Disable the element that received the change to prevent user interface
// interaction while the Ajax request is in progress. ajax.ajaxing prevents
// the element from triggering a new request, but does not prevent the user
// from changing its value.
$(this.element).addClass('progress-disabled').attr('disabled', true);
// Insert progressbar or throbber.
if (this.progress.type == 'bar') {
var progressBar = new Drupal.progressBar('ajax-progress-' + this.element.id, $.noop, this.progress.method, $.noop);
if (this.progress.message) {
progressBar.setProgress(-1, this.progress.message);
}
if (this.progress.url) {
progressBar.startMonitoring(this.progress.url, this.progress.interval || 1500);
}
this.progress.element = $(progressBar.element).addClass('ajax-progress ajax-progress-bar');
this.progress.object = progressBar;
$(this.element).after(this.progress.element);
}
else if (this.progress.type == 'throbber') {
this.progress.element = $('');
if (this.progress.message) {
$('.throbber', this.progress.element).after('' + this.progress.message + '
');
}
$(this.element).after(this.progress.element);
}
};
/**
* Handler for the form redirection completion.
*/
Drupal.ajax.prototype.success = function (response, status) {
// Remove the progress element.
if (this.progress.element) {
$(this.progress.element).remove();
}
if (this.progress.object) {
this.progress.object.stopMonitoring();
}
$(this.element).removeClass('progress-disabled').removeAttr('disabled');
Drupal.freezeHeight();
for (var i in response) {
if (response.hasOwnProperty(i) && response[i]['command'] && this.commands[response[i]['command']]) {
this.commands[response[i]['command']](this, response[i], status);
}
}
// Reattach behaviors, if they were detached in beforeSerialize(). The
// attachBehaviors() called on the new content from processing the response
// commands is not sufficient, because behaviors from the entire form need
// to be reattached.
if (this.form) {
var settings = this.settings || Drupal.settings;
Drupal.attachBehaviors(this.form, settings);
}
Drupal.unfreezeHeight();
// Remove any response-specific settings so they don't get used on the next
// call by mistake.
this.settings = null;
};
/**
* Build an effect object which tells us how to apply the effect when adding new HTML.
*/
Drupal.ajax.prototype.getEffect = function (response) {
var type = response.effect || this.effect;
var speed = response.speed || this.speed;
var effect = {};
if (type == 'none') {
effect.showEffect = 'show';
effect.hideEffect = 'hide';
effect.showSpeed = '';
}
else if (type == 'fade') {
effect.showEffect = 'fadeIn';
effect.hideEffect = 'fadeOut';
effect.showSpeed = speed;
}
else {
effect.showEffect = type + 'Toggle';
effect.hideEffect = type + 'Toggle';
effect.showSpeed = speed;
}
return effect;
};
/**
* Handler for the form redirection error.
*/
Drupal.ajax.prototype.error = function (xmlhttprequest, uri, customMessage) {
Drupal.displayAjaxError(Drupal.ajaxError(xmlhttprequest, uri, customMessage));
// Remove the progress element.
if (this.progress.element) {
$(this.progress.element).remove();
}
if (this.progress.object) {
this.progress.object.stopMonitoring();
}
// Undo hide.
$(this.wrapper).show();
// Re-enable the element.
$(this.element).removeClass('progress-disabled').removeAttr('disabled');
// Reattach behaviors, if they were detached in beforeSerialize().
if (this.form) {
var settings = this.settings || Drupal.settings;
Drupal.attachBehaviors(this.form, settings);
}
};
/**
* Provide a series of commands that the server can request the client perform.
*/
Drupal.ajax.prototype.commands = {
/**
* Command to insert new content into the DOM.
*/
insert: function (ajax, response, status) {
// Get information from the response. If it is not there, default to
// our presets.
var wrapper = response.selector ? $(response.selector) : $(ajax.wrapper);
var method = response.method || ajax.method;
var effect = ajax.getEffect(response);
// We don't know what response.data contains: it might be a string of text
// without HTML, so don't rely on jQuery correctly iterpreting
// $(response.data) as new HTML rather than a CSS selector. Also, if
// response.data contains top-level text nodes, they get lost with either
// $(response.data) or $('').replaceWith(response.data).
var new_content_wrapped = $('').html(response.data);
var new_content = new_content_wrapped.contents();
// For legacy reasons, the effects processing code assumes that new_content
// consists of a single top-level element. Also, it has not been
// sufficiently tested whether attachBehaviors() can be successfully called
// with a context object that includes top-level text nodes. However, to
// give developers full control of the HTML appearing in the page, and to
// enable Ajax content to be inserted in places where DIV elements are not
// allowed (e.g., within TABLE, TR, and SPAN parents), we check if the new
// content satisfies the requirement of a single top-level element, and
// only use the container DIV created above when it doesn't. For more
// information, please see http://drupal.org/node/736066.
if (new_content.length != 1 || new_content.get(0).nodeType != 1) {
new_content = new_content_wrapped;
}
// If removing content from the wrapper, detach behaviors first.
switch (method) {
case 'html':
case 'replaceWith':
case 'replaceAll':
case 'empty':
case 'remove':
var settings = response.settings || ajax.settings || Drupal.settings;
Drupal.detachBehaviors(wrapper, settings);
}
// Add the new content to the page.
wrapper[method](new_content);
// Immediately hide the new content if we're using any effects.
if (effect.showEffect != 'show') {
new_content.hide();
}
// Determine which effect to use and what content will receive the
// effect, then show the new content.
if ($('.ajax-new-content', new_content).length > 0) {
$('.ajax-new-content', new_content).hide();
new_content.show();
$('.ajax-new-content', new_content)[effect.showEffect](effect.showSpeed);
}
else if (effect.showEffect != 'show') {
new_content[effect.showEffect](effect.showSpeed);
}
// Attach all JavaScript behaviors to the new content, if it was successfully
// added to the page, this if statement allows #ajax['wrapper'] to be
// optional.
if (new_content.parents('html').length > 0) {
// Apply any settings from the returned JSON if available.
var settings = response.settings || ajax.settings || Drupal.settings;
Drupal.attachBehaviors(new_content, settings);
}
},
/**
* Command to remove a chunk from the page.
*/
remove: function (ajax, response, status) {
var settings = response.settings || ajax.settings || Drupal.settings;
Drupal.detachBehaviors($(response.selector), settings);
$(response.selector).remove();
},
/**
* Command to mark a chunk changed.
*/
changed: function (ajax, response, status) {
if (!$(response.selector).hasClass('ajax-changed')) {
$(response.selector).addClass('ajax-changed');
if (response.asterisk) {
$(response.selector).find(response.asterisk).append(' * ');
}
}
},
/**
* Command to provide an alert.
*/
alert: function (ajax, response, status) {
alert(response.text, response.title);
},
/**
* Command to provide the jQuery css() function.
*/
css: function (ajax, response, status) {
$(response.selector).css(response.argument);
},
/**
* Command to set the settings that will be used for other commands in this response.
*/
settings: function (ajax, response, status) {
if (response.merge) {
$.extend(true, Drupal.settings, response.settings);
}
else {
ajax.settings = response.settings;
}
},
/**
* Command to attach data using jQuery's data API.
*/
data: function (ajax, response, status) {
$(response.selector).data(response.name, response.value);
},
/**
* Command to apply a jQuery method.
*/
invoke: function (ajax, response, status) {
var $element = $(response.selector);
$element[response.method].apply($element, response.arguments);
},
/**
* Command to restripe a table.
*/
restripe: function (ajax, response, status) {
// :even and :odd are reversed because jQuery counts from 0 and
// we count from 1, so we're out of sync.
// Match immediate children of the parent element to allow nesting.
$('> tbody > tr:visible, > tr:visible', $(response.selector))
.removeClass('odd even')
.filter(':even').addClass('odd').end()
.filter(':odd').addClass('even');
},
/**
* Command to add css.
*
* Uses the proprietary addImport method if available as browsers which
* support that method ignore @import statements in dynamically added
* stylesheets.
*/
add_css: function (ajax, response, status) {
// Add the styles in the normal way.
$('head').prepend(response.data);
// Add imports in the styles using the addImport method if available.
var match, importMatch = /^@import url\("(.*)"\);$/igm;
if (document.styleSheets[0].addImport && importMatch.test(response.data)) {
importMatch.lastIndex = 0;
while (match = importMatch.exec(response.data)) {
document.styleSheets[0].addImport(match[1]);
}
}
},
/**
* Command to update a form's build ID.
*/
updateBuildId: function(ajax, response, status) {
$('input[name="form_build_id"][value="' + response['old'] + '"]').val(response['new']);
}
};
})(jQuery);
;/*})'"*/
;/*})'"*/
(function (D) {
var beforeSerialize = D.ajax.prototype.beforeSerialize;
D.ajax.prototype.beforeSerialize = function (element, options) {
beforeSerialize.call(this, element, options);
options.data['ajax_page_state[jquery_version]'] = D.settings.ajaxPageState.jquery_version;
}
})(Drupal);
;/*})'"*/
;/*})'"*/