diff --git a/docs/THIRDPARTY b/docs/THIRDPARTY index 40b3310daa..e36dcba1ba 100644 --- a/docs/THIRDPARTY +++ b/docs/THIRDPARTY @@ -96,6 +96,11 @@ Copyright: 2012 Twitter, Inc License: Apache-2.0 Comment: The software has been modified. +Files: static/third/bootstrap-typeahead/* +Copyright: 2012 Twitter, Inc +License: Apache-2.0 +Comment: Bootstrap typeahead. The software has been modified. + Files: static/third/bootstrap-notify/* Copyright: 2013 Nijiko Yonskai 2012 Goodybag, Inc. diff --git a/static/js/bundles/commons.js b/static/js/bundles/commons.js index e5680fb30c..0d7cd5165b 100644 --- a/static/js/bundles/commons.js +++ b/static/js/bundles/commons.js @@ -5,6 +5,7 @@ import "node_modules/jquery/dist/jquery.js"; import "node_modules/underscore/underscore.js"; import "js/blueslip.js"; import "third/bootstrap/js/bootstrap.js"; +import "third/bootstrap-typeahead/typeahead.js"; import "js/common.js"; import "node_modules/moment/min/moment.min.js"; import "node_modules/moment-timezone/builds/moment-timezone-with-data.min.js"; diff --git a/static/third/bootstrap-typeahead/typeahead.js b/static/third/bootstrap-typeahead/typeahead.js new file mode 100644 index 0000000000..2fff58ab59 --- /dev/null +++ b/static/third/bootstrap-typeahead/typeahead.js @@ -0,0 +1,365 @@ +/* ============================================================= + * bootstrap-typeahead.js v2.1.0 + * http://twitter.github.com/bootstrap/javascript.html#typeahead + * ============================================================= + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License 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($){ + + "use strict"; // jshint ;_; + + + /* TYPEAHEAD PUBLIC CLASS DEFINITION + * ================================= */ + + var Typeahead = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, $.fn.typeahead.defaults, options) + this.matcher = this.options.matcher || this.matcher + this.sorter = this.options.sorter || this.sorter + this.highlighter = this.options.highlighter || this.highlighter + this.updater = this.options.updater || this.updater + this.$menu = $(this.options.menu).appendTo('body') + this.source = this.options.source + this.shown = false + this.dropup = this.options.dropup + this.fixed = this.options.fixed || false; + + if (this.fixed) { + this.$menu.css('position', 'fixed'); + } + // The naturalSearch option causes arrow keys to immediately + // update the search box with the underlying values from the + // search suggestions. + this.listen() + } + + Typeahead.prototype = { + + constructor: Typeahead + + , select: function (e) { + var val = this.$menu.find('.active').data('typeahead-value') + this.$element + .val(this.updater(val, e)) + .change() + return this.hide() + } + + , set_value: function () { + var val = this.$menu.find('.active').data('typeahead-value') + this.$element.val(val) + } + + , updater: function (item) { + return item + } + + , show: function () { + var pos; + + if (this.fixed) { + // Relative to screen instead of to page + pos = this.$element[0].getBoundingClientRect(); + } else { + pos = this.$element.offset(); + } + + pos = $.extend({}, pos, { + height: this.$element[0].offsetHeight + }) + + // Zulip patch: Workaround for iOS safari problems + pos.top = this.$element.offset().top; + + var top_pos = pos.top + pos.height + if (this.dropup) { + top_pos = pos.top - this.$menu.outerHeight() + } + + this.$menu.css({ + top: top_pos + , left: pos.left + }) + + this.$menu.show() + this.shown = true + return this + } + + , hide: function () { + this.$menu.hide() + this.shown = false + return this + } + + , lookup: function (event) { + var items + + this.query = this.$element.is("[contenteditable]") ? this.$element.text() : this.$element.val(); + + if (!this.options.helpOnEmptyStrings) { + if (!this.query || this.query.length < this.options.minLength) { + return this.shown ? this.hide() : this + } + } + + items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source + + if (!items && this.shown) this.hide(); + return items ? this.process(items) : this + } + + , process: function (items) { + var that = this + + items = $.grep(items, function (item) { + return that.matcher(item) + }) + + items = this.sorter(items) + + if (!items.length) { + return this.shown ? this.hide() : this + } + + return this.render(items.slice(0, this.options.items)).show() + } + + , matcher: function (item) { + return ~item.toLowerCase().indexOf(this.query.toLowerCase()) + } + + , sorter: function (items) { + var beginswith = [] + , caseSensitive = [] + , caseInsensitive = [] + , item + + while (item = items.shift()) { + if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item) + else if (~item.indexOf(this.query)) caseSensitive.push(item) + else caseInsensitive.push(item) + } + + return beginswith.concat(caseSensitive, caseInsensitive) + } + + , highlighter: function (item) { + var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&') + return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) { + return '' + match + '' + }) + } + + , render: function (items) { + var that = this + + items = $(items).map(function (i, item) { + i = $(that.options.item).data('typeahead-value', item) + i.find('a').html(that.highlighter(item)) + return i[0] + }) + + items.first().addClass('active') + this.$menu.html(items) + return this + } + + , next: function (event) { + var active = this.$menu.find('.active').removeClass('active') + , next = active.next() + + if (!next.length) { + next = $(this.$menu.find('li')[0]) + } + + next.addClass('active') + + if (this.options.naturalSearch) { + this.set_value(); + } + } + + , prev: function (event) { + var active = this.$menu.find('.active').removeClass('active') + , prev = active.prev() + + if (!prev.length) { + prev = this.$menu.find('li').last() + } + + prev.addClass('active') + + if (this.options.naturalSearch) { + this.set_value(); + } + } + + , listen: function () { + this.$element + .on('blur', $.proxy(this.blur, this)) + .on('keypress', $.proxy(this.keypress, this)) + .on('keyup', $.proxy(this.keyup, this)) + + if (this.eventSupported('keydown')) { + this.$element.on('keydown', $.proxy(this.keydown, this)) + } + + this.$menu + .on('click', $.proxy(this.click, this)) + .on('mouseenter', 'li', $.proxy(this.mouseenter, this)) + } + + , eventSupported: function(eventName) { + var isSupported = eventName in this.$element + if (!isSupported) { + this.$element.setAttribute(eventName, 'return;') + isSupported = typeof this.$element[eventName] === 'function' + } + return isSupported + } + + , move: function (e) { + if (!this.shown) return + + switch(e.keyCode) { + case 9: // tab + case 13: // enter + case 27: // escape + e.preventDefault() + break + + case 38: // up arrow + e.preventDefault() + this.prev() + break + + case 40: // down arrow + e.preventDefault() + this.next() + break + } + + if ((this.options.stopAdvance || (e.keyCode != 9 && e.keyCode != 13)) + && $.inArray(e.keyCode, this.options.advanceKeyCodes)) { + e.stopPropagation() + } + } + + , keydown: function (e) { + this.suppressKeyPressRepeat = !~$.inArray(e.keyCode, [40,38,9,13,27]) + this.move(e) + } + + , keypress: function (e) { + if (this.suppressKeyPressRepeat) return + this.move(e) + } + + , keyup: function (e) { + switch(e.keyCode) { + case 40: // down arrow + case 38: // up arrow + break + + case 9: // tab + case 13: // enter + if (!this.shown) return + this.select(e) + break + + case 27: // escape + if (!this.shown) return + this.hide() + break + + default: + this.lookup() + } + + if ((this.options.stopAdvance || (e.keyCode != 9 && e.keyCode != 13)) + && $.inArray(e.keyCode, this.options.advanceKeyCodes)) { + e.stopPropagation() + } + + e.preventDefault() + } + + , blur: function (e) { + var that = this + setTimeout(function () { + if (!that.$menu.is(':hover')) { + that.hide(); + } + }, 150) + } + + , click: function (e) { + e.stopPropagation() + e.preventDefault() + this.select(e) + } + + , mouseenter: function (e) { + this.$menu.find('.active').removeClass('active') + $(e.currentTarget).addClass('active') + } + + } + + + /* TYPEAHEAD PLUGIN DEFINITION + * =========================== */ + + $.fn.typeahead = function (option) { + return this.each(function () { + var $this = $(this) + , data = $this.data('typeahead') + , options = typeof option == 'object' && option + if (!data) $this.data('typeahead', (data = new Typeahead(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.typeahead.defaults = { + source: [] + , items: 8 + , menu: '' + , item: '
  • ' + , minLength: 1 + , stopAdvance: false + , dropup: false + , advanceKeyCodes: [] + } + + $.fn.typeahead.Constructor = Typeahead + + + /* TYPEAHEAD DATA-API + * ================== */ + + $(function () { + $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) { + var $this = $(this) + if ($this.data('typeahead')) return + e.preventDefault() + $this.typeahead($this.data()) + }) + }) + +}(window.jQuery); diff --git a/static/third/bootstrap/js/bootstrap.js b/static/third/bootstrap/js/bootstrap.js index 12000a5efb..6d78e9d1b5 100644 --- a/static/third/bootstrap/js/bootstrap.js +++ b/static/third/bootstrap/js/bootstrap.js @@ -1789,370 +1789,6 @@ }) }) -}(window.jQuery);/* ============================================================= - * bootstrap-typeahead.js v2.1.0 - * http://twitter.github.com/bootstrap/javascript.html#typeahead - * ============================================================= - * Copyright 2012 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License 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($){ - - "use strict"; // jshint ;_; - - - /* TYPEAHEAD PUBLIC CLASS DEFINITION - * ================================= */ - - var Typeahead = function (element, options) { - this.$element = $(element) - this.options = $.extend({}, $.fn.typeahead.defaults, options) - this.matcher = this.options.matcher || this.matcher - this.sorter = this.options.sorter || this.sorter - this.highlighter = this.options.highlighter || this.highlighter - this.updater = this.options.updater || this.updater - this.$menu = $(this.options.menu).appendTo('body') - this.source = this.options.source - this.shown = false - this.dropup = this.options.dropup - this.fixed = this.options.fixed || false; - - if (this.fixed) { - this.$menu.css('position', 'fixed'); - } - // The naturalSearch option causes arrow keys to immediately - // update the search box with the underlying values from the - // search suggestions. - this.listen() - } - - Typeahead.prototype = { - - constructor: Typeahead - - , select: function (e) { - var val = this.$menu.find('.active').data('typeahead-value') - this.$element - .val(this.updater(val, e)) - .change() - return this.hide() - } - - , set_value: function () { - var val = this.$menu.find('.active').data('typeahead-value') - this.$element.val(val) - } - - , updater: function (item) { - return item - } - - , show: function () { - var pos; - - if (this.fixed) { - // Relative to screen instead of to page - pos = this.$element[0].getBoundingClientRect(); - } else { - pos = this.$element.offset(); - } - - pos = $.extend({}, pos, { - height: this.$element[0].offsetHeight - }) - - // Zulip patch: Workaround for iOS safari problems - pos.top = this.$element.offset().top; - - var top_pos = pos.top + pos.height - if (this.dropup) { - top_pos = pos.top - this.$menu.outerHeight() - } - - this.$menu.css({ - top: top_pos - , left: pos.left - }) - - this.$menu.show() - this.shown = true - return this - } - - , hide: function () { - this.$menu.hide() - this.shown = false - return this - } - - , lookup: function (event) { - var items - - this.query = this.$element.is("[contenteditable]") ? this.$element.text() : this.$element.val(); - - if (!this.options.helpOnEmptyStrings) { - if (!this.query || this.query.length < this.options.minLength) { - return this.shown ? this.hide() : this - } - } - - items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source - - if (!items && this.shown) this.hide(); - return items ? this.process(items) : this - } - - , process: function (items) { - var that = this - - items = $.grep(items, function (item) { - return that.matcher(item) - }) - - items = this.sorter(items) - - if (!items.length) { - return this.shown ? this.hide() : this - } - - return this.render(items.slice(0, this.options.items)).show() - } - - , matcher: function (item) { - return ~item.toLowerCase().indexOf(this.query.toLowerCase()) - } - - , sorter: function (items) { - var beginswith = [] - , caseSensitive = [] - , caseInsensitive = [] - , item - - while (item = items.shift()) { - if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item) - else if (~item.indexOf(this.query)) caseSensitive.push(item) - else caseInsensitive.push(item) - } - - return beginswith.concat(caseSensitive, caseInsensitive) - } - - , highlighter: function (item) { - var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&') - return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) { - return '' + match + '' - }) - } - - , render: function (items) { - var that = this - - items = $(items).map(function (i, item) { - i = $(that.options.item).data('typeahead-value', item) - i.find('a').html(that.highlighter(item)) - return i[0] - }) - - items.first().addClass('active') - this.$menu.html(items) - return this - } - - , next: function (event) { - var active = this.$menu.find('.active').removeClass('active') - , next = active.next() - - if (!next.length) { - next = $(this.$menu.find('li')[0]) - } - - next.addClass('active') - - if (this.options.naturalSearch) { - this.set_value(); - } - } - - , prev: function (event) { - var active = this.$menu.find('.active').removeClass('active') - , prev = active.prev() - - if (!prev.length) { - prev = this.$menu.find('li').last() - } - - prev.addClass('active') - - if (this.options.naturalSearch) { - this.set_value(); - } - } - - , listen: function () { - this.$element - .on('blur', $.proxy(this.blur, this)) - .on('keypress', $.proxy(this.keypress, this)) - .on('keyup', $.proxy(this.keyup, this)) - - if (this.eventSupported('keydown')) { - this.$element.on('keydown', $.proxy(this.keydown, this)) - } - - this.$menu - .on('click', $.proxy(this.click, this)) - .on('mouseenter', 'li', $.proxy(this.mouseenter, this)) - } - - , eventSupported: function(eventName) { - var isSupported = eventName in this.$element - if (!isSupported) { - this.$element.setAttribute(eventName, 'return;') - isSupported = typeof this.$element[eventName] === 'function' - } - return isSupported - } - - , move: function (e) { - if (!this.shown) return - - switch(e.keyCode) { - case 9: // tab - case 13: // enter - case 27: // escape - e.preventDefault() - break - - case 38: // up arrow - e.preventDefault() - this.prev() - break - - case 40: // down arrow - e.preventDefault() - this.next() - break - } - - if ((this.options.stopAdvance || (e.keyCode != 9 && e.keyCode != 13)) - && $.inArray(e.keyCode, this.options.advanceKeyCodes)) { - e.stopPropagation() - } - } - - , keydown: function (e) { - this.suppressKeyPressRepeat = !~$.inArray(e.keyCode, [40,38,9,13,27]) - this.move(e) - } - - , keypress: function (e) { - if (this.suppressKeyPressRepeat) return - this.move(e) - } - - , keyup: function (e) { - switch(e.keyCode) { - case 40: // down arrow - case 38: // up arrow - break - - case 9: // tab - case 13: // enter - if (!this.shown) return - this.select(e) - break - - case 27: // escape - if (!this.shown) return - this.hide() - break - - default: - this.lookup() - } - - if ((this.options.stopAdvance || (e.keyCode != 9 && e.keyCode != 13)) - && $.inArray(e.keyCode, this.options.advanceKeyCodes)) { - e.stopPropagation() - } - - e.preventDefault() - } - - , blur: function (e) { - var that = this - setTimeout(function () { - if (!that.$menu.is(':hover')) { - that.hide(); - } - }, 150) - } - - , click: function (e) { - e.stopPropagation() - e.preventDefault() - this.select(e) - } - - , mouseenter: function (e) { - this.$menu.find('.active').removeClass('active') - $(e.currentTarget).addClass('active') - } - - } - - - /* TYPEAHEAD PLUGIN DEFINITION - * =========================== */ - - $.fn.typeahead = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('typeahead') - , options = typeof option == 'object' && option - if (!data) $this.data('typeahead', (data = new Typeahead(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.typeahead.defaults = { - source: [] - , items: 8 - , menu: '' - , item: '
  • ' - , minLength: 1 - , stopAdvance: false - , dropup: false - , advanceKeyCodes: [] - } - - $.fn.typeahead.Constructor = Typeahead - - - /* TYPEAHEAD DATA-API - * ================== */ - - $(function () { - $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) { - var $this = $(this) - if ($this.data('typeahead')) return - e.preventDefault() - $this.typeahead($this.data()) - }) - }) - }(window.jQuery); /* ========================================================== * bootstrap-affix.js v2.1.0