Merge pull request #3542 from tschaub/examples
Generate example index and rebuild examples on source changes.
This commit is contained in:
@@ -1,5 +1,2 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
/build/
|
/build/
|
||||||
/examples/*.html.png
|
|
||||||
/examples/example-list.js
|
|
||||||
/examples/example-list.xml
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,269 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import time
|
|
||||||
from xml.dom.minidom import Document
|
|
||||||
|
|
||||||
try:
|
|
||||||
import xml.etree.ElementTree as ElementTree
|
|
||||||
except ImportError:
|
|
||||||
try:
|
|
||||||
import cElementTree as ElementTree # NOQA
|
|
||||||
except ImportError:
|
|
||||||
try:
|
|
||||||
import elementtree.ElementTree as ElementTree # NOQA
|
|
||||||
except ImportError:
|
|
||||||
import lxml.etree as ElementTree # NOQA
|
|
||||||
|
|
||||||
missing_deps = False
|
|
||||||
try:
|
|
||||||
import json
|
|
||||||
except ImportError:
|
|
||||||
try:
|
|
||||||
import simplejson as json # NOQA
|
|
||||||
except ImportError, E:
|
|
||||||
missing_deps = E
|
|
||||||
|
|
||||||
try:
|
|
||||||
from BeautifulSoup import BeautifulSoup
|
|
||||||
except ImportError, E:
|
|
||||||
missing_deps = E
|
|
||||||
|
|
||||||
feedName = "example-list.xml"
|
|
||||||
feedPath = "http://openlayers.github.io/ol3/master/examples/"
|
|
||||||
|
|
||||||
|
|
||||||
def getListOfExamples(relPath):
|
|
||||||
"""
|
|
||||||
returns list of .html filenames within a given path - excludes
|
|
||||||
index.html
|
|
||||||
"""
|
|
||||||
examples = os.listdir(relPath)
|
|
||||||
examples = [example for example in examples if
|
|
||||||
example.endswith('.html') and example != "index.html"]
|
|
||||||
return examples
|
|
||||||
|
|
||||||
|
|
||||||
def getExampleHtml(path):
|
|
||||||
"""
|
|
||||||
returns html of a specific example
|
|
||||||
"""
|
|
||||||
print '.',
|
|
||||||
f = open(path)
|
|
||||||
html = f.read()
|
|
||||||
f.close()
|
|
||||||
return html
|
|
||||||
|
|
||||||
|
|
||||||
def extractById(soup, tagId, value=None):
|
|
||||||
"""
|
|
||||||
returns full contents of a particular tag id
|
|
||||||
"""
|
|
||||||
beautifulTag = soup.find(id=tagId)
|
|
||||||
if beautifulTag:
|
|
||||||
if beautifulTag.contents:
|
|
||||||
value = str(beautifulTag.renderContents()).strip()
|
|
||||||
value = value.replace('\t', '')
|
|
||||||
value = value.replace('\n', '')
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def getRelatedClasses(html):
|
|
||||||
"""
|
|
||||||
parses the html, and returns a list of all OpenLayers Classes
|
|
||||||
used within (ie what parts of OL the javascript uses).
|
|
||||||
"""
|
|
||||||
rawstr = r'''(?P<class>ol\..*?)\('''
|
|
||||||
return re.findall(rawstr, html)
|
|
||||||
|
|
||||||
|
|
||||||
def parseHtml(html, ids):
|
|
||||||
"""
|
|
||||||
returns dictionary of items of interest
|
|
||||||
"""
|
|
||||||
soup = BeautifulSoup(html)
|
|
||||||
d = {}
|
|
||||||
for tagId in ids:
|
|
||||||
d[tagId] = extractById(soup, tagId)
|
|
||||||
#classes should eventually be parsed from docs - not automatically created.
|
|
||||||
classes = getRelatedClasses(html)
|
|
||||||
d['classes'] = classes
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
def getGitInfo(exampleDir, exampleName):
|
|
||||||
orig = os.getcwd()
|
|
||||||
os.chdir(exampleDir)
|
|
||||||
h = os.popen("git log -n 1 --pretty=format:'%an|%ai' " + exampleName)
|
|
||||||
os.chdir(orig)
|
|
||||||
log = h.read()
|
|
||||||
h.close()
|
|
||||||
d = {}
|
|
||||||
if log:
|
|
||||||
parts = log.split("|")
|
|
||||||
d["author"] = parts[0]
|
|
||||||
# compensate for spaces in git log time
|
|
||||||
td = parts[1].split(" ")
|
|
||||||
td.insert(1, "T")
|
|
||||||
d["date"] = "".join(td)
|
|
||||||
else:
|
|
||||||
d["author"] = ""
|
|
||||||
d["date"] = ""
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
def createFeed(examples):
|
|
||||||
doc = Document()
|
|
||||||
atomuri = "http://www.w3.org/2005/Atom"
|
|
||||||
feed = doc.createElementNS(atomuri, "feed")
|
|
||||||
feed.setAttribute("xmlns", atomuri)
|
|
||||||
title = doc.createElementNS(atomuri, "title")
|
|
||||||
title.appendChild(doc.createTextNode("OpenLayers Examples"))
|
|
||||||
feed.appendChild(title)
|
|
||||||
link = doc.createElementNS(atomuri, "link")
|
|
||||||
link.setAttribute("rel", "self")
|
|
||||||
link.setAttribute("href", feedPath + feedName)
|
|
||||||
|
|
||||||
modtime = time.strftime("%Y-%m-%dT%I:%M:%SZ", time.gmtime())
|
|
||||||
id = doc.createElementNS(atomuri, "id")
|
|
||||||
id.appendChild(doc.createTextNode(
|
|
||||||
"%s%s#%s" % (feedPath, feedName, modtime)))
|
|
||||||
feed.appendChild(id)
|
|
||||||
|
|
||||||
updated = doc.createElementNS(atomuri, "updated")
|
|
||||||
updated.appendChild(doc.createTextNode(modtime))
|
|
||||||
feed.appendChild(updated)
|
|
||||||
|
|
||||||
examples.sort(key=lambda x: x["modified"])
|
|
||||||
for example in sorted(examples, key=lambda x: x["modified"], reverse=True):
|
|
||||||
entry = doc.createElementNS(atomuri, "entry")
|
|
||||||
|
|
||||||
title = doc.createElementNS(atomuri, "title")
|
|
||||||
title.appendChild(doc.createTextNode(example["title"] or
|
|
||||||
example["example"]))
|
|
||||||
entry.appendChild(title)
|
|
||||||
|
|
||||||
tags = doc.createElementNS(atomuri, "tags")
|
|
||||||
tags.appendChild(doc.createTextNode(example["tags"] or
|
|
||||||
example["example"]))
|
|
||||||
entry.appendChild(tags)
|
|
||||||
|
|
||||||
link = doc.createElementNS(atomuri, "link")
|
|
||||||
link.setAttribute("href", "%s%s" % (feedPath, example["example"]))
|
|
||||||
entry.appendChild(link)
|
|
||||||
|
|
||||||
summary = doc.createElementNS(atomuri, "summary")
|
|
||||||
summary.appendChild(doc.createTextNode(example["shortdesc"] or
|
|
||||||
example["example"]))
|
|
||||||
entry.appendChild(summary)
|
|
||||||
|
|
||||||
updated = doc.createElementNS(atomuri, "updated")
|
|
||||||
updated.appendChild(doc.createTextNode(example["modified"]))
|
|
||||||
entry.appendChild(updated)
|
|
||||||
|
|
||||||
author = doc.createElementNS(atomuri, "author")
|
|
||||||
name = doc.createElementNS(atomuri, "name")
|
|
||||||
name.appendChild(doc.createTextNode(example["author"]))
|
|
||||||
author.appendChild(name)
|
|
||||||
entry.appendChild(author)
|
|
||||||
|
|
||||||
id = doc.createElementNS(atomuri, "id")
|
|
||||||
id.appendChild(doc.createTextNode("%s%s#%s" % (feedPath,
|
|
||||||
example["example"],
|
|
||||||
example["modified"])))
|
|
||||||
entry.appendChild(id)
|
|
||||||
|
|
||||||
feed.appendChild(entry)
|
|
||||||
|
|
||||||
doc.appendChild(feed)
|
|
||||||
return doc
|
|
||||||
|
|
||||||
|
|
||||||
def wordIndex(examples):
|
|
||||||
"""
|
|
||||||
Create an inverted index based on words in title and shortdesc. Keys are
|
|
||||||
lower cased words. Values are dictionaries with example index keys and
|
|
||||||
count values.
|
|
||||||
"""
|
|
||||||
index = {}
|
|
||||||
unword = re.compile("\\W+")
|
|
||||||
keys = ["shortdesc", "title", "tags"]
|
|
||||||
for i in range(len(examples)):
|
|
||||||
for key in keys:
|
|
||||||
text = examples[i][key]
|
|
||||||
if text:
|
|
||||||
words = unword.split(text)
|
|
||||||
for word in words:
|
|
||||||
if word:
|
|
||||||
word = word.lower()
|
|
||||||
if word in index:
|
|
||||||
if i in index[word]:
|
|
||||||
index[word][i] += 1
|
|
||||||
else:
|
|
||||||
index[word][i] = 1
|
|
||||||
else:
|
|
||||||
index[word] = {i: 1}
|
|
||||||
return index
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
|
|
||||||
if missing_deps:
|
|
||||||
print """This script requires json or simplejson and BeautifulSoup.
|
|
||||||
You don't have them. \n(%s)""" % E
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
if len(sys.argv) == 3:
|
|
||||||
inExampleDir = sys.argv[1]
|
|
||||||
outExampleDir = sys.argv[2]
|
|
||||||
else:
|
|
||||||
inExampleDir = "../examples"
|
|
||||||
outExampleDir = "../examples"
|
|
||||||
|
|
||||||
outFile = open(os.path.join(outExampleDir, "example-list.js"), "w")
|
|
||||||
|
|
||||||
print 'Reading examples from %s and writing out to %s' % (inExampleDir,
|
|
||||||
outFile.name)
|
|
||||||
|
|
||||||
exampleList = []
|
|
||||||
docIds = ['title', 'shortdesc', 'tags']
|
|
||||||
|
|
||||||
examples = getListOfExamples(inExampleDir)
|
|
||||||
|
|
||||||
modtime = time.strftime("%Y-%m-%dT%I:%M:%SZ", time.gmtime())
|
|
||||||
|
|
||||||
for example in examples:
|
|
||||||
path = os.path.join(inExampleDir, example)
|
|
||||||
html = getExampleHtml(path)
|
|
||||||
tagvalues = parseHtml(html, docIds)
|
|
||||||
tagvalues['example'] = example
|
|
||||||
# add in author/date info
|
|
||||||
d = getGitInfo(inExampleDir, example)
|
|
||||||
tagvalues["author"] = d["author"] or "anonymous"
|
|
||||||
tagvalues["modified"] = d["date"] or modtime
|
|
||||||
tagvalues['link'] = example
|
|
||||||
|
|
||||||
exampleList.append(tagvalues)
|
|
||||||
|
|
||||||
print
|
|
||||||
|
|
||||||
exampleList.sort(key=lambda x: x['example'].lower())
|
|
||||||
|
|
||||||
index = wordIndex(exampleList)
|
|
||||||
|
|
||||||
json = json.dumps({"examples": exampleList, "index": index})
|
|
||||||
# Give the json a global variable we can use in our js.
|
|
||||||
# This should be replaced or made optional.
|
|
||||||
json = 'var info=' + json + ';'
|
|
||||||
outFile.write(json)
|
|
||||||
outFile.close()
|
|
||||||
|
|
||||||
outFeedPath = os.path.join(outExampleDir, feedName)
|
|
||||||
print "writing feed to %s " % outFeedPath
|
|
||||||
atom = open(outFeedPath, 'w')
|
|
||||||
doc = createFeed(exampleList)
|
|
||||||
atom.write(doc.toxml())
|
|
||||||
atom.close()
|
|
||||||
|
|
||||||
print 'complete'
|
|
||||||
@@ -112,23 +112,24 @@ EXECUTABLES = [variables.CLEANCSS, variables.GIT, variables.GJSLINT,
|
|||||||
variables.JSDOC, variables.JSHINT, variables.PYTHON,
|
variables.JSDOC, variables.JSHINT, variables.PYTHON,
|
||||||
variables.PHANTOMJS]
|
variables.PHANTOMJS]
|
||||||
|
|
||||||
EXAMPLES = [path
|
EXAMPLES_SRC_ALL = [path for path in ifind('examples_src')]
|
||||||
for path in ifind('examples')
|
|
||||||
if path.endswith('.html')
|
|
||||||
if path != 'examples/index.html']
|
|
||||||
|
|
||||||
EXAMPLES_SRC = [path
|
EXAMPLES_SRC_HTML = [path
|
||||||
for path in ifind('examples')
|
for path in EXAMPLES_SRC_ALL
|
||||||
if path.endswith('.js')
|
if path.endswith('.html')
|
||||||
if not path.endswith('.combined.js')
|
if path != 'examples_src/index.html']
|
||||||
if path != 'examples/Jugl.js'
|
|
||||||
if path != 'examples/example-list.js']
|
EXAMPLES_SRC_JS = [example.replace('.html', '.js')
|
||||||
|
for example in EXAMPLES_SRC_HTML]
|
||||||
|
|
||||||
|
EXAMPLES_DEST_ALL = [path.replace('examples_src', 'examples')
|
||||||
|
for path in EXAMPLES_SRC_ALL]
|
||||||
|
|
||||||
EXAMPLES_JSON = ['build/' + example.replace('.html', '.json')
|
EXAMPLES_JSON = ['build/' + example.replace('.html', '.json')
|
||||||
for example in EXAMPLES]
|
for example in EXAMPLES_SRC_HTML]
|
||||||
|
|
||||||
EXAMPLES_COMBINED = ['build/' + example.replace('.html', '.combined.js')
|
EXAMPLES_COMBINED = ['build/' + example.replace('.html', '.combined.js')
|
||||||
for example in EXAMPLES]
|
for example in EXAMPLES_SRC_HTML]
|
||||||
|
|
||||||
GLSL_SRC = [path
|
GLSL_SRC = [path
|
||||||
for path in ifind('src')
|
for path in ifind('src')
|
||||||
@@ -261,17 +262,15 @@ virtual('build-examples', 'examples', 'build/examples/all.combined.js',
|
|||||||
EXAMPLES_COMBINED)
|
EXAMPLES_COMBINED)
|
||||||
|
|
||||||
|
|
||||||
virtual('examples', 'examples/example-list.xml', EXAMPLES_JSON)
|
virtual('examples', EXAMPLES_DEST_ALL)
|
||||||
|
|
||||||
|
|
||||||
@target('examples/example-list.xml', 'examples/example-list.js')
|
@rule(r'\Aexamples/(?P<filepath>.*)\Z')
|
||||||
def examples_examples_list_xml(t):
|
def examples_dest(name, match):
|
||||||
t.touch() # already generated by bin/exampleparser.py
|
def action(t):
|
||||||
|
t.run('node', 'tasks/build-examples.js')
|
||||||
|
dependencies = ['examples_src/%(filepath)s' % match.groupdict()]
|
||||||
@target('examples/example-list.js', 'bin/exampleparser.py', EXAMPLES)
|
return Target(name, action=action, dependencies=dependencies)
|
||||||
def examples_examples_list_js(t):
|
|
||||||
t.run('%(PYTHON)s', 'bin/exampleparser.py', 'examples', 'examples')
|
|
||||||
|
|
||||||
|
|
||||||
@target('build/examples/all.combined.js', 'build/examples/all.js',
|
@target('build/examples/all.combined.js', 'build/examples/all.js',
|
||||||
@@ -282,7 +281,7 @@ def build_examples_all_combined_js(t):
|
|||||||
report_sizes(t)
|
report_sizes(t)
|
||||||
|
|
||||||
|
|
||||||
@target('build/examples/all.js', EXAMPLES_SRC)
|
@target('build/examples/all.js', EXAMPLES_SRC_JS)
|
||||||
def build_examples_all_js(t):
|
def build_examples_all_js(t):
|
||||||
t.output('%(PYTHON)s', 'bin/combine-examples.py', t.dependencies)
|
t.output('%(PYTHON)s', 'bin/combine-examples.py', t.dependencies)
|
||||||
|
|
||||||
@@ -405,7 +404,7 @@ def examples_star_combined_js(name, match):
|
|||||||
return Target(name, action=action, dependencies=dependencies)
|
return Target(name, action=action, dependencies=dependencies)
|
||||||
|
|
||||||
|
|
||||||
@target('serve', 'examples', 'build/test_requires.js', 'build/test_rendering_requires.js',
|
@target('serve', 'build/test_requires.js', 'build/test_rendering_requires.js',
|
||||||
NPM_INSTALL)
|
NPM_INSTALL)
|
||||||
def serve(t):
|
def serve(t):
|
||||||
t.run('node', 'tasks/serve.js')
|
t.run('node', 'tasks/serve.js')
|
||||||
@@ -415,7 +414,7 @@ virtual('lint', 'build/lint-timestamp', 'build/check-requires-timestamp',
|
|||||||
'build/check-whitespace-timestamp', 'jshint')
|
'build/check-whitespace-timestamp', 'jshint')
|
||||||
|
|
||||||
|
|
||||||
@target('build/lint-timestamp', SRC, EXAMPLES_SRC, SPEC, SPEC_RENDERING,
|
@target('build/lint-timestamp', SRC, EXAMPLES_SRC_JS, SPEC, SPEC_RENDERING,
|
||||||
precious=True)
|
precious=True)
|
||||||
def build_lint_src_timestamp(t):
|
def build_lint_src_timestamp(t):
|
||||||
t.run('%(GJSLINT)s',
|
t.run('%(GJSLINT)s',
|
||||||
@@ -427,7 +426,7 @@ def build_lint_src_timestamp(t):
|
|||||||
|
|
||||||
virtual('jshint', 'build/jshint-timestamp')
|
virtual('jshint', 'build/jshint-timestamp')
|
||||||
|
|
||||||
@target('build/jshint-timestamp', SRC, EXAMPLES_SRC, SPEC, SPEC_RENDERING,
|
@target('build/jshint-timestamp', SRC, EXAMPLES_SRC_JS, SPEC, SPEC_RENDERING,
|
||||||
TASKS, NPM_INSTALL, precious=True)
|
TASKS, NPM_INSTALL, precious=True)
|
||||||
def build_jshint_timestamp(t):
|
def build_jshint_timestamp(t):
|
||||||
t.run(variables.JSHINT, '--verbose', t.newer(t.dependencies))
|
t.run(variables.JSHINT, '--verbose', t.newer(t.dependencies))
|
||||||
@@ -457,7 +456,7 @@ def _strip_comments(lines):
|
|||||||
yield lineno, line
|
yield lineno, line
|
||||||
|
|
||||||
|
|
||||||
@target('build/check-requires-timestamp', SRC, EXAMPLES_SRC, SHADER_SRC,
|
@target('build/check-requires-timestamp', SRC, EXAMPLES_SRC_JS, SHADER_SRC,
|
||||||
SPEC, SPEC_RENDERING)
|
SPEC, SPEC_RENDERING)
|
||||||
def build_check_requires_timestamp(t):
|
def build_check_requires_timestamp(t):
|
||||||
unused_count = 0
|
unused_count = 0
|
||||||
@@ -598,7 +597,7 @@ def build_check_requires_timestamp(t):
|
|||||||
t.touch()
|
t.touch()
|
||||||
|
|
||||||
|
|
||||||
@target('build/check-whitespace-timestamp', SRC, EXAMPLES_SRC,
|
@target('build/check-whitespace-timestamp', SRC, EXAMPLES_SRC_JS,
|
||||||
SPEC, SPEC_RENDERING, JSDOC_SRC, precious=True)
|
SPEC, SPEC_RENDERING, JSDOC_SRC, precious=True)
|
||||||
def build_check_whitespace_timestamp(t):
|
def build_check_whitespace_timestamp(t):
|
||||||
CR_RE = re.compile(r'\r')
|
CR_RE = re.compile(r'\r')
|
||||||
@@ -691,23 +690,16 @@ def host_examples(t):
|
|||||||
closure_lib_path = output('node', '-e',
|
closure_lib_path = output('node', '-e',
|
||||||
'process.stdout.write(require("closure-util").getLibraryPath())')
|
'process.stdout.write(require("closure-util").getLibraryPath())')
|
||||||
t.rm_rf(examples_dir)
|
t.rm_rf(examples_dir)
|
||||||
t.makedirs(examples_dir)
|
t.cp_r('examples', examples_dir)
|
||||||
|
for example in EXAMPLES_SRC_JS:
|
||||||
|
split_example_file(example, examples_dir % vars(variables))
|
||||||
|
t.cp('bin/loader_hosted_examples.js', examples_dir + '/loader.js')
|
||||||
t.rm_rf(build_dir)
|
t.rm_rf(build_dir)
|
||||||
t.makedirs(build_dir)
|
t.makedirs(build_dir)
|
||||||
t.rm_rf(css_dir)
|
t.rm_rf(css_dir)
|
||||||
t.makedirs(css_dir)
|
t.makedirs(css_dir)
|
||||||
t.cp(EXAMPLES, examples_dir)
|
|
||||||
for example in [path.replace('.html', '.js') for path in EXAMPLES]:
|
|
||||||
split_example_file(example, examples_dir % vars(variables))
|
|
||||||
for example in [path.replace('.html', '.css') for path in EXAMPLES]:
|
|
||||||
if os.path.isfile(example):
|
|
||||||
t.cp(example, examples_dir)
|
|
||||||
t.cp_r('examples/data', examples_dir + '/data')
|
|
||||||
t.cp('bin/loader_hosted_examples.js', examples_dir + '/loader.js')
|
|
||||||
t.cp('build/ol.js', 'build/ol-debug.js', build_dir)
|
t.cp('build/ol.js', 'build/ol-debug.js', build_dir)
|
||||||
t.cp('build/ol.css', css_dir)
|
t.cp('build/ol.css', css_dir)
|
||||||
t.cp('examples/index.html', 'examples/example-list.js',
|
|
||||||
'examples/example-list.xml', 'examples/Jugl.js', examples_dir)
|
|
||||||
t.rm_rf('build/hosted/%(BRANCH)s/closure-library')
|
t.rm_rf('build/hosted/%(BRANCH)s/closure-library')
|
||||||
t.cp_r(closure_lib_path, 'build/hosted/%(BRANCH)s/closure-library')
|
t.cp_r(closure_lib_path, 'build/hosted/%(BRANCH)s/closure-library')
|
||||||
t.rm_rf('build/hosted/%(BRANCH)s/ol')
|
t.rm_rf('build/hosted/%(BRANCH)s/ol')
|
||||||
@@ -726,8 +718,8 @@ def host_examples(t):
|
|||||||
|
|
||||||
@target('check-examples', 'host-examples', phony=True)
|
@target('check-examples', 'host-examples', phony=True)
|
||||||
def check_examples(t):
|
def check_examples(t):
|
||||||
examples = ['build/hosted/%(BRANCH)s/' + e
|
examples = ['build/hosted/%(BRANCH)s/' + e.replace('examples_src', 'examples')
|
||||||
for e in EXAMPLES
|
for e in EXAMPLES_SRC_HTML
|
||||||
if not open(e.replace('.html', '.js'), 'rU').readline().startswith('// NOCOMPILE')]
|
if not open(e.replace('.html', '.js'), 'rU').readline().startswith('// NOCOMPILE')]
|
||||||
all_examples = [e + '?mode=advanced' for e in examples]
|
all_examples = [e + '?mode=advanced' for e in examples]
|
||||||
# Run the examples checks in a pool of threads
|
# Run the examples checks in a pool of threads
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="text/javascript" src="Jugl.js"></script>
|
<script type="text/javascript" src="Jugl.js"></script>
|
||||||
<script type="text/javascript" src="example-list.js"></script>
|
<script type="text/javascript" src="index.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var template, target;
|
var template, target;
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -46,7 +46,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"clean-css": "2.2.16",
|
"clean-css": "2.2.16",
|
||||||
"coveralls": "2.11.2",
|
"coveralls": "2.11.2",
|
||||||
|
"debounce": "^1.0.0",
|
||||||
"expect.js": "0.3.1",
|
"expect.js": "0.3.1",
|
||||||
|
"gaze": "^0.5.1",
|
||||||
"istanbul": "0.3.13",
|
"istanbul": "0.3.13",
|
||||||
"jquery": "2.1.1",
|
"jquery": "2.1.1",
|
||||||
"jshint": "2.5.6",
|
"jshint": "2.5.6",
|
||||||
@@ -54,9 +56,9 @@
|
|||||||
"mocha-phantomjs": "3.5.1",
|
"mocha-phantomjs": "3.5.1",
|
||||||
"phantomjs": "1.9.10",
|
"phantomjs": "1.9.10",
|
||||||
"proj4": "2.3.3",
|
"proj4": "2.3.3",
|
||||||
|
"resemblejs": "1.2.0",
|
||||||
"sinon": "1.10.3",
|
"sinon": "1.10.3",
|
||||||
"slimerjs-edge": "0.10.0-pre-2",
|
"slimerjs-edge": "0.10.0-pre-2"
|
||||||
"resemblejs": "1.2.0"
|
|
||||||
},
|
},
|
||||||
"ext": [
|
"ext": [
|
||||||
"rbush"
|
"rbush"
|
||||||
|
|||||||
+79
-2
@@ -81,14 +81,88 @@ function augmentExamples(files, metalsmith, done) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inverted index of keywords from examples. Property names are
|
||||||
|
* lowercased words. Property values are objects mapping example index to word
|
||||||
|
* count.
|
||||||
|
* @param {Array.<Object>} exampleInfos Array of example info objects.
|
||||||
|
* @return {Object} Word index.
|
||||||
|
*/
|
||||||
|
function createWordIndex(exampleInfos) {
|
||||||
|
var index = {};
|
||||||
|
var keys = ['shortdesc', 'title', 'tags'];
|
||||||
|
exampleInfos.forEach(function(info, i) {
|
||||||
|
keys.forEach(function(key) {
|
||||||
|
var text = info[key];
|
||||||
|
var words = text ? text.split(/\W+/) : [];
|
||||||
|
words.forEach(function(word) {
|
||||||
|
if (word) {
|
||||||
|
word = word.toLowerCase();
|
||||||
|
var counts = index[word];
|
||||||
|
if (counts) {
|
||||||
|
if (index in counts) {
|
||||||
|
counts[i] += 1;
|
||||||
|
} else {
|
||||||
|
counts[i] = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
counts = {};
|
||||||
|
counts[i] = 1;
|
||||||
|
index[word] = counts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A plugin that generates the example index.js file. This file includes a
|
||||||
|
* list of example metadata objects and a word index used when searching for
|
||||||
|
* examples.
|
||||||
|
* @param {Object} files The file lookup provided by Metalsmith. Property names
|
||||||
|
* are file paths relative to the source directory. The file objects
|
||||||
|
* include any existing metadata (e.g. from YAML front-matter), the file
|
||||||
|
* contents, and stats.
|
||||||
|
* @param {Object} metalsmith The metalsmith instance the plugin is being used
|
||||||
|
* with.
|
||||||
|
* @param {function(Error)} done Called when done (with any error).
|
||||||
|
*/
|
||||||
|
function createIndex(files, metalsmith, done) {
|
||||||
|
setImmediate(done); // all remaining code is synchronous
|
||||||
|
var exampleInfos = [];
|
||||||
|
for (var filename in files) {
|
||||||
|
var example = files[filename];
|
||||||
|
if (markupRegEx.test(filename)) {
|
||||||
|
exampleInfos.push({
|
||||||
|
link: filename,
|
||||||
|
example: filename,
|
||||||
|
title: example.title,
|
||||||
|
shortdesc: example.shortdesc,
|
||||||
|
tags: example.tags
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var info = {
|
||||||
|
examples: exampleInfos,
|
||||||
|
index: createWordIndex(exampleInfos)
|
||||||
|
};
|
||||||
|
files['index.js'] = {
|
||||||
|
contents: new Buffer('var info = ' + JSON.stringify(info)),
|
||||||
|
mode: '0644'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function main(callback) {
|
function main(callback) {
|
||||||
new Metalsmith('.')
|
var smith = new Metalsmith('.')
|
||||||
.source(srcDir)
|
.source(srcDir)
|
||||||
.destination(destDir)
|
.destination(destDir)
|
||||||
.metadata({
|
.metadata({
|
||||||
olVersion: pkg.version
|
olVersion: pkg.version
|
||||||
})
|
})
|
||||||
.use(augmentExamples)
|
.use(augmentExamples)
|
||||||
|
.use(createIndex)
|
||||||
.use(templates({
|
.use(templates({
|
||||||
engine: 'handlebars',
|
engine: 'handlebars',
|
||||||
directory: templatesDir,
|
directory: templatesDir,
|
||||||
@@ -101,12 +175,15 @@ function main(callback) {
|
|||||||
.build(function(err) {
|
.build(function(err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
|
return smith;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
main(function(err) {
|
main(function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
process.stderr.write(err.message + '\n');
|
process.stderr.write(
|
||||||
|
'Building examples failed. See the full trace below.\n\n' +
|
||||||
|
err.stack + '\n');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
} else {
|
} else {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|||||||
+3
-8
@@ -1,17 +1,12 @@
|
|||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
|
||||||
var buildExt = require('./build-ext');
|
var buildExt = require('./build-ext');
|
||||||
var parseExamples = require('./parse-examples');
|
|
||||||
|
|
||||||
/**
|
buildExt(function(err) {
|
||||||
* Parse examples and build external modules.
|
|
||||||
*/
|
|
||||||
async.waterfall([
|
|
||||||
parseExamples,
|
|
||||||
buildExt
|
|
||||||
], function(err) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
process.stderr.write(err + '\n');
|
process.stderr.write(err + '\n');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
} else {
|
||||||
|
process.exit(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
var async = require('async');
|
|
||||||
var Parser = require('htmlparser2').Parser;
|
|
||||||
|
|
||||||
var buildExamples = require('./build-examples');
|
|
||||||
|
|
||||||
var exampleDir = path.join(__dirname, '..', 'examples');
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List all .html files in the example directory (excluding index.html).
|
|
||||||
* @param {function(Error, Array.<string>)} callback Called with any error or
|
|
||||||
* the list of paths to examples.
|
|
||||||
*/
|
|
||||||
function listExamples(callback) {
|
|
||||||
fs.readdir(exampleDir, function(err, items) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
var examplePaths = items.filter(function(item) {
|
|
||||||
return /\.html$/i.test(item) && item !== 'index.html';
|
|
||||||
}).map(function(item) {
|
|
||||||
return path.join(exampleDir, item);
|
|
||||||
});
|
|
||||||
callback(null, examplePaths);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse info from examples.
|
|
||||||
* @param {Array.<string>} examplePaths Paths to examples.
|
|
||||||
* @param {function(Error, Array.<Object>)} callback Called with any error or
|
|
||||||
* the list of example info objects.
|
|
||||||
*/
|
|
||||||
function parseExamples(examplePaths, callback) {
|
|
||||||
async.map(examplePaths, function(examplePath, next) {
|
|
||||||
fs.readFile(examplePath, function(err, data) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
var name = path.basename(examplePath);
|
|
||||||
var info = {
|
|
||||||
link: name,
|
|
||||||
example: name,
|
|
||||||
title: '',
|
|
||||||
shortdesc: '',
|
|
||||||
tags: ''
|
|
||||||
};
|
|
||||||
var key;
|
|
||||||
var openTag;
|
|
||||||
var parser = new Parser({
|
|
||||||
onopentag: function(tag, attrs) {
|
|
||||||
if (attrs.id in info) {
|
|
||||||
key = attrs.id;
|
|
||||||
openTag = tag;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ontext: function(text) {
|
|
||||||
if (key) {
|
|
||||||
info[key] += text.replace(/\n/g, '').trim() + ' ';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onclosetag: function(tag) {
|
|
||||||
if (tag === openTag) {
|
|
||||||
info[key] = info[key].trim();
|
|
||||||
key = undefined;
|
|
||||||
openTag = undefined;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onerror: function(err2) {
|
|
||||||
var message = 'Trouble parsing ' + examplePath + '\n' + err2.message;
|
|
||||||
next(new Error(message));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
parser.write(data.toString('utf8'));
|
|
||||||
parser.end();
|
|
||||||
next(null, info);
|
|
||||||
});
|
|
||||||
}, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an inverted index of keywords from examples. Property names are
|
|
||||||
* lowercased words. Property values are objects mapping example index to word
|
|
||||||
* count.
|
|
||||||
* @param {Array.<Object>} exampleInfos Array of example info objects.
|
|
||||||
* @return {Object} Word index.
|
|
||||||
*/
|
|
||||||
function createWordIndex(exampleInfos) {
|
|
||||||
var index = {};
|
|
||||||
var keys = ['shortdesc', 'title', 'tags'];
|
|
||||||
exampleInfos.forEach(function(info, i) {
|
|
||||||
keys.forEach(function(key) {
|
|
||||||
var text = info[key];
|
|
||||||
var words = text ? text.split(/\W+/) : [];
|
|
||||||
words.forEach(function(word) {
|
|
||||||
if (word) {
|
|
||||||
word = word.toLowerCase();
|
|
||||||
var counts = index[word];
|
|
||||||
if (counts) {
|
|
||||||
if (index in counts) {
|
|
||||||
counts[i] += 1;
|
|
||||||
} else {
|
|
||||||
counts[i] = 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
counts = {};
|
|
||||||
counts[i] = 1;
|
|
||||||
index[word] = counts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the example-list.js file with example info and word index.
|
|
||||||
* @param {Array.<Object>} exampleInfos Array of example info objects.
|
|
||||||
* @param {function(Error)} callback Called with any error.
|
|
||||||
*/
|
|
||||||
function writeExampleList(exampleInfos, callback) {
|
|
||||||
var info = {
|
|
||||||
examples: exampleInfos,
|
|
||||||
index: createWordIndex(exampleInfos)
|
|
||||||
};
|
|
||||||
var indexPath = path.join(exampleDir, 'example-list.js');
|
|
||||||
var str = 'var info = ' + JSON.stringify(info);
|
|
||||||
fs.writeFile(indexPath, str, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List examples, parse them, and write example list.
|
|
||||||
* @param {function(Error)} callback Called with any error.
|
|
||||||
*/
|
|
||||||
function main(callback) {
|
|
||||||
async.waterfall([
|
|
||||||
buildExamples,
|
|
||||||
listExamples,
|
|
||||||
parseExamples,
|
|
||||||
writeExampleList
|
|
||||||
], callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (require.main === module) {
|
|
||||||
main(function(err) {
|
|
||||||
if (err) {
|
|
||||||
process.stderr.write(err.message + '\n');
|
|
||||||
process.exit(1);
|
|
||||||
} else {
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = main;
|
|
||||||
+2
-5
@@ -107,13 +107,10 @@ This is useful for projects that use the Closure Compiler to build, but want to
|
|||||||
Called internally to parse the library for annotations and write out a `build/info.json` file.
|
Called internally to parse the library for annotations and write out a `build/info.json` file.
|
||||||
|
|
||||||
|
|
||||||
## `parse-examples.js`
|
|
||||||
|
|
||||||
Called after install to generate an example index. After new examples are added, run `node tasks/parse-examples.js` to regenerate the example index.
|
|
||||||
|
|
||||||
## `build-examples.js`
|
## `build-examples.js`
|
||||||
|
|
||||||
Called internally by `parse-examples.js` to build the examples from templates.
|
Builds examples and the example index.
|
||||||
|
|
||||||
|
|
||||||
## `serve.js`
|
## `serve.js`
|
||||||
|
|
||||||
|
|||||||
+46
-14
@@ -7,9 +7,12 @@
|
|||||||
var path = require('path');
|
var path = require('path');
|
||||||
var url = require('url');
|
var url = require('url');
|
||||||
|
|
||||||
|
var Gaze = require('gaze').Gaze;
|
||||||
var closure = require('closure-util');
|
var closure = require('closure-util');
|
||||||
|
var debounce = require('debounce');
|
||||||
var nomnom = require('nomnom');
|
var nomnom = require('nomnom');
|
||||||
|
|
||||||
|
var buildExamples = require('./build-examples');
|
||||||
var log = closure.log;
|
var log = closure.log;
|
||||||
|
|
||||||
|
|
||||||
@@ -68,6 +71,26 @@ var createServer = exports.createServer = function(callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the examples and exit on any error.
|
||||||
|
* @param {Function=} opt_callback Called when done building examples.
|
||||||
|
*/
|
||||||
|
function buildExamplesOrFatal(opt_callback) {
|
||||||
|
log.info('serve', 'Building examples.');
|
||||||
|
buildExamples(function(err) {
|
||||||
|
if (err) {
|
||||||
|
log.error('serve', 'Building examples failed.');
|
||||||
|
log.error('serve', err.message);
|
||||||
|
log.error('serve', 'Use "verbose" logging to see the full stack trace.');
|
||||||
|
log.verbose('serve', err.stack);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
log.verbose('serve', 'Done building examples.');
|
||||||
|
if (opt_callback) {
|
||||||
|
opt_callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If running this module directly start the server.
|
* If running this module directly start the server.
|
||||||
@@ -92,21 +115,30 @@ if (require.main === module) {
|
|||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
log.level = options.loglevel;
|
log.level = options.loglevel;
|
||||||
|
|
||||||
log.info('serve', 'Parsing dependencies ...');
|
buildExamplesOrFatal(function() {
|
||||||
createServer(function(err, server) {
|
log.info('serve', 'Parsing dependencies.');
|
||||||
if (err) {
|
createServer(function(err, server) {
|
||||||
log.error('serve', 'Parsing failed');
|
if (err) {
|
||||||
log.error('serve', err.message);
|
log.error('serve', 'Parsing failed');
|
||||||
process.exit(1);
|
log.error('serve', err.message);
|
||||||
}
|
process.exit(1);
|
||||||
server.listen(options.port, function() {
|
}
|
||||||
log.info('serve', 'Listening on http://localhost:' +
|
server.listen(options.port, function() {
|
||||||
options.port + '/ (Ctrl+C to stop)');
|
log.info('serve', 'Listening on http://localhost:' +
|
||||||
});
|
options.port + '/ (Ctrl+C to stop)');
|
||||||
server.on('error', function(err) {
|
});
|
||||||
log.error('serve', 'Server failed to start: ' + err.message);
|
server.on('error', function(err) {
|
||||||
process.exit(1);
|
log.error('serve', 'Server failed to start: ' + err.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var gaze = new Gaze('examples_src/**/*');
|
||||||
|
var debouncedBuild = debounce(buildExamplesOrFatal, 250);
|
||||||
|
gaze.on('all', function(event, filepath) {
|
||||||
|
log.verbose('serve', 'Watch event: ' + event + ' ' + filepath);
|
||||||
|
debouncedBuild();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user