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 <[email protected]> Authored: Wed Apr 29 13:32:34 2015 +0100 Committer: Alex Heneveld <[email protected]> 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
