You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ri...@apache.org on 2014/10/01 11:19:04 UTC
[4/6] git commit: Entries in blueprints list can be filtered by type
Entries in blueprints list can be filtered by type
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn-site/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn-site/commit/38f22b15
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn-site/tree/38f22b15
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn-site/diff/38f22b15
Branch: refs/heads/master
Commit: 38f22b15498c3c8aa094cdd2fca77a971e53d9f2
Parents: 5f27e05
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Wed Sep 24 16:43:15 2014 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Oct 1 10:07:42 2014 +0100
----------------------------------------------------------------------
learnmore/catalog/common.js | 8 +-
learnmore/catalog/index.html | 95 +++-
learnmore/catalog/items.css | 5 +-
learnmore/catalog/libs/js/bloodhound.js | 727 +++++++++++++++++++++++++++
4 files changed, 822 insertions(+), 13 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn-site/blob/38f22b15/learnmore/catalog/common.js
----------------------------------------------------------------------
diff --git a/learnmore/catalog/common.js b/learnmore/catalog/common.js
index 877052a..8e8a45f 100644
--- a/learnmore/catalog/common.js
+++ b/learnmore/catalog/common.js
@@ -25,7 +25,7 @@ var brooklyn = (function ($, _) {
},
entityCard: _.template(
- "<a class='plain' href='entities/<%=type%>.html'>" +
+ "<a class='plain' data-type='<%= type %>' href='entities/<%= type %>.html'>" +
"<div class='card'>" +
"<span class='glyphicon glyphicon-chevron-right'/>" +
"<div class='name'><%=name%></div>" +
@@ -35,7 +35,7 @@ var brooklyn = (function ($, _) {
"</a>"
),
policyCard: _.template(
- "<a class='plain' href='policies/<%=type%>.html'>" +
+ "<a class='plain' data-type='<%= type %>' href='policies/<%= type %>.html'>" +
"<div class='card'>" +
"<span class='glyphicon glyphicon-chevron-right'/>" +
"<div class='name'><%=name%></div>" +
@@ -45,7 +45,7 @@ var brooklyn = (function ($, _) {
"</a>"
),
enricherCard: _.template(
- "<a class='plain' href='enrichers/<%=type%>.html'>" +
+ "<a class='plain' data-type='<%= type %>' href='enrichers/<%= type %>.html'>" +
"<div class='card'>" +
"<span class='glyphicon glyphicon-chevron-right'/>" +
"<div class='name'><%=name%></div>" +
@@ -88,7 +88,7 @@ var brooklyn = (function ($, _) {
"<dt>return type</dt><dd class='java'><%=(returnType||' ')%></dd>" +
"</dl>" +
"</div>"
- ),
+ )
};
}(jQuery, _));
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn-site/blob/38f22b15/learnmore/catalog/index.html
----------------------------------------------------------------------
diff --git a/learnmore/catalog/index.html b/learnmore/catalog/index.html
index 0c2471e..4487346 100644
--- a/learnmore/catalog/index.html
+++ b/learnmore/catalog/index.html
@@ -31,7 +31,7 @@ under the License.
<div id="container">
<div id="header">
<div id="identity">
- <a href="http://brooklyncentral.github.com/" rel="home">Brooklyn</a>
+ <a href="https://brooklyn.incubator.apache.org/" rel="home">Brooklyn</a>
</div>
</div>
@@ -42,9 +42,15 @@ under the License.
</ul>
<div class="tab-content">
- <div class="tab-pane active" id="entities"></div>
- <div class="tab-pane" id="policies"></div>
- <div class="tab-pane" id="enrichers"></div>
+ <div class="tab-pane active" id="entities">
+ <input class="filter form-control" type="text" placeholder="Filter by type, e.g. webapp or nosql">
+ </div>
+ <div class="tab-pane" id="policies">
+ <input class="filter form-control" type="text" placeholder="Filter by type, e.g. ha">
+ </div>
+ <div class="tab-pane" id="enrichers">
+ <input class="filter form-control" type="text" placeholder="Filter by type, e.g. http">
+ </div>
<div class="tab-pane" id="locations"></div>
<div class="tab-pane" id="locationResolvers"></div>
</div>
@@ -53,15 +59,88 @@ under the License.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
<script src="../../style/js/underscore-min.js" type="text/javascript"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
+ <script src="libs/js/bloodhound.js" type="text/javascript"></script>
<script src="common.js" type="text/javascript"></script>
<script src="items.js" type="text/javascript"></script>
<script type="text/javascript">
+ if (!String.prototype.trim) {
+ String.prototype.trim = function () {
+ return this.replace(/^\s+|\s+$/g, '');
+ };
+ }
+ var card = function (collection, cardFunction, target) {
+ var cards = _.map(collection, cardFunction);
+ $(target).append(cards.join(""));
+ };
+ var ESCAPE_KEY = 27;
+
+ var filter = function (element) {
+ var $element = $(element),
+ $tab = $element.parent(),
+ kind = $tab.attr("id"),
+ collection = items[kind];
+ if (!collection) {
+ console.warn("Unable to determine type for input", element);
+ return;
+ }
+
+ // Number.MAX_VALUE configures Bloodhound to return all matches.
+ var bloodhound = new Bloodhound({
+ name: kind,
+ local: collection,
+ limit: Number.MAX_VALUE,
+ datumTokenizer: function (d) {
+ return Bloodhound.tokenizers.nonword(d.type);
+ },
+ queryTokenizer: Bloodhound.tokenizers.nonword
+ });
+ bloodhound.initialize();
+
+ // Filter items as input changes
+ var allAnchors = $tab.find("a").map(function (index, a) { return $(a); });
+ var hideAnchorsNotMatchingQuery = function () {
+ var query = $element.val();
+ query = query.trim();
+ if (!query) {
+ $tab.find("a").removeClass("hide");
+ } else {
+ var matchedTypes = {};
+ bloodhound.get(query, function (suggestions) {
+ _.each(suggestions, function (s) {
+ // approximate a set!
+ matchedTypes[s.type] = true;
+ });
+ });
+ _.each(allAnchors, function (a) {
+ if (_.has(matchedTypes, a.data("type"))) {
+ a.removeClass("hide");
+ } else {
+ a.addClass("hide");
+ }
+ });
+ }
+ };
+ $element.on("input", hideAnchorsNotMatchingQuery);
+ // In case page is loaded with text in input, e.g. from back button.
+ hideAnchorsNotMatchingQuery();
+
+ $element.on("keydown", function (e) {
+ if (e.keyCode == ESCAPE_KEY) {
+ $element.val("");
+ hideAnchorsNotMatchingQuery();
+ }
+ });
+ };
+
$(document).ready(function () {
- items.entities.forEach(function (element) { $("#entities").append(brooklyn.entityCard(element)); });
- items.policies.forEach(function (element) { $("#policies").append(brooklyn.policyCard(element)); });
- items.enrichers.forEach(function (element) { $("#enrichers").append(brooklyn.enricherCard(element)); });
- //items.locations.forEach(function (element) { $("#locations").append(brooklyn.locationCard(element)); });
+ card(items.entities, brooklyn.entityCard, "#entities");
+ card(items.policies, brooklyn.policyCard, "#policies");
+ card(items.enrichers, brooklyn.enricherCard, "#enrichers");
+ //transformItemAndAddToElement(items.locations, brooklyn.locationCard, "#locations");
//items.locationResolvers.forEach(function (element) { $("#locationResolvers").append("<tr><td>" + element + "</td></tr>"); });
+ $("input.filter").each(function (index, element) {
+ filter(element);
+ });
});
</script>
</body>
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn-site/blob/38f22b15/learnmore/catalog/items.css
----------------------------------------------------------------------
diff --git a/learnmore/catalog/items.css b/learnmore/catalog/items.css
index 0e9ff73..a0bf0f0 100644
--- a/learnmore/catalog/items.css
+++ b/learnmore/catalog/items.css
@@ -53,13 +53,16 @@ a:hover > .card {
.card {
position: relative;
padding: 12px;
- margin-bottom: 10px;
background-color: #f8f8f8;
color: #333;
border: 1px solid #E1E1E8;
border-radius: 6px;
font-size: 11pt;
}
+.card,
+.filter {
+ margin-bottom: 10px;
+}
a .glyphicon {
display: block;
position: absolute;
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn-site/blob/38f22b15/learnmore/catalog/libs/js/bloodhound.js
----------------------------------------------------------------------
diff --git a/learnmore/catalog/libs/js/bloodhound.js b/learnmore/catalog/libs/js/bloodhound.js
new file mode 100644
index 0000000..96a4c43
--- /dev/null
+++ b/learnmore/catalog/libs/js/bloodhound.js
@@ -0,0 +1,727 @@
+/*!
+ * typeahead.js 0.10.5
+ * https://github.com/twitter/typeahead.js
+ * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
+ */
+
+(function($) {
+ var _ = function() {
+ "use strict";
+ return {
+ isMsie: function() {
+ return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
+ },
+ isBlankString: function(str) {
+ return !str || /^\s*$/.test(str);
+ },
+ escapeRegExChars: function(str) {
+ return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
+ },
+ isString: function(obj) {
+ return typeof obj === "string";
+ },
+ isNumber: function(obj) {
+ return typeof obj === "number";
+ },
+ isArray: $.isArray,
+ isFunction: $.isFunction,
+ isObject: $.isPlainObject,
+ isUndefined: function(obj) {
+ return typeof obj === "undefined";
+ },
+ toStr: function toStr(s) {
+ return _.isUndefined(s) || s === null ? "" : s + "";
+ },
+ bind: $.proxy,
+ each: function(collection, cb) {
+ $.each(collection, reverseArgs);
+ function reverseArgs(index, value) {
+ return cb(value, index);
+ }
+ },
+ map: $.map,
+ filter: $.grep,
+ every: function(obj, test) {
+ var result = true;
+ if (!obj) {
+ return result;
+ }
+ $.each(obj, function(key, val) {
+ if (!(result = test.call(null, val, key, obj))) {
+ return false;
+ }
+ });
+ return !!result;
+ },
+ some: function(obj, test) {
+ var result = false;
+ if (!obj) {
+ return result;
+ }
+ $.each(obj, function(key, val) {
+ if (result = test.call(null, val, key, obj)) {
+ return false;
+ }
+ });
+ return !!result;
+ },
+ mixin: $.extend,
+ getUniqueId: function() {
+ var counter = 0;
+ return function() {
+ return counter++;
+ };
+ }(),
+ templatify: function templatify(obj) {
+ return $.isFunction(obj) ? obj : template;
+ function template() {
+ return String(obj);
+ }
+ },
+ defer: function(fn) {
+ setTimeout(fn, 0);
+ },
+ debounce: function(func, wait, immediate) {
+ var timeout, result;
+ return function() {
+ var context = this, args = arguments, later, callNow;
+ later = function() {
+ timeout = null;
+ if (!immediate) {
+ result = func.apply(context, args);
+ }
+ };
+ callNow = immediate && !timeout;
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ if (callNow) {
+ result = func.apply(context, args);
+ }
+ return result;
+ };
+ },
+ throttle: function(func, wait) {
+ var context, args, timeout, result, previous, later;
+ previous = 0;
+ later = function() {
+ previous = new Date();
+ timeout = null;
+ result = func.apply(context, args);
+ };
+ return function() {
+ var now = new Date(), remaining = wait - (now - previous);
+ context = this;
+ args = arguments;
+ if (remaining <= 0) {
+ clearTimeout(timeout);
+ timeout = null;
+ previous = now;
+ result = func.apply(context, args);
+ } else if (!timeout) {
+ timeout = setTimeout(later, remaining);
+ }
+ return result;
+ };
+ },
+ noop: function() {}
+ };
+ }();
+ var VERSION = "0.10.5";
+ var tokenizers = function() {
+ "use strict";
+ return {
+ nonword: nonword,
+ whitespace: whitespace,
+ obj: {
+ nonword: getObjTokenizer(nonword),
+ whitespace: getObjTokenizer(whitespace)
+ }
+ };
+ function whitespace(str) {
+ str = _.toStr(str);
+ return str ? str.split(/\s+/) : [];
+ }
+ function nonword(str) {
+ str = _.toStr(str);
+ return str ? str.split(/\W+/) : [];
+ }
+ function getObjTokenizer(tokenizer) {
+ return function setKey() {
+ var args = [].slice.call(arguments, 0);
+ return function tokenize(o) {
+ var tokens = [];
+ _.each(args, function(k) {
+ tokens = tokens.concat(tokenizer(_.toStr(o[k])));
+ });
+ return tokens;
+ };
+ };
+ }
+ }();
+ var LruCache = function() {
+ "use strict";
+ function LruCache(maxSize) {
+ this.maxSize = _.isNumber(maxSize) ? maxSize : 100;
+ this.reset();
+ if (this.maxSize <= 0) {
+ this.set = this.get = $.noop;
+ }
+ }
+ _.mixin(LruCache.prototype, {
+ set: function set(key, val) {
+ var tailItem = this.list.tail, node;
+ if (this.size >= this.maxSize) {
+ this.list.remove(tailItem);
+ delete this.hash[tailItem.key];
+ }
+ if (node = this.hash[key]) {
+ node.val = val;
+ this.list.moveToFront(node);
+ } else {
+ node = new Node(key, val);
+ this.list.add(node);
+ this.hash[key] = node;
+ this.size++;
+ }
+ },
+ get: function get(key) {
+ var node = this.hash[key];
+ if (node) {
+ this.list.moveToFront(node);
+ return node.val;
+ }
+ },
+ reset: function reset() {
+ this.size = 0;
+ this.hash = {};
+ this.list = new List();
+ }
+ });
+ function List() {
+ this.head = this.tail = null;
+ }
+ _.mixin(List.prototype, {
+ add: function add(node) {
+ if (this.head) {
+ node.next = this.head;
+ this.head.prev = node;
+ }
+ this.head = node;
+ this.tail = this.tail || node;
+ },
+ remove: function remove(node) {
+ node.prev ? node.prev.next = node.next : this.head = node.next;
+ node.next ? node.next.prev = node.prev : this.tail = node.prev;
+ },
+ moveToFront: function(node) {
+ this.remove(node);
+ this.add(node);
+ }
+ });
+ function Node(key, val) {
+ this.key = key;
+ this.val = val;
+ this.prev = this.next = null;
+ }
+ return LruCache;
+ }();
+ var PersistentStorage = function() {
+ "use strict";
+ var ls, methods;
+ try {
+ ls = window.localStorage;
+ ls.setItem("~~~", "!");
+ ls.removeItem("~~~");
+ } catch (err) {
+ ls = null;
+ }
+ function PersistentStorage(namespace) {
+ this.prefix = [ "__", namespace, "__" ].join("");
+ this.ttlKey = "__ttl__";
+ this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix));
+ }
+ if (ls && window.JSON) {
+ methods = {
+ _prefix: function(key) {
+ return this.prefix + key;
+ },
+ _ttlKey: function(key) {
+ return this._prefix(key) + this.ttlKey;
+ },
+ get: function(key) {
+ if (this.isExpired(key)) {
+ this.remove(key);
+ }
+ return decode(ls.getItem(this._prefix(key)));
+ },
+ set: function(key, val, ttl) {
+ if (_.isNumber(ttl)) {
+ ls.setItem(this._ttlKey(key), encode(now() + ttl));
+ } else {
+ ls.removeItem(this._ttlKey(key));
+ }
+ return ls.setItem(this._prefix(key), encode(val));
+ },
+ remove: function(key) {
+ ls.removeItem(this._ttlKey(key));
+ ls.removeItem(this._prefix(key));
+ return this;
+ },
+ clear: function() {
+ var i, key, keys = [], len = ls.length;
+ for (i = 0; i < len; i++) {
+ if ((key = ls.key(i)).match(this.keyMatcher)) {
+ keys.push(key.replace(this.keyMatcher, ""));
+ }
+ }
+ for (i = keys.length; i--; ) {
+ this.remove(keys[i]);
+ }
+ return this;
+ },
+ isExpired: function(key) {
+ var ttl = decode(ls.getItem(this._ttlKey(key)));
+ return _.isNumber(ttl) && now() > ttl ? true : false;
+ }
+ };
+ } else {
+ methods = {
+ get: _.noop,
+ set: _.noop,
+ remove: _.noop,
+ clear: _.noop,
+ isExpired: _.noop
+ };
+ }
+ _.mixin(PersistentStorage.prototype, methods);
+ return PersistentStorage;
+ function now() {
+ return new Date().getTime();
+ }
+ function encode(val) {
+ return JSON.stringify(_.isUndefined(val) ? null : val);
+ }
+ function decode(val) {
+ return JSON.parse(val);
+ }
+ }();
+ var Transport = function() {
+ "use strict";
+ var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10);
+ function Transport(o) {
+ o = o || {};
+ this.cancelled = false;
+ this.lastUrl = null;
+ this._send = o.transport ? callbackToDeferred(o.transport) : $.ajax;
+ this._get = o.rateLimiter ? o.rateLimiter(this._get) : this._get;
+ this._cache = o.cache === false ? new LruCache(0) : sharedCache;
+ }
+ Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
+ maxPendingRequests = num;
+ };
+ Transport.resetCache = function resetCache() {
+ sharedCache.reset();
+ };
+ _.mixin(Transport.prototype, {
+ _get: function(url, o, cb) {
+ var that = this, jqXhr;
+ if (this.cancelled || url !== this.lastUrl) {
+ return;
+ }
+ if (jqXhr = pendingRequests[url]) {
+ jqXhr.done(done).fail(fail);
+ } else if (pendingRequestsCount < maxPendingRequests) {
+ pendingRequestsCount++;
+ pendingRequests[url] = this._send(url, o).done(done).fail(fail).always(always);
+ } else {
+ this.onDeckRequestArgs = [].slice.call(arguments, 0);
+ }
+ function done(resp) {
+ cb && cb(null, resp);
+ that._cache.set(url, resp);
+ }
+ function fail() {
+ cb && cb(true);
+ }
+ function always() {
+ pendingRequestsCount--;
+ delete pendingRequests[url];
+ if (that.onDeckRequestArgs) {
+ that._get.apply(that, that.onDeckRequestArgs);
+ that.onDeckRequestArgs = null;
+ }
+ }
+ },
+ get: function(url, o, cb) {
+ var resp;
+ if (_.isFunction(o)) {
+ cb = o;
+ o = {};
+ }
+ this.cancelled = false;
+ this.lastUrl = url;
+ if (resp = this._cache.get(url)) {
+ _.defer(function() {
+ cb && cb(null, resp);
+ });
+ } else {
+ this._get(url, o, cb);
+ }
+ return !!resp;
+ },
+ cancel: function() {
+ this.cancelled = true;
+ }
+ });
+ return Transport;
+ function callbackToDeferred(fn) {
+ return function customSendWrapper(url, o) {
+ var deferred = $.Deferred();
+ fn(url, o, onSuccess, onError);
+ return deferred;
+ function onSuccess(resp) {
+ _.defer(function() {
+ deferred.resolve(resp);
+ });
+ }
+ function onError(err) {
+ _.defer(function() {
+ deferred.reject(err);
+ });
+ }
+ };
+ }
+ }();
+ var SearchIndex = function() {
+ "use strict";
+ function SearchIndex(o) {
+ o = o || {};
+ if (!o.datumTokenizer || !o.queryTokenizer) {
+ $.error("datumTokenizer and queryTokenizer are both required");
+ }
+ this.datumTokenizer = o.datumTokenizer;
+ this.queryTokenizer = o.queryTokenizer;
+ this.reset();
+ }
+ _.mixin(SearchIndex.prototype, {
+ bootstrap: function bootstrap(o) {
+ this.datums = o.datums;
+ this.trie = o.trie;
+ },
+ add: function(data) {
+ var that = this;
+ data = _.isArray(data) ? data : [ data ];
+ _.each(data, function(datum) {
+ var id, tokens;
+ id = that.datums.push(datum) - 1;
+ tokens = normalizeTokens(that.datumTokenizer(datum));
+ _.each(tokens, function(token) {
+ var node, chars, ch;
+ node = that.trie;
+ chars = token.split("");
+ while (ch = chars.shift()) {
+ node = node.children[ch] || (node.children[ch] = newNode());
+ node.ids.push(id);
+ }
+ });
+ });
+ },
+ get: function get(query) {
+ var that = this, tokens, matches;
+ tokens = normalizeTokens(this.queryTokenizer(query));
+ _.each(tokens, function(token) {
+ var node, chars, ch, ids;
+ if (matches && matches.length === 0) {
+ return false;
+ }
+ node = that.trie;
+ chars = token.split("");
+ while (node && (ch = chars.shift())) {
+ node = node.children[ch];
+ }
+ if (node && chars.length === 0) {
+ ids = node.ids.slice(0);
+ matches = matches ? getIntersection(matches, ids) : ids;
+ } else {
+ matches = [];
+ return false;
+ }
+ });
+ return matches ? _.map(unique(matches), function(id) {
+ return that.datums[id];
+ }) : [];
+ },
+ reset: function reset() {
+ this.datums = [];
+ this.trie = newNode();
+ },
+ serialize: function serialize() {
+ return {
+ datums: this.datums,
+ trie: this.trie
+ };
+ }
+ });
+ return SearchIndex;
+ function normalizeTokens(tokens) {
+ tokens = _.filter(tokens, function(token) {
+ return !!token;
+ });
+ tokens = _.map(tokens, function(token) {
+ return token.toLowerCase();
+ });
+ return tokens;
+ }
+ function newNode() {
+ return {
+ ids: [],
+ children: {}
+ };
+ }
+ function unique(array) {
+ var seen = {}, uniques = [];
+ for (var i = 0, len = array.length; i < len; i++) {
+ if (!seen[array[i]]) {
+ seen[array[i]] = true;
+ uniques.push(array[i]);
+ }
+ }
+ return uniques;
+ }
+ function getIntersection(arrayA, arrayB) {
+ var ai = 0, bi = 0, intersection = [];
+ arrayA = arrayA.sort(compare);
+ arrayB = arrayB.sort(compare);
+ var lenArrayA = arrayA.length, lenArrayB = arrayB.length;
+ while (ai < lenArrayA && bi < lenArrayB) {
+ if (arrayA[ai] < arrayB[bi]) {
+ ai++;
+ } else if (arrayA[ai] > arrayB[bi]) {
+ bi++;
+ } else {
+ intersection.push(arrayA[ai]);
+ ai++;
+ bi++;
+ }
+ }
+ return intersection;
+ function compare(a, b) {
+ return a - b;
+ }
+ }
+ }();
+ var oParser = function() {
+ "use strict";
+ return {
+ local: getLocal,
+ prefetch: getPrefetch,
+ remote: getRemote
+ };
+ function getLocal(o) {
+ return o.local || null;
+ }
+ function getPrefetch(o) {
+ var prefetch, defaults;
+ defaults = {
+ url: null,
+ thumbprint: "",
+ ttl: 24 * 60 * 60 * 1e3,
+ filter: null,
+ ajax: {}
+ };
+ if (prefetch = o.prefetch || null) {
+ prefetch = _.isString(prefetch) ? {
+ url: prefetch
+ } : prefetch;
+ prefetch = _.mixin(defaults, prefetch);
+ prefetch.thumbprint = VERSION + prefetch.thumbprint;
+ prefetch.ajax.type = prefetch.ajax.type || "GET";
+ prefetch.ajax.dataType = prefetch.ajax.dataType || "json";
+ !prefetch.url && $.error("prefetch requires url to be set");
+ }
+ return prefetch;
+ }
+ function getRemote(o) {
+ var remote, defaults;
+ defaults = {
+ url: null,
+ cache: true,
+ wildcard: "%QUERY",
+ replace: null,
+ rateLimitBy: "debounce",
+ rateLimitWait: 300,
+ send: null,
+ filter: null,
+ ajax: {}
+ };
+ if (remote = o.remote || null) {
+ remote = _.isString(remote) ? {
+ url: remote
+ } : remote;
+ remote = _.mixin(defaults, remote);
+ remote.rateLimiter = /^throttle$/i.test(remote.rateLimitBy) ? byThrottle(remote.rateLimitWait) : byDebounce(remote.rateLimitWait);
+ remote.ajax.type = remote.ajax.type || "GET";
+ remote.ajax.dataType = remote.ajax.dataType || "json";
+ delete remote.rateLimitBy;
+ delete remote.rateLimitWait;
+ !remote.url && $.error("remote requires url to be set");
+ }
+ return remote;
+ function byDebounce(wait) {
+ return function(fn) {
+ return _.debounce(fn, wait);
+ };
+ }
+ function byThrottle(wait) {
+ return function(fn) {
+ return _.throttle(fn, wait);
+ };
+ }
+ }
+ }();
+ (function(root) {
+ "use strict";
+ var old, keys;
+ old = root.Bloodhound;
+ keys = {
+ data: "data",
+ protocol: "protocol",
+ thumbprint: "thumbprint"
+ };
+ root.Bloodhound = Bloodhound;
+ function Bloodhound(o) {
+ if (!o || !o.local && !o.prefetch && !o.remote) {
+ $.error("one of local, prefetch, or remote is required");
+ }
+ this.limit = o.limit || 5;
+ this.sorter = getSorter(o.sorter);
+ this.dupDetector = o.dupDetector || ignoreDuplicates;
+ this.local = oParser.local(o);
+ this.prefetch = oParser.prefetch(o);
+ this.remote = oParser.remote(o);
+ this.cacheKey = this.prefetch ? this.prefetch.cacheKey || this.prefetch.url : null;
+ this.index = new SearchIndex({
+ datumTokenizer: o.datumTokenizer,
+ queryTokenizer: o.queryTokenizer
+ });
+ this.storage = this.cacheKey ? new PersistentStorage(this.cacheKey) : null;
+ }
+ Bloodhound.noConflict = function noConflict() {
+ root.Bloodhound = old;
+ return Bloodhound;
+ };
+ Bloodhound.tokenizers = tokenizers;
+ _.mixin(Bloodhound.prototype, {
+ _loadPrefetch: function loadPrefetch(o) {
+ var that = this, serialized, deferred;
+ if (serialized = this._readFromStorage(o.thumbprint)) {
+ this.index.bootstrap(serialized);
+ deferred = $.Deferred().resolve();
+ } else {
+ deferred = $.ajax(o.url, o.ajax).done(handlePrefetchResponse);
+ }
+ return deferred;
+ function handlePrefetchResponse(resp) {
+ that.clear();
+ that.add(o.filter ? o.filter(resp) : resp);
+ that._saveToStorage(that.index.serialize(), o.thumbprint, o.ttl);
+ }
+ },
+ _getFromRemote: function getFromRemote(query, cb) {
+ var that = this, url, uriEncodedQuery;
+ if (!this.transport) {
+ return;
+ }
+ query = query || "";
+ uriEncodedQuery = encodeURIComponent(query);
+ url = this.remote.replace ? this.remote.replace(this.remote.url, query) : this.remote.url.replace(this.remote.wildcard, uriEncodedQuery);
+ return this.transport.get(url, this.remote.ajax, handleRemoteResponse);
+ function handleRemoteResponse(err, resp) {
+ err ? cb([]) : cb(that.remote.filter ? that.remote.filter(resp) : resp);
+ }
+ },
+ _cancelLastRemoteRequest: function cancelLastRemoteRequest() {
+ this.transport && this.transport.cancel();
+ },
+ _saveToStorage: function saveToStorage(data, thumbprint, ttl) {
+ if (this.storage) {
+ this.storage.set(keys.data, data, ttl);
+ this.storage.set(keys.protocol, location.protocol, ttl);
+ this.storage.set(keys.thumbprint, thumbprint, ttl);
+ }
+ },
+ _readFromStorage: function readFromStorage(thumbprint) {
+ var stored = {}, isExpired;
+ if (this.storage) {
+ stored.data = this.storage.get(keys.data);
+ stored.protocol = this.storage.get(keys.protocol);
+ stored.thumbprint = this.storage.get(keys.thumbprint);
+ }
+ isExpired = stored.thumbprint !== thumbprint || stored.protocol !== location.protocol;
+ return stored.data && !isExpired ? stored.data : null;
+ },
+ _initialize: function initialize() {
+ var that = this, local = this.local, deferred;
+ deferred = this.prefetch ? this._loadPrefetch(this.prefetch) : $.Deferred().resolve();
+ local && deferred.done(addLocalToIndex);
+ this.transport = this.remote ? new Transport(this.remote) : null;
+ return this.initPromise = deferred.promise();
+ function addLocalToIndex() {
+ that.add(_.isFunction(local) ? local() : local);
+ }
+ },
+ initialize: function initialize(force) {
+ return !this.initPromise || force ? this._initialize() : this.initPromise;
+ },
+ add: function add(data) {
+ this.index.add(data);
+ },
+ get: function get(query, cb) {
+ var that = this, matches = [], cacheHit = false;
+ matches = this.index.get(query);
+ matches = this.sorter(matches).slice(0, this.limit);
+ matches.length < this.limit ? cacheHit = this._getFromRemote(query, returnRemoteMatches) : this._cancelLastRemoteRequest();
+ if (!cacheHit) {
+ (matches.length > 0 || !this.transport) && cb && cb(matches);
+ }
+ function returnRemoteMatches(remoteMatches) {
+ var matchesWithBackfill = matches.slice(0);
+ _.each(remoteMatches, function(remoteMatch) {
+ var isDuplicate;
+ isDuplicate = _.some(matchesWithBackfill, function(match) {
+ return that.dupDetector(remoteMatch, match);
+ });
+ !isDuplicate && matchesWithBackfill.push(remoteMatch);
+ return matchesWithBackfill.length < that.limit;
+ });
+ cb && cb(that.sorter(matchesWithBackfill));
+ }
+ },
+ clear: function clear() {
+ this.index.reset();
+ },
+ clearPrefetchCache: function clearPrefetchCache() {
+ this.storage && this.storage.clear();
+ },
+ clearRemoteCache: function clearRemoteCache() {
+ this.transport && Transport.resetCache();
+ },
+ ttAdapter: function ttAdapter() {
+ return _.bind(this.get, this);
+ }
+ });
+ return Bloodhound;
+ function getSorter(sortFn) {
+ return _.isFunction(sortFn) ? sort : noSort;
+ function sort(array) {
+ return array.sort(sortFn);
+ }
+ function noSort(array) {
+ return array;
+ }
+ }
+ function ignoreDuplicates() {
+ return false;
+ }
+ })(this);
+})(window.jQuery);
\ No newline at end of file