chukwa-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ey...@apache.org
Subject [07/22] chukwa git commit: CHUKWA-756. Added ajax-solr UI for log search. (Eric Yang)
Date Thu, 25 Jun 2015 20:48:38 GMT
http://git-wip-us.apache.org/repos/asf/chukwa/blob/0cad3aae/src/main/web/hicc/ajax-solr/core/ParameterStore.js
----------------------------------------------------------------------
diff --git a/src/main/web/hicc/ajax-solr/core/ParameterStore.js b/src/main/web/hicc/ajax-solr/core/ParameterStore.js
new file mode 100644
index 0000000..cb53db6
--- /dev/null
+++ b/src/main/web/hicc/ajax-solr/core/ParameterStore.js
@@ -0,0 +1,366 @@
+(function (callback) {
+  if (typeof define === 'function' && define.amd) {
+    define(['core/Core', 'core/Parameter'], callback);
+  }
+  else {
+    callback();
+  }
+}(function () {
+
+/**
+ * The ParameterStore, as its name suggests, stores Solr parameters. Widgets
+ * expose some of these parameters to the user. Whenever the user changes the
+ * values of these parameters, the state of the application changes. In order to
+ * allow the user to move back and forth between these states with the browser's
+ * Back and Forward buttons, and to bookmark these states, each state needs to
+ * be stored. The easiest method is to store the exposed parameters in the URL
+ * hash (see the <tt>ParameterHashStore</tt> class). However, you may implement
+ * your own storage method by extending this class.
+ *
+ * <p>For a list of possible parameters, please consult the links below.</p>
+ *
+ * @see http://wiki.apache.org/solr/CoreQueryParameters
+ * @see http://wiki.apache.org/solr/CommonQueryParameters
+ * @see http://wiki.apache.org/solr/SimpleFacetParameters
+ * @see http://wiki.apache.org/solr/HighlightingParameters
+ * @see http://wiki.apache.org/solr/MoreLikeThis
+ * @see http://wiki.apache.org/solr/SpellCheckComponent
+ * @see http://wiki.apache.org/solr/StatsComponent
+ * @see http://wiki.apache.org/solr/TermsComponent
+ * @see http://wiki.apache.org/solr/TermVectorComponent
+ * @see http://wiki.apache.org/solr/LocalParams
+ *
+ * @param properties A map of fields to set. Refer to the list of public fields.
+ * @class ParameterStore
+ */
+AjaxSolr.ParameterStore = AjaxSolr.Class.extend(
+  /** @lends AjaxSolr.ParameterStore.prototype */
+  {
+  constructor: function (attributes) {
+    /**
+     * @param {Object} [attributes]
+     * @param {String[]} [attributes.exposed] The names of the exposed
+     *   parameters. Any parameters that your widgets expose to the user,
+     *   directly or indirectly, should be listed here.
+     */
+    AjaxSolr.extend(this, {
+      exposed: [],
+      // The Solr parameters.
+      params: {},
+      // A reference to the parameter store's manager.
+      manager: null
+    }, attributes);
+  },
+
+  /**
+   * An abstract hook for child implementations.
+   *
+   * <p>This method should do any necessary one-time initializations.</p>
+   */
+  init: function () {},
+
+  /**
+   * Some Solr parameters may be specified multiple times. It is easiest to
+   * hard-code a list of such parameters. You may change the list by passing
+   * <code>{ multiple: /pattern/ }</code> as an argument to the constructor of
+   * this class or one of its children, e.g.:
+   *
+   * <p><code>new ParameterStore({ multiple: /pattern/ })</code>
+   *
+   * @param {String} name The name of the parameter.
+   * @returns {Boolean} Whether the parameter may be specified multiple times.
+   * @see http://lucene.apache.org/solr/api/org/apache/solr/handler/DisMaxRequestHandler.html
+   */
+  isMultiple: function (name) {
+    return name.match(/^(?:bf|bq|facet\.date|facet\.date\.other|facet\.date\.include|facet\.field|facet\.pivot|facet\.range|facet\.range\.other|facet\.range\.include|facet\.query|fq|group\.field|group\.func|group\.query|pf|qf)$/);
+  },
+
+  /**
+   * Returns a parameter. If the parameter doesn't exist, creates it.
+   *
+   * @param {String} name The name of the parameter.
+   * @returns {AjaxSolr.Parameter|AjaxSolr.Parameter[]} The parameter.
+   */
+  get: function (name) {
+    if (this.params[name] === undefined) {
+      var param = new AjaxSolr.Parameter({ name: name });
+      if (this.isMultiple(name)) {
+        this.params[name] = [ param ];
+      }
+      else {
+        this.params[name] = param;
+      }
+    }
+    return this.params[name];
+  },
+
+  /**
+   * If the parameter may be specified multiple times, returns the values of
+   * all identically-named parameters. If the parameter may be specified only
+   * once, returns the value of that parameter.
+   *
+   * @param {String} name The name of the parameter.
+   * @returns {String[]|Number[]} The value(s) of the parameter.
+   */
+  values: function (name) {
+    if (this.params[name] !== undefined) {
+      if (this.isMultiple(name)) {
+        var values = [];
+        for (var i = 0, l = this.params[name].length; i < l; i++) {
+          values.push(this.params[name][i].val());
+        }
+        return values;
+      }
+      else {
+        return [ this.params[name].val() ];
+      }
+    }
+    return [];
+  },
+
+  /**
+   * If the parameter may be specified multiple times, adds the given parameter
+   * to the list of identically-named parameters, unless one already exists with
+   * the same value. If it may be specified only once, replaces the parameter.
+   *
+   * @param {String} name The name of the parameter.
+   * @param {AjaxSolr.Parameter} [param] The parameter.
+   * @returns {AjaxSolr.Parameter|Boolean} The parameter, or false.
+   */
+  add: function (name, param) {
+    if (param === undefined) {
+      param = new AjaxSolr.Parameter({ name: name });
+    }
+    if (this.isMultiple(name)) {
+      if (this.params[name] === undefined) {
+        this.params[name] = [ param ];
+      }
+      else {
+        if (AjaxSolr.inArray(param.val(), this.values(name)) == -1) {
+          this.params[name].push(param);
+        }
+        else {
+          return false;
+        }
+      }
+    }
+    else {
+      this.params[name] = param;
+    }
+    return param;
+  },
+
+  /**
+   * Deletes a parameter.
+   *
+   * @param {String} name The name of the parameter.
+   * @param {Number} [index] The index of the parameter.
+   */
+  remove: function (name, index) {
+    if (index === undefined) {
+      delete this.params[name];
+    }
+    else {
+      this.params[name].splice(index, 1);
+      if (this.params[name].length == 0) {
+        delete this.params[name];
+      }
+    }
+  },
+
+  /**
+   * Finds all parameters with matching values.
+   *
+   * @param {String} name The name of the parameter.
+   * @param {String|Number|String[]|Number[]|RegExp} value The value.
+   * @returns {String|Number[]} The indices of the parameters found.
+   */
+  find: function (name, value) {
+    if (this.params[name] !== undefined) {
+      if (this.isMultiple(name)) {
+        var indices = [];
+        for (var i = 0, l = this.params[name].length; i < l; i++) {
+          if (AjaxSolr.equals(this.params[name][i].val(), value)) {
+            indices.push(i);
+          }
+        }
+        return indices.length ? indices : false;
+      }
+      else {
+        if (AjaxSolr.equals(this.params[name].val(), value)) {
+          return name;
+        }
+      }
+    }
+    return false;
+  },
+
+  /**
+   * If the parameter may be specified multiple times, creates a parameter using
+   * the given name and value, and adds it to the list of identically-named
+   * parameters, unless one already exists with the same value. If it may be
+   * specified only once, replaces the parameter.
+   *
+   * @param {String} name The name of the parameter.
+   * @param {String|Number|String[]|Number[]} value The value.
+   * @param {Object} [locals] The parameter's local parameters.
+   * @returns {AjaxSolr.Parameter|Boolean} The parameter, or false.
+   */
+  addByValue: function (name, value, locals) {
+    if (locals === undefined) {
+      locals = {};
+    }
+    if (this.isMultiple(name) && AjaxSolr.isArray(value)) {
+      var ret = [];
+      for (var i = 0, l = value.length; i < l; i++) {
+        ret.push(this.add(name, new AjaxSolr.Parameter({ name: name, value: value[i], locals:
locals })));
+      }
+      return ret;
+    }
+    else {
+      return this.add(name, new AjaxSolr.Parameter({ name: name, value: value, locals: locals
}));
+    }
+  },
+
+  /**
+   * Deletes any parameter with a matching value.
+   *
+   * @param {String} name The name of the parameter.
+   * @param {String|Number|String[]|Number[]|RegExp} value The value.
+   * @returns {String|Number[]} The indices deleted.
+   */
+  removeByValue: function (name, value) {
+    var indices = this.find(name, value);
+    if (indices) {
+      if (AjaxSolr.isArray(indices)) {
+        for (var i = indices.length - 1; i >= 0; i--) {
+          this.remove(name, indices[i]);
+        }
+      }
+      else {
+        this.remove(indices);
+      }
+    }
+    return indices;
+  },
+
+  /**
+   * Returns the Solr parameters as a query string.
+   *
+   * <p>IE6 calls the default toString() if you write <tt>store.toString()
+   * </tt>. So, we need to choose another name for toString().</p>
+   */
+  string: function () {
+    var params = [], string;
+    for (var name in this.params) {
+      if (this.isMultiple(name)) {
+        for (var i = 0, l = this.params[name].length; i < l; i++) {
+          string = this.params[name][i].string();
+          if (string) {
+            params.push(string);
+          }
+        }
+      }
+      else {
+        string = this.params[name].string();
+        if (string) {
+          params.push(string);
+        }
+      }
+    }
+    return params.join('&');
+  },
+
+  /**
+   * Parses a query string into Solr parameters.
+   *
+   * @param {String} str The string to parse.
+   */
+  parseString: function (str) {
+    var pairs = str.split('&');
+    for (var i = 0, l = pairs.length; i < l; i++) {
+      if (pairs[i]) { // ignore leading, trailing, and consecutive &'s
+        var param = new AjaxSolr.Parameter();
+        param.parseString(pairs[i]);
+        this.add(param.name, param);
+      }
+    }
+  },
+
+  /**
+   * Returns the exposed parameters as a query string.
+   *
+   * @returns {String} A string representation of the exposed parameters.
+   */
+  exposedString: function () {
+    var params = [], string;
+    for (var i = 0, l = this.exposed.length; i < l; i++) {
+      if (this.params[this.exposed[i]] !== undefined) {
+        if (this.isMultiple(this.exposed[i])) {
+          for (var j = 0, m = this.params[this.exposed[i]].length; j < m; j++) {
+            string = this.params[this.exposed[i]][j].string();
+            if (string) {
+              params.push(string);
+            }
+          }
+        }
+        else {
+          string = this.params[this.exposed[i]].string();
+          if (string) {
+            params.push(string);
+          }
+        }
+      }
+    }
+    return params.join('&');
+  },
+
+  /**
+   * Resets the values of the exposed parameters.
+   */
+  exposedReset: function () {
+    for (var i = 0, l = this.exposed.length; i < l; i++) {
+      this.remove(this.exposed[i]);
+    }
+  },
+
+  /**
+   * Loads the values of exposed parameters from persistent storage. It is
+   * necessary, in most cases, to reset the values of exposed parameters before
+   * setting the parameters to the values in storage. This is to ensure that a
+   * parameter whose name is not present in storage is properly reset.
+   *
+   * @param {Boolean} [reset=true] Whether to reset the exposed parameters.
+   *   before loading new values from persistent storage. Default: true.
+   */
+  load: function (reset) {
+    if (reset === undefined) {
+      reset = true;
+    }
+    if (reset) {
+      this.exposedReset();
+    }
+    this.parseString(this.storedString());
+  },
+
+  /**
+   * An abstract hook for child implementations.
+   *
+   * <p>Stores the values of the exposed parameters in persistent storage. This
+   * method should usually be called before each Solr request.</p>
+   */
+  save: function () {},
+
+  /**
+   * An abstract hook for child implementations.
+   *
+   * <p>Returns the string to parse from persistent storage.</p>
+   *
+   * @returns {String} The string from persistent storage.
+   */
+  storedString: function () {
+    return '';
+  }
+});
+
+}));

http://git-wip-us.apache.org/repos/asf/chukwa/blob/0cad3aae/src/main/web/hicc/ajax-solr/managers/Manager.jquery.js
----------------------------------------------------------------------
diff --git a/src/main/web/hicc/ajax-solr/managers/Manager.jquery.js b/src/main/web/hicc/ajax-solr/managers/Manager.jquery.js
new file mode 100644
index 0000000..03ec766
--- /dev/null
+++ b/src/main/web/hicc/ajax-solr/managers/Manager.jquery.js
@@ -0,0 +1,40 @@
+(function (callback) {
+  if (typeof define === 'function' && define.amd) {
+    define(['core/AbstractManager'], callback);
+  }
+  else {
+    callback();
+  }
+}(function () {
+
+/**
+ * @see http://wiki.apache.org/solr/SolJSON#JSON_specific_parameters
+ * @class Manager
+ * @augments AjaxSolr.AbstractManager
+ */
+AjaxSolr.Manager = AjaxSolr.AbstractManager.extend(
+  /** @lends AjaxSolr.Manager.prototype */
+  {
+  executeRequest: function (servlet, string, handler, errorHandler) {
+    var self = this,
+        options = {dataType: 'json'};
+    string = string || this.store.string();
+    handler = handler || function (data) {
+      self.handleResponse(data);
+    };
+    errorHandler = errorHandler || function (jqXHR, textStatus, errorThrown) {
+      self.handleError(textStatus + ', ' + errorThrown);
+    };
+    if (this.proxyUrl) {
+      options.url = this.proxyUrl;
+      options.data = {query: string};
+      options.type = 'POST';
+    }
+    else {
+      options.url = this.solrUrl + servlet + '?' + string + '&wt=json&json.wrf=?';
+    }
+    jQuery.ajax(options).done(handler).fail(errorHandler);
+  }
+});
+
+}));

http://git-wip-us.apache.org/repos/asf/chukwa/blob/0cad3aae/src/main/web/hicc/ajax-solr/widgets/ParameterHistoryStore.js
----------------------------------------------------------------------
diff --git a/src/main/web/hicc/ajax-solr/widgets/ParameterHistoryStore.js b/src/main/web/hicc/ajax-solr/widgets/ParameterHistoryStore.js
new file mode 100644
index 0000000..accc9de
--- /dev/null
+++ b/src/main/web/hicc/ajax-solr/widgets/ParameterHistoryStore.js
@@ -0,0 +1,82 @@
+;(function(history) {
+  /**
+   * A parameter store that stores the values of exposed parameters in the URL via History.js
+   * to maintain the application's state. This uses the HTML5 History API for newer browsers,
and
+   * falls back to using the hash in older browsers. Don't forget to add the following (or
similar)
+   * inside your <tt>head</tt> tag:
+   *
+   * <pre>
+   * <script src="history.js/scripts/bundled/html4+html5/jquery.history.js"></script>
+   * </pre>
+   *
+   * Configure the manager with:
+   *
+   * <pre>
+   * Manager.setStore(new AjaxSolr.ParameterHistoryStore());
+   * </pre>
+   *
+   * @class ParameterHistoryStore
+   * @augments AjaxSolr.ParameterStore
+   * @see https://github.com/browserstate/history.js
+   * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html
+   */
+  AjaxSolr.ParameterHistoryStore = AjaxSolr.ParameterStore.extend(
+    /** @lends AjaxSolr.ParameterHistoryStore.prototype */
+    {
+    init: function () {
+      if (this.exposed.length) {
+        if (!history) {
+          throw 'ParameterHistoryStore requires History.js';
+        }
+
+        history.Adapter.bind(window, 'statechange', this.stateChangeFunction(this));
+      }
+    },
+
+    /**
+     * Stores the values of the exposed parameters in both the local hash and History.js
+     * No other code should be made to change these two values.
+     */
+    save: function () {
+      this.hash = this.exposedString();
+      history.pushState({ params: this.hash }, null, '?' + this.hash);
+    },
+
+    /**
+     * @see ParameterStore#storedString()
+     */
+    storedString: function () {
+      var state = history.getState();
+
+      // Load the state from the History object.
+      if (state.data && state.data.params) {
+        return state.data.params;
+      }
+
+      // If initial load, load the state from the URL.
+      var url = state.cleanUrl, index = url.indexOf('?');
+      if (index == -1) {
+        return '';
+      }
+      else {
+        return url.substr(index + 1);
+      }
+    },
+
+    /**
+     * Called when History.js detects a state change. Checks if state is different to previous
state,
+     * and if so, sends a request to Solr. This needs to check if the state has changed since
it also
+     * gets called when we call pushState above.
+     */
+    stateChangeFunction: function (self) {
+      return function () {
+        var hash = self.storedString();
+
+        if (self.hash != hash) {
+          self.load();
+          self.manager.doRequest();
+        }
+      }
+    }
+  });
+})(window.History);

http://git-wip-us.apache.org/repos/asf/chukwa/blob/0cad3aae/src/main/web/hicc/ajax-solr/widgets/ParameterYUIStore.js
----------------------------------------------------------------------
diff --git a/src/main/web/hicc/ajax-solr/widgets/ParameterYUIStore.js b/src/main/web/hicc/ajax-solr/widgets/ParameterYUIStore.js
new file mode 100644
index 0000000..7ee377d
--- /dev/null
+++ b/src/main/web/hicc/ajax-solr/widgets/ParameterYUIStore.js
@@ -0,0 +1,90 @@
+/**
+ * A parameter store that stores the values of exposed parameters using the YUI
+ * History Manager to maintain the application's state. Don't forget to add the
+ * following inside your <tt>head</tt> tag:
+ *
+ * <pre>
+ * <script src="http://yui.yahooapis.com/2.9.0/build/yahoo/yahoo-min.js"></script>
+ * <script src="http://yui.yahooapis.com/2.9.0/build/event/event-min.js"></script>
+ * <script src="http://yui.yahooapis.com/2.9.0/build/history/history-min.js"></script>
+ * </pre>
+ *
+ * And the following inside your <tt>body</tt> tag:
+ *
+ * <pre>
+ * <iframe id="yui-history-iframe" src="path-to-existing-asset" style="position:absolute;top:0;left:0;width:1px;height:1px;visibility:hidden"></iframe>
+ * <input id="yui-history-field" type="hidden">
+ * </pre>
+ *
+ * Configure the manager with:
+ *
+ * <pre>
+ * Manager.setStore(new AjaxSolr.ParameterYUIStore());
+ * </pre>
+ *
+ * @see http://developer.yahoo.com/yui/history/
+ * @class ParameterYUIStore
+ * @augments AjaxSolr.ParameterStore
+ */
+AjaxSolr.ParameterYUIStore = AjaxSolr.ParameterStore.extend(
+  /** @lends AjaxSolr.ParameterYUIStore.prototype */
+  {
+  /**
+   * @param {Object} [attributes]
+   * @param {String} [attributes.module] The name of the YUI History Manager
+   *   module to use for the parameter store. Defaults to "q".
+   */
+  constructor: function (attributes) {
+    AjaxSolr.ParameterYUIStore.__super__.constructor.apply(this, arguments);
+    AjaxSolr.extend(this, {
+      module: 'q',
+      // Whether the YUI History Manager is initialized.
+      initialized: false,
+      // Whether the parameter store is curring loading state.
+      loading: false,
+      // Whether the parameter store is curring saving state.
+      saving: false
+    }, attributes);
+  },
+
+  /**
+   * Initializes the YUI History Manager.
+   */
+  init: function () {
+    if (this.exposed.length) {
+      var self = this;
+      YAHOO.util.History.register(this.module, YAHOO.util.History.getBookmarkedState(this.module)
|| this.exposedString(), function () {
+        if (!self.saving) {
+          self.loading = true;
+          self.load();
+          self.manager.doRequest();
+          self.loading = false;
+        }
+      });
+      YAHOO.util.History.onReady(function () {
+        self.initialized = true;
+        self.load();
+        self.manager.doRequest();
+      });
+      YAHOO.util.History.initialize('yui-history-field', 'yui-history-iframe');
+    }
+  },
+
+  /**
+   * Stores the values of the exposed parameters in the YUI History Manager.
+   */
+  save: function () {
+    if (!self.loading) {
+      this.saving = true;
+      YAHOO.util.History.navigate(this.module, this.exposedString());
+      this.saving = false;
+    }
+  },
+
+  /**
+   * @see ParameterStore#storedString()
+   */
+  storedString: function () {
+    return this.initialized ? YAHOO.util.History.getCurrentState(this.module) : this.exposedString();
+  }
+});

http://git-wip-us.apache.org/repos/asf/chukwa/blob/0cad3aae/src/main/web/hicc/ajax-solr/widgets/jquery/AutocompleteTermWidget.js
----------------------------------------------------------------------
diff --git a/src/main/web/hicc/ajax-solr/widgets/jquery/AutocompleteTermWidget.js b/src/main/web/hicc/ajax-solr/widgets/jquery/AutocompleteTermWidget.js
new file mode 100644
index 0000000..1afa34c
--- /dev/null
+++ b/src/main/web/hicc/ajax-solr/widgets/jquery/AutocompleteTermWidget.js
@@ -0,0 +1,134 @@
+(function ($) {
+
+/**
+ * A <i>term</i> autocomplete search box, using jQueryUI.autocomplete. This
+ * implementation uses Solr's facet.prefix technique. This technique benefits
+ * from honoring the filter query state and by being able to put words prior to
+ * the last one the user is typing into a filter query as well to get even more
+ * relevant completion suggestions.
+ *
+ * Index instructions:
+ * 1. Put a facet warming query into Solr's "firstSearcher" in solrconfig.xml,
+ * for the target field.
+ * 2. Use appropriate text analysis to include a tokenizer (not keyword) and do
+ * <i>not</i> do stemming or else you will see stems suggested. A 'light'
+ * stemmer may produce acceptable stems.
+ * 3. If you are auto-completing in a search box that would normally be using
+ * the dismax query parser AND your qf parameter references more than one field,
+ * then you might want to use a catch-all search field to autocomplete on.
+ *
+ * For large indexes, another implementation approach like the Suggester feature
+ * or TermsComponent might be better than a faceting approach.
+ *
+ * Other types of autocomplete (a.k.a. suggest) are "search-results",
+ * "query-log", and "facet-value". This widget does term autocompletion.
+ *
+ * @author David Smiley <david.w.smiley at gmail.com>
+ */
+AjaxSolr.AutocompleteTermWidget = AjaxSolr.AbstractTextWidget.extend(
+  /** @lends AjaxSolr.AutocompleteTermWidget.prototype */
+  {
+  /**
+   * @param {Object} attributes
+   * @param {String} attributes.field The Solr field to autocomplete indexed
+   *   terms from.
+   * @param {Boolean} [attributes.tokenized] Whether the underlying field is
+   *   tokenized. This component will take words before the last word
+   *   (whitespace separated) and generate a filter query for those words, while
+   *   only the last word will be used for facet.prefix. For field-value
+   *   completion (on just one field) or query log completion, you would have a
+   *   non-tokenized field to complete against. Defaults to <tt>true</tt>.
+   * @param {Boolean} [attributes.lowercase] Indicates whether to lowercase the
+   *   facet.prefix value. Defaults to <tt>true</tt>.
+   * @param {Number} [attributes.limit] The maximum number of results to show.
+   *   Defaults to 10.
+   * @param {Number} [attributes.minLength] The minimum number of characters
+   *   required to show suggestions. Defaults to 2.
+   * @param {String} [attributes.servlet] The URL path that follows the solr
+   *   webapp, for use in auto-complete queries. If not specified, the manager's
+   *   servlet property will be used. You may prepend the servlet with a core if
+   *   using multiple cores. It is a good idea to use a non-default one to
+   *   differentiate these requests in server logs and Solr statistics.
+   */
+  constructor: function (attributes) {
+    AjaxSolr.AutocompleteTermWidget.__super__.constructor.apply(this, arguments);
+    AjaxSolr.extend(this, {
+      field: null,
+      tokenized: true,
+      lowercase: true,
+      limit: 10,
+      minLength: 2,
+      servlet: null
+    }, attributes);
+  },
+
+  init: function () {
+    var self = this;
+
+    if (!this.field) {
+      throw '"field" must be set on AutocompleteTermWidget.';
+    }
+    this.servlet = this.servlet || this.manager.servlet;
+
+    $(this.target).find('input').bind('keydown', function (e) {
+      if (e.which == 13) {
+        var q = $(this).val();
+        if (self.set(q)) {
+          self.doRequest();
+        }
+      }
+    });
+
+    $(this.target).find('input').autocomplete({
+      source: function (request, response) { // note: must always call response()
+        // If term ends with a space:
+        if (request.term.charAt(request.term.length - 1).replace(/^ +/, '').replace(/ +$/,
'') == '') {
+          response();
+          return;
+        }
+
+        var term = request.term,
+            facetPrefix = term, // before the last word (if we tokenize)
+            fq = '',
+            store = new AjaxSolr.ParameterStore();
+
+        store.addByValue('fq', self.manager.store.values('fq'));
+
+        if (self.tokenized) {
+          // Split out the last word of the term from the words before it.
+          var lastSpace = term.lastIndexOf(' ');
+          if (lastSpace > -1) {
+            fq = term.substring(0, lastSpace);
+            facetPrefix = term.substring(lastSpace + 1);
+            store.addByValue('fq', '{!dismax qf=' + self.field + '}' + fq);
+          }
+        }
+        if (self.lowercase) {
+          facetPrefix = facetPrefix.toLowerCase();
+        }
+
+        store.addByValue('facet.field', self.field);
+        store.addByValue('facet.limit', self.limit);
+        store.addByValue('facet.prefix', facetPrefix);
+
+        self.manager.executeRequest(self.servlet, 'json.nl=arrarr&q=*:*&rows=0&facet=true&facet.mincount=1&'
+ store.string(), function (data) {
+          response($.map(data.facet_counts.facet_fields[self.field], function (term) {
+            var q = (fq + ' ' + term[0]).replace(/^ +/, '').replace(/ +$/, '');
+            return {
+              label: q + ' (' + term[1] + ')',
+              value: q
+            }
+          }));
+        });
+      },
+      minLength: this.minLength,
+      select: function(event, ui) {
+        if (self.set(ui.item.value)) {
+          self.doRequest();
+        }
+      }
+    });
+  }
+});
+
+})(jQuery);

http://git-wip-us.apache.org/repos/asf/chukwa/blob/0cad3aae/src/main/web/hicc/ajax-solr/widgets/jquery/PagerWidget.js
----------------------------------------------------------------------
diff --git a/src/main/web/hicc/ajax-solr/widgets/jquery/PagerWidget.js b/src/main/web/hicc/ajax-solr/widgets/jquery/PagerWidget.js
new file mode 100644
index 0000000..768ec79
--- /dev/null
+++ b/src/main/web/hicc/ajax-solr/widgets/jquery/PagerWidget.js
@@ -0,0 +1,256 @@
+(function (callback) {
+  if (typeof define === 'function' && define.amd) {
+    define(['core/AbstractWidget'], callback);
+  }
+  else {
+    callback();
+  }
+}(function () {
+
+(function ($) {
+
+/**
+ * A pager widget for jQuery.
+ *
+ * <p>Heavily inspired by the Ruby on Rails will_paginate gem.</p>
+ *
+ * @expects this.target to be a list.
+ * @class PagerWidget
+ * @augments AjaxSolr.AbstractWidget
+ * @todo Don't use the manager to send the request. Request only the results,
+ * not the facets. Update only itself and the results widget.
+ */
+AjaxSolr.PagerWidget = AjaxSolr.AbstractWidget.extend(
+  /** @lends AjaxSolr.PagerWidget.prototype */
+  {
+  /**
+   * @param {Object} [attributes]
+   * @param {Number} [attributes.innerWindow] How many links are shown around
+   *   the current page. Defaults to 4.
+   * @param {Number} [attributes.outerWindow] How many links are around the
+   *   first and the last page. Defaults to 1.
+   * @param {String} [attributes.prevLabel] The previous page link label.
+   *   Defaults to "&laquo; Previous".
+   * @param {String} [attributes.nextLabel] The next page link label. Defaults
+   *   to "Next &raquo;".
+   * @param {String} [attributes.separator] Separator between pagination links.
+   *   Defaults to " ".
+   */
+  constructor: function (attributes) {
+    AjaxSolr.PagerWidget.__super__.constructor.apply(this, arguments);
+    AjaxSolr.extend(this, {
+      innerWindow: 4,
+      outerWindow: 1,
+      prevLabel: '&laquo; Previous',
+      nextLabel: 'Next &raquo;',
+      separator: ' ',
+      // The current page number.
+      currentPage: null,
+      // The total number of pages.
+      totalPages: null
+    }, attributes);
+  },
+
+  /**
+   * @returns {String} The gap in page links, which is represented by:
+   *   <span class="pager-gap">&hellip;</span>
+   */
+  gapMarker: function () {
+    return '<span class="pager-gap">&hellip;</span>';
+  },
+
+  /**
+   * @returns {Array} The links for the visible page numbers.
+   */
+  windowedLinks: function () {
+    var links = [];
+
+    var prev = null;
+
+    visible = this.visiblePageNumbers();
+    for (var i = 0, l = visible.length; i < l; i++) {
+      if (prev && visible[i] > prev + 1) links.push(this.gapMarker());
+      links.push(this.pageLinkOrSpan(visible[i], [ 'pager-current' ]));
+      prev = visible[i];
+    }
+
+    return links;
+  },
+
+  /**
+   * @returns {Array} The visible page numbers according to the window options.
+   */
+  visiblePageNumbers: function () {
+    var windowFrom = this.currentPage - this.innerWindow;
+    var windowTo = this.currentPage + this.innerWindow;
+
+    // If the window is truncated on one side, make the other side longer
+    if (windowTo > this.totalPages) {
+      windowFrom = Math.max(0, windowFrom - (windowTo - this.totalPages));
+      windowTo = this.totalPages;
+    }
+    if (windowFrom < 1) {
+      windowTo = Math.min(this.totalPages, windowTo + (1 - windowFrom));
+      windowFrom = 1;
+    }
+
+    var visible = [];
+
+    // Always show the first page
+    visible.push(1);
+    // Don't add inner window pages twice
+    for (var i = 2; i <= Math.min(1 + this.outerWindow, windowFrom - 1); i++) {
+      visible.push(i);
+    }
+    // If the gap is just one page, close the gap
+    if (1 + this.outerWindow == windowFrom - 2) {
+      visible.push(windowFrom - 1);
+    }
+    // Don't add the first or last page twice
+    for (var i = Math.max(2, windowFrom); i <= Math.min(windowTo, this.totalPages - 1);
i++) {
+      visible.push(i);
+    }
+    // If the gap is just one page, close the gap
+    if (this.totalPages - this.outerWindow == windowTo + 2) {
+      visible.push(windowTo + 1);
+    }
+    // Don't add inner window pages twice
+    for (var i = Math.max(this.totalPages - this.outerWindow, windowTo + 1); i < this.totalPages;
i++) {
+      visible.push(i);
+    }
+    // Always show the last page, unless it's the first page
+    if (this.totalPages > 1) {
+      visible.push(this.totalPages);
+    }
+
+    return visible;
+  },
+
+  /**
+   * @param {Number} page A page number.
+   * @param {String} classnames CSS classes to add to the page link.
+   * @param {String} text The inner HTML of the page link (optional).
+   * @returns The link or span for the given page.
+   */
+  pageLinkOrSpan: function (page, classnames, text) {
+    text = text || page;
+
+    if (page && page != this.currentPage) {
+      return $('<a href="#"></a>').html(text).attr('rel', this.relValue(page)).addClass(classnames[1]).click(this.clickHandler(page));
+    }
+    else {
+      return $('<span></span>').html(text).addClass(classnames.join(' '));
+    }
+  },
+
+  /**
+   * @param {Number} page A page number.
+   * @returns {Function} The click handler for the page link.
+   */
+  clickHandler: function (page) {
+    var self = this;
+    return function () {
+      self.manager.store.get('start').val((page - 1) * self.perPage());
+      self.doRequest();
+      return false;
+    }
+  },
+
+  /**
+   * @param {Number} page A page number.
+   * @returns {String} The <tt>rel</tt> attribute for the page link.
+   */
+  relValue: function (page) {
+    switch (page) {
+      case this.previousPage():
+        return 'prev' + (page == 1 ? 'start' : '');
+      case this.nextPage():
+        return 'next';
+      case 1:
+        return 'start';
+      default:
+        return '';
+    }
+  },
+
+  /**
+   * @returns {Number} The page number of the previous page or null if no previous page.
+   */
+  previousPage: function () {
+    return this.currentPage > 1 ? (this.currentPage - 1) : null;
+  },
+
+  /**
+   * @returns {Number} The page number of the next page or null if no next page.
+   */
+  nextPage: function () {
+    return this.currentPage < this.totalPages ? (this.currentPage + 1) : null;
+  },
+
+  /**
+   * An abstract hook for child implementations.
+   *
+   * @param {Number} perPage The number of items shown per results page.
+   * @param {Number} offset The index in the result set of the first document to render.
+   * @param {Number} total The total number of documents in the result set.
+   */
+  renderHeader: function (perPage, offset, total) {},
+
+  /**
+   * Render the pagination links.
+   *
+   * @param {Array} links The links for the visible page numbers.
+   */
+  renderLinks: function (links) {
+    if (this.totalPages) {
+      links.unshift(this.pageLinkOrSpan(this.previousPage(), [ 'pager-disabled', 'pager-prev'
], this.prevLabel));
+      links.push(this.pageLinkOrSpan(this.nextPage(), [ 'pager-disabled', 'pager-next' ],
this.nextLabel));
+
+      var $target = $(this.target);
+      $target.empty();
+
+      for (var i = 0, l = links.length; i < l; i++) {
+        var $li = $('<li></li>');
+        if (this.separator && i > 0) {
+          $li.append(this.separator);
+        }
+        $target.append($li.append(links[i]));
+      }
+    }
+  },
+
+  /**
+   * @returns {Number} The number of results to display per page.
+   */
+  perPage: function () {
+    return parseInt(this.manager.response.responseHeader && this.manager.response.responseHeader.params
&& this.manager.response.responseHeader.params.rows || this.manager.store.get('rows').val()
|| 10);
+  },
+
+  /**
+   * @returns {Number} The Solr offset parameter's value.
+   */
+  getOffset: function () {
+    return parseInt(this.manager.response.responseHeader && this.manager.response.responseHeader.params
&& this.manager.response.responseHeader.params.start || this.manager.store.get('start').val()
|| 0);
+  },
+
+  afterRequest: function () {
+    var perPage = this.perPage();
+    var offset  = this.getOffset();
+    var total   = parseInt(this.manager.response.response.numFound);
+
+    // Normalize the offset to a multiple of perPage.
+    offset = offset - offset % perPage;
+
+    this.currentPage = Math.ceil((offset + 1) / perPage);
+    this.totalPages = Math.ceil(total / perPage);
+
+    $(this.target).empty();
+
+    this.renderLinks(this.windowedLinks());
+    this.renderHeader(perPage, offset, total);
+  }
+});
+
+})(jQuery);
+
+}));


Mime
View raw message