From 10bb04c9cfa3fe4425d4d21b72c57aaabd73fe40 Mon Sep 17 00:00:00 2001 From: Adrian Moennich Date: Thu, 12 Mar 2015 10:36:41 +0100 Subject: [PATCH] Search: Wrap JS in IIFE --- search/indico_search/static/js/search.js | 880 ++++++++++++----------- 1 file changed, 441 insertions(+), 439 deletions(-) diff --git a/search/indico_search/static/js/search.js b/search/indico_search/static/js/search.js index 1621c54..e2ea9ef 100644 --- a/search/indico_search/static/js/search.js +++ b/search/indico_search/static/js/search.js @@ -15,500 +15,502 @@ * along with Indico; if not, see . */ -type('IntelligentSearchBox', ['RealtimeTextBox'], { - _highlight: function(elem, token) { - // Code for highlighting the matched part of the string - var idx = elem.toLowerCase().indexOf(token.toLowerCase()); - if (idx >= 0) { - var before = elem.slice(0, idx); - var after = elem.slice(idx + token.length, elem.length); - return [before, Html.span('highlight', elem.slice(idx, idx + token.length)), after]; - } - }, - - _truncateTitle: function(title) { - var max = 27; - if (title.length > max) { - return title.slice(0, max / 2) + ' ... ' + title.slice(-max / 2); - } else { - return title; - } - }, - - _showSuggestions: function(text, suggList, totalNumber) { - var self = this; - if (!this.suggestionBox) { - // Create the suggestion box only once - - // totalNumber will change during time - this.totalNumber = new WatchValue(totalNumber); - this.suggestionList = Html.ul(); - - var infoBox = Html.div('help', $T("Some category suggestions...")); - - // Create the suggestion box - this.suggestionBox = Html.div({ - style: { - position: 'absolute', - top: pixels(23), - left: 0 - }, - className: 'suggestion-box' - }, - infoBox, - this.suggestionList, - $B(Html.div('other-results'), this.totalNumber, function(number) { - return number === 0 ? '' : number + ' ' + $T('other results - please write more...'); - })); - - this.container.append(this.suggestionBox); - } - - // Prepare regular expression for highlighting - var tokens = filter(escapeHTML(text).split(' '), function(t) { - return t !== ''; - }); - var tokenRE = new RegExp('(' + tokens.join('|') + ')', 'gi'); - - - // Bind the list to the DOM element - var counter = 0; - - $B(this.suggestionList, suggList, function(elem) { - var titleHtml = escapeHTML(self._truncateTitle(elem.title)).replace(tokenRE, '$1'); - var index = counter; - var title = Html.span('title'); - title.dom.innerHTML = titleHtml; - var pathText = Util.truncateCategPath(elem.path).join(' >> '); - var path = Html.div('path', pathText); - var liItem = Html.li({}, title, path); - - // Use mouse to control selector as well - - liItem.observeEvent('mouseover', function() { - if (self.selectorPos != index) { - self._clearSelector(); - self.selectorPos = index; - self._setSelector(); - } - }); - - liItem.observeClick(function() { - window.location = elem.url; - }); - - counter++; - return liItem; - }); - - this.suggestionList.observeEvent('mouseout', function() { - self._clearSelector(); - self.selectorPos = -1; - }); - - // Update - this.totalNumber.set(totalNumber); - }, - - _hideSuggestions: function() { - this.container.remove(this.suggestionBox); - this.suggestionBox = null; - this.selectorPos = -1; - this.suggestions = null; - }, - - _retrieveOptions: function(expression) { - var self = this; - this.querying = true; - - $.ajax({ - url: self.searchUrl, - type: 'GET', - data: { - term: expression - }, - dataType: 'json', - complete: function() { - self.querying = false; - self.timeOfLastQuery = (new Date()).getTime(); - }, - success: function(data) { - if (handleAjaxError(data)) { - return; - } - - if (data.results.length) { - self.suggestions = data.results; - self._showSuggestions(expression, data.results, data.count - data.results.length); - } else { - self._hideSuggestions(); - } - - var currentText = trim(self.get()); - - // if the text changed meanwhile and it - // is still long enough - if (currentText != expression && currentText.length > 1) { - // request - self._textTyped(); - } else if (currentText.length <= 1) { - // if it is not long enough - self._hideSuggestions(); - } +(function(global) { + type('IntelligentSearchBox', ['RealtimeTextBox'], { + _highlight: function(elem, token) { + // Code for highlighting the matched part of the string + var idx = elem.toLowerCase().indexOf(token.toLowerCase()); + if (idx >= 0) { + var before = elem.slice(0, idx); + var after = elem.slice(idx + token.length, elem.length); + return [before, Html.span('highlight', elem.slice(idx, idx + token.length)), after]; } - }); - }, + }, - _getTimeSinceLastQuery: function() { - var now = new Date(); - return now.getTime() - this.timeOfLastQuery; - }, + _truncateTitle: function(title) { + var max = 27; + if (title.length > max) { + return title.slice(0, max / 2) + ' ... ' + title.slice(-max / 2); + } else { + return title; + } + }, - _waitForRequestTime: function() { - var self = this; - if (!this.queuedRequest) { - // This should never happen... - return; - } + _showSuggestions: function(text, suggList, totalNumber) { + var self = this; + if (!this.suggestionBox) { + // Create the suggestion box only once - if (this._getTimeSinceLastQuery() > 1000) { - this._textTyped(); - this.queuedRequest = false; - } else { - setTimeout(function() { - self._waitForRequestTime(); - }, 300); - } - }, + // totalNumber will change during time + this.totalNumber = new WatchValue(totalNumber); + this.suggestionList = Html.ul(); - /* - * Called each time a new character is typed - * strips white spaces, and calls for a request if needed - */ - _textTyped: function(key) { + var infoBox = Html.div('help', $T("Some category suggestions...")); - var self = this; - var text = trim(this.get()); + // Create the suggestion box + this.suggestionBox = Html.div({ + style: { + position: 'absolute', + top: pixels(23), + left: 0 + }, + className: 'suggestion-box' + }, + infoBox, + this.suggestionList, + $B(Html.div('other-results'), this.totalNumber, function(number) { + return number === 0 ? '' : number + ' ' + $T('other results - please write more...'); + })); - if (text.length > 1) { + this.container.append(this.suggestionBox); + } - // if we're not already querying and enough time has passed - // since the last request - if (!this.querying && this._getTimeSinceLastQuery() > 1000) { - this._retrieveOptions(text); - } else if (!this.queuedRequest) { - // otherwise, if we can't do a request right now - // and no other request is queued - this.queuedRequest = true; + // Prepare regular expression for highlighting + var tokens = filter(escapeHTML(text).split(' '), function(t) { + return t !== ''; + }); + var tokenRE = new RegExp('(' + tokens.join('|') + ')', 'gi'); + + // Bind the list to the DOM element + var counter = 0; + + $B(this.suggestionList, suggList, function(elem) { + var titleHtml = escapeHTML(self._truncateTitle(elem.title)).replace(tokenRE, '$1'); + var index = counter; + var title = Html.span('title'); + title.dom.innerHTML = titleHtml; + var pathText = Util.truncateCategPath(elem.path).join(' >> '); + var path = Html.div('path', pathText); + var liItem = Html.li({}, title, path); + + // Use mouse to control selector as well + + liItem.observeEvent('mouseover', function() { + if (self.selectorPos != index) { + self._clearSelector(); + self.selectorPos = index; + self._setSelector(); + } + }); + + liItem.observeClick(function() { + window.location = elem.url; + }); + + counter++; + return liItem; + }); + + this.suggestionList.observeEvent('mouseout', function() { + self._clearSelector(); + self.selectorPos = -1; + }); + + // Update + this.totalNumber.set(totalNumber); + }, + + _hideSuggestions: function() { + this.container.remove(this.suggestionBox); + this.suggestionBox = null; + this.selectorPos = -1; + this.suggestions = null; + }, + + _retrieveOptions: function(expression) { + var self = this; + this.querying = true; + + $.ajax({ + url: self.searchUrl, + type: 'GET', + data: { + term: expression + }, + dataType: 'json', + complete: function() { + self.querying = false; + self.timeOfLastQuery = (new Date()).getTime(); + }, + success: function(data) { + if (handleAjaxError(data)) { + return; + } + + if (data.results.length) { + self.suggestions = data.results; + self._showSuggestions(expression, data.results, data.count - data.results.length); + } else { + self._hideSuggestions(); + } + + var currentText = trim(self.get()); + + // if the text changed meanwhile and it + // is still long enough + if (currentText != expression && currentText.length > 1) { + // request + self._textTyped(); + } else if (currentText.length <= 1) { + // if it is not long enough + self._hideSuggestions(); + } + } + }); + }, + + _getTimeSinceLastQuery: function() { + var now = new Date(); + return now.getTime() - this.timeOfLastQuery; + }, + + _waitForRequestTime: function() { + var self = this; + if (!this.queuedRequest) { + // This should never happen... + return; + } + + if (this._getTimeSinceLastQuery() > 1000) { + this._textTyped(); + this.queuedRequest = false; + } else { setTimeout(function() { self._waitForRequestTime(); }, 300); } - } else if (this.suggestionBox) { - this._hideSuggestions(); - } - }, + }, - _openSelection: function(event) { - if (this.selectorPos >= 0) { - window.location = this.suggestions[this.selectorPos].url; - return false; - } - return true; - }, + /* + * Called each time a new character is typed + * strips white spaces, and calls for a request if needed + */ + _textTyped: function(key) { - /* - * Move the selector (gray area) up or down - */ - _moveSelector: function(direction) { + var self = this; + var text = trim(this.get()); - if (this.suggestionBox) { - var suggNum = this.suggestionList.length.get(); + if (text.length > 1) { - if (this.selectorPos < 0) { - this.selectorPos = direction == 'down' ? 0 : suggNum - 1; - } else { - this._clearSelector(); - this.selectorPos += direction == 'up' ? -1 : 1; + // if we're not already querying and enough time has passed + // since the last request + if (!this.querying && this._getTimeSinceLastQuery() > 1000) { + this._retrieveOptions(text); + } else if (!this.queuedRequest) { + // otherwise, if we can't do a request right now + // and no other request is queued + this.queuedRequest = true; - if (this.selectorPos >= suggNum) { - this.selectorPos = -1; - } else if (this.selectorPos < 0) { - this.selectorPos = -1; + setTimeout(function() { + self._waitForRequestTime(); + }, 300); + } + } else if (this.suggestionBox) { + this._hideSuggestions(); + } + }, + + _openSelection: function(event) { + if (this.selectorPos >= 0) { + window.location = this.suggestions[this.selectorPos].url; + return false; + } + return true; + }, + + /* + * Move the selector (gray area) up or down + */ + _moveSelector: function(direction) { + + if (this.suggestionBox) { + var suggNum = this.suggestionList.length.get(); + + if (this.selectorPos < 0) { + this.selectorPos = direction == 'down' ? 0 : suggNum - 1; + } else { + this._clearSelector(); + this.selectorPos += direction == 'up' ? -1 : 1; + + if (this.selectorPos >= suggNum) { + this.selectorPos = -1; + } else if (this.selectorPos < 0) { + this.selectorPos = -1; + } } } + + this._setSelector(); + }, + + _setSelector: function() { + if (this.selectorPos >= 0) { + this.suggestionList.item(this.selectorPos).dom.className = 'selected'; + } + }, + + _clearSelector: function() { + if (this.selectorPos >= 0) { + this.suggestionList.item(this.selectorPos).dom.className = ''; + } + }, + + isAnyItemSelected: function() { + return this.selectorPos > 0; } - this._setSelector(); - }, + }, function(args, container, searchUrl) { + args.autocomplete = 'off'; + this.RealtimeTextBox(args); + this.selectorPos = -1; + this.querying = false; + this.container = container; + this.timeOfLastQuery = 0; + this.searchUrl = searchUrl; - _setSelector: function() { - if (this.selectorPos >= 0) { - this.suggestionList.item(this.selectorPos).dom.className = 'selected'; - } - }, + var self = this; - _clearSelector: function() { - if (this.selectorPos >= 0) { - this.suggestionList.item(this.selectorPos).dom.className = ''; - } - }, - - isAnyItemSelected: function() { - return this.selectorPos > 0; - } - -}, function(args, container, searchUrl) { - args.autocomplete = 'off'; - this.RealtimeTextBox(args); - this.selectorPos = -1; - this.querying = false; - this.container = container; - this.timeOfLastQuery = 0; - this.searchUrl = searchUrl; - - var self = this; - - this.observe(function(key, event) { - self._textTyped(key); - return true; - }); - - this.observeOtherKeys(function(text, key, event) { - if (key == 38 || key == 40) { - self._moveSelector(key == 38 ? 'up' : 'down'); - return false; - } else if (key == 27) { - self._hideSuggestions(); - return false; - } else if (key == 13) { - return self._openSelection(event); - } else { + this.observe(function(key, event) { + self._textTyped(key); return true; - } + }); + + this.observeOtherKeys(function(text, key, event) { + if (key == 38 || key == 40) { + self._moveSelector(key == 38 ? 'up' : 'down'); + return false; + } else if (key == 27) { + self._hideSuggestions(); + return false; + } else if (key == 13) { + return self._openSelection(event); + } else { + return true; + } + }); + + $E(document.body).observeClick(function(event) { + // Close box if a click is done outside of it + + /* for some unknown reason, onclick is called on the submit button, + * as soon as the return key is pressed in any of the textfields. + * To make it even better, onclick is called before onkeyup, + * which forces us to do the last two checks. + */ + + if (self.suggestionBox && !self.suggestionList.ancestorOf($E(eventTarget(event))) && $E(eventTarget(event)) != self.input) { + self._hideSuggestions(); + } + }); }); - $E(document.body).observeClick(function(event) { - // Close box if a click is done outside of it - /* for some unknown reason, onclick is called on the submit button, - * as soon as the return key is pressed in any of the textfields. - * To make it even better, onclick is called before onkeyup, - * which forces us to do the last two checks. - */ + $.widget('indico.search_tag', { + options: { + categ_title: 'Home', + everywhere: true, + search_category_url: '#', + search_url: '#', + form: null + }, - if (self.suggestionBox && !self.suggestionList.ancestorOf($E(eventTarget(event))) && $E(eventTarget(event)) != self.input) { - self._hideSuggestions(); - } - }); -}); + _transition: function(title, no_check) { + var $tag = this.$tag; + var old_width = $tag.width(); + $tag.fadeTo('fast', 0.3).width('').find('.where').html(title); + var new_width = $tag.width(); -$.widget('indico.search_tag', { - options: { - categ_title: 'Home', - everywhere: true, - search_category_url: '#', - search_url: '#', - form: null - }, + // store target width + $tag.fadeTo('fast', 0.5).width(old_width).data('target-width', new_width); - _transition: function(title, no_check) { - var $tag = this.$tag; - var old_width = $tag.width(); + $tag.animate({ + width: ((new_width < old_width) && !no_check) ? old_width : new_width + }, 200, 'linear'); - $tag.fadeTo('fast', 0.3).width('').find('.where').html(title); - var new_width = $tag.width(); + }, - // store target width - $tag.fadeTo('fast', 0.5).width(old_width).data('target-width', new_width); + _create: function() { + var self = this; - $tag.animate({ - width: ((new_width < old_width) && !no_check) ? old_width : new_width - }, 200, 'linear'); + var tag_template = _.template('
' + + '
x
' + + '
<%= categ_title %>
' + + '
'); - }, + var $tag = this.$tag = $(tag_template({ + categ_title: this.options.everywhere ? $T('Everywhere') : this.options.categ_title + })); - _create: function() { - var self = this; + $(this.element).replaceWith($tag); + var $where = $('.where', $tag); - var tag_template = _.template('
' + - '
x
' + - '
<%= categ_title %>
' + - '
'); + if (this.options.everywhere) { + $tag.addClass('everywhere'); + $('.cross', this.$tag).hide(); + } else { + $tag.addClass('in-category'); + $('.cross', this.$tag).show(); + } - var $tag = this.$tag = $(tag_template({ - categ_title: this.options.everywhere ? $T('Everywhere') : this.options.categ_title - })); + $('.cross', $tag).on('click', function() { + self.search_everywhere(); + }); - $(this.element).replaceWith($tag); - var $where = $('.where', $tag); + var $parent = $tag.parent(); - if (this.options.everywhere) { - $tag.addClass('everywhere'); + $parent.on('mouseenter', '.search-tag.everywhere', function() { + self.show_categ(); + }); + $parent.on('mouseover', '.search-tag.everywhere', function(e) { + self.show_tip(e); + }); + $parent.on('mouseleave', '.search-tag.everywhere', function() { + $where.removeData('hasFocus'); + }); + $parent.on('click', '.search-tag.in-category-over', function() { + self.confirm_tip(); + }); + $parent.on('mouseleave', '.search-tag.in-category-over', function() { + self.back_to_everywhere(); + }); + }, + + confirm_tip: function() { + var $where = $('.where', this.$tag); + var $tag = this.$tag; + + $tag.qtip('destroy'); + $where.fadeOut('fast', function() { + $(this).fadeIn('fast'); + }); + + this.$tag.addClass('in-category').removeClass('everywhere').removeClass('in-category-over'); + this.options.form.attr('action', this.options.search_category_url); + + // use target-width as search-tag may still be growing + $tag.animate({ + width: $tag.data('target-width') + $('.cross', $tag).width() + 10 + }, 200, 'swing', function() { + $('.cross', $tag).fadeIn('fast'); + }); + }, + + search_everywhere: function() { $('.cross', this.$tag).hide(); - } else { - $tag.addClass('in-category'); - $('.cross', this.$tag).show(); - } + this.$tag.addClass('everywhere').removeClass('in-category'); + this._transition($T('Everywhere'), true); + this.options.form.attr('action', this.options.search_url); + }, - $('.cross', $tag).on('click', function() { - self.search_everywhere(); - }); + show_categ: function() { + var $tag = this.$tag; + var $where = $('.where', $tag); + var self = this; + $where.data('hasFocus', true); + setTimeout(function() { + if ($where.data('hasFocus')) { + self._transition(self.options.categ_title); + $tag.addClass('in-category-over'); + } + }, 200); + }, - var $parent = $tag.parent(); + show_tip: function(event) { + this.$tag.qtip({ + content: format($T('Click to search inside {title}'), {title: this.options.categ_title}), + position: { + at: 'bottom center', + my: 'top center' + }, + style: { + classes: 'qtip-shadow' + }, + show: { + event: event.type, + ready: true + } + }, event); + }, - $parent.on('mouseenter', '.search-tag.everywhere', function() { - self.show_categ(); - }); - $parent.on('mouseover', '.search-tag.everywhere', function(e) { - self.show_tip(e); - }); - $parent.on('mouseleave', '.search-tag.everywhere', function() { + back_to_everywhere: function() { + var $where = $('.where', this.$tag); + var self = this; + + this.$tag.removeClass('in-category-over'); $where.removeData('hasFocus'); - }); - $parent.on('click', '.search-tag.in-category-over', function() { - self.confirm_tip(); - }); - $parent.on('mouseleave', '.search-tag.in-category-over', function() { - self.back_to_everywhere(); - }); - }, - - confirm_tip: function() { - var $where = $('.where', this.$tag); - var $tag = this.$tag; - - $tag.qtip('destroy'); - $where.fadeOut('fast', function() { - $(this).fadeIn('fast'); - }); - - this.$tag.addClass('in-category').removeClass('everywhere').removeClass('in-category-over'); - this.options.form.attr('action', this.options.search_category_url); - - // use target-width as search-tag may still be growing - $tag.animate({ - width: $tag.data('target-width') + $('.cross', $tag).width() + 10 - }, 200, 'swing', function() { - $('.cross', $tag).fadeIn('fast'); - }); - }, - - search_everywhere: function() { - $('.cross', this.$tag).hide(); - this.$tag.addClass('everywhere').removeClass('in-category'); - this._transition($T('Everywhere'), true); - this.options.form.attr('action', this.options.search_url); - }, - - show_categ: function() { - var $tag = this.$tag; - var $where = $('.where', $tag); - var self = this; - $where.data('hasFocus', true); - setTimeout(function() { - if ($where.data('hasFocus')) { - self._transition(self.options.categ_title); - $tag.addClass('in-category-over'); - } - }, 200); - }, - - show_tip: function(event) { - this.$tag.qtip({ - content: format($T('Click to search inside {title}'), {title: this.options.categ_title}), - position: { - at: 'bottom center', - my: 'top center' - }, - style: { - classes: 'qtip-shadow' - }, - show: { - event: event.type, - ready: true - } - }, event); - }, - - back_to_everywhere: function() { - var $where = $('.where', this.$tag); - var self = this; - - this.$tag.removeClass('in-category-over'); - $where.removeData('hasFocus'); - setTimeout(function() { - if (!$where.data('hasFocus')) { - self._transition($T('Everywhere'), true); - self.$tag.addClass('everywhere'); - } - }, 200); - } -}); - - -function categorySearchBox(options) { - // expected options: categoryNamesUrl, searchUrl, searchCategoryUrl, categoryName, isRoot - var form = $('#category-search-form'); - var extra = form.find('.extra-options'); - var controls = form.find('.search-controls'); - var extraConfigured = false; - $('#category-search-expand').on('click', function() { - if (extra.is(':visible')) { - extra.slideUp('fast'); - } else { - extra.slideDown('fast'); - if (!extraConfigured) { - extraConfigured = true; - extra.css('display', 'table').position({ - of: controls, - my: 'right top', - at: 'right bottom' - }); - } + setTimeout(function() { + if (!$where.data('hasFocus')) { + self._transition($T('Everywhere'), true); + self.$tag.addClass('everywhere'); + } + }, 200); } }); - function verifyForm() { - var startDate = $('#search-start_date').val(); - var endDate = $('#search-end_date').val(); - return (!startDate || Util.parseDateTime(startDate, IndicoDateTimeFormats.DefaultHourless)) && - (!endDate || Util.parseDateTime(endDate, IndicoDateTimeFormats.DefaultHourless)); - } - var intelligentSearchBox = new IntelligentSearchBox({ - name: 'search-phrase', - id: 'search-phrase', - style: {backgroundColor: 'transparent', outline: 'none'} - }, $E('category-search-box'), options.categoryNamesUrl); + global.categorySearchBox = function categorySearchBox(options) { + // expected options: categoryNamesUrl, searchUrl, searchCategoryUrl, categoryName, isRoot + var form = $('#category-search-form'); + var extra = form.find('.extra-options'); + var controls = form.find('.search-controls'); + var extraConfigured = false; + $('#category-search-expand').on('click', function() { + if (extra.is(':visible')) { + extra.slideUp('fast'); + } else { + extra.slideDown('fast'); + if (!extraConfigured) { + extraConfigured = true; + extra.css('display', 'table').position({ + of: controls, + my: 'right top', + at: 'right bottom' + }); + } + } + }); - $('#search-start_date, #search-end_date').datepicker(); - $E('search-phrase').replaceWith(intelligentSearchBox.draw()); - - $('.search-button').on('click', function() { - if (verifyForm()) { - $('#category-search-form').submit(); + function verifyForm() { + var startDate = $('#search-start_date').val(); + var endDate = $('#search-end_date').val(); + return (!startDate || Util.parseDateTime(startDate, IndicoDateTimeFormats.DefaultHourless)) && + (!endDate || Util.parseDateTime(endDate, IndicoDateTimeFormats.DefaultHourless)); } - }); - $('#search-phrase').on('keypress', function(e) { - if (e.which == 13 && !intelligentSearchBox.isAnyItemSelected()) { + var intelligentSearchBox = new IntelligentSearchBox({ + name: 'search-phrase', + id: 'search-phrase', + style: {backgroundColor: 'transparent', outline: 'none'} + }, $E('category-search-box'), options.categoryNamesUrl); + + $('#search-start_date, #search-end_date').datepicker(); + $E('search-phrase').replaceWith(intelligentSearchBox.draw()); + + $('.search-button').on('click', function() { if (verifyForm()) { $('#category-search-form').submit(); } - } - }); - - if (!options.isRoot) { - $('#category-search-form .search-button').before($('
')); - $('.search-tag').search_tag({ - everywhere: true, - categ_title: options.categoryName, - form: $('#category-search-form'), - search_url: options.searchUrl, - search_category_url: options.searchCategoryUrl }); + + $('#search-phrase').on('keypress', function(e) { + if (e.which == 13 && !intelligentSearchBox.isAnyItemSelected()) { + if (verifyForm()) { + $('#category-search-form').submit(); + } + } + }); + + if (!options.isRoot) { + $('#category-search-form .search-button').before($('
')); + $('.search-tag').search_tag({ + everywhere: true, + categ_title: options.categoryName, + form: $('#category-search-form'), + search_url: options.searchUrl, + search_category_url: options.searchCategoryUrl + }); + } } -} +})(window);