ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From oleew...@apache.org
Subject [5/7] ambari git commit: AMBARI-18015. Visual search : Changing the input to textarea. (Dharmesh Makwana via oleewere)
Date Fri, 05 Aug 2016 11:59:09 GMT
http://git-wip-us.apache.org/repos/asf/ambari/blob/52d04b17/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/custom/visualsearch/visualsearch.js
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/custom/visualsearch/visualsearch.js b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/custom/visualsearch/visualsearch.js
new file mode 100644
index 0000000..c8fe317
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/custom/visualsearch/visualsearch.js
@@ -0,0 +1,1984 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+
+// This is the annotated source code for
+// [VisualSearch.js](http://documentcloud.github.com/visualsearch/),
+// a rich search box for real data.
+// 
+// The annotated source HTML is generated by
+// [Docco](http://jashkenas.github.com/docco/).
+
+/** @license VisualSearch.js 0.4.0
+ *  (c) 2011 Samuel Clay, @samuelclay, DocumentCloud Inc.
+ *  VisualSearch.js may be freely distributed under the MIT license.
+ *  For all details and documentation:
+ *  http://documentcloud.github.com/visualsearch
+ */
+
+(function() {
+
+  var $ = jQuery; // Handle namespaced jQuery
+
+  // Setting up VisualSearch globals. These will eventually be made instance-based.
+  if (!window.VS) window.VS = {};
+  if (!VS.app)    VS.app    = {};
+  if (!VS.ui)     VS.ui     = {};
+  if (!VS.model)  VS.model  = {};
+  if (!VS.utils)  VS.utils  = {};
+
+  // Sets the version for VisualSearch to be used programatically elsewhere.
+  VS.VERSION = '0.5.0';
+
+  VS.VisualSearch = function(options) {
+    var defaults = {
+      container   : '',
+      query       : '',
+      autosearch  : true,
+      unquotable  : [],
+      remainder   : 'text',
+      showFacets  : true,
+      readOnly    : false,
+      callbacks   : {
+        search          : $.noop,
+        focus           : $.noop,
+        blur            : $.noop,
+        facetMatches    : $.noop,
+        valueMatches    : $.noop,
+        clearSearch     : $.noop,
+        removedFacet    : $.noop
+      }
+    };
+    this.options           = _.extend({}, defaults, options);
+    this.options.callbacks = _.extend({}, defaults.callbacks, options.callbacks);
+    
+    VS.app.hotkeys.initialize();
+    this.searchQuery   = new VS.model.SearchQuery();
+    this.searchBox     = new VS.ui.SearchBox({
+        app: this, 
+        showFacets: this.options.showFacets
+    });
+
+    if (options.container) {
+      var searchBox = this.searchBox.render().el;
+      $(this.options.container).html(searchBox);
+    }
+    this.searchBox.value(this.options.query || '');
+
+    // Disable page caching for browsers that incorrectly cache the visual search inputs.
+    // This forces the browser to re-render the page when it is retrieved in its history.
+    $(window).bind('unload', function(e) {});
+
+    // Gives the user back a reference to the `searchBox` so they
+    // can use public methods.
+    return this;
+  };
+
+  // Entry-point used to tie all parts of VisualSearch together. It will either attach
+  // itself to `options.container`, or pass back the `searchBox` so it can be rendered
+  // at will.
+  VS.init = function(options) {
+    return new VS.VisualSearch(options);
+  };
+
+})();
+
+(function() {
+
+var $ = jQuery; // Handle namespaced jQuery
+
+// The search box is responsible for managing the many facet views and input views.
+VS.ui.SearchBox = Backbone.View.extend({
+
+  id  : 'search',
+
+  events : {
+    'click .VS-cancel-search-box' : 'clearSearch',
+    'mousedown .VS-search-box'    : 'maybeFocusSearch',
+    'dblclick .VS-search-box'     : 'highlightSearch',
+    'click .VS-search-box'        : 'maybeTripleClick'
+  },
+
+  // Creating a new SearchBox registers handlers for re-rendering facets when necessary,
+  // as well as handling typing when a facet is selected.
+  initialize : function(options) {
+    this.options = _.extend({}, this.options, options);
+
+    this.app = this.options.app;
+    this.flags = {
+      allSelected : false
+    };
+    this.facetViews = [];
+    this.inputViews = [];
+    _.bindAll(this, 'renderFacets', '_maybeDisableFacets', 'disableFacets',
+              'deselectAllFacets', 'addedFacet', 'removedFacet', 'changedFacet');
+    this.app.searchQuery
+            .bind('reset', this.renderFacets)
+            .bind('add', this.addedFacet)
+            .bind('remove', this.removedFacet)
+            .bind('change', this.changedFacet);
+    $(document).bind('keydown', this._maybeDisableFacets);
+  },
+
+  // Renders the search box, but requires placement on the page through `this.el`.
+  render : function() {
+    $(this.el).append(JST['search_box']({
+      readOnly: this.app.options.readOnly
+    }));
+    $(document.body).setMode('no', 'search');
+
+    return this;
+  },
+
+  // # Querying Facets #
+
+  // Either gets a serialized query string or sets the faceted query from a query string.
+  value : function(query) {
+    if (query == null) return this.serialize();
+    return this.setQuery(query);
+  },
+
+  // Uses the VS.app.searchQuery collection to serialize the current query from the various
+  // facets that are in the search box.
+  serialize : function() {
+    var query           = [];
+    var inputViewsCount = this.inputViews.length;
+
+    this.app.searchQuery.each(_.bind(function(facet, i) {
+      query.push(this.inputViews[i].value());
+      query.push(facet.serialize());
+    }, this));
+
+    if (inputViewsCount) {
+      query.push(this.inputViews[inputViewsCount-1].value());
+    }
+
+    return _.compact(query).join(' ');
+  },
+
+  // Returns any facet views that are currently selected. Useful for changing the value
+  // callbacks based on what else is in the search box and which facet is being edited.
+  selected: function() {
+    return _.select(this.facetViews, function(view) {
+      return view.modes.editing == 'is' || view.modes.selected == 'is';
+    });
+  },
+
+  // Similar to `this.selected`, returns any facet models that are currently selected.
+  selectedModels: function() {
+    return _.pluck(this.selected(), 'model');
+  },
+
+  // Takes a query string and uses the SearchParser to parse and render it. Note that
+  // `VS.app.SearchParser` refreshes the `VS.app.searchQuery` collection, which is bound
+  // here to call `this.renderFacets`.
+  setQuery : function(query) {
+    this.currentQuery = query;
+    VS.app.SearchParser.parse(this.app, query);
+  },
+
+  // Returns the position of a facet/input view. Useful when moving between facets.
+  viewPosition : function(view) {
+    var views    = view.type == 'facet' ? this.facetViews : this.inputViews;
+    var position = _.indexOf(views, view);
+    if (position == -1) position = 0;
+    return position;
+  },
+
+  // Used to launch a search. Hitting enter or clicking the search button.
+  searchEvent : function(e) {
+    var query = this.value();
+    this.focusSearch(e);
+    this.value(query);
+    this.app.options.callbacks.search(query, this.app.searchQuery);
+  },
+
+  // # Rendering Facets #
+
+  // Add a new facet. Facet will be focused and ready to accept a value. Can also
+  // specify position, in the case of adding facets from an inbetween input.
+  addFacet : function(category, initialQuery, position) {
+    category     = VS.utils.inflector.trim(category);
+    initialQuery = VS.utils.inflector.trim(initialQuery || '');
+    if (!category) return;
+
+    var model = new VS.model.SearchFacet({
+      category : category,
+      value    : initialQuery || '',
+      app      : this.app
+    });
+    this.app.searchQuery.add(model, {at: position});
+  },
+
+  // Renders a newly added facet, and selects it.
+  addedFacet : function (model) {
+    this.renderFacets();
+    var facetView = _.detect(this.facetViews, function(view) {
+      if (view.model == model) return true;
+    });
+
+    _.defer(function() {
+      facetView.enableEdit();
+    });
+  },
+
+  // Changing a facet programmatically re-renders it.
+  changedFacet: function () {
+    this.renderFacets();
+  },
+
+  // When removing a facet, potentially do something. For now, the adjacent
+  // remaining facet is selected, but this is handled by the facet's view,
+  // since its position is unknown by the time the collection triggers this
+  // remove callback.
+  removedFacet : function (facet, query, options) {
+    this.app.options.callbacks.removedFacet(facet, query, options);
+  },
+
+  // Renders each facet as a searchFacet view.
+  renderFacets : function() {
+    this.facetViews = [];
+    this.inputViews = [];
+
+    this.$('.VS-search-inner').empty();
+
+    this.app.searchQuery.each(_.bind(this.renderFacet, this));
+
+    // Add on an n+1 empty search input on the very end.
+    this.renderSearchInput();
+    this.renderPlaceholder();
+  },
+
+  // Render a single facet, using its category and query value.
+  renderFacet : function(facet, position) {
+    var view = new VS.ui.SearchFacet({
+      app   : this.app,
+      model : facet,
+      order : position
+    });
+
+    // Input first, facet second.
+    this.renderSearchInput();
+    this.facetViews.push(view);
+    this.$('.VS-search-inner').children().eq(position*2).after(view.render().el);
+    $('.search_input').children('textarea').css({'width':0,'height':0});
+    view.calculateSize();
+    _.defer(_.bind(view.calculateSize, view));
+    $('.search_facet_input_container').css({'width': ($('.search_facet').width() - ($('.category').width() + 10) )+'px'})
+    return view;
+  },
+
+  // Render a single input, used to create and autocomplete facets
+  renderSearchInput : function() {
+    var input = new VS.ui.SearchInput({
+      position: this.inputViews.length,
+      app: this.app,
+      showFacets: this.options.showFacets
+    });
+    this.$('.VS-search-inner').append(input.render().el);
+    this.inputViews.push(input);
+  },
+
+  // Handles showing/hiding the placeholder text
+  renderPlaceholder : function() {
+    var $placeholder = this.$('.VS-placeholder');
+    if (this.app.searchQuery.length) {
+      $placeholder.addClass("VS-hidden");
+    } else {
+      $placeholder.removeClass("VS-hidden")
+                  .text(this.app.options.placeholder);
+    }
+  },
+
+  // # Modifying Facets #
+
+  // Clears out the search box. Command+A + delete can trigger this, as can a cancel button.
+  //
+  // If a `clearSearch` callback was provided, the callback is invoked and
+  // provided with a function performs the actual removal of the data.  This
+  // allows third-party developers to either clear data asynchronously, or
+  // prior to performing their custom "clear" logic.
+  clearSearch : function(e) {
+    if (this.app.options.readOnly) return;
+    var actualClearSearch = _.bind(function() {
+      this.disableFacets();
+      this.value('');
+      this.flags.allSelected = false;
+      this.searchEvent(e);
+      this.focusSearch(e);
+    }, this);
+
+    if (this.app.options.callbacks.clearSearch != $.noop) {
+      this.app.options.callbacks.clearSearch(actualClearSearch);
+    } else {
+      actualClearSearch();
+    }
+  },
+
+  // Command+A selects all facets.
+  selectAllFacets : function() {
+    this.flags.allSelected = true;
+
+    $(document).one('click.selectAllFacets', this.deselectAllFacets);
+
+    _.each(this.facetViews, function(facetView, i) {
+      facetView.selectFacet();
+    });
+    _.each(this.inputViews, function(inputView, i) {
+      inputView.selectText();
+    });
+  },
+
+  // Used by facets and input to see if all facets are currently selected.
+  allSelected : function(deselect) {
+    if (deselect) this.flags.allSelected = false;
+    return this.flags.allSelected;
+  },
+
+  // After `selectAllFacets` is engaged, this method is bound to the entire document.
+  // This immediate disables and deselects all facets, but it also checks if the user
+  // has clicked on either a facet or an input, and properly selects the view.
+  deselectAllFacets : function(e) {
+    this.disableFacets();
+
+    if (this.$(e.target).is('.category,textarea')) {
+      var el   = $(e.target).closest('.search_facet,.search_input');
+      var view = _.detect(this.facetViews.concat(this.inputViews), function(v) {
+        return v.el == el[0];
+      });
+      if (view.type == 'facet') {
+        view.selectFacet();
+      } else if (view.type == 'textarea') {
+        _.defer(function() {
+          view.enableEdit(true);
+        });
+      }
+    }
+  },
+
+  // Disables all facets except for the passed in view. Used when switching between
+  // facets, so as not to have to keep state of active facets.
+  disableFacets : function(keepView) {
+    _.each(this.inputViews, function(view) {
+      if (view && view != keepView &&
+          (view.modes.editing == 'is' || view.modes.selected == 'is')) {
+        view.disableEdit();
+      }
+    });
+    _.each(this.facetViews, function(view) {
+      if (view && view != keepView &&
+          (view.modes.editing == 'is' || view.modes.selected == 'is')) {
+        view.disableEdit();
+        view.deselectFacet();
+      }
+    });
+
+    this.flags.allSelected = false;
+    this.removeFocus();
+    $(document).unbind('click.selectAllFacets');
+  },
+
+  // Resize all inputs to account for extra keystrokes which may be changing the facet
+  // width incorrectly. This is a safety check to ensure inputs are correctly sized.
+  resizeFacets : function(view) {
+    _.each(this.facetViews, function(facetView, i) {
+      if (!view || facetView == view) {
+        facetView.resize();
+      }
+    });
+  },
+
+  // Handles keydown events on the document. Used to complete the Cmd+A deletion, and
+  // blurring focus.
+  _maybeDisableFacets : function(e) {
+    if (this.flags.allSelected && VS.app.hotkeys.key(e) == 'backspace') {
+      e.preventDefault();
+      this.clearSearch(e);
+      return false;
+    } else if (this.flags.allSelected && VS.app.hotkeys.printable(e)) {
+      this.clearSearch(e);
+    }
+  },
+
+  // # Focusing Facets #
+
+  // Move focus between facets and inputs. Takes a direction as well as many options
+  // for skipping over inputs and only to facets, placement of cursor position in facet
+  // (i.e. at the end), and selecting the text in the input/facet.
+  focusNextFacet : function(currentView, direction, options) {
+    options = options || {};
+    var viewCount    = this.facetViews.length;
+    var viewPosition = options.viewPosition || this.viewPosition(currentView);
+
+    if (!options.skipToFacet) {
+      // Correct for bouncing between matching text and facet arrays.
+      if (currentView.type == 'text'  && direction > 0) direction -= 1;
+      if (currentView.type == 'facet' && direction < 0) direction += 1;
+    } else if (options.skipToFacet && currentView.type == 'text' &&
+               viewCount == viewPosition && direction >= 0) {
+      // Special case of looping around to a facet from the last search input box.
+      return false;
+    }
+    var view, next = Math.min(viewCount, viewPosition + direction);
+
+    if (currentView.type == 'text') {
+      if (next >= 0 && next < viewCount) {
+        view = this.facetViews[next];
+      } else if (next == viewCount) {
+        view = this.inputViews[this.inputViews.length-1];
+      }
+      if (view && options.selectFacet && view.type == 'facet') {
+        view.selectFacet();
+      } else if (view) {
+        view.enableEdit();
+        view.setCursorAtEnd(direction || options.startAtEnd);
+      }
+    } else if (currentView.type == 'facet') {
+      if (options.skipToFacet) {
+        if (next >= viewCount || next < 0) {
+          view = _.last(this.inputViews);
+          view.enableEdit();
+        } else {
+          view = this.facetViews[next];
+          view.enableEdit();
+          view.setCursorAtEnd(direction || options.startAtEnd);
+        }
+      } else {
+        view = this.inputViews[next];
+        view.enableEdit();
+      }
+    }
+    if (options.selectText) view.selectText();
+    this.resizeFacets();
+
+    return true;
+  },
+
+  maybeFocusSearch : function(e) {
+    if (this.app.options.readOnly) return;
+    if ($(e.target).is('.VS-search-box') ||
+        $(e.target).is('.VS-search-inner') ||
+        e.type == 'keydown') {
+      this.focusSearch(e);
+    }
+  },
+
+  // Bring focus to last input field.
+  focusSearch : function(e, selectText) {
+    if (this.app.options.readOnly) return;
+    var view = this.inputViews[this.inputViews.length-1];
+    view.enableEdit(selectText);
+    if (!selectText) view.setCursorAtEnd(-1);
+    if (e.type == 'keydown') {
+      view.keydown(e);
+      view.box.trigger('keydown');
+    }
+    _.defer(_.bind(function() {
+      if (!this.$('textarea:focus').length) {
+        view.enableEdit(selectText);
+      }
+    }, this));
+  },
+
+  // Double-clicking on the search wrapper should select the existing text in
+  // the last search input. Also start the triple-click timer.
+  highlightSearch : function(e) {
+    if (this.app.options.readOnly) return;
+    if ($(e.target).is('.VS-search-box') ||
+        $(e.target).is('.VS-search-inner') ||
+        e.type == 'keydown') {
+      var lastinput = this.inputViews[this.inputViews.length-1];
+      lastinput.startTripleClickTimer();
+      this.focusSearch(e, true);
+    }
+  },
+
+  maybeTripleClick : function(e) {
+    var lastinput = this.inputViews[this.inputViews.length-1];
+    return lastinput.maybeTripleClick(e);
+  },
+
+  // Used to show the user is focused on some input inside the search box.
+  addFocus : function() {
+    if (this.app.options.readOnly) return;
+    this.app.options.callbacks.focus();
+    this.$('.VS-search-box').addClass('VS-focus');
+  },
+
+  // User is no longer focused on anything in the search box.
+  removeFocus : function() {
+    this.app.options.callbacks.blur();
+    var focus = _.any(this.facetViews.concat(this.inputViews), function(view) {
+      return view.isFocused();
+    });
+    if (!focus) this.$('.VS-search-box').removeClass('VS-focus');
+  },
+
+  // Show a menu which adds pre-defined facets to the search box. This is unused for now.
+  showFacetCategoryMenu : function(e) {
+    e.preventDefault();
+    e.stopPropagation();
+    if (this.facetCategoryMenu && this.facetCategoryMenu.modes.open == 'is') {
+      return this.facetCategoryMenu.close();
+    }
+
+    var items = [
+      {title: 'Account', onClick: _.bind(this.addFacet, this, 'account', '')},
+      {title: 'Project', onClick: _.bind(this.addFacet, this, 'project', '')},
+      {title: 'Filter', onClick: _.bind(this.addFacet, this, 'filter', '')},
+      {title: 'Access', onClick: _.bind(this.addFacet, this, 'access', '')}
+    ];
+
+    var menu = this.facetCategoryMenu || (this.facetCategoryMenu = new dc.ui.Menu({
+      items       : items,
+      standalone  : true
+    }));
+
+    this.$('.VS-icon-search').after(menu.render().open().content);
+    return false;
+  }
+
+});
+
+})();
+
+(function() {
+
+var $ = jQuery; // Handle namespaced jQuery
+
+// This is the visual search facet that holds the category and its autocompleted
+// input field.
+VS.ui.SearchFacet = Backbone.View.extend({
+
+  type : 'facet',
+
+  className : 'search_facet',
+
+  events : {
+    'click .category'           : 'selectFacet',
+    'keydown textarea'             : 'keydown',
+    'mousedown textarea'           : 'enableEdit',
+    'mouseover .VS-icon-cancel' : 'showDelete',
+    'mouseout .VS-icon-cancel'  : 'hideDelete',
+    'click .VS-icon-cancel'     : 'remove'
+  },
+
+  initialize : function(options) {
+    this.options = _.extend({}, this.options, options);
+
+    this.flags = {
+      canClose : false
+    };
+    _.bindAll(this, 'set', 'keydown', 'deselectFacet', 'deferDisableEdit');
+    this.app = this.options.app;
+  },
+
+  // Rendering the facet sets up autocompletion, events on blur, and populates
+  // the facet's input with its starting value.
+  render : function() {
+    $(this.el).html(JST['search_facet']({
+      model : this.model,
+      readOnly: this.app.options.readOnly
+    }));
+
+    this.setMode('not', 'editing');
+    this.setMode('not', 'selected');
+    this.box = this.$('textarea');
+    this.box.val(this.model.label());
+    this.box.bind('blur', this.deferDisableEdit);
+    // Handle paste events with `propertychange`
+    this.box.bind('textarea propertychange', this.keydown);
+    this.setupAutocomplete();
+    return this;
+  },
+
+  // This method is used to setup the facet's input to auto-grow.
+  // This is defered in the searchBox so it can be attached to the
+  // DOM to get the correct font-size.
+  calculateSize : function() {
+    this.box.autoGrowInput();
+    //this.box.unbind('updated.autogrow');
+    this.box.bind('updated.autogrow', _.bind(this.moveAutocomplete, this));
+  },
+
+  // Forces a recalculation of this facet's input field's value. Called when
+  // the facet is focused, removed, or otherwise modified.
+  resize : function(e) {
+   // this.box.trigger('resize.autogrow', e);
+  },
+
+  // Watches the facet's input field to see if it matches the beginnings of
+  // words in `autocompleteValues`, which is different for every category.
+  // If the value, when selected from the autocompletion menu, is different
+  // than what it was, commit the facet and search for it.
+  setupAutocomplete : function() {
+    this.box.autocomplete({
+      source    : _.bind(this.autocompleteValues, this),
+      minLength : 0,
+      delay     : 0,
+      autoFocus : true,
+      position  : {offset : "0 5"},
+      create    : _.bind(function(e, ui) {
+        $(this.el).find('.ui-autocomplete-input').css('z-index','auto');
+      }, this),
+      select    : _.bind(function(e, ui) {
+        e.preventDefault();
+        var originalValue = this.model.get('value');
+        this.set(ui.item.value);
+        if (originalValue != ui.item.value || this.box.val() != ui.item.value) {
+          if (this.app.options.autosearch) {
+            this.search(e);
+          } else {
+              this.app.searchBox.renderFacets();
+              this.app.searchBox.focusNextFacet(this, 1, {viewPosition: this.options.order});
+          }
+        }
+        return false;
+      }, this),
+      open      : _.bind(function(e, ui) {
+        var box = this.box;
+        this.box.autocomplete('widget').find('.ui-menu-item').each(function() {
+          var $value = $(this),
+              autoCompleteData = $value.data('item.autocomplete') || $value.data('ui-autocomplete-item');
+
+          if (autoCompleteData['value'] == box.val() && box.data('ui-autocomplete').menu.activate) {
+            box.data('ui-autocomplete').menu.activate(new $.Event("mouseover"), $value);
+          }
+        });
+      }, this)
+    });
+    this.box.autocomplete('widget').addClass('VS-interface');
+  },
+
+  // As the facet's input field grows, it may move to the next line in the
+  // search box. `autoGrowInput` triggers an `updated` event on the input
+  // field, which is bound to this method to move the autocomplete menu.
+  moveAutocomplete : function() {
+    var autocomplete = this.box.data('ui-autocomplete');
+    if (autocomplete) {
+      autocomplete.menu.element.position({
+        my        : "left top",
+        at        : "left bottom",
+        of        : this.box.data('ui-autocomplete').element,
+        collision : "flip",
+        offset    : "0 5"
+      });
+    }
+  },
+
+  // When a user enters a facet and it is being edited, immediately show
+  // the autocomplete menu and size it to match the contents.
+  searchAutocomplete : function(e) {
+    var autocomplete = this.box.data('ui-autocomplete');
+    if (autocomplete) {
+      var menu = autocomplete.menu.element;
+      autocomplete.search();
+
+      // Resize the menu based on the correctly measured width of what's bigger:
+      // the menu's original size or the menu items' new size.
+      menu.outerWidth(Math.max(
+        menu.width('').outerWidth(),
+        autocomplete.element.outerWidth()
+      ));
+    }
+  },
+
+  // Closes the autocomplete menu. Called on disabling, selecting, deselecting,
+  // and anything else that takes focus out of the facet's input field.
+  closeAutocomplete : function() {
+    var autocomplete = this.box.data('ui-autocomplete');
+    if (autocomplete) autocomplete.close();
+  },
+
+  // Search terms used in the autocomplete menu. These are specific to the facet,
+  // and only match for the facet's category. The values are then matched on the
+  // first letter of any word in matches, and finally sorted according to the
+  // value's own category. You can pass `preserveOrder` as an option in the
+  // `facetMatches` callback to skip any further ordering done client-side.
+  autocompleteValues : function(req, resp) {
+    var category = this.model.get('category');
+    var value    = this.model.get('value');
+    var searchTerm = req.term;
+
+    this.app.options.callbacks.valueMatches(category, searchTerm, function(matches, options) {
+      options = options || {};
+      matches = matches || [];
+
+      if (searchTerm && value != searchTerm) {
+        if (options.preserveMatches) {
+          resp(matches);
+        } else {
+          var re = VS.utils.inflector.escapeRegExp(searchTerm || '');
+          var matcher = new RegExp('\\b' + re, 'i');
+          matches = $.grep(matches, function(item) {
+            return matcher.test(item) ||
+                   matcher.test(item.value) ||
+                   matcher.test(item.label);
+        });
+        }
+      }
+
+      if (options.preserveOrder) {
+        resp(matches);
+      } else {
+        resp(_.sortBy(matches, function(match) {
+          if (match == value || match.value == value) return '';
+          else return match;
+        }));
+      }
+    });
+
+  },
+
+  // Sets the facet's model's value.
+  set : function(value) {
+    if (!value) return;
+    this.model.set({'value': value});
+  },
+
+  // Before the searchBox performs a search, we need to close the
+  // autocomplete menu.
+  search : function(e, direction) {
+    if (!direction) direction = 1;
+    this.closeAutocomplete();
+    this.app.searchBox.searchEvent(e);
+    _.defer(_.bind(function() {
+      this.app.searchBox.focusNextFacet(this, direction, {viewPosition: this.options.order});
+    }, this));
+  },
+
+  // Begin editing the facet's input. This is called when the user enters
+  // the input either from another facet or directly clicking on it.
+  //
+  // This method tells all other facets and inputs to disable so it can have
+  // the sole focus. It also prepares the autocompletion menu.
+  enableEdit : function() {
+    if (this.app.options.readOnly) return;
+    if (this.modes.editing != 'is') {
+      this.setMode('is', 'editing');
+      this.deselectFacet();
+      if (this.box.val() == '') {
+        this.box.val(this.model.get('value'));
+      }
+    }
+
+    this.flags.canClose = false;
+    this.app.searchBox.disableFacets(this);
+    this.app.searchBox.addFocus();
+    _.defer(_.bind(function() {
+      this.app.searchBox.addFocus();
+    }, this));
+    this.resize();
+    this.searchAutocomplete();
+    this.box.focus();
+  },
+
+  // When the user blurs the input, they may either be going to another input
+  // or off the search box entirely. If they go to another input, this facet
+  // will be instantly disabled, and the canClose flag will be turned back off.
+  //
+  // However, if the user clicks elsewhere on the page, this method starts a timer
+  // that checks if any of the other inputs are selected or are being edited. If
+  // not, then it can finally close itself and its autocomplete menu.
+  deferDisableEdit : function() {
+    this.flags.canClose = true;
+    _.delay(_.bind(function() {
+      if (this.flags.canClose && !this.box.is(':focus') &&
+          this.modes.editing == 'is' && this.modes.selected != 'is') {
+        this.disableEdit();
+      }
+    }, this), 250);
+  },
+
+  // Called either by other facets receiving focus or by the timer in `deferDisableEdit`,
+  // this method will turn off the facet, remove any text selection, and close
+  // the autocomplete menu.
+  disableEdit : function() {
+    var newFacetQuery = VS.utils.inflector.trim(this.box.val());
+    if (newFacetQuery != this.model.get('value')) {
+      this.set(newFacetQuery);
+    }
+    this.flags.canClose = false;
+    this.box.selectRange(0, 0);
+    this.box.blur();
+    this.setMode('not', 'editing');
+    this.closeAutocomplete();
+    this.app.searchBox.removeFocus();
+  },
+
+  // Selects the facet, which blurs the facet's input and highlights the facet.
+  // If this is the only facet being selected (and not part of a select all event),
+  // we attach a mouse/keyboard watcher to check if the next action by the user
+  // should delete this facet or just deselect it.
+  selectFacet : function(e) {
+    if (e) e.preventDefault();
+    if (this.app.options.readOnly) return;
+    var allSelected = this.app.searchBox.allSelected();
+    if (this.modes.selected == 'is') return;
+
+    if (this.box.is(':focus')) {
+      this.box.setCursorPosition(0);
+      this.box.blur();
+    }
+
+    this.flags.canClose = false;
+    this.closeAutocomplete();
+    this.setMode('is', 'selected');
+    this.setMode('not', 'editing');
+    if (!allSelected || e) {
+      $(document).unbind('keydown.facet', this.keydown);
+      $(document).unbind('click.facet', this.deselectFacet);
+      _.defer(_.bind(function() {
+        $(document).unbind('keydown.facet').bind('keydown.facet', this.keydown);
+        $(document).unbind('click.facet').one('click.facet', this.deselectFacet);
+      }, this));
+      this.app.searchBox.disableFacets(this);
+      this.app.searchBox.addFocus();
+    }
+    return false;
+  },
+
+  // Turns off highlighting on the facet. Called in a variety of ways, this
+  // only deselects the facet if it is selected, and then cleans up the
+  // keyboard/mouse watchers that were created when the facet was first
+  // selected.
+  deselectFacet : function(e) {
+    if (e) e.preventDefault();
+    if (this.modes.selected == 'is') {
+      this.setMode('not', 'selected');
+      this.closeAutocomplete();
+      this.app.searchBox.removeFocus();
+    }
+    $(document).unbind('keydown.facet', this.keydown);
+    $(document).unbind('click.facet', this.deselectFacet);
+    return false;
+  },
+
+  // Is the user currently focused in this facet's input field?
+  isFocused : function() {
+    return this.box.is(':focus');
+  },
+
+  // Hovering over the delete button styles the facet so the user knows that
+  // the delete button will kill the entire facet.
+  showDelete : function() {
+    $(this.el).addClass('search_facet_maybe_delete');
+  },
+
+  // On `mouseout`, the user is no longer hovering on the delete button.
+  hideDelete : function() {
+    $(this.el).removeClass('search_facet_maybe_delete');
+  },
+
+  // When switching between facets, depending on the direction the cursor is
+  // coming from, the cursor in this facet's input field should match the original
+  // direction.
+  setCursorAtEnd : function(direction) {
+    if (direction == -1) {
+      this.box.setCursorPosition(this.box.val().length);
+    } else {
+      this.box.setCursorPosition(0);
+    }
+  },
+
+  // Deletes the facet and sends the cursor over to the nearest input field.
+  remove : function(e) {
+    var committed = this.model.get('value');
+    this.deselectFacet();
+    this.disableEdit();
+    this.app.searchQuery.remove(this.model);
+    if (committed && this.app.options.autosearch) {
+      this.search(e, -1);
+    } else {
+      this.app.searchBox.renderFacets();
+      this.app.searchBox.focusNextFacet(this, -1, {viewPosition: this.options.order});
+    }
+  },
+
+  // Selects the text in the facet's input field. When the user tabs between
+  // facets, convention is to highlight the entire field.
+  selectText: function() {
+    this.box.selectRange(0, this.box.val().length);
+  },
+
+  // Handles all keyboard inputs when in the facet's input field. This checks
+  // for movement between facets and inputs, entering a new value that needs
+  // to be autocompleted, as well as the removal of this facet.
+  keydown : function(e) {
+    var key = VS.app.hotkeys.key(e);
+
+    if (key == 'enter' && this.box.val()) {
+      this.disableEdit();
+      this.search(e);
+    } else if (key == 'left') {
+      if (this.modes.selected == 'is') {
+        this.deselectFacet();
+        this.app.searchBox.focusNextFacet(this, -1, {startAtEnd: -1});
+      } else if (this.box.getCursorPosition() == 0 && !this.box.getSelection().length) {
+        this.selectFacet();
+      }
+    } else if (key == 'right') {
+      if (this.modes.selected == 'is') {
+        e.preventDefault();
+        this.deselectFacet();
+        this.setCursorAtEnd(0);
+        this.enableEdit();
+      } else if (this.box.getCursorPosition() == this.box.val().length) {
+        e.preventDefault();
+        this.disableEdit();
+        this.app.searchBox.focusNextFacet(this, 1);
+      }
+    } else if (VS.app.hotkeys.shift && key == 'tab') {
+      e.preventDefault();
+      this.app.searchBox.focusNextFacet(this, -1, {
+        startAtEnd  : -1,
+        skipToFacet : true,
+        selectText  : true
+      });
+    } else if (key == 'tab') {
+      e.preventDefault();
+      this.app.searchBox.focusNextFacet(this, 1, {
+        skipToFacet : true,
+        selectText  : true
+      });
+    } else if (VS.app.hotkeys.command && (e.which == 97 || e.which == 65)) {
+      e.preventDefault();
+      this.app.searchBox.selectAllFacets();
+      return false;
+    } else if (VS.app.hotkeys.printable(e) && this.modes.selected == 'is') {
+      this.app.searchBox.focusNextFacet(this, -1, {startAtEnd: -1});
+      this.remove(e);
+    } else if (key == 'backspace') {
+       $(document).on('keydown.backspace', function(e) {
+          if (VS.app.hotkeys.key(e) === 'backspace') {
+             e.preventDefault();
+          }
+       });
+
+       $(document).on('keyup.backspace', function(e) {
+          $(document).off('.backspace');
+       });
+
+      if (this.modes.selected == 'is') {
+        e.preventDefault();
+        this.remove(e);
+      } else if (this.box.getCursorPosition() == 0 &&
+                 !this.box.getSelection().length) {
+        e.preventDefault();
+        this.selectFacet();
+      }
+       e.stopPropagation();
+    }
+
+    // Handle paste events
+    if (e.which == null) {
+        // this.searchAutocomplete(e);
+        _.defer(_.bind(this.resize, this, e));
+    } else {
+      this.resize(e);
+    }
+  }
+
+});
+
+})();
+
+(function() {
+
+var $ = jQuery; // Handle namespaced jQuery
+
+// This is the visual search input that is responsible for creating new facets.
+// There is one input placed in between all facets.
+VS.ui.SearchInput = Backbone.View.extend({
+
+  type : 'text',
+
+  className : 'search_input ui-menu',
+
+  events : {
+    'keypress textarea'  : 'keypress',
+    'keydown textarea'   : 'keydown',
+    'keyup textarea'     : 'keyup',
+    'click textarea'     : 'maybeTripleClick',
+    'dblclick textarea'  : 'startTripleClickTimer'
+  },
+
+  initialize : function(options) {
+    this.options = _.extend({}, this.options, options);
+    
+    this.app = this.options.app;
+    this.flags = {
+      canClose : false
+    };
+    _.bindAll(this, 'removeFocus', 'addFocus', 'moveAutocomplete', 'deferDisableEdit');
+  },
+
+  // Rendering the input sets up autocomplete, events on focusing and blurring
+  // the input, and the auto-grow of the input.
+  render : function() {
+    $(this.el).html(JST['search_input']({
+      readOnly: this.app.options.readOnly
+    }));
+
+    this.setMode('not', 'editing');
+    this.setMode('not', 'selected');
+    this.box = this.$('textarea');
+    this.box.autoGrowInput();
+    this.box.bind('updated.autogrow', this.moveAutocomplete);
+    this.box.bind('blur',  this.deferDisableEdit);
+    this.box.bind('focus', this.addFocus);
+    this.setupAutocomplete();
+
+    return this;
+  },
+
+  // Watches the input and presents an autocompleted menu, taking the
+  // remainder of the input field and adding a separate facet for it.
+  //
+  // See `addTextFacetRemainder` for explanation on how the remainder works.
+  setupAutocomplete : function() {
+    this.box.autocomplete({
+      appendTo :(this.options.app.options.container) ? this.options.app.options.container : null,
+      minLength : this.options.showFacets ? 0 : 1,
+      delay     : 50,
+      autoFocus : true,
+      position  : {offset : "0 -1"},
+      source    : _.bind(this.autocompleteValues, this),
+      // Prevent changing the input value on focus of an option
+      focus     : function() { return false; },
+      create    : _.bind(function(e, ui) {
+        $(this.el).find('.ui-autocomplete-input').css('z-index','auto');
+      }, this),
+      select    : _.bind(function(e, ui) {
+        e.preventDefault();
+        // stopPropogation does weird things in jquery-ui 1.9
+        // e.stopPropagation();
+        var remainder = this.addTextFacetRemainder(ui.item.label || ui.item.value);
+        var position  = this.options.position + (remainder ? 1 : 0);
+        this.app.searchBox.addFacet(ui.item instanceof String ? ui.item : ui.item.value, '', position);
+        return false;
+      }, this)
+    });
+
+    // Renders the results grouped by the categories they belong to.
+    this.box.data('ui-autocomplete')._renderMenu = function(ul, items) {
+      var category = '';
+      _.each(items, _.bind(function(item, i) {
+        if (item.category && item.category != category) {
+          ul.append('<li class="ui-autocomplete-category">'+item.category+'</li>');
+          category = item.category;
+        }
+
+        if(this._renderItemData) {
+          this._renderItemData(ul, item);
+        } else {
+          this._renderItem(ul, item);
+        }
+
+      }, this));
+    };
+
+    this.box.autocomplete('widget').addClass('VS-interface');
+  },
+
+  // Search terms used in the autocomplete menu. The values are matched on the
+  // first letter of any word in matches, and finally sorted according to the
+  // value's own category. You can pass `preserveOrder` as an option in the
+  // `facetMatches` callback to skip any further ordering done client-side.
+  autocompleteValues : function(req, resp) {
+    var searchTerm = req.term;
+    var lastWord   = searchTerm.match(/\w+\*?$/); // Autocomplete only last word.
+    var re         = VS.utils.inflector.escapeRegExp(lastWord && lastWord[0] || '');
+    this.app.options.callbacks.facetMatches(function(prefixes, options) {
+      options = options || {};
+      prefixes = prefixes || [];
+
+      // Only match from the beginning of the word.
+      var matcher    = new RegExp('^' + re, 'i');
+      var matches    = $.grep(prefixes, function(item) {
+        return item && matcher.test(item.label || item);
+      });
+
+      if (options.preserveOrder) {
+        resp(matches);
+      } else {
+        resp(_.sortBy(matches, function(match) {
+          if (match.label) return match.category + '-' + match.label;
+          else             return match;
+        }));
+      }
+    });
+
+  },
+
+  // Closes the autocomplete menu. Called on disabling, selecting, deselecting,
+  // and anything else that takes focus out of the facet's input field.
+  closeAutocomplete : function() {
+    var autocomplete = this.box.data('ui-autocomplete');
+    if (autocomplete) autocomplete.close();
+  },
+
+  // As the input field grows, it may move to the next line in the
+  // search box. `autoGrowInput` triggers an `updated` event on the input
+  // field, which is bound to this method to move the autocomplete menu.
+  moveAutocomplete : function() {
+    var autocomplete = this.box.data('ui-autocomplete');
+    if (autocomplete) {
+      autocomplete.menu.element.position({
+        my        : "left top",
+        at        : "left bottom",
+        of        : this.box.data('ui-autocomplete').element,
+        collision : "none",
+        offset    : '0 -1'
+      });
+    }
+  },
+
+  // When a user enters a facet and it is being edited, immediately show
+  // the autocomplete menu and size it to match the contents.
+  searchAutocomplete : function(e) {
+    var autocomplete = this.box.data('ui-autocomplete');
+    if (autocomplete) {
+      var menu = autocomplete.menu.element;
+      autocomplete.search();
+
+      // Resize the menu based on the correctly measured width of what's bigger:
+      // the menu's original size or the menu items' new size.
+      menu.outerWidth(Math.max(
+        menu.width('').outerWidth(),
+        autocomplete.element.outerWidth()
+      ));
+    }
+  },
+
+  // If a user searches for "word word category", the category would be
+  // matched and autocompleted, and when selected, the "word word" would
+  // also be caught as the remainder and then added in its own facet.
+  addTextFacetRemainder : function(facetValue) {
+    var boxValue = this.box.val();
+    var lastWord = boxValue.match(/\b(\w+)$/);
+
+    if (!lastWord) {
+      return '';
+    }
+
+    var matcher = new RegExp(lastWord[0], "i");
+    if (facetValue.search(matcher) == 0) {
+      boxValue = boxValue.replace(/\b(\w+)$/, '');
+    }
+    boxValue = boxValue.replace('^\s+|\s+$', '');
+
+    if (boxValue) {
+      this.app.searchBox.addFacet(this.app.options.remainder, boxValue, this.options.position);
+    }
+
+    return boxValue;
+  },
+
+  // Directly called to focus the input. This is different from `addFocus`
+  // because this is not called by a focus event. This instead calls a
+  // focus event causing the input to become focused.
+  enableEdit : function(selectText) {
+    this.addFocus();
+    if (selectText) {
+      this.selectText();
+    }
+    this.box.focus();
+  },
+
+  // Event called on user focus on the input. Tells all other input and facets
+  // to give up focus, and starts revving the autocomplete.
+  addFocus : function() {
+    this.flags.canClose = false;
+    if (!this.app.searchBox.allSelected()) {
+      this.app.searchBox.disableFacets(this);
+    }
+    this.app.searchBox.addFocus();
+    this.setMode('is', 'editing');
+    this.setMode('not', 'selected');
+    if (!this.app.searchBox.allSelected()) {
+        this.searchAutocomplete();
+    }
+  },
+
+  // Directly called to blur the input. This is different from `removeFocus`
+  // because this is not called by a blur event.
+  disableEdit : function() {
+    this.box.blur();
+    this.removeFocus();
+  },
+
+  // Event called when user blur's the input, either through the keyboard tabbing
+  // away or the mouse clicking off. Cleans up
+  removeFocus : function() {
+    this.flags.canClose = false;
+    this.app.searchBox.removeFocus();
+    this.setMode('not', 'editing');
+    this.setMode('not', 'selected');
+    this.closeAutocomplete();
+  },
+
+  // When the user blurs the input, they may either be going to another input
+  // or off the search box entirely. If they go to another input, this facet
+  // will be instantly disabled, and the canClose flag will be turned back off.
+  //
+  // However, if the user clicks elsewhere on the page, this method starts a timer
+  // that checks if any of the other inputs are selected or are being edited. If
+  // not, then it can finally close itself and its autocomplete menu.
+  deferDisableEdit : function() {
+    this.flags.canClose = true;
+    _.delay(_.bind(function() {
+      if (this.flags.canClose &&
+          !this.box.is(':focus') &&
+          this.modes.editing == 'is') {
+        this.disableEdit();
+      }
+    }, this), 250);
+  },
+
+  // Starts a timer that will cause a triple-click, which highlights all facets.
+  startTripleClickTimer : function() {
+    this.tripleClickTimer = setTimeout(_.bind(function() {
+      this.tripleClickTimer = null;
+    }, this), 500);
+  },
+
+  // Event on click that checks if a triple click is in play. The
+  // `tripleClickTimer` is counting down, ready to be engaged and intercept
+  // the click event to force a select all instead.
+  maybeTripleClick : function(e) {
+    if (this.app.options.readOnly) return;
+    if (!!this.tripleClickTimer) {
+      e.preventDefault();
+      this.app.searchBox.selectAllFacets();
+      return false;
+    }
+  },
+
+  // Is the user currently focused in the input field?
+  isFocused : function() {
+    return this.box.is(':focus');
+  },
+
+  // When serializing the facets, the inputs need to also have their values represented,
+  // in case they contain text that is not yet faceted (but will be once the search is
+  // completed).
+  value : function() {
+    return this.box.val();
+  },
+
+  // When switching between facets and inputs, depending on the direction the cursor
+  // is coming from, the cursor in this facet's input field should match the original
+  // direction.
+  setCursorAtEnd : function(direction) {
+    if (direction == -1) {
+      this.box.setCursorPosition(this.box.val().length);
+    } else {
+      this.box.setCursorPosition(0);
+    }
+  },
+
+  // Selects the entire range of text in the input. Useful when tabbing between inputs
+  // and facets.
+  selectText : function() {
+    this.box.selectRange(0, this.box.val().length);
+    if (!this.app.searchBox.allSelected()) {
+      this.box.focus();
+    } else {
+      this.setMode('is', 'selected');
+    }
+  },
+
+  // Before the searchBox performs a search, we need to close the
+  // autocomplete menu.
+  search : function(e, direction) {
+    if (!direction) direction = 0;
+    this.closeAutocomplete();
+    this.app.searchBox.searchEvent(e);
+    _.defer(_.bind(function() {
+      this.app.searchBox.focusNextFacet(this, direction);
+    }, this));
+  },
+
+  // Callback fired on key press in the search box. We search when they hit return.
+  keypress : function(e) {
+    var key = VS.app.hotkeys.key(e);
+    if (key == 'enter') {
+      return this.search(e, 100);
+    } else if (VS.app.hotkeys.colon(e)) {
+     // this.box.trigger('resize.autogrow', e);
+      var query    = this.box.val();
+      var prefixes = [];
+      this.app.options.callbacks.facetMatches(function(p) {
+          prefixes = p;
+      });
+      var labels   = _.map(prefixes, function(prefix) {
+        if (prefix.label) return prefix.label;
+        else              return prefix;
+      });
+      if (_.contains(labels, query)) {
+        e.preventDefault();
+        var remainder = this.addTextFacetRemainder(query);
+        var position  = this.options.position + (remainder?1:0);
+        this.app.searchBox.addFacet(query, '', position);
+        return false;
+      }
+    } else if (key == 'backspace') {
+      if (this.box.getCursorPosition() == 0 && !this.box.getSelection().length) {
+        e.preventDefault();
+        e.stopPropagation();
+        e.stopImmediatePropagation();
+        this.app.searchBox.resizeFacets();
+        return false;
+      }
+    }
+  },
+
+  // Handles all keyboard inputs when in the input field. This checks
+  // for movement between facets and inputs, entering a new value that needs
+  // to be autocompleted, as well as stepping between facets with backspace.
+  keydown : function(e) {
+    var key = VS.app.hotkeys.key(e);
+
+    if (key == 'left') {
+      if (this.box.getCursorPosition() == 0) {
+        e.preventDefault();
+        this.app.searchBox.focusNextFacet(this, -1, {startAtEnd: -1});
+      }
+    } else if (key == 'right') {
+      if (this.box.getCursorPosition() == this.box.val().length) {
+        e.preventDefault();
+        this.app.searchBox.focusNextFacet(this, 1, {selectFacet: true});
+      }
+    } else if (VS.app.hotkeys.shift && key == 'tab') {
+      e.preventDefault();
+      this.app.searchBox.focusNextFacet(this, -1, {selectText: true});
+    } else if (key == 'tab') {
+      var value = this.box.val();
+      if (value.length) {
+        e.preventDefault();
+        var remainder = this.addTextFacetRemainder(value);
+        var position  = this.options.position + (remainder?1:0);
+        if (value != remainder) {
+            this.app.searchBox.addFacet(value, '', position);
+        }
+      } else {
+        var foundFacet = this.app.searchBox.focusNextFacet(this, 0, {
+          skipToFacet: true,
+          selectText: true
+        });
+        if (foundFacet) {
+          e.preventDefault();
+        }
+      }
+    } else if (VS.app.hotkeys.command &&
+               String.fromCharCode(e.which).toLowerCase() == 'a') {
+      e.preventDefault();
+      this.app.searchBox.selectAllFacets();
+      return false;
+    } else if (key == 'backspace' && !this.app.searchBox.allSelected()) {
+      if (this.box.getCursorPosition() == 0 && !this.box.getSelection().length) {
+        e.preventDefault();
+        this.app.searchBox.focusNextFacet(this, -1, {backspace: true});
+        return false;
+      }
+    } else if (key == 'end') {
+      var view = this.app.searchBox.inputViews[this.app.searchBox.inputViews.length-1];
+      view.setCursorAtEnd(-1);
+    } else if (key == 'home') {
+      var view = this.app.searchBox.inputViews[0];
+      view.setCursorAtEnd(-1);
+    }
+
+  },
+
+  // We should get the value of an input should be done
+  // on keyup since keydown gets the previous value and not the current one
+  keyup : function(e) {
+    //this.box.trigger('resize.autogrow', e);
+  }
+
+});
+
+})();
+
+(function(){
+
+  var $ = jQuery; // Handle namespaced jQuery
+
+  // Makes the view enter a mode. Modes have both a 'mode' and a 'group',
+  // and are mutually exclusive with any other modes in the same group.
+  // Setting will update the view's modes hash, as well as set an HTML class
+  // of *[mode]_[group]* on the view's element. Convenient way to swap styles
+  // and behavior.
+  Backbone.View.prototype.setMode = function(mode, group) {
+    this.modes || (this.modes = {});
+    if (this.modes[group] === mode) return;
+    $(this.el).setMode(mode, group);
+    this.modes[group] = mode;
+  };
+
+})();
+(function() {
+
+var $ = jQuery; // Handle namespaced jQuery
+
+// DocumentCloud workspace hotkeys. To tell if a key is currently being pressed,
+// just ask `VS.app.hotkeys.[key]` on `keypress`, or ask `VS.app.hotkeys.key(e)`
+// on `keydown`.
+//
+// For the most headache-free way to use this utility, check modifier keys,
+// like shift and command, with `VS.app.hotkeys.shift`, and check every other
+// key with `VS.app.hotkeys.key(e) == 'key_name'`.
+VS.app.hotkeys = {
+
+  // Keys that will be mapped to the `hotkeys` namespace.
+  KEYS: {
+    '16':  'shift',
+    '17':  'command',
+    '91':  'command',
+    '93':  'command',
+    '224': 'command',
+    '13':  'enter',
+    '37':  'left',
+    '38':  'upArrow',
+    '39':  'right',
+    '40':  'downArrow',
+    '46':  'delete',
+    '8':   'backspace',
+    '35':  'end',
+    '36':  'home',
+    '9':   'tab',
+    '188': 'comma'
+  },
+
+  // Binds global keydown and keyup events to listen for keys that match `this.KEYS`.
+  initialize : function() {
+    _.bindAll(this, 'down', 'up', 'blur');
+    $(document).bind('keydown', this.down);
+    $(document).bind('keyup', this.up);
+    $(window).bind('blur', this.blur);
+  },
+
+  // On `keydown`, turn on all keys that match.
+  down : function(e) {
+    var key = this.KEYS[e.which];
+    if (key) this[key] = true;
+  },
+
+  // On `keyup`, turn off all keys that match.
+  up : function(e) {
+    var key = this.KEYS[e.which];
+    if (key) this[key] = false;
+  },
+
+  // If an input is blurred, all keys need to be turned off, since they are no longer
+  // able to modify the document.
+  blur : function(e) {
+    for (var key in this.KEYS) this[this.KEYS[key]] = false;
+  },
+
+  // Check a key from an event and return the common english name.
+  key : function(e) {
+    return this.KEYS[e.which];
+  },
+
+  // Colon is special, since the value is different between browsers.
+  colon : function(e) {
+    var charCode = e.which;
+    return charCode && String.fromCharCode(charCode) == ":";
+  },
+
+  // Check a key from an event and match it against any known characters.
+  // The `keyCode` is different depending on the event type: `keydown` vs. `keypress`.
+  //
+  // These were determined by looping through every `keyCode` and `charCode` that
+  // resulted from `keydown` and `keypress` events and counting what was printable.
+  printable : function(e) {
+    var code = e.which;
+    if (e.type == 'keydown') {
+      if (code == 32 ||                      // space
+          (code >= 48 && code <= 90) ||      // 0-1a-z
+          (code >= 96 && code <= 111) ||     // 0-9+-/*.
+          (code >= 186 && code <= 192) ||    // ;=,-./^
+          (code >= 219 && code <= 222)) {    // (\)'
+        return true;
+      }
+    } else {
+      // [space]!"#$%&'()*+,-.0-9:;<=>?@A-Z[\]^_`a-z{|} and unicode characters
+      if ((code >= 32 && code <= 126)  ||
+          (code >= 160 && code <= 500) ||
+          (String.fromCharCode(code) == ":")) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+};
+
+})();
+(function() {
+
+var $ = jQuery; // Handle namespaced jQuery
+
+// Naive English transformations on words. Only used for a few transformations 
+// in VisualSearch.js.
+VS.utils.inflector = {
+
+  // Delegate to the ECMA5 String.prototype.trim function, if available.
+  trim : function(s) {
+    return s.trim ? s.trim() : s.replace(/^\s+|\s+$/g, '');
+  },
+  
+  // Escape strings that are going to be used in a regex. Escapes punctuation
+  // that would be incorrect in a regex.
+  escapeRegExp : function(s) {
+    return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
+  }
+};
+
+})();
+(function() {
+
+var $ = jQuery; // Handle namespaced jQuery
+
+$.fn.extend({
+
+  // Makes the selector enter a mode. Modes have both a 'mode' and a 'group',
+  // and are mutually exclusive with any other modes in the same group.
+  // Setting will update the view's modes hash, as well as set an HTML class
+  // of *[mode]_[group]* on the view's element. Convenient way to swap styles
+  // and behavior.
+  setMode : function(state, group) {
+    group    = group || 'mode';
+    var re   = new RegExp("\\w+_" + group + "(\\s|$)", 'g');
+    var mode = (state === null) ? "" : state + "_" + group;
+    this.each(function() {
+      this.className = (this.className.replace(re, '')+' '+mode)
+                       .replace(/\s\s/g, ' ');
+    });
+    return mode;
+  },
+
+  // When attached to an input element, this will cause the width of the input
+  // to match its contents. This calculates the width of the contents of the input
+  // by measuring a hidden shadow div that should match the styling of the input.
+  autoGrowInput: function() {
+    return this.each(function() {
+      var $input  = $(this);
+      var $tester = $('<div />').css({
+        opacity     : 0,
+        top         : -9999,
+        left        : -9999,
+        position    : 'absolute',
+        whiteSpace  : 'nowrap'
+      }).addClass('VS-input-width-tester').addClass('VS-interface');
+
+      // Watch for input value changes on all of these events. `resize`
+      // event is called explicitly when the input has been changed without
+      // a single keypress.
+      var events = {};
+      $input.next('.VS-input-width-tester').remove();
+      $input.after($tester);
+      $input.unbind(events).bind(events, function(e, realEvent) {
+        if (realEvent) e = realEvent;
+        var value = $input.val();
+
+        // Watching for the backspace key is tricky because it may not
+        // actually be deleting the character, but instead the key gets
+        // redirected to move the cursor from facet to facet.
+        if (VS.app.hotkeys.key(e) == 'backspace') {
+          var position = $input.getCursorPosition();
+          if (position > 0) value = value.slice(0, position-1) +
+                                    value.slice(position, value.length);
+        } else if (VS.app.hotkeys.printable(e) &&
+                   !VS.app.hotkeys.command) {
+          value += String.fromCharCode(e.which);
+        }
+        value = value.replace(/&/g, '&amp;')
+                     .replace(/\s/g,'&nbsp;')
+                     .replace(/</g, '&lt;')
+                     .replace(/>/g, '&gt;');
+
+        $tester.html(value);
+
+        $input.width($tester.width() + 10 + parseInt($input.css('min-width')));
+        //$input.trigger('updated.autogrow');
+      });
+
+      // Sets the width of the input on initialization.
+      //$input.trigger('resize.autogrow');
+    });
+  },
+
+
+  // Cross-browser method used for calculating where the cursor is in an
+  // input field.
+  getCursorPosition: function() {
+    var position = 0;
+    var input    = this.get(0);
+
+    if (document.selection) { // IE
+      input.focus();
+      var sel    = document.selection.createRange();
+      var selLen = document.selection.createRange().text.length;
+      sel.moveStart('character', -input.value.length);
+      position   = sel.text.length - selLen;
+    } else if (input && $(input).is(':visible') &&
+               input.selectionStart != null) { // Firefox/Safari
+      position = input.selectionStart;
+    }
+
+    return position;
+  },
+
+  // A simple proxy for `selectRange` that sets the cursor position in an
+  // input field.
+  setCursorPosition: function(position) {
+    return this.each(function() {
+      return $(this).selectRange(position, position);
+    });
+  },
+
+  // Cross-browser way to select text in an input field.
+  selectRange: function(start, end) {
+    return this.filter(':visible').each(function() {
+      if (this.setSelectionRange) { // FF/Webkit
+        this.focus();
+        this.setSelectionRange(start, end);
+      } else if (this.createTextRange) { // IE
+        var range = this.createTextRange();
+        range.collapse(true);
+        range.moveEnd('character', end);
+        range.moveStart('character', start);
+        if (end - start >= 0) range.select();
+      }
+    });
+  },
+
+  // Returns an object that contains the text selection range values for
+  // an input field.
+  getSelection: function() {
+    var input = this[0];
+
+    if (input.selectionStart != null) { // FF/Webkit
+      var start = input.selectionStart;
+      var end   = input.selectionEnd;
+      return {
+        start   : start,
+        end     : end,
+        length  : end-start,
+        text    : input.value.substr(start, end-start)
+      };
+    } else if (document.selection) { // IE
+      var range = document.selection.createRange();
+      if (range) {
+        var textRange = input.createTextRange();
+        var copyRange = textRange.duplicate();
+        textRange.moveToBookmark(range.getBookmark());
+        copyRange.setEndPoint('EndToStart', textRange);
+        var start = copyRange.text.length;
+        var end   = start + range.text.length;
+        return {
+          start   : start,
+          end     : end,
+          length  : end-start,
+          text    : range.text
+        };
+      }
+    }
+    return {start: 0, end: 0, length: 0};
+  }
+
+});
+
+// Debugging in Internet Explorer. This allows you to use 
+// `console.log(['message', var1, var2, ...])`. Just remove the `false` and
+// add your console.logs. This will automatically stringify objects using
+// `JSON.stringify', so you can read what's going out. Think of this as a
+// *Diet Firebug Lite Zero with Lemon*.
+if (false) {
+  window.console = {};
+  var _$ied;
+  window.console.log = function(msg) {
+    if (_.isArray(msg)) {
+      var message = msg[0];
+      var vars = _.map(msg.slice(1), function(arg) {
+        return JSON.stringify(arg);
+      }).join(' - ');
+    }
+    if(!_$ied){
+      _$ied = $('<div><ol></ol></div>').css({
+        'position': 'fixed',
+        'bottom': 10,
+        'left': 10,
+        'zIndex': 20000,
+        'width': $('body').width() - 80,
+        'border': '1px solid #000',
+        'padding': '10px',
+        'backgroundColor': '#fff',
+        'fontFamily': 'arial,helvetica,sans-serif',
+        'fontSize': '11px'
+      });
+      $('body').append(_$ied);
+    }
+    var $message = $('<li>'+message+' - '+vars+'</li>').css({
+      'borderBottom': '1px solid #999999'
+    });
+    _$ied.find('ol').append($message);
+    _.delay(function() {
+      $message.fadeOut(500);
+    }, 5000);
+  };
+
+}
+
+})();
+
+(function() {
+
+var $ = jQuery; // Handle namespaced jQuery
+
+// Used to extract keywords and facets from the free text search.
+var QUOTES_RE   = "('[^']+'|\"[^\"]+\")";
+var FREETEXT_RE = "('[^']+'|\"[^\"]+\"|[^'\"\\s]\\S*)";
+var CATEGORY_RE = FREETEXT_RE +                     ':\\s*';
+VS.app.SearchParser = {
+
+  // Matches `category: "free text"`, with and without quotes.
+  ALL_FIELDS : new RegExp(CATEGORY_RE + FREETEXT_RE, 'g'),
+
+  // Matches a single category without the text. Used to correctly extract facets.
+  CATEGORY   : new RegExp(CATEGORY_RE),
+
+  // Called to parse a query into a collection of `SearchFacet` models.
+  parse : function(instance, query) {
+    var searchFacets = this._extractAllFacets(instance, query);
+    instance.searchQuery.reset(searchFacets);
+    return searchFacets;
+  },
+
+  // Walks the query and extracts facets, categories, and free text.
+  _extractAllFacets : function(instance, query) {
+    var facets = [];
+    var originalQuery = query;
+    while (query) {
+      var category, value;
+      originalQuery = query;
+      var field = this._extractNextField(query);
+      if (!field) {
+        category = instance.options.remainder;
+        value    = this._extractSearchText(query);
+        query    = VS.utils.inflector.trim(query.replace(value, ''));
+      } else if (field.indexOf(':') != -1) {
+        category = field.match(this.CATEGORY)[1].replace(/(^['"]|['"]$)/g, '');
+        value    = field.replace(this.CATEGORY, '').replace(/(^['"]|['"]$)/g, '');
+        query    = VS.utils.inflector.trim(query.replace(field, ''));
+      } else if (field.indexOf(':') == -1) {
+        category = instance.options.remainder;
+        value    = field;
+        query    = VS.utils.inflector.trim(query.replace(value, ''));
+      }
+
+      if (category && value) {
+          var searchFacet = new VS.model.SearchFacet({
+            category : category,
+            value    : VS.utils.inflector.trim(value),
+            app      : instance
+          });
+          facets.push(searchFacet);
+      }
+      if (originalQuery == query) break;
+    }
+
+    return facets;
+  },
+
+  // Extracts the first field found, capturing any free text that comes
+  // before the category.
+  _extractNextField : function(query) {
+    var textRe = new RegExp('^\\s*(\\S+)\\s+(?=' + QUOTES_RE + FREETEXT_RE + ')');
+    var textMatch = query.match(textRe);
+    if (textMatch && textMatch.length >= 1) {
+      return textMatch[1];
+    } else {
+      return this._extractFirstField(query);
+    }
+  },
+
+  // If there is no free text before the facet, extract the category and value.
+  _extractFirstField : function(query) {
+    var fields = query.match(this.ALL_FIELDS);
+    return fields && fields.length && fields[0];
+  },
+
+  // If the found match is not a category and facet, extract the trimmed free text.
+  _extractSearchText : function(query) {
+    query = query || '';
+    var text = VS.utils.inflector.trim(query.replace(this.ALL_FIELDS, ''));
+    return text;
+  }
+
+};
+
+})();
+
+(function() {
+
+var $ = jQuery; // Handle namespaced jQuery
+
+// The model that holds individual search facets and their categories.
+// Held in a collection by `VS.app.searchQuery`.
+VS.model.SearchFacet = Backbone.Model.extend({
+
+  // Extract the category and value and serialize it in preparation for
+  // turning the entire searchBox into a search query that can be sent
+  // to the server for parsing and searching.
+  serialize : function() {
+    var category = this.quoteCategory(this.get('category'));
+    var value    = VS.utils.inflector.trim(this.get('value'));
+    var remainder = this.get("app").options.remainder;
+
+    if (!value) return '';
+
+    if (!_.contains(this.get("app").options.unquotable || [], category) && category != remainder) {
+      value = this.quoteValue(value);
+    }
+
+    if (category != remainder) {
+      category = category + ': ';
+    } else {
+      category = "";
+    }
+    return category + value;
+  },
+  
+  // Wrap categories that have spaces or any kind of quote with opposite matching
+  // quotes to preserve the complex category during serialization.
+  quoteCategory : function(category) {
+    var hasDoubleQuote = (/"/).test(category);
+    var hasSingleQuote = (/'/).test(category);
+    var hasSpace       = (/\s/).test(category);
+    
+    if (hasDoubleQuote && !hasSingleQuote) {
+      return "'" + category + "'";
+    } else if (hasSpace || (hasSingleQuote && !hasDoubleQuote)) {
+      return '"' + category + '"';
+    } else {
+      return category;
+    }
+  },
+  
+  // Wrap values that have quotes in opposite matching quotes. If a value has
+  // both single and double quotes, just use the double quotes.
+  quoteValue : function(value) {
+    var hasDoubleQuote = (/"/).test(value);
+    var hasSingleQuote = (/'/).test(value);
+    
+    if (hasDoubleQuote && !hasSingleQuote) {
+      return "'" + value + "'";
+    } else {
+      return '"' + value + '"';
+    }
+  },
+  
+  // If provided, use a custom label instead of the raw value.
+  label : function() {
+      return this.get('label') || this.get('value');
+  }
+
+});
+
+})();
+(function() {
+
+var $ = jQuery; // Handle namespaced jQuery
+
+// Collection which holds all of the individual facets (category: value).
+// Used for finding and removing specific facets.
+VS.model.SearchQuery = Backbone.Collection.extend({
+
+  // Model holds the category and value of the facet.
+  model : VS.model.SearchFacet,
+  
+  // Turns all of the facets into a single serialized string.
+  serialize : function() {
+    return this.map(function(facet){ return facet.serialize(); }).join(' ');
+  },
+  
+  facets : function() {
+    return this.map(function(facet) {
+      var value = {};
+      value[facet.get('category')] = facet.get('value');
+      return value;
+    });
+  },
+
+  // Find a facet by its category. Multiple facets with the same category
+  // is fine, but only the first is returned.
+  find : function(category) {
+    var facet = this.detect(function(facet) {
+      return facet.get('category').toLowerCase() == category.toLowerCase();
+    });
+    return facet && facet.get('value');
+  },
+
+  // Counts the number of times a specific category is in the search query.
+  count : function(category) {
+    return this.select(function(facet) {
+      return facet.get('category').toLowerCase() == category.toLowerCase();
+    }).length;
+  },
+
+  // Returns an array of extracted values from each facet in a category.
+  values : function(category) {
+    var facets = this.select(function(facet) {
+      return facet.get('category').toLowerCase() == category.toLowerCase();
+    });
+    return _.map(facets, function(facet) { return facet.get('value'); });
+  },
+
+  // Checks all facets for matches of either a category or both category and value.
+  has : function(category, value) {
+    return this.any(function(facet) {
+      var categoryMatched = facet.get('category').toLowerCase() == category.toLowerCase();
+      if (!value) return categoryMatched;
+      return categoryMatched && facet.get('value') == value;
+    });
+  },
+
+  // Used to temporarily hide specific categories and serialize the search query.
+  withoutCategory : function() {
+    var categories = _.map(_.toArray(arguments), function(cat) { return cat.toLowerCase(); });
+    return this.map(function(facet) {
+      if (!_.include(categories, facet.get('category').toLowerCase())) { 
+        return facet.serialize();
+      };
+    }).join(' ');
+  }
+
+});
+
+})();
+(function(){
+window.JST = window.JST || {};
+
+window.JST['search_box'] = _.template('<div class="VS-search <% if (readOnly) { %>VS-readonly<% } %>">\n  <div class="VS-search-box-wrapper VS-search-box">\n    <div class="VS-icon VS-icon-search"></div>\n    <div class="VS-placeholder"></div>\n    <div class="VS-search-inner"></div>\n    <div class="VS-icon VS-icon-cancel VS-cancel-search-box" title="clear search"></div>\n  </div>\n</div>');
+window.JST['search_facet'] = _.template('<% if (model.has(\'category\')) { %>\n  <div class="category"><%= model.get(\'category\') %>:</div>\n<% } %>\n\n<div class="search_facet_input_container">\n  <textarea type="text" class="search_facet_input ui-menu VS-interface" value="" <% if (readOnly) { %>disabled="disabled"<% } %> />\n</div>\n\n<div class="search_facet_remove VS-icon VS-icon-cancel"></div>');
+window.JST['search_input'] = _.template('<textarea type="text" class="ui-menu" <% if (readOnly) { %>disabled="disabled"<% } %> />');
+})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/52d04b17/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/.bower.json
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/.bower.json b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/.bower.json
deleted file mode 100644
index 51f799f..0000000
--- a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/.bower.json
+++ /dev/null
@@ -1,45 +0,0 @@
-{
-  "name": "visualsearch",
-  "version": "0.5.0",
-  "homepage": "http://documentcloud.github.io/visualsearch/",
-  "authors": [
-    "Samuel Clay",
-    "@samuelclay"
-  ],
-  "description": "A Rich Search Box for Real Data",
-  "main": [
-    "build-min/visualsearch-datauri.css",
-    "build-min/visualsearch.css",
-    "build-min/visualsearch.js"
-  ],
-  "license": "MIT",
-  "dependencies": {
-    "jQuery": ">=1.4",
-    "jquery-ui": ">=1.8",
-    "backbone": ">=0.9.10",
-    "underscore": ">=1.4.3"
-  },
-  "ignore": [
-    "**/.*",
-    "node_modules",
-    "bower_components",
-    "test",
-    "tests",
-    "Rakefile",
-    "vendor",
-    "*.html",
-    "build",
-    "docs",
-    "*.yml"
-  ],
-  "_release": "0.5.0",
-  "_resolution": {
-    "type": "version",
-    "tag": "0.5.0",
-    "commit": "6e7613d99eabe1f19984b8d0347d9d8f12567d7e"
-  },
-  "_source": "git://github.com/documentcloud/visualsearch.git",
-  "_target": "~0.5.0",
-  "_originalSource": "visualsearch",
-  "_direct": true
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/52d04b17/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/models/search_facets.js
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/models/search_facets.js b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/models/search_facets.js
deleted file mode 100644
index 394cecd..0000000
--- a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/models/search_facets.js
+++ /dev/null
@@ -1,67 +0,0 @@
-(function() {
-
-var $ = jQuery; // Handle namespaced jQuery
-
-// The model that holds individual search facets and their categories.
-// Held in a collection by `VS.app.searchQuery`.
-VS.model.SearchFacet = Backbone.Model.extend({
-
-  // Extract the category and value and serialize it in preparation for
-  // turning the entire searchBox into a search query that can be sent
-  // to the server for parsing and searching.
-  serialize : function() {
-    var category = this.quoteCategory(this.get('category'));
-    var value    = VS.utils.inflector.trim(this.get('value'));
-    var remainder = this.get("app").options.remainder;
-
-    if (!value) return '';
-
-    if (!_.contains(this.get("app").options.unquotable || [], category) && category != remainder) {
-      value = this.quoteValue(value);
-    }
-
-    if (category != remainder) {
-      category = category + ': ';
-    } else {
-      category = "";
-    }
-    return category + value;
-  },
-  
-  // Wrap categories that have spaces or any kind of quote with opposite matching
-  // quotes to preserve the complex category during serialization.
-  quoteCategory : function(category) {
-    var hasDoubleQuote = (/"/).test(category);
-    var hasSingleQuote = (/'/).test(category);
-    var hasSpace       = (/\s/).test(category);
-    
-    if (hasDoubleQuote && !hasSingleQuote) {
-      return "'" + category + "'";
-    } else if (hasSpace || (hasSingleQuote && !hasDoubleQuote)) {
-      return '"' + category + '"';
-    } else {
-      return category;
-    }
-  },
-  
-  // Wrap values that have quotes in opposite matching quotes. If a value has
-  // both single and double quotes, just use the double quotes.
-  quoteValue : function(value) {
-    var hasDoubleQuote = (/"/).test(value);
-    var hasSingleQuote = (/'/).test(value);
-    
-    if (hasDoubleQuote && !hasSingleQuote) {
-      return "'" + value + "'";
-    } else {
-      return '"' + value + '"';
-    }
-  },
-  
-  // If provided, use a custom label instead of the raw value.
-  label : function() {
-      return this.get('label') || this.get('value');
-  }
-
-});
-
-})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/52d04b17/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/models/search_query.js
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/models/search_query.js b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/models/search_query.js
deleted file mode 100644
index 819b8d2..0000000
--- a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/models/search_query.js
+++ /dev/null
@@ -1,70 +0,0 @@
-(function() {
-
-var $ = jQuery; // Handle namespaced jQuery
-
-// Collection which holds all of the individual facets (category: value).
-// Used for finding and removing specific facets.
-VS.model.SearchQuery = Backbone.Collection.extend({
-
-  // Model holds the category and value of the facet.
-  model : VS.model.SearchFacet,
-  
-  // Turns all of the facets into a single serialized string.
-  serialize : function() {
-    return this.map(function(facet){ return facet.serialize(); }).join(' ');
-  },
-  
-  facets : function() {
-    return this.map(function(facet) {
-      var value = {};
-      value[facet.get('category')] = facet.get('value');
-      return value;
-    });
-  },
-
-  // Find a facet by its category. Multiple facets with the same category
-  // is fine, but only the first is returned.
-  find : function(category) {
-    var facet = this.detect(function(facet) {
-      return facet.get('category').toLowerCase() == category.toLowerCase();
-    });
-    return facet && facet.get('value');
-  },
-
-  // Counts the number of times a specific category is in the search query.
-  count : function(category) {
-    return this.select(function(facet) {
-      return facet.get('category').toLowerCase() == category.toLowerCase();
-    }).length;
-  },
-
-  // Returns an array of extracted values from each facet in a category.
-  values : function(category) {
-    var facets = this.select(function(facet) {
-      return facet.get('category').toLowerCase() == category.toLowerCase();
-    });
-    return _.map(facets, function(facet) { return facet.get('value'); });
-  },
-
-  // Checks all facets for matches of either a category or both category and value.
-  has : function(category, value) {
-    return this.any(function(facet) {
-      var categoryMatched = facet.get('category').toLowerCase() == category.toLowerCase();
-      if (!value) return categoryMatched;
-      return categoryMatched && facet.get('value') == value;
-    });
-  },
-
-  // Used to temporarily hide specific categories and serialize the search query.
-  withoutCategory : function() {
-    var categories = _.map(_.toArray(arguments), function(cat) { return cat.toLowerCase(); });
-    return this.map(function(facet) {
-      if (!_.include(categories, facet.get('category').toLowerCase())) { 
-        return facet.serialize();
-      };
-    }).join(' ');
-  }
-
-});
-
-})();

http://git-wip-us.apache.org/repos/asf/ambari/blob/52d04b17/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/search_box.jst
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/search_box.jst b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/search_box.jst
deleted file mode 100644
index 137075f..0000000
--- a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/search_box.jst
+++ /dev/null
@@ -1,8 +0,0 @@
-<div class="VS-search <% if (readOnly) { %>VS-readonly<% } %>">
-  <div class="VS-search-box-wrapper VS-search-box">
-    <div class="VS-icon VS-icon-search"></div>
-    <div class="VS-placeholder"></div>
-    <div class="VS-search-inner"></div>
-    <div class="VS-icon VS-icon-cancel VS-cancel-search-box" title="clear search"></div>
-  </div>
-</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/52d04b17/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/search_facet.jst
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/search_facet.jst b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/search_facet.jst
deleted file mode 100644
index 80838d7..0000000
--- a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/search_facet.jst
+++ /dev/null
@@ -1,9 +0,0 @@
-<% if (model.has('category')) { %>
-  <div class="category"><%- model.get('category') %>:</div>
-<% } %>
-
-<div class="search_facet_input_container">
-  <input type="text" class="search_facet_input ui-menu VS-interface" value="" <% if (readOnly) { %>disabled="disabled"<% } %> />
-</div>
-
-<div class="search_facet_remove VS-icon VS-icon-cancel"></div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/52d04b17/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/search_input.jst
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/search_input.jst b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/search_input.jst
deleted file mode 100644
index ba17970..0000000
--- a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/search_input.jst
+++ /dev/null
@@ -1 +0,0 @@
-<input type="text" class="ui-menu" <% if (readOnly) { %>disabled="disabled"<% } %> />
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/52d04b17/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/templates.js
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/templates.js b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/templates.js
deleted file mode 100644
index 42c46d0..0000000
--- a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/templates/templates.js
+++ /dev/null
@@ -1,7 +0,0 @@
-(function(){
-window.JST = window.JST || {};
-
-window.JST['search_box'] = _.template('<div class="VS-search <% if (readOnly) { %>VS-readonly<% } %>">\n  <div class="VS-search-box-wrapper VS-search-box">\n    <div class="VS-icon VS-icon-search"></div>\n    <div class="VS-placeholder"></div>\n    <div class="VS-search-inner"></div>\n    <div class="VS-icon VS-icon-cancel VS-cancel-search-box" title="clear search"></div>\n  </div>\n</div>');
-window.JST['search_facet'] = _.template('<% if (model.has(\'category\')) { %>\n  <div class="category"><%- model.get(\'category\') %>:</div>\n<% } %>\n\n<div class="search_facet_input_container">\n  <input type="text" class="search_facet_input ui-menu VS-interface" value="" <% if (readOnly) { %>disabled="disabled"<% } %> />\n</div>\n\n<div class="search_facet_remove VS-icon VS-icon-cancel"></div>');
-window.JST['search_input'] = _.template('<input type="text" class="ui-menu" <% if (readOnly) { %>disabled="disabled"<% } %> />');
-})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/52d04b17/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/utils/backbone_extensions.js
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/utils/backbone_extensions.js b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/utils/backbone_extensions.js
deleted file mode 100644
index de85fa9..0000000
--- a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/utils/backbone_extensions.js
+++ /dev/null
@@ -1,17 +0,0 @@
-(function(){
-
-  var $ = jQuery; // Handle namespaced jQuery
-
-  // Makes the view enter a mode. Modes have both a 'mode' and a 'group',
-  // and are mutually exclusive with any other modes in the same group.
-  // Setting will update the view's modes hash, as well as set an HTML class
-  // of *[mode]_[group]* on the view's element. Convenient way to swap styles
-  // and behavior.
-  Backbone.View.prototype.setMode = function(mode, group) {
-    this.modes || (this.modes = {});
-    if (this.modes[group] === mode) return;
-    $(this.el).setMode(mode, group);
-    this.modes[group] = mode;
-  };
-
-})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/52d04b17/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/utils/hotkeys.js
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/utils/hotkeys.js b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/utils/hotkeys.js
deleted file mode 100644
index 64a292a..0000000
--- a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/utils/hotkeys.js
+++ /dev/null
@@ -1,99 +0,0 @@
-(function() {
-
-var $ = jQuery; // Handle namespaced jQuery
-
-// DocumentCloud workspace hotkeys. To tell if a key is currently being pressed,
-// just ask `VS.app.hotkeys.[key]` on `keypress`, or ask `VS.app.hotkeys.key(e)`
-// on `keydown`.
-//
-// For the most headache-free way to use this utility, check modifier keys,
-// like shift and command, with `VS.app.hotkeys.shift`, and check every other
-// key with `VS.app.hotkeys.key(e) == 'key_name'`.
-VS.app.hotkeys = {
-
-  // Keys that will be mapped to the `hotkeys` namespace.
-  KEYS: {
-    '16':  'shift',
-    '17':  'command',
-    '91':  'command',
-    '93':  'command',
-    '224': 'command',
-    '13':  'enter',
-    '37':  'left',
-    '38':  'upArrow',
-    '39':  'right',
-    '40':  'downArrow',
-    '46':  'delete',
-    '8':   'backspace',
-    '35':  'end',
-    '36':  'home',
-    '9':   'tab',
-    '188': 'comma'
-  },
-
-  // Binds global keydown and keyup events to listen for keys that match `this.KEYS`.
-  initialize : function() {
-    _.bindAll(this, 'down', 'up', 'blur');
-    $(document).bind('keydown', this.down);
-    $(document).bind('keyup', this.up);
-    $(window).bind('blur', this.blur);
-  },
-
-  // On `keydown`, turn on all keys that match.
-  down : function(e) {
-    var key = this.KEYS[e.which];
-    if (key) this[key] = true;
-  },
-
-  // On `keyup`, turn off all keys that match.
-  up : function(e) {
-    var key = this.KEYS[e.which];
-    if (key) this[key] = false;
-  },
-
-  // If an input is blurred, all keys need to be turned off, since they are no longer
-  // able to modify the document.
-  blur : function(e) {
-    for (var key in this.KEYS) this[this.KEYS[key]] = false;
-  },
-
-  // Check a key from an event and return the common english name.
-  key : function(e) {
-    return this.KEYS[e.which];
-  },
-
-  // Colon is special, since the value is different between browsers.
-  colon : function(e) {
-    var charCode = e.which;
-    return charCode && String.fromCharCode(charCode) == ":";
-  },
-
-  // Check a key from an event and match it against any known characters.
-  // The `keyCode` is different depending on the event type: `keydown` vs. `keypress`.
-  //
-  // These were determined by looping through every `keyCode` and `charCode` that
-  // resulted from `keydown` and `keypress` events and counting what was printable.
-  printable : function(e) {
-    var code = e.which;
-    if (e.type == 'keydown') {
-      if (code == 32 ||                      // space
-          (code >= 48 && code <= 90) ||      // 0-1a-z
-          (code >= 96 && code <= 111) ||     // 0-9+-/*.
-          (code >= 186 && code <= 192) ||    // ;=,-./^
-          (code >= 219 && code <= 222)) {    // (\)'
-        return true;
-      }
-    } else {
-      // [space]!"#$%&'()*+,-.0-9:;<=>?@A-Z[\]^_`a-z{|} and unicode characters
-      if ((code >= 32 && code <= 126)  ||
-          (code >= 160 && code <= 500) ||
-          (String.fromCharCode(code) == ":")) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-};
-
-})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/52d04b17/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/utils/inflector.js
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/utils/inflector.js b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/utils/inflector.js
deleted file mode 100644
index 8a1d08c..0000000
--- a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/other/visualsearch/utils/inflector.js
+++ /dev/null
@@ -1,21 +0,0 @@
-(function() {
-
-var $ = jQuery; // Handle namespaced jQuery
-
-// Naive English transformations on words. Only used for a few transformations 
-// in VisualSearch.js.
-VS.utils.inflector = {
-
-  // Delegate to the ECMA5 String.prototype.trim function, if available.
-  trim : function(s) {
-    return s.trim ? s.trim() : s.replace(/^\s+|\s+$/g, '');
-  },
-  
-  // Escape strings that are going to be used in a regex. Escapes punctuation
-  // that would be incorrect in a regex.
-  escapeRegExp : function(s) {
-    return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
-  }
-};
-
-})();
\ No newline at end of file


Mime
View raw message