diff --git a/.gitignore b/.gitignore index 86eb146..8157fcc 100755 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ .elasticbeanstalk/ .DS_Store /public/data +/public/assets *.png coverage .tags diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index aa51d07..a75c088 100755 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -13,11 +13,9 @@ //= require jquery //= require jquery_ujs //= require turbolinks -//= require wysiwyg/wysihtml5-0.3.0.js //= require jquery.min.js //= require jquery.scrollUp.js //= require bootstrap.js -//= require wysiwyg/bootstrap-wysihtml5.js //= require bootstrap-colorpicker.js //= require date-picker/date.js //= require date-picker/daterangepicker.js diff --git a/app/assets/javascripts/select2.js b/app/assets/javascripts/select2.js deleted file mode 100755 index 213f4cf..0000000 --- a/app/assets/javascripts/select2.js +++ /dev/null @@ -1,2407 +0,0 @@ -/* - Copyright 2012 Igor Vaynberg - - Version: 3.2 Timestamp: Mon Sep 10 10:38:04 PDT 2012 - - Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in - compliance with the License. You may obtain a copy of the License in the LICENSE file, or at: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software distributed under the License is - distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and limitations under the License. - */ - (function ($) { - if(typeof $.fn.each2 == "undefined"){ - $.fn.extend({ - /* - * 4-10 times faster .each replacement - * use it carefully, as it overrides jQuery context of element on each iteration - */ - each2 : function (c) { - var j = $([0]), i = -1, l = this.length; - while ( - ++i < l - && (j.context = j[0] = this[i]) - && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object - ); - return this; - } - }); - } -})(jQuery); - -(function ($, undefined) { - "use strict"; - /*global document, window, jQuery, console */ - - if (window.Select2 !== undefined) { - return; - } - - var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer; - - KEY = { - TAB: 9, - ENTER: 13, - ESC: 27, - SPACE: 32, - LEFT: 37, - UP: 38, - RIGHT: 39, - DOWN: 40, - SHIFT: 16, - CTRL: 17, - ALT: 18, - PAGE_UP: 33, - PAGE_DOWN: 34, - HOME: 36, - END: 35, - BACKSPACE: 8, - DELETE: 46, - isArrow: function (k) { - k = k.which ? k.which : k; - switch (k) { - case KEY.LEFT: - case KEY.RIGHT: - case KEY.UP: - case KEY.DOWN: - return true; - } - return false; - }, - isControl: function (e) { - var k = e.which; - switch (k) { - case KEY.SHIFT: - case KEY.CTRL: - case KEY.ALT: - return true; - } - - if (e.metaKey) return true; - - return false; - }, - isFunctionKey: function (k) { - k = k.which ? k.which : k; - return k >= 112 && k <= 123; - } - }; - - nextUid=(function() { var counter=1; return function() { return counter++; }; }()); - - function indexOf(value, array) { - var i = 0, l = array.length, v; - - if (typeof value === "undefined") { - return -1; - } - - if (value.constructor === String) { - for (; i < l; i = i + 1) if (value.localeCompare(array[i]) === 0) return i; - } else { - for (; i < l; i = i + 1) { - v = array[i]; - if (v.constructor === String) { - if (v.localeCompare(value) === 0) return i; - } else { - if (v === value) return i; - } - } - } - return -1; - } - - /** - * Compares equality of a and b taking into account that a and b may be strings, in which case localeCompare is used - * @param a - * @param b - */ - function equal(a, b) { - if (a === b) return true; - if (a === undefined || b === undefined) return false; - if (a === null || b === null) return false; - if (a.constructor === String) return a.localeCompare(b) === 0; - if (b.constructor === String) return b.localeCompare(a) === 0; - return false; - } - - /** - * Splits the string into an array of values, trimming each value. An empty array is returned for nulls or empty - * strings - * @param string - * @param separator - */ - function splitVal(string, separator) { - var val, i, l; - if (string === null || string.length < 1) return []; - val = string.split(separator); - for (i = 0, l = val.length; i < l; i = i + 1) val[i] = $.trim(val[i]); - return val; - } - - function getSideBorderPadding(element) { - return element.outerWidth() - element.width(); - } - - function installKeyUpChangeEvent(element) { - var key="keyup-change-value"; - element.bind("keydown", function () { - if ($.data(element, key) === undefined) { - $.data(element, key, element.val()); - } - }); - element.bind("keyup", function () { - var val= $.data(element, key); - if (val !== undefined && element.val() !== val) { - $.removeData(element, key); - element.trigger("keyup-change"); - } - }); - } - - $(document).delegate("body", "mousemove", function (e) { - $.data(document, "select2-lastpos", {x: e.pageX, y: e.pageY}); - }); - - /** - * filters mouse events so an event is fired only if the mouse moved. - * - * filters out mouse events that occur when mouse is stationary but - * the elements under the pointer are scrolled. - */ - function installFilteredMouseMove(element) { - element.bind("mousemove", function (e) { - var lastpos = $.data(document, "select2-lastpos"); - if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) { - $(e.target).trigger("mousemove-filtered", e); - } - }); - } - - /** - * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made - * within the last quietMillis milliseconds. - * - * @param quietMillis number of milliseconds to wait before invoking fn - * @param fn function to be debounced - * @param ctx object to be used as this reference within fn - * @return debounced version of fn - */ - function debounce(quietMillis, fn, ctx) { - ctx = ctx || undefined; - var timeout; - return function () { - var args = arguments; - window.clearTimeout(timeout); - timeout = window.setTimeout(function() { - fn.apply(ctx, args); - }, quietMillis); - }; - } - - /** - * A simple implementation of a thunk - * @param formula function used to lazily initialize the thunk - * @return {Function} - */ - function thunk(formula) { - var evaluated = false, - value; - return function() { - if (evaluated === false) { value = formula(); evaluated = true; } - return value; - }; - }; - - function installDebouncedScroll(threshold, element) { - var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);}); - element.bind("scroll", function (e) { - if (indexOf(e.target, element.get()) >= 0) notify(e); - }); - } - - function killEvent(event) { - event.preventDefault(); - event.stopPropagation(); - } - - function measureTextWidth(e) { - if (!sizer){ - var style = e[0].currentStyle || window.getComputedStyle(e[0], null); - sizer = $("
").css({ - position: "absolute", - left: "-10000px", - top: "-10000px", - display: "none", - fontSize: style.fontSize, - fontFamily: style.fontFamily, - fontStyle: style.fontStyle, - fontWeight: style.fontWeight, - letterSpacing: style.letterSpacing, - textTransform: style.textTransform, - whiteSpace: "nowrap" - }); - $("body").append(sizer); - } - sizer.text(e.val()); - return sizer.width(); - } - - function markMatch(text, term, markup) { - var match=text.toUpperCase().indexOf(term.toUpperCase()), - tl=term.length; - - if (match<0) { - markup.push(text); - return; - } - - markup.push(text.substring(0, match)); - markup.push(""); - markup.push(text.substring(match, match + tl)); - markup.push(""); - markup.push(text.substring(match + tl, text.length)); - } - - /** - * Produces an ajax-based query function - * - * @param options object containing configuration paramters - * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax - * @param options.url url for the data - * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url. - * @param options.dataType request data type: ajax, jsonp, other datatatypes supported by jQuery's $.ajax function or the transport function if specified - * @param options.traditional a boolean flag that should be true if you wish to use the traditional style of param serialization for the ajax request - * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often - * @param options.results a function(remoteData, pageNumber) that converts data returned form the remote request to the format expected by Select2. - * The expected format is an object containing the following keys: - * results array of objects that will be used as choices - * more (optional) boolean indicating whether there are more results available - * Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true} - */ - function ajax(options) { - var timeout, // current scheduled but not yet executed request - requestSequence = 0, // sequence used to drop out-of-order responses - handler = null, - quietMillis = options.quietMillis || 100; - - return function (query) { - window.clearTimeout(timeout); - timeout = window.setTimeout(function () { - requestSequence += 1; // increment the sequence - var requestNumber = requestSequence, // this request's sequence number - data = options.data, // ajax data function - transport = options.transport || $.ajax, - traditional = options.traditional || false, - type = options.type || 'GET'; // set type of request (GET or POST) - - data = data.call(this, query.term, query.page, query.context); - - if( null !== handler) { handler.abort(); } - - handler = transport.call(null, { - url: options.url, - dataType: options.dataType, - data: data, - type: type, - traditional: traditional, - success: function (data) { - if (requestNumber < requestSequence) { - return; - } - // TODO 3.0 - replace query.page with query so users have access to term, page, etc. - var results = options.results(data, query.page); - query.callback(results); - } - }); - }, quietMillis); - }; - } - - /** - * Produces a query function that works with a local array - * - * @param options object containing configuration parameters. The options parameter can either be an array or an - * object. - * - * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys. - * - * If the object form is used ti is assumed that it contains 'data' and 'text' keys. The 'data' key should contain - * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text' - * key can either be a String in which case it is expected that each element in the 'data' array has a key with the - * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract - * the text. - */ - function local(options) { - var data = options, // data elements - dataText, - text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search - - if (!$.isArray(data)) { - text = data.text; - // if text is not a function we assume it to be a key name - if (!$.isFunction(text)) { - dataText = data.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available - text = function (item) { return item[dataText]; }; - } - data = data.results; - } - - return function (query) { - var t = query.term, filtered = { results: [] }, process; - if (t === "") { - query.callback({results: data}); - return; - } - - process = function(datum, collection) { - var group, attr; - datum = datum[0]; - if (datum.children) { - group = {}; - for (attr in datum) { - if (datum.hasOwnProperty(attr)) group[attr]=datum[attr]; - } - group.children=[]; - $(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); }); - if (group.children.length) { - collection.push(group); - } - } else { - if (query.matcher(t, text(datum))) { - collection.push(datum); - } - } - }; - - $(data).each2(function(i, datum) { process(datum, filtered.results); }); - query.callback(filtered); - }; - } - - // TODO javadoc - function tags(data) { - // TODO even for a function we should probably return a wrapper that does the same object/string check as - // the function for arrays. otherwise only functions that return objects are supported. - if ($.isFunction(data)) { - return data; - } - - // if not a function we assume it to be an array - - return function (query) { - var t = query.term, filtered = {results: []}; - $(data).each(function () { - var isObject = this.text !== undefined, - text = isObject ? this.text : this; - if (t === "" || query.matcher(t, text)) { - filtered.results.push(isObject ? this : {id: this, text: this}); - } - }); - query.callback(filtered); - }; - } - - /** - * Checks if the formatter function should be used. - * - * Throws an error if it is not a function. Returns true if it should be used, - * false if no formatting should be performed. - * - * @param formatter - */ - function checkFormatter(formatter, formatterName) { - if ($.isFunction(formatter)) return true; - if (!formatter) return false; - throw new Error("formatterName must be a function or a falsy value"); - } - - function evaluate(val) { - return $.isFunction(val) ? val() : val; - } - - function countResults(results) { - var count = 0; - $.each(results, function(i, item) { - if (item.children) { - count += countResults(item.children); - } else { - count++; - } - }); - return count; - } - - /** - * Default tokenizer. This function uses breaks the input on substring match of any string from the - * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those - * two options have to be defined in order for the tokenizer to work. - * - * @param input text user has typed so far or pasted into the search field - * @param selection currently selected choices - * @param selectCallback function(choice) callback tho add the choice to selection - * @param opts select2's opts - * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value - */ - function defaultTokenizer(input, selection, selectCallback, opts) { - var original = input, // store the original so we can compare and know if we need to tell the search to update its text - dupe = false, // check for whether a token we extracted represents a duplicate selected choice - token, // token - index, // position at which the separator was found - i, l, // looping variables - separator; // the matched separator - - if (!opts.createSearchChoice || !opts.tokenSeparators || opts.tokenSeparators.length < 1) return undefined; - - while (true) { - index = -1; - - for (i = 0, l = opts.tokenSeparators.length; i < l; i++) { - separator = opts.tokenSeparators[i]; - index = input.indexOf(separator); - if (index >= 0) break; - } - - if (index < 0) break; // did not find any token separator in the input string, bail - - token = input.substring(0, index); - input = input.substring(index + separator.length); - - if (token.length > 0) { - token = opts.createSearchChoice(token, selection); - if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) { - dupe = false; - for (i = 0, l = selection.length; i < l; i++) { - if (equal(opts.id(token), opts.id(selection[i]))) { - dupe = true; break; - } - } - - if (!dupe) selectCallback(token); - } - } - } - - if (original.localeCompare(input) != 0) return input; - } - - /** - * blurs any Select2 container that has focus when an element outside them was clicked or received focus - * - * also takes care of clicks on label tags that point to the source element - */ - $(document).ready(function () { - $(document).delegate("body", "mousedown touchend", function (e) { - var target = $(e.target).closest("div.select2-container").get(0), attr; - if (target) { - $(document).find("div.select2-container-active").each(function () { - if (this !== target) $(this).data("select2").blur(); - }); - } else { - target = $(e.target).closest("div.select2-drop").get(0); - $(document).find("div.select2-drop-active").each(function () { - if (this !== target) $(this).data("select2").blur(); - }); - } - - target=$(e.target); - attr = target.attr("for"); - if ("LABEL" === e.target.tagName && attr && attr.length > 0) { - target = $("#"+attr); - target = target.data("select2"); - if (target !== undefined) { target.focus(); e.preventDefault();} - } - }); - }); - - /** - * Creates a new class - * - * @param superClass - * @param methods - */ - function clazz(SuperClass, methods) { - var constructor = function () {}; - constructor.prototype = new SuperClass; - constructor.prototype.constructor = constructor; - constructor.prototype.parent = SuperClass.prototype; - constructor.prototype = $.extend(constructor.prototype, methods); - return constructor; - } - - AbstractSelect2 = clazz(Object, { - - // abstract - bind: function (func) { - var self = this; - return function () { - func.apply(self, arguments); - }; - }, - - // abstract - init: function (opts) { - var results, search, resultsSelector = ".select2-results"; - - // prepare options - this.opts = opts = this.prepareOpts(opts); - - this.id=opts.id; - - // destroy if called on an existing component - if (opts.element.data("select2") !== undefined && - opts.element.data("select2") !== null) { - this.destroy(); - } - - this.enabled=true; - this.container = this.createContainer(); - - this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid()); - this.containerSelector="#"+this.containerId.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1'); - this.container.attr("id", this.containerId); - - // cache the body so future lookups are cheap - this.body = thunk(function() { return opts.element.closest("body"); }); - - if (opts.element.attr("class") !== undefined) { - this.container.addClass(opts.element.attr("class").replace(/validate\[[\S ]+] ?/, '')); - } - - this.container.css(evaluate(opts.containerCss)); - this.container.addClass(evaluate(opts.containerCssClass)); - - // swap container for the element - this.opts.element - .data("select2", this) - .hide() - .before(this.container); - this.container.data("select2", this); - - this.dropdown = this.container.find(".select2-drop"); - this.dropdown.addClass(evaluate(opts.dropdownCssClass)); - this.dropdown.data("select2", this); - - this.results = results = this.container.find(resultsSelector); - this.search = search = this.container.find("input.select2-input"); - - search.attr("tabIndex", this.opts.element.attr("tabIndex")); - - this.resultsPage = 0; - this.context = null; - - // initialize the container - this.initContainer(); - this.initContainerWidth(); - - installFilteredMouseMove(this.results); - this.dropdown.delegate(resultsSelector, "mousemove-filtered", this.bind(this.highlightUnderEvent)); - - installDebouncedScroll(80, this.results); - this.dropdown.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded)); - - // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel - if ($.fn.mousewheel) { - results.mousewheel(function (e, delta, deltaX, deltaY) { - var top = results.scrollTop(), height; - if (deltaY > 0 && top - deltaY <= 0) { - results.scrollTop(0); - killEvent(e); - } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) { - results.scrollTop(results.get(0).scrollHeight - results.height()); - killEvent(e); - } - }); - } - - installKeyUpChangeEvent(search); - search.bind("keyup-change", this.bind(this.updateResults)); - search.bind("focus", function () { search.addClass("select2-focused"); if (search.val() === " ") search.val(""); }); - search.bind("blur", function () { search.removeClass("select2-focused");}); - - this.dropdown.delegate(resultsSelector, "mouseup", this.bind(function (e) { - if ($(e.target).closest(".select2-result-selectable:not(.select2-disabled)").length > 0) { - this.highlightUnderEvent(e); - this.selectHighlighted(e); - } else { - this.focusSearch(); - } - killEvent(e); - })); - - // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening - // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's - // dom it will trigger the popup close, which is not what we want - this.dropdown.bind("click mouseup mousedown", function (e) { e.stopPropagation(); }); - - if ($.isFunction(this.opts.initSelection)) { - // initialize selection based on the current value of the source element - this.initSelection(); - - // if the user has provided a function that can set selection based on the value of the source element - // we monitor the change event on the element and trigger it, allowing for two way synchronization - this.monitorSource(); - } - - if (opts.element.is(":disabled") || opts.element.is("[readonly='readonly']")) this.disable(); - }, - - // abstract - destroy: function () { - var select2 = this.opts.element.data("select2"); - if (select2 !== undefined) { - select2.container.remove(); - select2.dropdown.remove(); - select2.opts.element - .removeData("select2") - .unbind(".select2") - .show(); - } - }, - - // abstract - prepareOpts: function (opts) { - var element, select, idKey, ajaxUrl; - - element = opts.element; - - if (element.get(0).tagName.toLowerCase() === "select") { - this.select = select = opts.element; - } - - if (select) { - // these options are not allowed when attached to a select because they are picked up off the element itself - $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () { - if (this in opts) { - throw new Error("Option '" + this + "' is not allowed for Select2 when attached to a " , - " " , - " " , - ""].join("")); - return container; - }, - - // single - opening: function () { - this.search.show(); - this.parent.opening.apply(this, arguments); - this.dropdown.removeClass("select2-offscreen"); - }, - - // single - close: function () { - if (!this.opened()) return; - this.parent.close.apply(this, arguments); - this.dropdown.removeAttr("style").addClass("select2-offscreen").insertAfter(this.selection).show(); - }, - - // single - focus: function () { - this.close(); - this.selection.focus(); - }, - - // single - isFocused: function () { - return this.selection[0] === document.activeElement; - }, - - // single - cancel: function () { - this.parent.cancel.apply(this, arguments); - this.selection.focus(); - }, - - // single - initContainer: function () { - - var selection, - container = this.container, - dropdown = this.dropdown, - clickingInside = false; - - this.selection = selection = container.find(".select2-choice"); - - this.search.bind("keydown", this.bind(function (e) { - if (!this.enabled) return; - - if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { - // prevent the page from scrolling - killEvent(e); - return; - } - - if (this.opened()) { - switch (e.which) { - case KEY.UP: - case KEY.DOWN: - this.moveHighlight((e.which === KEY.UP) ? -1 : 1); - killEvent(e); - return; - case KEY.TAB: - case KEY.ENTER: - this.selectHighlighted(); - killEvent(e); - return; - case KEY.ESC: - this.cancel(e); - killEvent(e); - return; - } - } else { - - if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { - return; - } - - if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { - return; - } - - this.open(); - - if (e.which === KEY.ENTER) { - // do not propagate the event otherwise we open, and propagate enter which closes - return; - } - } - })); - - this.search.bind("focus", this.bind(function() { - this.selection.attr("tabIndex", "-1"); - })); - this.search.bind("blur", this.bind(function() { - if (!this.opened()) this.container.removeClass("select2-container-active"); - window.setTimeout(this.bind(function() { this.selection.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10); - })); - - selection.bind("mousedown", this.bind(function (e) { - clickingInside = true; - - if (this.opened()) { - this.close(); - this.selection.focus(); - } else if (this.enabled) { - this.open(); - } - - clickingInside = false; - })); - - dropdown.bind("mousedown", this.bind(function() { this.search.focus(); })); - - selection.bind("focus", this.bind(function() { - this.container.addClass("select2-container-active"); - // hide the search so the tab key does not focus on it - this.search.attr("tabIndex", "-1"); - })); - - selection.bind("blur", this.bind(function() { - if (!this.opened()) { - this.container.removeClass("select2-container-active"); - } - window.setTimeout(this.bind(function() { this.search.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10); - })); - - selection.bind("keydown", this.bind(function(e) { - if (!this.enabled) return; - - if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { - // prevent the page from scrolling - killEvent(e); - return; - } - - if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) - || e.which === KEY.ESC) { - return; - } - - if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { - return; - } - - if (e.which == KEY.DELETE) { - if (this.opts.allowClear) { - this.clear(); - } - return; - } - - this.open(); - - if (e.which === KEY.ENTER) { - // do not propagate the event otherwise we open, and propagate enter which closes - killEvent(e); - return; - } - - // do not set the search input value for non-alpha-numeric keys - // otherwise pressing down results in a '(' being set in the search field - if (e.which < 48 ) { // '0' == 48 - killEvent(e); - return; - } - - var keyWritten = String.fromCharCode(e.which).toLowerCase(); - - if (e.shiftKey) { - keyWritten = keyWritten.toUpperCase(); - } - - // focus the field before calling val so the cursor ends up after the value instead of before - this.search.focus(); - this.search.val(keyWritten); - - // prevent event propagation so it doesnt replay on the now focussed search field and result in double key entry - killEvent(e); - })); - - selection.delegate("abbr", "mousedown", this.bind(function (e) { - if (!this.enabled) return; - this.clear(); - killEvent(e); - this.close(); - this.triggerChange(); - this.selection.focus(); - })); - - this.setPlaceholder(); - - this.search.bind("focus", this.bind(function() { - this.container.addClass("select2-container-active"); - })); - }, - - // single - clear: function() { - this.opts.element.val(""); - this.selection.find("span").empty(); - this.selection.removeData("select2-data"); - this.setPlaceholder(); - }, - - /** - * Sets selection based on source element's value - */ - // single - initSelection: function () { - var selected; - if (this.opts.element.val() === "") { - this.close(); - this.setPlaceholder(); - } else { - var self = this; - this.opts.initSelection.call(null, this.opts.element, function(selected){ - if (selected !== undefined && selected !== null) { - self.updateSelection(selected); - self.close(); - self.setPlaceholder(); - } - }); - } - }, - - // single - prepareOpts: function () { - var opts = this.parent.prepareOpts.apply(this, arguments); - - if (opts.element.get(0).tagName.toLowerCase() === "select") { - // install the selection initializer - opts.initSelection = function (element, callback) { - var selected = element.find(":selected"); - // a single select box always has a value, no need to null check 'selected' - if ($.isFunction(callback)) - callback({id: selected.attr("value"), text: selected.text()}); - }; - } - - return opts; - }, - - // single - setPlaceholder: function () { - var placeholder = this.getPlaceholder(); - - if (this.opts.element.val() === "" && placeholder !== undefined) { - - // check for a first blank option if attached to a select - if (this.select && this.select.find("option:first").text() !== "") return; - - this.selection.find("span").html(this.opts.escapeMarkup(placeholder)); - - this.selection.addClass("select2-default"); - - this.selection.find("abbr").hide(); - } - }, - - // single - postprocessResults: function (data, initial) { - var selected = 0, self = this, showSearchInput = true; - - // find the selected element in the result list - - this.results.find(".select2-result-selectable").each2(function (i, elm) { - if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) { - selected = i; - return false; - } - }); - - // and highlight it - - this.highlight(selected); - - // hide the search box if this is the first we got the results and there are a few of them - - if (initial === true) { - showSearchInput = this.showSearchInput = countResults(data.results) >= this.opts.minimumResultsForSearch; - this.dropdown.find(".select2-search")[showSearchInput ? "removeClass" : "addClass"]("select2-search-hidden"); - - //add "select2-with-searchbox" to the container if search box is shown - $(this.dropdown, this.container)[showSearchInput ? "addClass" : "removeClass"]("select2-with-searchbox"); - } - - }, - - // single - onSelect: function (data) { - var old = this.opts.element.val(); - - this.opts.element.val(this.id(data)); - this.updateSelection(data); - this.close(); - this.selection.focus(); - - if (!equal(old, this.id(data))) { this.triggerChange(); } - }, - - // single - updateSelection: function (data) { - - var container=this.selection.find("span"), formatted; - - this.selection.data("select2-data", data); - - container.empty(); - formatted=this.opts.formatSelection(data, container); - if (formatted !== undefined) { - container.append(this.opts.escapeMarkup(formatted)); - } - - this.selection.removeClass("select2-default"); - - if (this.opts.allowClear && this.getPlaceholder() !== undefined) { - this.selection.find("abbr").show(); - } - }, - - // single - val: function () { - var val, data = null, self = this; - - if (arguments.length === 0) { - return this.opts.element.val(); - } - - val = arguments[0]; - - if (this.select) { - this.select - .val(val) - .find(":selected").each2(function (i, elm) { - data = {id: elm.attr("value"), text: elm.text()}; - return false; - }); - this.updateSelection(data); - this.setPlaceholder(); - } else { - if (this.opts.initSelection === undefined) { - throw new Error("cannot call val() if initSelection() is not defined"); - } - // val is an id. !val is true for [undefined,null,''] - if (!val) { - this.clear(); - return; - } - this.opts.element.val(val); - this.opts.initSelection(this.opts.element, function(data){ - self.opts.element.val(!data ? "" : self.id(data)); - self.updateSelection(data); - self.setPlaceholder(); - }); - } - }, - - // single - clearSearch: function () { - this.search.val(""); - }, - - // single - data: function(value) { - var data; - - if (arguments.length === 0) { - data = this.selection.data("select2-data"); - if (data == undefined) data = null; - return data; - } else { - if (!value || value === "") { - this.clear(); - } else { - this.opts.element.val(!value ? "" : this.id(value)); - this.updateSelection(value); - } - } - } - }); - - MultiSelect2 = clazz(AbstractSelect2, { - - // multi - createContainer: function () { - var container = $("
", { - "class": "select2-container select2-container-multi" - }).html([ - " " , - ""].join("")); - return container; - }, - - // multi - prepareOpts: function () { - var opts = this.parent.prepareOpts.apply(this, arguments); - - // TODO validate placeholder is a string if specified - - if (opts.element.get(0).tagName.toLowerCase() === "select") { - // install sthe selection initializer - opts.initSelection = function (element,callback) { - - var data = []; - element.find(":selected").each2(function (i, elm) { - data.push({id: elm.attr("value"), text: elm.text()}); - }); - - if ($.isFunction(callback)) - callback(data); - }; - } - - return opts; - }, - - // multi - initContainer: function () { - - var selector = ".select2-choices", selection; - - this.searchContainer = this.container.find(".select2-search-field"); - this.selection = selection = this.container.find(selector); - - this.search.bind("keydown", this.bind(function (e) { - if (!this.enabled) return; - - if (e.which === KEY.BACKSPACE && this.search.val() === "") { - this.close(); - - var choices, - selected = selection.find(".select2-search-choice-focus"); - if (selected.length > 0) { - this.unselect(selected.first()); - this.search.width(10); - killEvent(e); - return; - } - - choices = selection.find(".select2-search-choice"); - if (choices.length > 0) { - choices.last().addClass("select2-search-choice-focus"); - } - } else { - selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); - } - - if (this.opened()) { - switch (e.which) { - case KEY.UP: - case KEY.DOWN: - this.moveHighlight((e.which === KEY.UP) ? -1 : 1); - killEvent(e); - return; - case KEY.ENTER: - case KEY.TAB: - this.selectHighlighted(); - killEvent(e); - return; - case KEY.ESC: - this.cancel(e); - killEvent(e); - return; - } - } - - if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) - || e.which === KEY.BACKSPACE || e.which === KEY.ESC) { - return; - } - - if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { - return; - } - - this.open(); - - if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { - // prevent the page from scrolling - killEvent(e); - } - })); - - this.search.bind("keyup", this.bind(this.resizeSearch)); - - this.search.bind("blur", this.bind(function(e) { - this.container.removeClass("select2-container-active"); - this.search.removeClass("select2-focused"); - this.clearSearch(); - e.stopImmediatePropagation(); - })); - - this.container.delegate(selector, "mousedown", this.bind(function (e) { - if (!this.enabled) return; - if ($(e.target).closest(".select2-search-choice").length > 0) { - // clicked inside a select2 search choice, do not open - return; - } - this.clearPlaceholder(); - this.open(); - this.focusSearch(); - e.preventDefault(); - })); - - this.container.delegate(selector, "focus", this.bind(function () { - if (!this.enabled) return; - this.container.addClass("select2-container-active"); - this.dropdown.addClass("select2-drop-active"); - this.clearPlaceholder(); - })); - - // set the placeholder if necessary - this.clearSearch(); - }, - - // multi - enable: function() { - if (this.enabled) return; - - this.parent.enable.apply(this, arguments); - - this.search.removeAttr("disabled"); - }, - - // multi - disable: function() { - if (!this.enabled) return; - - this.parent.disable.apply(this, arguments); - - this.search.attr("disabled", true); - }, - - // multi - initSelection: function () { - var data; - if (this.opts.element.val() === "") { - this.updateSelection([]); - this.close(); - // set the placeholder if necessary - this.clearSearch(); - } - if (this.select || this.opts.element.val() !== "") { - var self = this; - this.opts.initSelection.call(null, this.opts.element, function(data){ - if (data !== undefined && data !== null) { - self.updateSelection(data); - self.close(); - // set the placeholder if necessary - self.clearSearch(); - } - }); - } - }, - - // multi - clearSearch: function () { - var placeholder = this.getPlaceholder(); - - if (placeholder !== undefined && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) { - this.search.val(placeholder).addClass("select2-default"); - // stretch the search box to full width of the container so as much of the placeholder is visible as possible - this.resizeSearch(); - } else { - // we set this to " " instead of "" and later clear it on focus() because there is a firefox bug - // that does not properly render the caret when the field starts out blank - this.search.val(" ").width(10); - } - }, - - // multi - clearPlaceholder: function () { - if (this.search.hasClass("select2-default")) { - this.search.val("").removeClass("select2-default"); - } else { - // work around for the space character we set to avoid firefox caret bug - if (this.search.val() === " ") this.search.val(""); - } - }, - - // multi - opening: function () { - this.parent.opening.apply(this, arguments); - - this.clearPlaceholder(); - this.resizeSearch(); - this.focusSearch(); - }, - - // multi - close: function () { - if (!this.opened()) return; - this.parent.close.apply(this, arguments); - }, - - // multi - focus: function () { - this.close(); - this.search.focus(); - }, - - // multi - isFocused: function () { - return this.search.hasClass("select2-focused"); - }, - - // multi - updateSelection: function (data) { - var ids = [], filtered = [], self = this; - - // filter out duplicates - $(data).each(function () { - if (indexOf(self.id(this), ids) < 0) { - ids.push(self.id(this)); - filtered.push(this); - } - }); - data = filtered; - - this.selection.find(".select2-search-choice").remove(); - $(data).each(function () { - self.addSelectedChoice(this); - }); - self.postprocessResults(); - }, - - tokenize: function() { - var input = this.search.val(); - input = this.opts.tokenizer(input, this.data(), this.bind(this.onSelect), this.opts); - if (input != null && input != undefined) { - this.search.val(input); - if (input.length > 0) { - this.open(); - } - } - - }, - - // multi - onSelect: function (data) { - this.addSelectedChoice(data); - if (this.select) { this.postprocessResults(); } - - if (this.opts.closeOnSelect) { - this.close(); - this.search.width(10); - } else { - if (this.countSelectableResults()>0) { - this.search.width(10); - this.resizeSearch(); - this.positionDropdown(); - } else { - // if nothing left to select close - this.close(); - } - } - - // since its not possible to select an element that has already been - // added we do not need to check if this is a new element before firing change - this.triggerChange({ added: data }); - - this.focusSearch(); - }, - - // multi - cancel: function () { - this.close(); - this.focusSearch(); - }, - - // multi - addSelectedChoice: function (data) { - var choice=$( - "
  • " + - "
    " + - " " + - "
  • "), - id = this.id(data), - val = this.getVal(), - formatted; - - formatted=this.opts.formatSelection(data, choice); - choice.find("div").replaceWith("
    "+this.opts.escapeMarkup(formatted)+"
    "); - choice.find(".select2-search-choice-close") - .bind("mousedown", killEvent) - .bind("click dblclick", this.bind(function (e) { - if (!this.enabled) return; - - $(e.target).closest(".select2-search-choice").fadeOut('fast', this.bind(function(){ - this.unselect($(e.target)); - this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); - this.close(); - this.focusSearch(); - })).dequeue(); - killEvent(e); - })).bind("focus", this.bind(function () { - if (!this.enabled) return; - this.container.addClass("select2-container-active"); - this.dropdown.addClass("select2-drop-active"); - })); - - choice.data("select2-data", data); - choice.insertBefore(this.searchContainer); - - val.push(id); - this.setVal(val); - }, - - // multi - unselect: function (selected) { - var val = this.getVal(), - data, - index; - - selected = selected.closest(".select2-search-choice"); - - if (selected.length === 0) { - throw "Invalid argument: " + selected + ". Must be .select2-search-choice"; - } - - data = selected.data("select2-data"); - - index = indexOf(this.id(data), val); - - if (index >= 0) { - val.splice(index, 1); - this.setVal(val); - if (this.select) this.postprocessResults(); - } - selected.remove(); - this.triggerChange({ removed: data }); - }, - - // multi - postprocessResults: function () { - var val = this.getVal(), - choices = this.results.find(".select2-result-selectable"), - compound = this.results.find(".select2-result-with-children"), - self = this; - - choices.each2(function (i, choice) { - var id = self.id(choice.data("select2-data")); - if (indexOf(id, val) >= 0) { - choice.addClass("select2-disabled").removeClass("select2-result-selectable"); - } else { - choice.removeClass("select2-disabled").addClass("select2-result-selectable"); - } - }); - - compound.each2(function(i, e) { - if (e.find(".select2-result-selectable").length==0) { - e.addClass("select2-disabled"); - } else { - e.removeClass("select2-disabled"); - } - }); - - choices.each2(function (i, choice) { - if (!choice.hasClass("select2-disabled") && choice.hasClass("select2-result-selectable")) { - self.highlight(0); - return false; - } - }); - - }, - - // multi - resizeSearch: function () { - - var minimumWidth, left, maxWidth, containerLeft, searchWidth, - sideBorderPadding = getSideBorderPadding(this.search); - - minimumWidth = measureTextWidth(this.search) + 10; - - left = this.search.offset().left; - - maxWidth = this.selection.width(); - containerLeft = this.selection.offset().left; - - searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding; - if (searchWidth < minimumWidth) { - searchWidth = maxWidth - sideBorderPadding; - } - - if (searchWidth < 40) { - searchWidth = maxWidth - sideBorderPadding; - } - this.search.width(searchWidth); - }, - - // multi - getVal: function () { - var val; - if (this.select) { - val = this.select.val(); - return val === null ? [] : val; - } else { - val = this.opts.element.val(); - return splitVal(val, this.opts.separator); - } - }, - - // multi - setVal: function (val) { - var unique; - if (this.select) { - this.select.val(val); - } else { - unique = []; - // filter out duplicates - $(val).each(function () { - if (indexOf(this, unique) < 0) unique.push(this); - }); - this.opts.element.val(unique.length === 0 ? "" : unique.join(this.opts.separator)); - } - }, - - // multi - val: function () { - var val, data = [], self=this; - - if (arguments.length === 0) { - return this.getVal(); - } - - val = arguments[0]; - - if (!val) { - this.opts.element.val(""); - this.updateSelection([]); - this.clearSearch(); - return; - } - - // val is a list of ids - this.setVal(val); - - if (this.select) { - this.select.find(":selected").each(function () { - data.push({id: $(this).attr("value"), text: $(this).text()}); - }); - this.updateSelection(data); - } else { - if (this.opts.initSelection === undefined) { - throw new Error("val() cannot be called if initSelection() is not defined") - } - - this.opts.initSelection(this.opts.element, function(data){ - var ids=$(data).map(self.id); - self.setVal(ids); - self.updateSelection(data); - self.clearSearch(); - }); - } - this.clearSearch(); - }, - - // multi - onSortStart: function() { - if (this.select) { - throw new Error("Sorting of elements is not supported when attached to instead."); - } - - // collapse search field into 0 width so its container can be collapsed as well - this.search.width(0); - // hide the container - this.searchContainer.hide(); - }, - - // multi - onSortEnd:function() { - - var val=[], self=this; - - // show search and move it to the end of the list - this.searchContainer.show(); - // make sure the search container is the last item in the list - this.searchContainer.appendTo(this.searchContainer.parent()); - // since we collapsed the width in dragStarted, we resize it here - this.resizeSearch(); - - // update selection - - this.selection.find(".select2-search-choice").each(function() { - val.push(self.opts.id($(this).data("select2-data"))); - }); - this.setVal(val); - this.triggerChange(); - }, - - // multi - data: function(values) { - var self=this, ids; - if (arguments.length === 0) { - return this.selection - .find(".select2-search-choice") - .map(function() { return $(this).data("select2-data"); }) - .get(); - } else { - if (!values) { values = []; } - ids = $.map(values, function(e) { return self.opts.id(e)}); - this.setVal(ids); - this.updateSelection(values); - this.clearSearch(); - } - } - }); - - $.fn.select2 = function () { - - var args = Array.prototype.slice.call(arguments, 0), - opts, - select2, - value, multiple, allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "onSortStart", "onSortEnd", "enable", "disable", "positionDropdown", "data"]; - - this.each(function () { - if (args.length === 0 || typeof(args[0]) === "object") { - opts = args.length === 0 ? {} : $.extend({}, args[0]); - opts.element = $(this); - - if (opts.element.get(0).tagName.toLowerCase() === "select") { - multiple = opts.element.attr("multiple"); - } else { - multiple = opts.multiple || false; - if ("tags" in opts) {opts.multiple = multiple = true;} - } - - select2 = multiple ? new MultiSelect2() : new SingleSelect2(); - select2.init(opts); - } else if (typeof(args[0]) === "string") { - - if (indexOf(args[0], allowedMethods) < 0) { - throw "Unknown method: " + args[0]; - } - - value = undefined; - select2 = $(this).data("select2"); - if (select2 === undefined) return; - if (args[0] === "container") { - value=select2.container; - } else { - value = select2[args[0]].apply(select2, args.slice(1)); - } - if (value !== undefined) {return false;} - } else { - throw "Invalid arguments to select2 plugin: " + args; - } - }); - return (value === undefined) ? this : value; - }; - - // plugin defaults, accessible to users - $.fn.select2.defaults = { - width: "copy", - closeOnSelect: true, - openOnEnter: true, - containerCss: {}, - dropdownCss: {}, - containerCssClass: "", - dropdownCssClass: "", - formatResult: function(result, container, query) { - var markup=[]; - markMatch(result.text, query.term, markup); - return markup.join(""); - }, - formatSelection: function (data, container) { - return data ? data.text : undefined; - }, - formatResultCssClass: function(data) {return undefined;}, - formatNoMatches: function () { return "No matches found"; }, - formatInputTooShort: function (input, min) { return "Please enter " + (min - input.length) + " more characters"; }, - formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); }, - formatLoadMore: function (pageNumber) { return "Loading more results..."; }, - formatSearching: function () { return "Searching..."; }, - minimumResultsForSearch: 0, - minimumInputLength: 0, - maximumSelectionSize: 0, - id: function (e) { return e.id; }, - matcher: function(term, text) { - return text.toUpperCase().indexOf(term.toUpperCase()) >= 0; - }, - separator: ",", - tokenSeparators: [], - tokenizer: defaultTokenizer, - escapeMarkup: function (markup) { - if (markup && typeof(markup) === "string") { - return markup.replace(/&/g, "&"); - } - return markup; - }, - blurOnChange: false - }; - - // exports - window.Select2 = { - query: { - ajax: ajax, - local: local, - tags: tags - }, util: { - debounce: debounce, - markMatch: markMatch - }, "class": { - "abstract": AbstractSelect2, - "single": SingleSelect2, - "multi": MultiSelect2 - } - }; - -}(jQuery)); diff --git a/app/assets/javascripts/wysiwyg/bootstrap-wysihtml5.js b/app/assets/javascripts/wysiwyg/bootstrap-wysihtml5.js deleted file mode 100755 index adfaa57..0000000 --- a/app/assets/javascripts/wysiwyg/bootstrap-wysihtml5.js +++ /dev/null @@ -1,492 +0,0 @@ -!function($, wysi) { - "use strict"; - - var tpl = { - "font-styles": function(locale, options) { - var size = (options && options.size) ? ' btn-'+options.size : ''; - return ""; - }, - - "emphasis": function(locale, options) { - var size = (options && options.size) ? ' btn-'+options.size : ''; - return "
  • " + - "
    " + - "" + locale.emphasis.bold + "" + - "" + locale.emphasis.italic + "" + - "" + locale.emphasis.underline + "" + - "
    " + - "
  • "; - }, - - "lists": function(locale, options) { - var size = (options && options.size) ? ' btn-'+options.size : ''; - return "
  • " + - "
    " + - "" + - "" + - "" + - "" + - "
    " + - "
  • "; - }, - - "link": function(locale, options) { - var size = (options && options.size) ? ' btn-'+options.size : ''; - return "
  • " + - "" + - "" + - "
  • "; - }, - - "image": function(locale, options) { - var size = (options && options.size) ? ' btn-'+options.size : ''; - return "
  • " + - "" + - "" + - "
  • "; - }, - - "html": function(locale, options) { - var size = (options && options.size) ? ' btn-'+options.size : ''; - return "
  • " + - "
    " + - "" + - "
    " + - "
  • "; - }, - - "color": function(locale, options) { - var size = (options && options.size) ? ' btn-'+options.size : ''; - return ""; - } - }; - - var templates = function(key, locale, options) { - return tpl[key](locale, options); - }; - - - var Wysihtml5 = function(el, options) { - this.el = el; - var toolbarOpts = options || defaultOptions; - for(var t in toolbarOpts.customTemplates) { - tpl[t] = toolbarOpts.customTemplates[t]; - } - this.toolbar = this.createToolbar(el, toolbarOpts); - this.editor = this.createEditor(options); - - window.editor = this.editor; - - $('iframe.wysihtml5-sandbox').each(function(i, el){ - $(el.contentWindow).off('focus.wysihtml5').on({ - 'focus.wysihtml5' : function(){ - $('li.dropdown').removeClass('open'); - } - }); - }); - }; - - Wysihtml5.prototype = { - - constructor: Wysihtml5, - - createEditor: function(options) { - options = options || {}; - options.toolbar = this.toolbar[0]; - - var editor = new wysi.Editor(this.el[0], options); - - if(options && options.events) { - for(var eventName in options.events) { - editor.on(eventName, options.events[eventName]); - } - } - return editor; - }, - - createToolbar: function(el, options) { - var self = this; - var toolbar = $("