Skip to content

Instantly share code, notes, and snippets.

@ForbesLindesay
Last active August 29, 2015 14:02
Show Gist options
  • Save ForbesLindesay/74b01d2fedf06b8f325e to your computer and use it in GitHub Desktop.
Save ForbesLindesay/74b01d2fedf06b8f325e to your computer and use it in GitHub Desktop.
'use strict';
// run this in the packages directory of the metor repository to generate "meteor-packages.html"
var fs = require('fs');
var uglify = require('uglify-js');
function getDependenciesFromSource(str) {
var dependencies = {
summary: '',
use: [],
require: []
};
function visitor(node) {
if (node.TYPE === 'Call' && node.expression.TYPE === 'Dot') {
var method = node.expression.property;
if (method === 'use' || method === 'require') {
dependencies[method] = dependencies[method].concat(
node.args.reduce(function (acc, next) {
return acc.concat(next.TYPE === 'Array' ? next.elements : [next])
}, []).filter(function (arg) {
return arg.TYPE === 'String' && arg.value !== 'client' && arg.value !== 'server';
}).map(function (arg) {
return arg.value;
}));
}
if (method === 'describe') {
node.args.filter(function (arg) {
return arg.TYPE === 'Object';
}).reduce(function (acc, obj) {
return acc.concat(obj.properties);
}, []).filter(function (prop) {
return prop.TYPE === 'ObjectKeyVal' && prop.key === 'summary' && prop.value.TYPE === 'String';
}).forEach(function (prop) {
dependencies.summary = prop.value.value
});
}
}
}
var ast = uglify.parse(str);
ast.walk(new uglify.TreeWalker(visitor));
function unique(item, i, arr) {
return arr.indexOf(item) === i;
}
return {
summary: dependencies.summary,
use: dependencies.use.sort().filter(unique),
require: dependencies.require.sort().filter(unique)
};
}
var nodesByName = {};
var nodes = [];
function parse(name) {
if (nodesByName['key:' + name]) return nodesByName['key:' + name];
var node = nodesByName['key:' + name] = {
name: name,
summary: '',
require: [],
use: []
};
nodes.push(node);
var str = fs.readFileSync(__dirname + '/' + name + '/package.js', 'utf8');
var deps = getDependenciesFromSource(str);
node.summary = deps.summary;
node.require = deps.require;
node.use = deps.use.filter(function (name) {
return name !== node.name;
});
return node;
}
fs.readdirSync(__dirname).filter(function (name) {
return fs.existsSync(__dirname + '/' + name + '/package.js');
}).forEach(parse);
fs.writeFileSync(__dirname + '/data.json', JSON.stringify(nodes, null, ' '));
fs.writeFileSync(__dirname + '/meteor-packages.html', fs.readFileSync(__dirname + '/index.html', 'utf8').replace(/{{data}}/g, JSON.stringify(nodes, null, ' ')));
<!DOCTYPE html>
<html>
<head>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet"/>
<style>
.btn-clear {
display: block;
background: none;
border: none;
width: 100%;
text-align: left;
outline: none;
padding: 0;
}
</style>
</head>
<body>
<div class="container">
<h1>Meteor JS Packages</h1>
<form role="form">
<div class="form-group">
<label class="sr-only" for="search">Search</label>
<input type="search" class="form-control" id="search" placeholder="Search...">
<p class="help-block">Enter spaces to search for multiple different modules.</p>
<p class="help-block">Click on modules to expand and see their dependencies.</p>
</div>
</form>
<div id="display">
</div>
</div>
<script id="data" type="application/json">{{data}}</script>
<script>
var expanded = [];
var display = document.getElementById('display');
var search = document.getElementById('search');
var raw = JSON.parse(document.getElementById('data').textContent);
var byName = {};
raw.forEach(function (node) {
byName[node.name] = node;
});
search.value = decodeURIComponent(location.hash).substring(1);
function filter(raw) {
return raw.filter(function (node) {
if (search.value.trim() === '') return true;
return search.value.split(' ').filter(function (name) {
return name.trim() !== '';
}).some(function (searchTerm) {
return node.name.indexOf(searchTerm) !== -1;
})
});
}
function render() {
location.hash = encodeURIComponent(search.value);
display.innerHTML = renderCollection(filter(raw), '');
}
function renderCollection(data, path) {
return '<ul class="list-group">' + data.map(function (node) {
var p = path + node.name;
var isCircular = path.indexOf(node.name + '.') === 0 || path.indexOf('.' + node.name + '.') !== -1;
var isExpandable = node.use.length > 0 && !isCircular;
var isExpanded = expanded.indexOf(p) !== -1;
var open = '<li class="list-group-item">';
var badge = !isExpanded && isExpandable ?
'<span class="badge pull-right">' + node.use.length + '</span>' :
isCircular ? '<span class="label label-danger pull-right">circular</span>' : '';
var name = '<p class="lead">' + node.name + '</p>';
var summary = '<p>' + node.summary + '</p>';
var button = '<button data-name="' + p + '"type="button" class="btn-clear">' + badge + name + summary + '</button>';
var children = isExpanded && isExpandable ? renderCollection(node.use.map(function (name) {
return byName[name];
}), p + '.') : '';
var close = '</li>';
if (!isExpandable) return open + badge + name + summary + close;
return open + button + children + close;
}).join('') + '</ul>';
}
render();
search.addEventListener('keyup', render, false);
search.addEventListener('change', render, false);
display.addEventListener('click', function (e) {
var target = e.target;
while (target && target.hasAttribute && !target.hasAttribute('data-name')) {
target = target.parentNode;
}
if (!(target && target.hasAttribute)) return;
var name = target.getAttribute('data-name');
var i = expanded.indexOf(name);
if (i === -1) {
expanded.push(name);
} else {
expanded.splice(i, 1);
}
render();
})
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet"/>
<style>
.btn-clear {
display: block;
background: none;
border: none;
width: 100%;
text-align: left;
outline: none;
padding: 0;
}
</style>
</head>
<body>
<div class="container">
<h1>Meteor JS Packages</h1>
<form role="form">
<div class="form-group">
<label class="sr-only" for="search">Search</label>
<input type="search" class="form-control" id="search" placeholder="Search...">
<p class="help-block">Enter spaces to search for multiple different modules.</p>
<p class="help-block">Click on modules to expand and see their dependencies.</p>
</div>
</form>
<div id="display">
</div>
</div>
<script id="data" type="application/json">[
{
"name": "accounts-base",
"summary": "A user account system",
"require": [],
"use": [
"autopublish",
"callback-hook",
"check",
"deps",
"ejson",
"livedata",
"localstorage",
"mongo-livedata",
"oauth-encryption",
"random",
"service-configuration",
"test-helpers",
"tinytest",
"ui",
"underscore"
]
},
{
"name": "accounts-facebook",
"summary": "Login service for Facebook accounts",
"require": [],
"use": [
"accounts-base",
"accounts-oauth",
"facebook"
]
},
{
"name": "accounts-github",
"summary": "Login service for Github accounts",
"require": [],
"use": [
"accounts-base",
"accounts-oauth",
"github"
]
},
{
"name": "accounts-google",
"summary": "Login service for Google accounts",
"require": [],
"use": [
"accounts-base",
"accounts-oauth",
"google",
"random",
"underscore"
]
},
{
"name": "accounts-meetup",
"summary": "Login service for Meetup accounts",
"require": [],
"use": [
"accounts-base",
"accounts-oauth",
"meetup"
]
},
{
"name": "accounts-meteor-developer",
"summary": "Login service for Meteor developer accounts",
"require": [],
"use": [
"accounts-base",
"accounts-oauth",
"meteor-developer",
"random",
"underscore"
]
},
{
"name": "accounts-oauth",
"summary": "Common code for OAuth-based login services",
"require": [],
"use": [
"accounts-base",
"check",
"oauth",
"random",
"underscore",
"webapp"
]
},
{
"name": "accounts-password",
"summary": "Password support for accounts",
"require": [],
"use": [
"accounts-base",
"check",
"deps",
"email",
"livedata",
"random",
"srp",
"test-helpers",
"tinytest",
"underscore"
]
},
{
"name": "accounts-twitter",
"summary": "Login service for Twitter accounts",
"require": [],
"use": [
"accounts-base",
"accounts-oauth",
"http",
"twitter",
"underscore"
]
},
{
"name": "accounts-ui",
"summary": "Simple templates to add login widgets to an app",
"require": [],
"use": [
"accounts-ui-unstyled",
"less"
]
},
{
"name": "accounts-ui-unstyled",
"summary": "Unstyled version of login widgets",
"require": [],
"use": [
"accounts-base",
"accounts-oauth",
"accounts-password",
"deps",
"service-configuration",
"session",
"templating",
"tinytest",
"underscore"
]
},
{
"name": "accounts-weibo",
"summary": "Login service for Sina Weibo accounts",
"require": [],
"use": [
"accounts-base",
"accounts-oauth",
"weibo"
]
},
{
"name": "amplify",
"summary": "API for Persistent Storage, PubSub and Request",
"require": [],
"use": [
"jquery"
]
},
{
"name": "appcache",
"summary": "Enable the application cache in the browser",
"require": [],
"use": [
"autoupdate",
"reload",
"routepolicy",
"underscore",
"webapp"
]
},
{
"name": "application-configuration",
"summary": "Interaction with the configuration sources for your apps",
"require": [],
"use": [
"ejson",
"follower-livedata",
"livedata",
"logging",
"mongo-livedata",
"underscore"
]
},
{
"name": "audit-argument-checks",
"summary": "Try to detect inadequate input sanitization",
"require": [],
"use": []
},
{
"name": "autopublish",
"summary": "Publish the entire database to all clients",
"require": [],
"use": []
},
{
"name": "autoupdate",
"summary": "Update the client when new client code is available",
"require": [],
"use": [
"deps",
"livedata",
"mongo-livedata",
"reload",
"retry",
"webapp"
]
},
{
"name": "backbone",
"summary": "A minimalist client-side MVC framework",
"require": [],
"use": [
"jquery",
"json",
"underscore"
]
},
{
"name": "binary-heap",
"summary": "Binary Heap datastructure implementation",
"require": [],
"use": [
"id-map",
"tinytest",
"underscore"
]
},
{
"name": "bootstrap",
"summary": "Front-end framework from Twitter",
"require": [
"path"
],
"use": [
"jquery"
]
},
{
"name": "browser-policy",
"summary": "Configure security policies enforced by the browser",
"require": [],
"use": [
"browser-policy-content",
"browser-policy-framing",
"ejson",
"tinytest"
]
},
{
"name": "browser-policy-common",
"summary": "Common code for browser-policy packages",
"require": [],
"use": [
"webapp"
]
},
{
"name": "browser-policy-content",
"summary": "Configure content security policies",
"require": [],
"use": [
"browser-policy-common",
"underscore",
"webapp"
]
},
{
"name": "browser-policy-framing",
"summary": "Restrict which websites can frame your app",
"require": [],
"use": [
"browser-policy-common",
"underscore"
]
},
{
"name": "callback-hook",
"summary": "Register callbacks on a hook",
"require": [],
"use": [
"underscore"
]
},
{
"name": "check",
"summary": "Check whether a value matches a pattern",
"require": [],
"use": [
"ejson",
"tinytest",
"underscore"
]
},
{
"name": "code-prettify",
"summary": "Syntax highlighting of code, from Google",
"require": [
"path"
],
"use": []
},
{
"name": "coffeescript",
"summary": "Javascript dialect with fewer braces and semicolons",
"require": [],
"use": [
"coffeescript-test-helper",
"tinytest"
]
},
{
"name": "coffeescript-test-helper",
"summary": "Used by the coffeescript package's tests",
"require": [],
"use": [
"coffeescript"
]
},
{
"name": "ctl",
"summary": "Default control program for an application",
"require": [],
"use": [
"application-configuration",
"ctl-helper",
"follower-livedata",
"livedata",
"mongo-livedata",
"underscore"
]
},
{
"name": "ctl-helper",
"summary": "Helpers for control programs",
"require": [],
"use": [
"application-configuration",
"follower-livedata",
"livedata",
"logging",
"mongo-livedata",
"underscore"
]
},
{
"name": "d3",
"summary": "Library for manipulating documents based on data",
"require": [],
"use": []
},
{
"name": "deps",
"summary": "Dependency mananger to allow reactive callbacks",
"require": [],
"use": [
"tinytest"
]
},
{
"name": "dev-bundle-fetcher",
"summary": "A shell script for downloading the Meteor dev bundle",
"require": [],
"use": []
},
{
"name": "disable-oplog",
"summary": "Disables oplog tailing",
"require": [],
"use": []
},
{
"name": "ejson",
"summary": "Extended and Extensible JSON library",
"require": [],
"use": [
"json",
"tinytest",
"underscore"
]
},
{
"name": "email",
"summary": "Send email messages",
"require": [],
"use": [
"application-configuration",
"tinytest",
"underscore"
]
},
{
"name": "facebook",
"summary": "Facebook OAuth flow",
"require": [],
"use": [
"http",
"oauth",
"oauth2",
"random",
"service-configuration",
"templating",
"underscore"
]
},
{
"name": "facts",
"summary": "Publish internal app statistics",
"require": [],
"use": [
"autopublish",
"livedata",
"mongo-livedata",
"templating",
"underscore"
]
},
{
"name": "follower-livedata",
"summary": "Maintain a connection to the leader of an election set",
"require": [],
"use": [
"ejson",
"livedata",
"logging",
"underscore"
]
},
{
"name": "force-ssl",
"summary": "Require this application to use HTTPS",
"require": [],
"use": [
"livedata",
"underscore",
"webapp"
]
},
{
"name": "geojson-utils",
"summary": "GeoJSON utility functions (from https://github.com/maxogden/geojson-js-utils)",
"require": [],
"use": [
"tinytest"
]
},
{
"name": "github",
"summary": "Github OAuth flow",
"require": [],
"use": [
"http",
"oauth",
"oauth2",
"random",
"service-configuration",
"templating",
"underscore"
]
},
{
"name": "google",
"summary": "Google OAuth flow",
"require": [],
"use": [
"http",
"oauth",
"oauth2",
"random",
"service-configuration",
"templating",
"underscore"
]
},
{
"name": "handlebars",
"summary": "Deprecated",
"require": [],
"use": []
},
{
"name": "html-tools",
"summary": "Standards-compliant HTML tools",
"require": [],
"use": [
"htmljs",
"spacebars-compiler",
"tinytest",
"underscore"
]
},
{
"name": "htmljs",
"summary": "Small library for expressing HTML trees",
"require": [],
"use": [
"html-tools",
"tinytest",
"underscore"
]
},
{
"name": "http",
"summary": "Make HTTP calls to remote servers",
"require": [],
"use": [
"jquery",
"random",
"test-helpers",
"underscore",
"webapp"
]
},
{
"name": "id-map",
"summary": "Dictionary data structure allowing non-string keys",
"require": [],
"use": [
"ejson",
"json",
"underscore"
]
},
{
"name": "insecure",
"summary": "Allow all database writes by default",
"require": [],
"use": []
},
{
"name": "jquery",
"summary": "Manipulate the DOM using CSS selectors",
"require": [],
"use": []
},
{
"name": "jquery-history",
"summary": "pushState module from the jQuery project",
"require": [],
"use": [
"jquery",
"json"
]
},
{
"name": "jquery-layout",
"summary": "Easily create arbitrary multicolumn layouts",
"require": [],
"use": [
"jquery"
]
},
{
"name": "jquery-waypoints",
"summary": "Run a function when the user scrolls past an element",
"require": [],
"use": [
"coffeescript",
"jquery"
]
},
{
"name": "js-analyze",
"summary": "JavaScript code analysis for Meteor",
"require": [],
"use": []
},
{
"name": "js-analyze-tests",
"summary": "Tests for JavaScript code analysis for Meteor",
"require": [],
"use": [
"js-analyze",
"tinytest"
]
},
{
"name": "json",
"summary": "Provides JSON.stringify and JSON.parse for older browsers",
"require": [],
"use": []
},
{
"name": "jsparse",
"summary": "Full-featured JavaScript parser",
"require": [],
"use": [
"tinytest",
"underscore"
]
},
{
"name": "less",
"summary": "The dynamic stylesheet language",
"require": [],
"use": [
"templating",
"test-helpers",
"tinytest"
]
},
{
"name": "livedata",
"summary": "Meteor's latency-compensated distributed data framework",
"require": [],
"use": [
"audit-argument-checks",
"autopublish",
"callback-hook",
"check",
"deps",
"ejson",
"facts",
"http",
"json",
"logging",
"minimongo",
"mongo-livedata",
"random",
"reload",
"retry",
"routepolicy",
"test-helpers",
"tinytest",
"underscore",
"webapp"
]
},
{
"name": "localstorage",
"summary": "Simulates local storage on IE 6,7 using userData",
"require": [],
"use": [
"random",
"tinytest"
]
},
{
"name": "logging",
"summary": "Logging facility.",
"require": [],
"use": [
"ejson",
"tinytest",
"underscore"
]
},
{
"name": "meetup",
"summary": "Meetup OAuth flow",
"require": [],
"use": [
"http",
"oauth",
"oauth2",
"random",
"service-configuration",
"templating",
"underscore"
]
},
{
"name": "meteor",
"summary": "Core Meteor environment",
"require": [],
"use": [
"test-helpers",
"tinytest",
"underscore"
]
},
{
"name": "meteor-developer",
"summary": "Meteor developer accounts OAuth flow",
"require": [],
"use": [
"http",
"oauth",
"oauth2",
"random",
"service-configuration",
"templating",
"underscore"
]
},
{
"name": "meyerweb-reset",
"summary": "reset.css v2.0 from http://meyerweb.com/eric/tools/css/reset/",
"require": [],
"use": []
},
{
"name": "minifiers",
"summary": "JavaScript and CSS minifiers",
"require": [],
"use": [
"tinytest",
"underscore"
]
},
{
"name": "minimongo",
"summary": "Meteor's client-side datastore: a port of MongoDB to Javascript",
"require": [],
"use": [
"deps",
"ejson",
"geojson-utils",
"id-map",
"json",
"ordered-dict",
"random",
"test-helpers",
"tinytest",
"underscore"
]
},
{
"name": "mongo-livedata",
"summary": "Adaptor for using MongoDB and Minimongo over DDP",
"require": [],
"use": [
"application-configuration",
"autopublish",
"binary-heap",
"callback-hook",
"check",
"deps",
"disable-oplog",
"ejson",
"facts",
"insecure",
"json",
"livedata",
"logging",
"minimongo",
"random",
"test-helpers",
"tinytest",
"underscore",
"webapp"
]
},
{
"name": "oauth",
"summary": "Common code for OAuth-based services",
"require": [],
"use": [
"logging",
"mongo-livedata",
"oauth-encryption",
"random",
"routepolicy",
"service-configuration",
"tinytest",
"underscore",
"webapp"
]
},
{
"name": "oauth-encryption",
"summary": "Encrypt account secrets stored in the database",
"require": [],
"use": [
"tinytest",
"underscore"
]
},
{
"name": "oauth1",
"summary": "Common code for OAuth1-based login services",
"require": [],
"use": [
"http",
"oauth",
"oauth-encryption",
"random",
"service-configuration",
"tinytest",
"underscore"
]
},
{
"name": "oauth2",
"summary": "Common code for OAuth2-based login services",
"require": [],
"use": [
"oauth",
"oauth-encryption",
"random",
"service-configuration",
"tinytest"
]
},
{
"name": "observe-sequence",
"summary": "Observe changes to various sequence types such as arrays, cursors and objects",
"require": [],
"use": [
"deps",
"ejson",
"minimongo",
"random",
"tinytest",
"underscore"
]
},
{
"name": "ordered-dict",
"summary": "Ordered traversable dictionary with a mutable ordering",
"require": [],
"use": [
"underscore"
]
},
{
"name": "preserve-inputs",
"summary": "Deprecated package (now empty)",
"require": [],
"use": []
},
{
"name": "random",
"summary": "Random number generator and utilities",
"require": [],
"use": [
"tinytest",
"underscore"
]
},
{
"name": "reactive-dict",
"summary": "Reactive dictionary",
"require": [],
"use": [
"deps",
"ejson",
"mongo-livedata",
"tinytest",
"underscore"
]
},
{
"name": "reload",
"summary": "Reload the page while preserving application state.",
"require": [],
"use": [
"json",
"logging",
"underscore"
]
},
{
"name": "retry",
"summary": "Retry logic with exponential backoff",
"require": [],
"use": [
"random",
"underscore"
]
},
{
"name": "routepolicy",
"summary": "route policy declarations",
"require": [],
"use": [
"tinytest",
"underscore",
"webapp"
]
},
{
"name": "service-configuration",
"summary": "Manage the configuration for third-party services",
"require": [],
"use": [
"accounts-base",
"mongo-livedata"
]
},
{
"name": "session",
"summary": "Session variable",
"require": [],
"use": [
"deps",
"ejson",
"mongo-livedata",
"reactive-dict",
"reload",
"tinytest",
"underscore"
]
},
{
"name": "showdown",
"summary": "Markdown-to-HTML processor",
"require": [],
"use": [
"ui"
]
},
{
"name": "spacebars",
"summary": "Handlebars-like template language for Meteor",
"require": [],
"use": [
"htmljs",
"spacebars-common",
"templating",
"test-helpers",
"tinytest",
"ui"
]
},
{
"name": "spacebars-common",
"summary": "Common code for spacebars and spacebars-compiler",
"require": [],
"use": []
},
{
"name": "spacebars-compiler",
"summary": "Compiler for Spacebars template language",
"require": [],
"use": [
"html-tools",
"minifiers",
"spacebars-common",
"tinytest",
"underscore"
]
},
{
"name": "spacebars-tests",
"summary": "Additional tests for Spacebars",
"require": [],
"use": [
"jquery",
"showdown",
"spacebars",
"templating",
"test-helpers",
"tinytest",
"underscore"
]
},
{
"name": "spiderable",
"summary": "Makes the application crawlable to web spiders",
"require": [],
"use": [
"templating",
"tinytest",
"underscore",
"webapp"
]
},
{
"name": "srp",
"summary": "Library for Secure Remote Password (SRP) exchanges",
"require": [],
"use": [
"check",
"random",
"tinytest",
"underscore"
]
},
{
"name": "standard-app-packages",
"summary": "Include a standard set of Meteor packages in your app",
"require": [],
"use": [
"autoupdate",
"reload"
]
},
{
"name": "star-translate",
"summary": "A package for translating old bundles into stars",
"require": [],
"use": [
"dev-bundle-fetcher"
]
},
{
"name": "startup",
"summary": "Deprecated package (now empty)",
"require": [],
"use": []
},
{
"name": "stylus",
"summary": "Expressive, dynamic, robust CSS",
"require": [],
"use": [
"templating",
"test-helpers",
"tinytest"
]
},
{
"name": "templating",
"summary": "Allows templates to be defined in .html files",
"require": [],
"use": [
"deps",
"htmljs",
"minimongo",
"session",
"spacebars-compiler",
"test-helpers",
"tinytest",
"ui",
"underscore"
]
},
{
"name": "test-helpers",
"summary": "Utility functions for tests",
"require": [],
"use": [
"deps",
"ejson",
"jquery",
"livedata",
"random",
"tinytest",
"underscore"
]
},
{
"name": "test-in-browser",
"summary": "Run tests interactively in the browser",
"require": [],
"use": [
"autoupdate",
"bootstrap",
"deps",
"livedata",
"random",
"reload",
"session",
"spacebars",
"templating",
"tinytest",
"ui",
"underscore"
]
},
{
"name": "test-in-console",
"summary": "Run tests noninteractively, with results going to the console.",
"require": [],
"use": [
"check",
"ejson",
"http",
"random",
"tinytest",
"underscore"
]
},
{
"name": "tinytest",
"summary": "Tiny testing framework",
"require": [],
"use": [
"check",
"livedata",
"mongo-livedata",
"random",
"underscore"
]
},
{
"name": "twitter",
"summary": "Twitter OAuth flow",
"require": [],
"use": [
"http",
"oauth",
"oauth1",
"random",
"service-configuration",
"templating",
"underscore"
]
},
{
"name": "ui",
"summary": "Meteor UI Components framework",
"require": [],
"use": [
"deps",
"ejson",
"html-tools",
"htmljs",
"jquery",
"minimongo",
"observe-sequence",
"ordered-dict",
"random",
"spacebars-compiler",
"test-helpers",
"tinytest",
"underscore"
]
},
{
"name": "underscore",
"summary": "Collection of small helpers: _.map, _.each, ...",
"require": [],
"use": [
"meteor"
]
},
{
"name": "underscore-tests",
"summary": "Tests for the underscore package",
"require": [],
"use": [
"tinytest",
"underscore"
]
},
{
"name": "webapp",
"summary": "Serves a Meteor app over HTTP",
"require": [],
"use": [
"application-configuration",
"follower-livedata",
"htmljs",
"http",
"logging",
"routepolicy",
"spacebars",
"spacebars-compiler",
"tinytest",
"ui",
"underscore"
]
},
{
"name": "weibo",
"summary": "Weibo OAuth flow",
"require": [],
"use": [
"http",
"oauth",
"oauth2",
"random",
"service-configuration",
"templating"
]
}
]</script>
<script>
var expanded = [];
var display = document.getElementById('display');
var search = document.getElementById('search');
var raw = JSON.parse(document.getElementById('data').textContent);
var byName = {};
raw.forEach(function (node) {
byName[node.name] = node;
});
search.value = decodeURIComponent(location.hash).substring(1);
function filter(raw) {
return raw.filter(function (node) {
if (search.value.trim() === '') return true;
return search.value.split(' ').filter(function (name) {
return name.trim() !== '';
}).some(function (searchTerm) {
return node.name.indexOf(searchTerm) !== -1;
})
});
}
function render() {
location.hash = encodeURIComponent(search.value);
display.innerHTML = renderCollection(filter(raw), '');
}
function renderCollection(data, path) {
return '<ul class="list-group">' + data.map(function (node) {
var p = path + node.name;
var isCircular = path.indexOf(node.name + '.') === 0 || path.indexOf('.' + node.name + '.') !== -1;
var isExpandable = node.use.length > 0 && !isCircular;
var isExpanded = expanded.indexOf(p) !== -1;
var open = '<li class="list-group-item">';
var badge = !isExpanded && isExpandable ?
'<span class="badge pull-right">' + node.use.length + '</span>' :
isCircular ? '<span class="label label-danger pull-right">circular</span>' : '';
var name = '<p class="lead">' + node.name + '</p>';
var summary = '<p>' + node.summary + '</p>';
var button = '<button data-name="' + p + '"type="button" class="btn-clear">' + badge + name + summary + '</button>';
var children = isExpanded && isExpandable ? renderCollection(node.use.map(function (name) {
return byName[name];
}), p + '.') : '';
var close = '</li>';
if (!isExpandable) return open + badge + name + summary + close;
return open + button + children + close;
}).join('') + '</ul>';
}
render();
search.addEventListener('keyup', render, false);
search.addEventListener('change', render, false);
display.addEventListener('click', function (e) {
var target = e.target;
while (target && target.hasAttribute && !target.hasAttribute('data-name')) {
target = target.parentNode;
}
if (!(target && target.hasAttribute)) return;
var name = target.getAttribute('data-name');
var i = expanded.indexOf(name);
if (i === -1) {
expanded.push(name);
} else {
expanded.splice(i, 1);
}
render();
})
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment