You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2015/05/22 11:04:51 UTC
[06/23] incubator-brooklyn git commit: item lister resources put in
an appropriate subdir; other related tidies
item lister resources put in an appropriate subdir; other related tidies
code was very ad hoc, but also polluting the root of the all jar; now resources at least are in a clean subdir
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/461ac9f1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/461ac9f1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/461ac9f1
Branch: refs/heads/master
Commit: 461ac9f1f318b31558a45c011332803379632805
Parents: 1b080fb
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed Apr 29 13:32:34 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:22:22 2015 +0100
----------------------------------------------------------------------
usage/cli/pom.xml | 5 +-
.../src/main/java/brooklyn/cli/ItemLister.java | 77 +-
.../java/brooklyn/cli/lister/ClassFinder.java | 7 +-
.../brooklyn/cli/lister/ItemDescriptors.java | 20 +-
.../main/resources/brooklyn-object-list.html | 147 ----
.../statics/brooklyn-object-list.html | 147 ++++
.../brooklyn/item-lister/statics/common.js | 94 +++
.../brooklyn/item-lister/statics/items.css | 153 ++++
.../statics/style/js/catalog/bloodhound.js | 727 +++++++++++++++++++
.../statics/style/js/underscore-min.js | 6 +
.../statics/style/js/underscore-min.map | 1 +
.../item-lister/templates/enricher.html | 59 ++
.../brooklyn/item-lister/templates/entity.html | 66 ++
.../item-lister/templates/location.html | 62 ++
.../brooklyn/item-lister/templates/policy.html | 59 ++
usage/cli/src/main/resources/common.js | 94 ---
usage/cli/src/main/resources/enricher.html | 59 --
usage/cli/src/main/resources/entity.html | 66 --
usage/cli/src/main/resources/items.css | 153 ----
.../src/main/resources/libs/js/bloodhound.js | 727 -------------------
usage/cli/src/main/resources/location.html | 62 --
usage/cli/src/main/resources/policy.html | 59 --
22 files changed, 1457 insertions(+), 1393 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/pom.xml
----------------------------------------------------------------------
diff --git a/usage/cli/pom.xml b/usage/cli/pom.xml
index 1042649..451b0ed 100644
--- a/usage/cli/pom.xml
+++ b/usage/cli/pom.xml
@@ -159,11 +159,12 @@
<configuration>
<excludes combine.children="append">
<!--
- bloodhound.js is copyright Twitter, Inc. It is included in NOTICE.
+ bloodhound.js is copyright Twitter, Inc. underscore is copyright Jeremy Ashkenas, DocumentCloud.
+ Both are included in the LICENSE notes.
It is used by the HTML generated by the CLI tool for entity descriptions,
i.e. by brooklyn.cli.itemlister.ItemLister
-->
- <exclude>**/src/main/resources/libs/js/bloodhound.js</exclude>
+ <exclude>**/src/main/resources/brooklyn/item-lister/statics/style/js/**</exclude>
</excludes>
</configuration>
</plugin>
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/java/brooklyn/cli/ItemLister.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/ItemLister.java b/usage/cli/src/main/java/brooklyn/cli/ItemLister.java
index 3c567dc..e616fb5 100644
--- a/usage/cli/src/main/java/brooklyn/cli/ItemLister.java
+++ b/usage/cli/src/main/java/brooklyn/cli/ItemLister.java
@@ -22,8 +22,10 @@ import io.airlift.command.Command;
import io.airlift.command.Option;
import java.io.File;
+import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
@@ -43,6 +45,7 @@ import brooklyn.policy.Enricher;
import brooklyn.policy.Policy;
import brooklyn.util.ResourceUtils;
import brooklyn.util.collections.MutableSet;
+import brooklyn.util.net.Urls;
import brooklyn.util.os.Os;
import brooklyn.util.text.Strings;
import brooklyn.util.text.TemplateProcessor;
@@ -54,7 +57,6 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
-
import com.google.common.base.Charsets;
import com.google.common.base.Splitter;
import com.google.common.collect.FluentIterable;
@@ -66,6 +68,9 @@ import com.google.common.io.Files;
public class ItemLister {
private static final Logger LOG = LoggerFactory.getLogger(ItemLister.class);
+ private static final String BASE = "brooklyn/item-lister";
+ private static final String BASE_TEMPLATES = BASE+"/"+"templates";
+ private static final String BASE_STATICS = BASE+"/"+"statics";
@Command(name = "list-objects", description = "List Brooklyn objects (Entities, Policies, Enrichers and Locations)")
public static class ListAllCommand extends AbstractMain.BrooklynCommandCollectingArgs {
@@ -91,8 +96,8 @@ public class ItemLister {
@SuppressWarnings("unchecked")
@Override
public Void call() throws Exception {
- LOG.info("Retrieving objects");
List<URL> urls = getUrls();
+ LOG.info("Retrieving objects from "+urls);
// TODO Remove duplication from separate ListPolicyCommand etc
List<Class<? extends Entity>> entityTypes = getTypes(urls, Entity.class);
@@ -113,48 +118,63 @@ public class ItemLister {
if (outputFolder == null) {
System.out.println(json);
} else {
- LOG.info("Outputting item list to " + outputFolder);
+ LOG.info("Outputting item list (size "+itemCount+") to " + outputFolder);
String outputPath = Os.mergePaths(outputFolder, "index.html");
String parentDir = (new File(outputPath).getParentFile()).getAbsolutePath();
mkdir(parentDir, "entities");
mkdir(parentDir, "policies");
mkdir(parentDir, "enrichers");
mkdir(parentDir, "locations");
- mkdir(parentDir, "locationResolvers");
+ mkdir(parentDir, "locationResolvers"); //TODO nothing written here yet...
+
+ mkdir(parentDir, "style");
+ mkdir(Os.mergePaths(parentDir, "style"), "js");
+ mkdir(Os.mergePaths(parentDir, "style", "js"), "catalog");
+
Files.write("var items = " + json, new File(Os.mergePaths(outputFolder, "items.js")), Charsets.UTF_8);
ResourceUtils resourceUtils = ResourceUtils.create(this);
- String js = resourceUtils.getResourceAsString("common.js");
- Files.write(js, new File(Os.mergePaths(outputFolder, "common.js")), Charsets.UTF_8);
- String css = resourceUtils.getResourceAsString("items.css");
- Files.write(css, new File(Os.mergePaths(outputFolder, "items.css")), Charsets.UTF_8);
- String mainHtml = resourceUtils.getResourceAsString("brooklyn-object-list.html");
- Files.write(mainHtml, new File(Os.mergePaths(outputFolder, "index.html")), Charsets.UTF_8);
+
+ // root - just loads the above JSON
+ copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, "brooklyn-object-list.html", "index.html");
+
+ // statics - structure mirrors docs (not for any real reason however... the json is usually enough for our docs)
+ copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, "common.js");
+ copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, "items.css");
+ copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, "style/js/underscore-min.js");
+ copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, "style/js/underscore-min.map");
+ copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, "style/js/catalog/bloodhound.js");
+
+ // now make pages for each item
+
List<Map<String, Object>> entities = (List<Map<String, Object>>) result.get("entities");
- String entityTemplateHtml = resourceUtils.getResourceAsString("entity.html");
+ String entityTemplateHtml = resourceUtils.getResourceAsString(Urls.mergePaths(BASE_TEMPLATES, "entity.html"));
for (Map<String, Object> entity : entities) {
String type = (String) entity.get("type");
String name = (String) entity.get("name");
String entityHtml = TemplateProcessor.processTemplateContents(entityTemplateHtml, ImmutableMap.of("type", type, "name", name));
Files.write(entityHtml, new File(Os.mergePaths(outputFolder, "entities", type + ".html")), Charsets.UTF_8);
}
+
List<Map<String, Object>> policies = (List<Map<String, Object>>) result.get("policies");
- String policyTemplateHtml = resourceUtils.getResourceAsString("policy.html");
+ String policyTemplateHtml = resourceUtils.getResourceAsString(Urls.mergePaths(BASE_TEMPLATES, "policy.html"));
for (Map<String, Object> policy : policies) {
String type = (String) policy.get("type");
String name = (String) policy.get("name");
String policyHtml = TemplateProcessor.processTemplateContents(policyTemplateHtml, ImmutableMap.of("type", type, "name", name));
Files.write(policyHtml, new File(Os.mergePaths(outputFolder, "policies", type + ".html")), Charsets.UTF_8);
}
+
List<Map<String, Object>> enrichers = (List<Map<String, Object>>) result.get("enrichers");
- String enricherTemplateHtml = resourceUtils.getResourceAsString("enricher.html");
+ String enricherTemplateHtml = resourceUtils.getResourceAsString(Urls.mergePaths(BASE_TEMPLATES, "enricher.html"));
for (Map<String, Object> enricher : enrichers) {
String type = (String) enricher.get("type");
String name = (String) enricher.get("name");
String enricherHtml = TemplateProcessor.processTemplateContents(enricherTemplateHtml, ImmutableMap.of("type", type, "name", name));
Files.write(enricherHtml, new File(Os.mergePaths(outputFolder, "enrichers", type + ".html")), Charsets.UTF_8);
}
+
List<Map<String, Object>> locations = (List<Map<String, Object>>) result.get("locations");
- String locationTemplateHtml = resourceUtils.getResourceAsString("location.html");
+ String locationTemplateHtml = resourceUtils.getResourceAsString(Urls.mergePaths(BASE_TEMPLATES, "location.html"));
for (Map<String, Object> location : locations) {
String type = (String) location.get("type");
String locationHtml = TemplateProcessor.processTemplateContents(locationTemplateHtml, ImmutableMap.of("type", type));
@@ -165,6 +185,14 @@ public class ItemLister {
return null;
}
+ private void copyFromItemListerClasspathBaseStaticsToOutputDir(ResourceUtils resourceUtils, String item) throws IOException {
+ copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, item, item);
+ }
+ private void copyFromItemListerClasspathBaseStaticsToOutputDir(ResourceUtils resourceUtils, String item, String dest) throws IOException {
+ String js = resourceUtils.getResourceAsString(Urls.mergePaths(BASE_STATICS, item));
+ Files.write(js, new File(Os.mergePaths(outputFolder, dest)), Charsets.UTF_8);
+ }
+
private void mkdir(String rootDir, String dirName) {
(new File(Os.mergePaths(rootDir, dirName))).mkdirs();
}
@@ -185,7 +213,10 @@ public class ItemLister {
}
} else {
for (String jar : jars) {
- urls.addAll(ClassFinder.toJarUrls(jar));
+ List<URL> expanded = ClassFinder.toJarUrls(jar);
+ if (expanded.isEmpty())
+ LOG.warn("No jars found at: "+jar);
+ urls.addAll(expanded);
}
}
return urls;
@@ -194,6 +225,8 @@ public class ItemLister {
private <T extends BrooklynObject> List<Class<? extends T>> getTypes(List<URL> urls, Class<T> type) {
return getTypes(urls, type, null);
}
+
+ int itemCount = 0;
private <T extends BrooklynObject> List<Class<? extends T>> getTypes(List<URL> urls, Class<T> type, Boolean catalogOnlyOverride) {
FluentIterable<Class<? extends T>> fluent = FluentIterable.from(ClassFinder.findClasses(urls, type));
@@ -203,19 +236,21 @@ public class ItemLister {
if (catalogOnlyOverride == null ? catalogOnly : catalogOnlyOverride) {
fluent = fluent.filter(ClassFinder.withAnnotation(Catalog.class));
}
- ImmutableList<Class<? extends T>> result = fluent.toList();
+ List<Class<? extends T>> filtered = fluent.toList();
+ Collection<Class<? extends T>> result;
if (ignoreImpls) {
- MutableSet<Class<? extends T>> mutableResult = MutableSet.copyOf(result);
- for (Class<? extends T> clazz : result) {
+ result = MutableSet.copyOf(filtered);
+ for (Class<? extends T> clazz : filtered) {
ImplementedBy implementedBy = clazz.getAnnotation(ImplementedBy.class);
if (implementedBy != null) {
- mutableResult.remove(implementedBy.value());
+ result.remove(implementedBy.value());
}
}
- return ImmutableList.copyOf(mutableResult);
} else {
- return result;
+ result = filtered;
}
+ itemCount += result.size();
+ return ImmutableList.copyOf(result);
}
private String toJson(Object obj) throws JsonProcessingException {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java b/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java
index 101e019..a66fa7d 100644
--- a/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java
+++ b/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java
@@ -49,6 +49,7 @@ import brooklyn.util.javalang.UrlClassLoader;
import brooklyn.util.net.Urls;
import brooklyn.util.os.Os;
+import com.google.common.annotations.Beta;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -91,7 +92,9 @@ public class ClassFinder {
}
};
}
-
+
+ /** finds a jar at a url, or for directories, jars under a path */
+ @Beta
public static List<URL> toJarUrls(String url) throws MalformedURLException {
if (url==null) throw new NullPointerException("Cannot read from null");
if (url=="") throw new NullPointerException("Cannot read from empty string");
@@ -126,6 +129,8 @@ public class ClassFinder {
log.info("Cannot read "+file+"; not a file or directory");
}
}
+ } else {
+ result.add(new URL("file://"+tidiedFile));
}
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java b/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java
index 3039154..989a29d 100644
--- a/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java
+++ b/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java
@@ -24,6 +24,9 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import brooklyn.basic.BrooklynDynamicType;
import brooklyn.basic.BrooklynObject;
import brooklyn.basic.BrooklynType;
@@ -42,6 +45,8 @@ import brooklyn.rest.domain.SummaryComparators;
import brooklyn.rest.transform.EffectorTransformer;
import brooklyn.rest.transform.EntityTransformer;
import brooklyn.rest.transform.SensorTransformer;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.RuntimeInterruptedException;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
@@ -50,6 +55,8 @@ import com.google.common.collect.Sets;
public class ItemDescriptors {
+ private static final Logger LOG = LoggerFactory.getLogger(ItemDescriptors.class);
+
public static List<Map<String, Object>> toItemDescriptors(Iterable<? extends Class<? extends BrooklynObject>> types, boolean headingsOnly) {
return toItemDescriptors(types, headingsOnly, null);
}
@@ -58,8 +65,17 @@ public class ItemDescriptors {
List<Map<String, Object>> itemDescriptors = Lists.newArrayList();
for (Class<? extends BrooklynObject> type : types) {
- Map<String, Object> itemDescriptor = toItemDescriptor(type, headingsOnly);
- itemDescriptors.add(itemDescriptor);
+ try {
+ Map<String, Object> itemDescriptor = toItemDescriptor(type, headingsOnly);
+ itemDescriptors.add(itemDescriptor);
+ } catch (Throwable throwable) {
+ if (throwable instanceof InterruptedException)
+ throw new RuntimeInterruptedException((InterruptedException) throwable);
+ if (throwable instanceof RuntimeInterruptedException)
+ throw (RuntimeInterruptedException) throwable;
+
+ LOG.warn("Could not load "+type+": "+throwable);
+ }
}
if (!Strings.isNullOrEmpty(sortField)) {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn-object-list.html
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn-object-list.html b/usage/cli/src/main/resources/brooklyn-object-list.html
deleted file mode 100644
index 4487346..0000000
--- a/usage/cli/src/main/resources/brooklyn-object-list.html
+++ /dev/null
@@ -1,147 +0,0 @@
-<!DOCTYPE html>
-<!--
-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.
--->
-<html lang="en">
- <head>
- <meta charset="utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
- <link rel="stylesheet" href="items.css" type="text/css" media="screen"/>
- <title>Brooklyn Objects</title>
- </head>
-
- <body>
- <div id="container">
- <div id="header">
- <div id="identity">
- <a href="https://brooklyn.incubator.apache.org/" rel="home">Brooklyn</a>
- </div>
- </div>
-
- <ul class="nav nav-tabs">
- <li class="active"><a href="#entities" data-toggle="tab">Entities</a></li>
- <li><a href="#policies" data-toggle="tab">Policies</a></li>
- <li><a href="#enrichers" data-toggle="tab">Enrichers</a></li>
- </ul>
-
- <div class="tab-content">
- <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>
- </div>
-
- <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 () {
- 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>
-</html>
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/statics/brooklyn-object-list.html
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/statics/brooklyn-object-list.html b/usage/cli/src/main/resources/brooklyn/item-lister/statics/brooklyn-object-list.html
new file mode 100644
index 0000000..c30d3db
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/statics/brooklyn-object-list.html
@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+ <link rel="stylesheet" href="items.css" type="text/css" media="screen"/>
+ <title>Brooklyn Objects</title>
+ </head>
+
+ <body>
+ <div id="container">
+ <div id="header">
+ <div id="identity">
+ <a href="https://brooklyn.incubator.apache.org/" rel="home">Brooklyn</a>
+ </div>
+ </div>
+
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#entities" data-toggle="tab">Entities</a></li>
+ <li><a href="#policies" data-toggle="tab">Policies</a></li>
+ <li><a href="#enrichers" data-toggle="tab">Enrichers</a></li>
+ </ul>
+
+ <div class="tab-content">
+ <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>
+ </div>
+
+ <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="style/js/catalog/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 () {
+ 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>
+</html>
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/statics/common.js
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/statics/common.js b/usage/cli/src/main/resources/brooklyn/item-lister/statics/common.js
new file mode 100644
index 0000000..8e8a45f
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/statics/common.js
@@ -0,0 +1,94 @@
+/**
+ * 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.
+ */
+
+var brooklyn = (function ($, _) {
+
+ return {
+ findItemOfType: function(items, type) {
+ return _.findWhere(items, { type: type });
+ },
+
+ entityCard: _.template(
+ "<a class='plain' data-type='<%= type %>' href='entities/<%= type %>.html'>" +
+ "<div class='card'>" +
+ "<span class='glyphicon glyphicon-chevron-right'/>" +
+ "<div class='name'><%=name%></div>" +
+ "<div class='type'><%=type%></div>" +
+ "<div class='description'><%=description%></div>" +
+ "</div>" +
+ "</a>"
+ ),
+ policyCard: _.template(
+ "<a class='plain' data-type='<%= type %>' href='policies/<%= type %>.html'>" +
+ "<div class='card'>" +
+ "<span class='glyphicon glyphicon-chevron-right'/>" +
+ "<div class='name'><%=name%></div>" +
+ "<div class='type'><%=type%></div>" +
+ "<div class='description'><%=description%></div>" +
+ "</div>" +
+ "</a>"
+ ),
+ enricherCard: _.template(
+ "<a class='plain' data-type='<%= type %>' href='enrichers/<%= type %>.html'>" +
+ "<div class='card'>" +
+ "<span class='glyphicon glyphicon-chevron-right'/>" +
+ "<div class='name'><%=name%></div>" +
+ "<div class='type'><%=type%></div>" +
+ "<div class='description'><%=description%></div>" +
+ "</div>" +
+ "</a>"
+ ),
+
+ typeSummary: _.template(
+ "<div class='summaryLabel'><%=name%></div>" +
+ "<div class='summaryType'><%=type%></div>" +
+ "<div class='description'><%=description%></div>"
+ ),
+
+ configKeyCard: _.template(
+ "<div class='card configKey'>" +
+ "<div class='name'><%=name%></div>" +
+ "<dl>" +
+ "<dt>description</dt><dd><%=(description||' ')%></dd>" +
+ "<dt>value type</dt><dd class='java'><%=(type||' ')%></dd>" +
+ "<dt>default value</dt><dd><%=(defaultValue||' ')%></dd>" +
+ "</dl>" +
+ "</div>"
+ ),
+ sensorCard: _.template(
+ "<div class='card sensor'>" +
+ "<div class='name'><%=name%></div>" +
+ "<dl>" +
+ "<dt>description</dt><dd><%=(description||' ')%></dd>" +
+ "<dt>value type</dt><dd class='java'><%=(type||' ')%></dd>" +
+ "</dl>" +
+ "</div>"
+ ),
+ effectorCard: _.template(
+ "<div class='card effector'>" +
+ "<div class='name'><%=name%></div>" +
+ "<dl>" +
+ "<dt>description</dt><dd><%=(description||' ')%></dd>" +
+ "<dt>return type</dt><dd class='java'><%=(returnType||' ')%></dd>" +
+ "</dl>" +
+ "</div>"
+ )
+ };
+
+}(jQuery, _));
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/statics/items.css
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/statics/items.css b/usage/cli/src/main/resources/brooklyn/item-lister/statics/items.css
new file mode 100644
index 0000000..a0bf0f0
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/statics/items.css
@@ -0,0 +1,153 @@
+/*
+ * 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.
+*/
+/* landing page */
+body {
+ margin: 0px;
+ padding: 10px 0px 20px 0px;
+ font-family: arial, helvetica, sans-serif;
+ background-color: #ffffff;
+ color: #393939;
+ font-size: 15px;
+}
+
+.nav-tabs {
+ clear: both;
+ font-weight: bold;
+ font-size: 12pt;
+}
+.nav-tabs a {
+ color: #4d9d3a;
+}
+.nav-tabs a:hover {
+ color: #4d9d3a;
+}
+
+.tab-content {
+ padding: 20px;
+ padding-bottom: 10px;
+ border: 1px solid #ddd;
+ border-top: none;
+}
+
+a:hover > .card {
+ top: -2px;
+ background-color: #f4f4f4;
+ box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.2);
+}
+.card {
+ position: relative;
+ padding: 12px;
+ 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;
+ right: 0;
+ top: 0;
+ padding: 8px;
+ font-size: 16pt;
+ color: #aaa;
+}
+a:hover .glyphicon {
+ color: #888;
+}
+a.plain {
+ text-decoration: none !important;
+}
+.name {
+ font-size: 12pt;
+ font-weight: bold;
+}
+.type {
+ font-family: monospace;
+ color: #888;
+ margin-top: 2px;
+}
+#summary .description {
+ margin: 15px 0 25px 0;
+}
+.card .description {
+ margin: 10px 40px 0 20px;
+}
+
+#summary {
+ clear: both;
+ margin: 10px 0 20px 0;
+}
+.summaryLabel {
+ font-size: 20px;
+ font-weight: bold;
+}
+.summaryType {
+ font-family: monospace;
+ font-size: 12pt;
+ color: #888;
+}
+.java {
+ font-family: monospace;
+}
+
+.card dl {
+ margin-bottom: 0;
+ margin-top: 5px;
+}
+dt {
+ clear: both;
+ float: left;
+ width: 8em;
+ text-align: right;
+ font-weight: normal;
+}
+dd {
+ margin-left: 9em;
+}
+
+#container {
+ width: 980px;
+ padding: 0;
+ margin: 0 auto;
+}
+
+#identity {
+ float: left;
+ margin: 0;
+ padding: 30px 0 15px 10px;
+}
+
+#identity a {
+ text-decoration: none;
+ display: block;
+ margin: 0;
+ color: #4d9d3a;
+ font-size: 2.5em;
+ padding: 0;
+ background: transparent url(images/brooklyn.gif) no-repeat 0 0;
+ width: 206px;
+ height: 44px;
+ text-indent: -1000px;
+ overflow: hidden;
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/catalog/bloodhound.js
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/catalog/bloodhound.js b/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/catalog/bloodhound.js
new file mode 100644
index 0000000..96a4c43
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/catalog/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
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/underscore-min.js
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/underscore-min.js b/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/underscore-min.js
new file mode 100644
index 0000000..11f1d96
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/underscore-min.js
@@ -0,0 +1,6 @@
+// Underscore.js 1.7.0
+// http://underscorejs.org
+// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+// Underscore may be freely distributed under the MIT license.
+(function(){var n=this,t=n._,r=Array.prototype,e=Object.prototype,u=Function.prototype,i=r.push,a=r.slice,o=r.concat,l=e.toString,c=e.hasOwnProperty,f=Array.isArray,s=Object.keys,p=u.bind,h=function(n){return n instanceof h?n:this instanceof h?void(this._wrapped=n):new h(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=h),exports._=h):n._=h,h.VERSION="1.7.0";var g=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}};h.iteratee=function(n,t,r){return null==n?h.identity:h.isFunction(n)?g(n,t,r):h.isObject(n)?h.matches(n):h.property(n)},h.each=h.forEach=function(n,t,r){if(null==n)return n;t=g(t,r);var e,u=n.length;if(u===+u)for(e=0;u>e;e++)t(n[e],e,n);else{var i=h.keys(n);for(e
=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},h.map=h.collect=function(n,t,r){if(null==n)return[];t=h.iteratee(t,r);for(var e,u=n.length!==+n.length&&h.keys(n),i=(u||n).length,a=Array(i),o=0;i>o;o++)e=u?u[o]:o,a[o]=t(n[e],e,n);return a};var v="Reduce of empty array with no initial value";h.reduce=h.foldl=h.inject=function(n,t,r,e){null==n&&(n=[]),t=g(t,e,4);var u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length,o=0;if(arguments.length<3){if(!a)throw new TypeError(v);r=n[i?i[o++]:o++]}for(;a>o;o++)u=i?i[o]:o,r=t(r,n[u],u,n);return r},h.reduceRight=h.foldr=function(n,t,r,e){null==n&&(n=[]),t=g(t,e,4);var u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;if(arguments.length<3){if(!a)throw new TypeError(v);r=n[i?i[--a]:--a]}for(;a--;)u=i?i[a]:a,r=t(r,n[u],u,n);return r},h.find=h.detect=function(n,t,r){var e;return t=h.iteratee(t,r),h.some(n,function(n,r,u){return t(n,r,u)?(e=n,!0):void 0}),e},h.filter=h.select=function(n,t,r){var e=[];return null==n?e:(t=h.iteratee(t,r),h.each(n,
function(n,r,u){t(n,r,u)&&e.push(n)}),e)},h.reject=function(n,t,r){return h.filter(n,h.negate(h.iteratee(t)),r)},h.every=h.all=function(n,t,r){if(null==n)return!0;t=h.iteratee(t,r);var e,u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;for(e=0;a>e;e++)if(u=i?i[e]:e,!t(n[u],u,n))return!1;return!0},h.some=h.any=function(n,t,r){if(null==n)return!1;t=h.iteratee(t,r);var e,u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;for(e=0;a>e;e++)if(u=i?i[e]:e,t(n[u],u,n))return!0;return!1},h.contains=h.include=function(n,t){return null==n?!1:(n.length!==+n.length&&(n=h.values(n)),h.indexOf(n,t)>=0)},h.invoke=function(n,t){var r=a.call(arguments,2),e=h.isFunction(t);return h.map(n,function(n){return(e?t:n[t]).apply(n,r)})},h.pluck=function(n,t){return h.map(n,h.property(t))},h.where=function(n,t){return h.filter(n,h.matches(t))},h.findWhere=function(n,t){return h.find(n,h.matches(t))},h.max=function(n,t,r){var e,u,i=-1/0,a=-1/0;if(null==t&&null!=n){n=n.length===+n.length?n:h.values(n);for(va
r o=0,l=n.length;l>o;o++)e=n[o],e>i&&(i=e)}else t=h.iteratee(t,r),h.each(n,function(n,r,e){u=t(n,r,e),(u>a||u===-1/0&&i===-1/0)&&(i=n,a=u)});return i},h.min=function(n,t,r){var e,u,i=1/0,a=1/0;if(null==t&&null!=n){n=n.length===+n.length?n:h.values(n);for(var o=0,l=n.length;l>o;o++)e=n[o],i>e&&(i=e)}else t=h.iteratee(t,r),h.each(n,function(n,r,e){u=t(n,r,e),(a>u||1/0===u&&1/0===i)&&(i=n,a=u)});return i},h.shuffle=function(n){for(var t,r=n&&n.length===+n.length?n:h.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=h.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},h.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=h.values(n)),n[h.random(n.length-1)]):h.shuffle(n).slice(0,Math.max(0,t))},h.sortBy=function(n,t,r){return t=h.iteratee(t,r),h.pluck(h.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var m=func
tion(n){return function(t,r,e){var u={};return r=h.iteratee(r,e),h.each(t,function(e,i){var a=r(e,i,t);n(u,e,a)}),u}};h.groupBy=m(function(n,t,r){h.has(n,r)?n[r].push(t):n[r]=[t]}),h.indexBy=m(function(n,t,r){n[r]=t}),h.countBy=m(function(n,t,r){h.has(n,r)?n[r]++:n[r]=1}),h.sortedIndex=function(n,t,r,e){r=h.iteratee(r,e,1);for(var u=r(t),i=0,a=n.length;a>i;){var o=i+a>>>1;r(n[o])<u?i=o+1:a=o}return i},h.toArray=function(n){return n?h.isArray(n)?a.call(n):n.length===+n.length?h.map(n,h.identity):h.values(n):[]},h.size=function(n){return null==n?0:n.length===+n.length?n.length:h.keys(n).length},h.partition=function(n,t,r){t=h.iteratee(t,r);var e=[],u=[];return h.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},h.first=h.head=h.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:0>t?[]:a.call(n,0,t)},h.initial=function(n,t,r){return a.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},h.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:a.call(n,Math.max(n.l
ength-t,0))},h.rest=h.tail=h.drop=function(n,t,r){return a.call(n,null==t||r?1:t)},h.compact=function(n){return h.filter(n,h.identity)};var y=function(n,t,r,e){if(t&&h.every(n,h.isArray))return o.apply(e,n);for(var u=0,a=n.length;a>u;u++){var l=n[u];h.isArray(l)||h.isArguments(l)?t?i.apply(e,l):y(l,t,r,e):r||e.push(l)}return e};h.flatten=function(n,t){return y(n,t,!1,[])},h.without=function(n){return h.difference(n,a.call(arguments,1))},h.uniq=h.unique=function(n,t,r,e){if(null==n)return[];h.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=h.iteratee(r,e));for(var u=[],i=[],a=0,o=n.length;o>a;a++){var l=n[a];if(t)a&&i===l||u.push(l),i=l;else if(r){var c=r(l,a,n);h.indexOf(i,c)<0&&(i.push(c),u.push(l))}else h.indexOf(u,l)<0&&u.push(l)}return u},h.union=function(){return h.uniq(y(arguments,!0,!0,[]))},h.intersection=function(n){if(null==n)return[];for(var t=[],r=arguments.length,e=0,u=n.length;u>e;e++){var i=n[e];if(!h.contains(t,i)){for(var a=1;r>a&&h.contains(arguments[a],i);a++);a===r&&t.p
ush(i)}}return t},h.difference=function(n){var t=y(a.call(arguments,1),!0,!0,[]);return h.filter(n,function(n){return!h.contains(t,n)})},h.zip=function(n){if(null==n)return[];for(var t=h.max(arguments,"length").length,r=Array(t),e=0;t>e;e++)r[e]=h.pluck(arguments,e);return r},h.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},h.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=h.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}for(;u>e;e++)if(n[e]===t)return e;return-1},h.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=n.length;for("number"==typeof r&&(e=0>r?e+r+1:Math.min(e,r+1));--e>=0;)if(n[e]===t)return e;return-1},h.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=r||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=Array(e),i=0;e>i;i++,n+=r)u[i]=n;return u};var d=function(){};h.bind=function(n,t){var r,e;if(p&&n.bind===p)return p.apply(n,a.c
all(arguments,1));if(!h.isFunction(n))throw new TypeError("Bind must be called on a function");return r=a.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(a.call(arguments)));d.prototype=n.prototype;var u=new d;d.prototype=null;var i=n.apply(u,r.concat(a.call(arguments)));return h.isObject(i)?i:u}},h.partial=function(n){var t=a.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===h&&(e[u]=arguments[r++]);for(;r<arguments.length;)e.push(arguments[r++]);return n.apply(this,e)}},h.bindAll=function(n){var t,r,e=arguments.length;if(1>=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=h.bind(n[r],n);return n},h.memoize=function(n,t){var r=function(e){var u=r.cache,i=t?t.apply(this,arguments):e;return h.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},h.delay=function(n,t){var r=a.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},h.de
fer=function(n){return h.delay.apply(h,[n,1].concat(a.call(arguments,1)))},h.throttle=function(n,t,r){var e,u,i,a=null,o=0;r||(r={});var l=function(){o=r.leading===!1?0:h.now(),a=null,i=n.apply(e,u),a||(e=u=null)};return function(){var c=h.now();o||r.leading!==!1||(o=c);var f=t-(c-o);return e=this,u=arguments,0>=f||f>t?(clearTimeout(a),a=null,o=c,i=n.apply(e,u),a||(e=u=null)):a||r.trailing===!1||(a=setTimeout(l,f)),i}},h.debounce=function(n,t,r){var e,u,i,a,o,l=function(){var c=h.now()-a;t>c&&c>0?e=setTimeout(l,t-c):(e=null,r||(o=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,a=h.now();var c=r&&!e;return e||(e=setTimeout(l,t)),c&&(o=n.apply(i,u),i=u=null),o}},h.wrap=function(n,t){return h.partial(t,n)},h.negate=function(n){return function(){return!n.apply(this,arguments)}},h.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},h.after=function(n,t){return function(){return--
n<1?t.apply(this,arguments):void 0}},h.before=function(n,t){var r;return function(){return--n>0?r=t.apply(this,arguments):t=null,r}},h.once=h.partial(h.before,2),h.keys=function(n){if(!h.isObject(n))return[];if(s)return s(n);var t=[];for(var r in n)h.has(n,r)&&t.push(r);return t},h.values=function(n){for(var t=h.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},h.pairs=function(n){for(var t=h.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},h.invert=function(n){for(var t={},r=h.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},h.functions=h.methods=function(n){var t=[];for(var r in n)h.isFunction(n[r])&&t.push(r);return t.sort()},h.extend=function(n){if(!h.isObject(n))return n;for(var t,r,e=1,u=arguments.length;u>e;e++){t=arguments[e];for(r in t)c.call(t,r)&&(n[r]=t[r])}return n},h.pick=function(n,t,r){var e,u={};if(null==n)return u;if(h.isFunction(t)){t=g(t,r);for(e in n){var i=n[e];t(i,e,n)&&(u[e]=i)}}else{var l=o.apply([],a.call(argume
nts,1));n=new Object(n);for(var c=0,f=l.length;f>c;c++)e=l[c],e in n&&(u[e]=n[e])}return u},h.omit=function(n,t,r){if(h.isFunction(t))t=h.negate(t);else{var e=h.map(o.apply([],a.call(arguments,1)),String);t=function(n,t){return!h.contains(e,t)}}return h.pick(n,t,r)},h.defaults=function(n){if(!h.isObject(n))return n;for(var t=1,r=arguments.length;r>t;t++){var e=arguments[t];for(var u in e)n[u]===void 0&&(n[u]=e[u])}return n},h.clone=function(n){return h.isObject(n)?h.isArray(n)?n.slice():h.extend({},n):n},h.tap=function(n,t){return t(n),n};var b=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof h&&(n=n._wrapped),t instanceof h&&(t=t._wrapped);var u=l.call(n);if(u!==l.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i
=r.length;i--;)if(r[i]===n)return e[i]===t;var a=n.constructor,o=t.constructor;if(a!==o&&"constructor"in n&&"constructor"in t&&!(h.isFunction(a)&&a instanceof a&&h.isFunction(o)&&o instanceof o))return!1;r.push(n),e.push(t);var c,f;if("[object Array]"===u){if(c=n.length,f=c===t.length)for(;c--&&(f=b(n[c],t[c],r,e)););}else{var s,p=h.keys(n);if(c=p.length,f=h.keys(t).length===c)for(;c--&&(s=p[c],f=h.has(t,s)&&b(n[s],t[s],r,e)););}return r.pop(),e.pop(),f};h.isEqual=function(n,t){return b(n,t,[],[])},h.isEmpty=function(n){if(null==n)return!0;if(h.isArray(n)||h.isString(n)||h.isArguments(n))return 0===n.length;for(var t in n)if(h.has(n,t))return!1;return!0},h.isElement=function(n){return!(!n||1!==n.nodeType)},h.isArray=f||function(n){return"[object Array]"===l.call(n)},h.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},h.each(["Arguments","Function","String","Number","Date","RegExp"],function(n){h["is"+n]=function(t){return l.call(t)==="[object "+n+"]"}}),h.
isArguments(arguments)||(h.isArguments=function(n){return h.has(n,"callee")}),"function"!=typeof/./&&(h.isFunction=function(n){return"function"==typeof n||!1}),h.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},h.isNaN=function(n){return h.isNumber(n)&&n!==+n},h.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===l.call(n)},h.isNull=function(n){return null===n},h.isUndefined=function(n){return n===void 0},h.has=function(n,t){return null!=n&&c.call(n,t)},h.noConflict=function(){return n._=t,this},h.identity=function(n){return n},h.constant=function(n){return function(){return n}},h.noop=function(){},h.property=function(n){return function(t){return t[n]}},h.matches=function(n){var t=h.pairs(n),r=t.length;return function(n){if(null==n)return!r;n=new Object(n);for(var e=0;r>e;e++){var u=t[e],i=u[0];if(u[1]!==n[i]||!(i in n))return!1}return!0}},h.times=function(n,t,r){var e=Array(Math.max(0,n));t=g(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},h.random=funct
ion(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},h.now=Date.now||function(){return(new Date).getTime()};var _={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},w=h.invert(_),j=function(n){var t=function(t){return n[t]},r="(?:"+h.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};h.escape=j(_),h.unescape=j(w),h.result=function(n,t){if(null==n)return void 0;var r=n[t];return h.isFunction(r)?n[t]():r};var x=0;h.uniqueId=function(n){var t=++x+"";return n?n+t:t},h.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var A=/(.)^/,k={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},O=/\\|'|\r|\n|\u2028|\u2029/g,F=function(n){return"\\"+k[n]};h.template=function(n,t,r){!t&&r&&(t=r),t=h.defaults({},t,h.templateSettings);var e=RegExp([(t.escape||A).source,(t.interpolate||A).source,(t.evaluate||A).source]
.join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,a,o){return i+=n.slice(u,o).replace(O,F),u=o+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":a&&(i+="';\n"+a+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var a=new Function(t.variable||"obj","_",i)}catch(o){throw o.source=i,o}var l=function(n){return a.call(this,n,h)},c=t.variable||"obj";return l.source="function("+c+"){\n"+i+"}",l},h.chain=function(n){var t=h(n);return t._chain=!0,t};var E=function(n){return this._chain?h(n).chain():n};h.mixin=function(n){h.each(h.functions(n),function(t){var r=h[t]=n[t];h.prototype[t]=function(){var n=[this._wrapped];return i.apply(n,arguments),E.call(this,r.apply(h,n))}})},h.mixin(h),h.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=r[n];h.prototype[n]=
function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],E.call(this,r)}}),h.each(["concat","join","slice"],function(n){var t=r[n];h.prototype[n]=function(){return E.call(this,t.apply(this._wrapped,arguments))}}),h.prototype.value=function(){return this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return h})}).call(this);
+//# sourceMappingURL=underscore-min.map
\ No newline at end of file