move scripts into subdir
authorDan Vanderkam <danvdk@gmail.com>
Sun, 22 Mar 2015 14:31:00 +0000 (10:31 -0400)
committerDan Vanderkam <danvdk@gmail.com>
Sun, 22 Mar 2015 21:46:48 +0000 (17:46 -0400)
12 files changed:
check-combined-unaffected.sh [deleted file]
generate-documentation.py [deleted file]
generate-download.py [deleted file]
generate-jsdoc.sh [deleted file]
push-to-web.sh [deleted file]
release.sh [deleted file]
scripts/generate-documentation.py [new file with mode: 0755]
scripts/generate-download.py [new file with mode: 0755]
scripts/generate-jsdoc.sh [new file with mode: 0755]
scripts/push-to-web.sh [new file with mode: 0755]
scripts/release.sh [new file with mode: 0755]
test.sh [deleted file]

diff --git a/check-combined-unaffected.sh b/check-combined-unaffected.sh
deleted file mode 100755 (executable)
index de1721e..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-# Ensures that dygraph-combined.js is unaffected.
-# Helpful for pull requests, where this is a common mistake.
-
-grep 'var' dygraph-combined.js > /dev/null
-if [ $? -eq 0 ]; then
-  echo 'Please revert changes to dygraph-combined.js' >&2
-  echo 'You can do this by running:  ' >& 2
-  echo '' >& 2
-  echo '    git checkout dygraph-combined.js' >&2
-  echo '' >& 2
-  exit 1
-fi
-
-exit 0
diff --git a/generate-documentation.py b/generate-documentation.py
deleted file mode 100755 (executable)
index 31781b9..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-#!/usr/bin/env python
-
-# Generate docs/options.html
-
-import glob
-import json
-import os
-import re
-import sys
-
-# Set this to the path to a test file to get debug output for just that test
-# file. Can be helpful to figure out why a test is not being shown for a
-# particular option.
-debug_tests = []  # [ 'tests/zoom.html' ]
-
-# Pull options reference JSON out of dygraph.js
-js = ''
-in_json = False
-for line in file('dygraph-options-reference.js'):
-  if '<JSON>' in line:
-    in_json = True
-  elif '</JSON>' in line:
-    in_json = False
-  elif in_json:
-    js += line
-
-# TODO(danvk): better errors here.
-assert js
-docs = json.loads(js)
-
-# Go through the tests and find uses of each option.
-for opt in docs:
-  docs[opt]['tests'] = []
-  docs[opt]['gallery'] = []
-
-# This is helpful for differentiating uses of options like 'width' and 'height'
-# from appearances of identically-named options in CSS.
-def find_braces(txt):
-  """Really primitive method to find text inside of {..} braces.
-  Doesn't work if there's an unmatched brace in a string, e.g. '{'. """
-  out = ''
-  level = 0
-  for char in txt:
-    if char == '{':
-      level += 1
-    if level >= 1:
-      out += char
-    if char == '}':
-      level -= 1
-  return out
-
-def search_files(type, files):
-  # Find text followed by a colon. These won't all be options, but those that
-  # have the same name as a Dygraph option probably will be.
-  prop_re = re.compile(r'\b([a-zA-Z0-9]+) *:')
-  for test_file in files:
-    if os.path.isfile(test_file): # Basically skips directories
-      text = file(test_file).read()
-
-      # Hack for slipping past gallery demos that have title in their attributes
-      # so they don't appear as reasons for the demo to have 'title' options.
-      if type == "gallery":
-        idx = text.find("function(")
-        if idx >= 0:
-          text = text[idx:]
-      braced_html = find_braces(text)
-      if debug_tests:
-        print braced_html
-
-      ms = re.findall(prop_re, braced_html)
-      for opt in ms:
-        if debug_tests: print '\n'.join(ms)
-        if opt in docs and test_file not in docs[opt][type]:
-          docs[opt][type].append(test_file)
-
-search_files("tests", glob.glob("tests/*.html"))
-search_files("gallery", glob.glob("gallery/*.js")) #TODO add grep "Gallery.register\("
-
-if debug_tests: sys.exit(0)
-
-# Extract a labels list.
-labels = []
-for _, opt in docs.iteritems():
-  for label in opt['labels']:
-    if label not in labels:
-      labels.append(label)
-
-print """
-<!--#include virtual="header.html" -->
-
-<!--
-  DO NOT EDIT THIS FILE!
-
-  This file is generated by generate-documentation.py.
--->
-
-<link rel=stylesheet href="options.css" />
-
-"""
-
-print """
-<div class="col-lg-3">
-<div class="dygraphs-side-nav affix-top" data-spy="affix" data-offset-top="0">
-<ul class='nav'>
-  <li><a href="#usage">Usage</a>
-"""
-for label in sorted(labels):
-  print '  <li><a href="#%s">%s</a>\n' % (label, label)
-print '</ul></div></div>\n\n'
-
-print """
-<div id='content' class='col-lg-9'>
-<h2>Options Reference</h2>
-<p>Dygraphs tries to do a good job of displaying your data without any further configuration. But inevitably, you're going to want to tinker. Dygraphs provides a rich set of options for configuring its display and behavior.</p>
-
-<a name="usage"></a><h3>Usage</h3>
-<p>You specify options in the third parameter to the dygraphs constructor:</p>
-<pre>g = new Dygraph(div,
-                data,
-                {
-                  option1: value1,
-                  option2: value2,
-                  ...
-                });
-</pre>
-
-<p>After you've created a Dygraph, you can change an option by calling the <code>updateOptions</code> method:</p>
-<pre>g.updateOptions({
-                  new_option1: value1,
-                  new_option2: value2
-                });
-</pre>
-
-<p>Some options can be set on a per-axis and per-series basis. See the docs on <a href="per-axis.html">per-axis and per-series options</a> to learn how to do this. The options which may be set in this way are marked as such on this page.</p>
-
-<p>For options which are functions (e.g. callbacks and formatters), the value of <code>this</code> is set to the Dygraph object.</p>
-
-<p>And, without further ado, here's the complete list of options:</p>
-"""
-
-def test_name(f):
-  """Takes 'tests/demo.html' -> 'demo'"""
-  return f.replace('tests/', '').replace('.html', '')
-
-def gallery_name(f):
-  """Takes 'gallery/demo.js' -> 'demo'"""
-  return f.replace('gallery/', '').replace('.js', '')
-
-def urlify_gallery(f):
-  """Takes 'gallery/demo.js' -> 'demo'"""
-  return f.replace('gallery/', 'gallery/#g/').replace('.js', '')
-
-
-for label in sorted(labels):
-  print '<a name="%s"></a><h3>%s</h3>\n' % (label, label)
-
-  for opt_name in sorted(docs.keys()):
-    opt = docs[opt_name]
-    if label not in opt['labels']: continue
-    tests = opt['tests']
-    if not tests:
-      examples_html = '<font color=red>NONE</font>'
-    else:
-      examples_html = ' '.join(
-        '<a href="%s">%s</a>' % (f, test_name(f)) for f in tests)
-
-    gallery = opt['gallery']
-    if not gallery:
-      gallery_html = '<font color=red>NONE</font>'
-    else:
-      gallery_html = ' '.join(
-        '<a href="%s">%s</a>' % (urlify_gallery(f), gallery_name(f)) for f in gallery)
-
-    if 'parameters' in opt:
-      parameters = opt['parameters']
-      parameters_html = '\n'.join("<i>%s</i>: %s<br/>" % (p[0], p[1]) for p in parameters)
-      parameters_html = "\n  <div class='parameters'>\n%s</div>" % (parameters_html);
-    else:
-      parameters_html = ''
-
-    if not opt['type']: opt['type'] = '(missing)'
-    if not opt['default']: opt['default'] = '(missing)'
-    if not opt['description']: opt['description'] = '(missing)'
-
-    print """
-  <div class='option'><a name="%(name)s"></a><b>%(name)s</b>
-  <a class="link" href="#%(name)s">#</a>
-  <br/>
-  <p>%(desc)s</p>
-  <i>Type: %(type)s</i><br/>%(parameters)s
-  <i>Default: %(default)s</i></p>
-  Gallery Samples: %(gallery_html)s<br/>
-  Other Examples: %(examples_html)s<br/>
-  <br/></div>
-  """ % { 'name': opt_name,
-          'type': opt['type'],
-          'parameters': parameters_html,
-          'default': opt['default'],
-          'desc': opt['description'],
-          'examples_html': examples_html,
-          'gallery_html': gallery_html}
-
-
-print """
-<a name="point_properties"></a><h3>Point Properties</h3>
-Some callbacks take a point argument. Its properties are:<br/>
-<ul>
-<li>xval/yval: The data coordinates of the point (with dates/times as millis since epoch)</li>
-<li>canvasx/canvasy: The canvas coordinates at which the point is drawn.</li>
-<li>name: The name of the data series to which the point belongs</li>
-<li>idx: The row number of the point in the data set</li>
-</ul>
-</div> <!-- #content -->
-
-<!--#include virtual="footer.html" -->
-"""
-
-# This page was super-helpful:
-# http://jsbeautifier.org/
diff --git a/generate-download.py b/generate-download.py
deleted file mode 100755 (executable)
index 2d2dd48..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/usr/bin/env python
-
-# Generates docs/download.html
-# Run:
-# ./generate-download.py > docs/download.html
-
-import json
-
-releases = json.load(file('releases.json'))
-
-def file_links(release):
-  v = release['version']
-  return ['<a href="%(v)s/%(f)s">%(f)s</a>' % {
-    'f': f, 'v': v} for f in release['files']]
-
-
-# Validation of releases.json
-for idx, release in enumerate(releases):
-  if idx == 0: continue
-  assert 'version' in release, 'Release missing version: %s' % release
-  assert 'files' in release, 'Release missing files: %s' % release
-  assert release['version'] < releases[idx - 1]['version'], (
-      'Releases should be in reverse chronological order in releases.json')
-
-current_html = '<p>' + ('</p><p>'.join(file_links(releases[0]))) + '</p>'
-
-
-previous_lis = []
-for release in releases[1:]:
-  previous_lis.append('<li>%(v)s: %(files)s (<a href="%(v)s/">%(v)s docs</a>)' % {
-      'v': release['version'],
-      'files': ', '.join(file_links(release))
-    })
-
-
-print '''
-<!--#include virtual="header.html" -->
-
-<!--
-  DO NOT EDIT THIS FILE!
-
-  This file is generated by generate-download.py.
--->
-
-<script src="modernizr.custom.18445.js"></script>
-<p>The current version of dygraphs is <b>%(version)s</b>. Most users will want to download minified files for this version:</p>
-
-<div id="current-release" class="panel">
-%(current_html)s
-</div>
-
-<p>There's a hosted version of dygraphs on <a href="https://cdnjs.com/libraries/dygraph">cdnjs.com</a>:</p>
-
-<pre>&lt;script src="//cdnjs.cloudflare.com/ajax/libs/dygraph/%(version)s/dygraph-combined.js"&gt;&lt;/script&gt;</pre>
-
-<p>You can install dygraphs using <a href="https://www.npmjs.org/package/dygraphs">NPM</a> or <a href="http://bower.io/search/?q=dygraphs">Bower</a>.</p>
-
-<p>To install using NPM:</p>
-<pre>$ npm install dygraphs
-# dygraphs is now in node_modules/dygraphs/dygraph-combined.js</pre>
-
-<p>To install using bower:</p>
-<pre>$ bower install dygraphs
-# dygraphs is now in bower_components/dygraphs/dygraph-combined.js</pre>
-
-<p>Most distributions include a source map. For non-concatenated JS, see <a href="https://github.com/danvk/dygraphs/blob/master/dygraph-dev.js">dygraph-dev.js</a> on <a href="https://github.com/danvk/dygraphs/">github</a>.</a>
-
-<p>To generate your own minified JS, run:</p>
-
-<pre>git clone https://github.com/danvk/dygraphs.git
-./generate-combined.sh
-</pre>
-
-<p>This will create a dygraph.min.js file in the dygraphs directory.</p>
-
-<p>You may also download files for previously-released versions:</p>
-
-<ul>
-%(previous_lis)s
-</ul>
-
-<p>See <a href="/versions.html">Version History</a> for more information on each release.</p>
-
-
-<!--#include virtual="footer.html" -->
-''' % {
-    'version': releases[0]['version'],
-    'current_html': current_html,
-    'previous_lis': '\n'.join(previous_lis)
-    }
diff --git a/generate-jsdoc.sh b/generate-jsdoc.sh
deleted file mode 100755 (executable)
index 6512fb0..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# Generates JSDoc in the /jsdoc dir. Clears any existing jsdoc there.
-
-rm -rf jsdoc
-echo Generating JSDoc...
-java -jar jsdoc-toolkit/jsrun.jar \
-  jsdoc-toolkit/app/run.js \
-  -d=jsdoc -t=jsdoc-toolkit/templates/jsdoc \
-  dygraph.js \
-| tee /tmp/dygraphs-jsdocerrors.txt
-
-if [ -s /tmp/dygraphs-jsdocerrors.txt ]; then
-  echo Please fix any jsdoc errors/warnings before sending patches.
-fi
-
-chmod -R a+rX jsdoc
-
-echo Done
diff --git a/push-to-web.sh b/push-to-web.sh
deleted file mode 100755 (executable)
index 0ea59ac..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/bash
-# This script generates the combined JS file, pushes all content to a web site
-# and then reverts the combined file.
-
-if [ "$1" == "" ] ; then
-  echo "usage: $0 destination"
-  exit 1
-fi
-
-set -x
-site=$1
-
-# Produce dygraph-combined.js and dygraph-combined-dev.js
-./generate-combined.sh
-./generate-combined.sh cat-dev > dygraph-combined-dev.js
-
-# Generate documentation.
-./generate-documentation.py > docs/options.html
-chmod a+r docs/options.html
-if [ -s docs/options.html ] ; then
-  ./generate-jsdoc.sh
-  ./generate-download.py > docs/download.html
-
-  temp_dir=$(mktemp -d /tmp/dygraphs-docs.XXXX)
-  cd docs
-  ./ssi_expander.py $temp_dir
-  cd ..
-
-  # Make sure everything will be readable on the web.
-  # This is like "chmod -R a+rX", but excludes the .git directory.
-  find . -path ./.git -prune -o -print | xargs chmod a+rX
-
-  # Copy everything to the site.
-  rsync -avzr gallery common tests jsdoc experimental plugins datahandler polyfills extras $site \
-  && \
-  rsync -avzr --copy-links dashed-canvas.js dygraph*.js gadget.xml thumbnail.png screenshot.png $temp_dir/* $site/
-else
-  echo "generate-documentation.py failed"
-fi
-
-# Revert changes to dygraph-combined.js and docs.
-make clean-combined-test
-rm dygraph-combined-dev.js
-git checkout docs/download.html
-rm docs/options.html
-rm -rf $temp_dir
diff --git a/release.sh b/release.sh
deleted file mode 100755 (executable)
index 21c0c34..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/bin/bash
-# This script "releases" a version of dygraphs.
-
-if [ $# -ne 1 ]; then
-  echo "Usage: $0 X.Y.Z" >&2
-  exit 1
-fi
-
-VERSION=$1
-echo $VERSION | egrep '\d+\.\d+\.\d+' > /dev/null
-if [ $? -ne 0 ]; then
-  echo "Version must be of the form 1.2.3 (got '$VERSION')" >&2
-  exit 1
-fi
-
-# Make sure this is being run from a release branch with the correct name.
-branch=$(git rev-parse --abbrev-ref HEAD)
-if [ $branch != "release-$VERSION" ]; then
-  echo "Must be on a branch named 'release-$VERSION' (found '$branch')" >&2
-  exit 1
-fi
-
-git status | grep 'working directory clean' > /dev/null
-if [ $? -ne 0 ]; then
-  echo "Must release with a clean working directory. Commit your changes." >&2
-  exit 1
-fi
-
-grep "$VERSION" package.json
-if [ $? -ne 0 ]; then
-  echo "Version in package.json doesn't match command line argument." >&2
-  exit 1
-fi
-
-grep "v$VERSION" bower.json
-if [ $? -ne 0 ]; then
-  echo "Version in bower.json doesn't match command line argument." >&2
-  exit 1
-fi
-
-grep "$VERSION" releases.json
-if [ $? -ne 0 ]; then
-  echo "Version $VERSION does not appear in releases.json." >&2
-  exit 1
-fi
-
-rm dygraph-combined.js  # changes to this will make the tests fail.
-make lint test test-combined
-if [ $? -ne 0 ]; then
-  echo "Tests failed. Won't release!" >&2
-  exit 1
-fi
-git reset --hard  # make test-combined deletes the source map
-
-# Push a permanent copy of documentation & generated files to a versioned copy
-# of the site. This is where the downloadable files are generated.
-# TODO(danvk): make sure this actually generates the downloadable files!
-echo "Pushing docs and generated files to dygraphs.com/$VERSION"
-./push-to-web.sh dygraphs.com:dygraphs.com/$VERSION
-if [ $? -ne 0 ]; then
-  echo "Push to web failed" >&2
-  exit 1
-fi
-
-# Everything is good.
-# Switch to the "releases" branch, merge this change and tag it.
-echo "Switching branches to do the release."
-git checkout releases
-git merge --no-ff $branch
-
-COMMIT=$(git rev-parse HEAD)
-echo "Tagging commit $COMMIT as version $VERSION"
-git tag -a "v$VERSION" -m "Release of version $VERSION"
-git push --tags
-
-echo "Release was successful!"
-echo "Pushing the new version to dygraphs.com..."
-./push-to-web.sh dygraphs.com:dygraphs.com
-
-echo "Success!\n"
-echo "Don't forget to merge changes on this branch back into master:"
-echo "git merge --no-ff $branch"
-
-# Discourage users from working on the "releases" branch.
-git checkout master
diff --git a/scripts/generate-documentation.py b/scripts/generate-documentation.py
new file mode 100755 (executable)
index 0000000..31781b9
--- /dev/null
@@ -0,0 +1,219 @@
+#!/usr/bin/env python
+
+# Generate docs/options.html
+
+import glob
+import json
+import os
+import re
+import sys
+
+# Set this to the path to a test file to get debug output for just that test
+# file. Can be helpful to figure out why a test is not being shown for a
+# particular option.
+debug_tests = []  # [ 'tests/zoom.html' ]
+
+# Pull options reference JSON out of dygraph.js
+js = ''
+in_json = False
+for line in file('dygraph-options-reference.js'):
+  if '<JSON>' in line:
+    in_json = True
+  elif '</JSON>' in line:
+    in_json = False
+  elif in_json:
+    js += line
+
+# TODO(danvk): better errors here.
+assert js
+docs = json.loads(js)
+
+# Go through the tests and find uses of each option.
+for opt in docs:
+  docs[opt]['tests'] = []
+  docs[opt]['gallery'] = []
+
+# This is helpful for differentiating uses of options like 'width' and 'height'
+# from appearances of identically-named options in CSS.
+def find_braces(txt):
+  """Really primitive method to find text inside of {..} braces.
+  Doesn't work if there's an unmatched brace in a string, e.g. '{'. """
+  out = ''
+  level = 0
+  for char in txt:
+    if char == '{':
+      level += 1
+    if level >= 1:
+      out += char
+    if char == '}':
+      level -= 1
+  return out
+
+def search_files(type, files):
+  # Find text followed by a colon. These won't all be options, but those that
+  # have the same name as a Dygraph option probably will be.
+  prop_re = re.compile(r'\b([a-zA-Z0-9]+) *:')
+  for test_file in files:
+    if os.path.isfile(test_file): # Basically skips directories
+      text = file(test_file).read()
+
+      # Hack for slipping past gallery demos that have title in their attributes
+      # so they don't appear as reasons for the demo to have 'title' options.
+      if type == "gallery":
+        idx = text.find("function(")
+        if idx >= 0:
+          text = text[idx:]
+      braced_html = find_braces(text)
+      if debug_tests:
+        print braced_html
+
+      ms = re.findall(prop_re, braced_html)
+      for opt in ms:
+        if debug_tests: print '\n'.join(ms)
+        if opt in docs and test_file not in docs[opt][type]:
+          docs[opt][type].append(test_file)
+
+search_files("tests", glob.glob("tests/*.html"))
+search_files("gallery", glob.glob("gallery/*.js")) #TODO add grep "Gallery.register\("
+
+if debug_tests: sys.exit(0)
+
+# Extract a labels list.
+labels = []
+for _, opt in docs.iteritems():
+  for label in opt['labels']:
+    if label not in labels:
+      labels.append(label)
+
+print """
+<!--#include virtual="header.html" -->
+
+<!--
+  DO NOT EDIT THIS FILE!
+
+  This file is generated by generate-documentation.py.
+-->
+
+<link rel=stylesheet href="options.css" />
+
+"""
+
+print """
+<div class="col-lg-3">
+<div class="dygraphs-side-nav affix-top" data-spy="affix" data-offset-top="0">
+<ul class='nav'>
+  <li><a href="#usage">Usage</a>
+"""
+for label in sorted(labels):
+  print '  <li><a href="#%s">%s</a>\n' % (label, label)
+print '</ul></div></div>\n\n'
+
+print """
+<div id='content' class='col-lg-9'>
+<h2>Options Reference</h2>
+<p>Dygraphs tries to do a good job of displaying your data without any further configuration. But inevitably, you're going to want to tinker. Dygraphs provides a rich set of options for configuring its display and behavior.</p>
+
+<a name="usage"></a><h3>Usage</h3>
+<p>You specify options in the third parameter to the dygraphs constructor:</p>
+<pre>g = new Dygraph(div,
+                data,
+                {
+                  option1: value1,
+                  option2: value2,
+                  ...
+                });
+</pre>
+
+<p>After you've created a Dygraph, you can change an option by calling the <code>updateOptions</code> method:</p>
+<pre>g.updateOptions({
+                  new_option1: value1,
+                  new_option2: value2
+                });
+</pre>
+
+<p>Some options can be set on a per-axis and per-series basis. See the docs on <a href="per-axis.html">per-axis and per-series options</a> to learn how to do this. The options which may be set in this way are marked as such on this page.</p>
+
+<p>For options which are functions (e.g. callbacks and formatters), the value of <code>this</code> is set to the Dygraph object.</p>
+
+<p>And, without further ado, here's the complete list of options:</p>
+"""
+
+def test_name(f):
+  """Takes 'tests/demo.html' -> 'demo'"""
+  return f.replace('tests/', '').replace('.html', '')
+
+def gallery_name(f):
+  """Takes 'gallery/demo.js' -> 'demo'"""
+  return f.replace('gallery/', '').replace('.js', '')
+
+def urlify_gallery(f):
+  """Takes 'gallery/demo.js' -> 'demo'"""
+  return f.replace('gallery/', 'gallery/#g/').replace('.js', '')
+
+
+for label in sorted(labels):
+  print '<a name="%s"></a><h3>%s</h3>\n' % (label, label)
+
+  for opt_name in sorted(docs.keys()):
+    opt = docs[opt_name]
+    if label not in opt['labels']: continue
+    tests = opt['tests']
+    if not tests:
+      examples_html = '<font color=red>NONE</font>'
+    else:
+      examples_html = ' '.join(
+        '<a href="%s">%s</a>' % (f, test_name(f)) for f in tests)
+
+    gallery = opt['gallery']
+    if not gallery:
+      gallery_html = '<font color=red>NONE</font>'
+    else:
+      gallery_html = ' '.join(
+        '<a href="%s">%s</a>' % (urlify_gallery(f), gallery_name(f)) for f in gallery)
+
+    if 'parameters' in opt:
+      parameters = opt['parameters']
+      parameters_html = '\n'.join("<i>%s</i>: %s<br/>" % (p[0], p[1]) for p in parameters)
+      parameters_html = "\n  <div class='parameters'>\n%s</div>" % (parameters_html);
+    else:
+      parameters_html = ''
+
+    if not opt['type']: opt['type'] = '(missing)'
+    if not opt['default']: opt['default'] = '(missing)'
+    if not opt['description']: opt['description'] = '(missing)'
+
+    print """
+  <div class='option'><a name="%(name)s"></a><b>%(name)s</b>
+  <a class="link" href="#%(name)s">#</a>
+  <br/>
+  <p>%(desc)s</p>
+  <i>Type: %(type)s</i><br/>%(parameters)s
+  <i>Default: %(default)s</i></p>
+  Gallery Samples: %(gallery_html)s<br/>
+  Other Examples: %(examples_html)s<br/>
+  <br/></div>
+  """ % { 'name': opt_name,
+          'type': opt['type'],
+          'parameters': parameters_html,
+          'default': opt['default'],
+          'desc': opt['description'],
+          'examples_html': examples_html,
+          'gallery_html': gallery_html}
+
+
+print """
+<a name="point_properties"></a><h3>Point Properties</h3>
+Some callbacks take a point argument. Its properties are:<br/>
+<ul>
+<li>xval/yval: The data coordinates of the point (with dates/times as millis since epoch)</li>
+<li>canvasx/canvasy: The canvas coordinates at which the point is drawn.</li>
+<li>name: The name of the data series to which the point belongs</li>
+<li>idx: The row number of the point in the data set</li>
+</ul>
+</div> <!-- #content -->
+
+<!--#include virtual="footer.html" -->
+"""
+
+# This page was super-helpful:
+# http://jsbeautifier.org/
diff --git a/scripts/generate-download.py b/scripts/generate-download.py
new file mode 100755 (executable)
index 0000000..2d2dd48
--- /dev/null
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+
+# Generates docs/download.html
+# Run:
+# ./generate-download.py > docs/download.html
+
+import json
+
+releases = json.load(file('releases.json'))
+
+def file_links(release):
+  v = release['version']
+  return ['<a href="%(v)s/%(f)s">%(f)s</a>' % {
+    'f': f, 'v': v} for f in release['files']]
+
+
+# Validation of releases.json
+for idx, release in enumerate(releases):
+  if idx == 0: continue
+  assert 'version' in release, 'Release missing version: %s' % release
+  assert 'files' in release, 'Release missing files: %s' % release
+  assert release['version'] < releases[idx - 1]['version'], (
+      'Releases should be in reverse chronological order in releases.json')
+
+current_html = '<p>' + ('</p><p>'.join(file_links(releases[0]))) + '</p>'
+
+
+previous_lis = []
+for release in releases[1:]:
+  previous_lis.append('<li>%(v)s: %(files)s (<a href="%(v)s/">%(v)s docs</a>)' % {
+      'v': release['version'],
+      'files': ', '.join(file_links(release))
+    })
+
+
+print '''
+<!--#include virtual="header.html" -->
+
+<!--
+  DO NOT EDIT THIS FILE!
+
+  This file is generated by generate-download.py.
+-->
+
+<script src="modernizr.custom.18445.js"></script>
+<p>The current version of dygraphs is <b>%(version)s</b>. Most users will want to download minified files for this version:</p>
+
+<div id="current-release" class="panel">
+%(current_html)s
+</div>
+
+<p>There's a hosted version of dygraphs on <a href="https://cdnjs.com/libraries/dygraph">cdnjs.com</a>:</p>
+
+<pre>&lt;script src="//cdnjs.cloudflare.com/ajax/libs/dygraph/%(version)s/dygraph-combined.js"&gt;&lt;/script&gt;</pre>
+
+<p>You can install dygraphs using <a href="https://www.npmjs.org/package/dygraphs">NPM</a> or <a href="http://bower.io/search/?q=dygraphs">Bower</a>.</p>
+
+<p>To install using NPM:</p>
+<pre>$ npm install dygraphs
+# dygraphs is now in node_modules/dygraphs/dygraph-combined.js</pre>
+
+<p>To install using bower:</p>
+<pre>$ bower install dygraphs
+# dygraphs is now in bower_components/dygraphs/dygraph-combined.js</pre>
+
+<p>Most distributions include a source map. For non-concatenated JS, see <a href="https://github.com/danvk/dygraphs/blob/master/dygraph-dev.js">dygraph-dev.js</a> on <a href="https://github.com/danvk/dygraphs/">github</a>.</a>
+
+<p>To generate your own minified JS, run:</p>
+
+<pre>git clone https://github.com/danvk/dygraphs.git
+./generate-combined.sh
+</pre>
+
+<p>This will create a dygraph.min.js file in the dygraphs directory.</p>
+
+<p>You may also download files for previously-released versions:</p>
+
+<ul>
+%(previous_lis)s
+</ul>
+
+<p>See <a href="/versions.html">Version History</a> for more information on each release.</p>
+
+
+<!--#include virtual="footer.html" -->
+''' % {
+    'version': releases[0]['version'],
+    'current_html': current_html,
+    'previous_lis': '\n'.join(previous_lis)
+    }
diff --git a/scripts/generate-jsdoc.sh b/scripts/generate-jsdoc.sh
new file mode 100755 (executable)
index 0000000..6512fb0
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Generates JSDoc in the /jsdoc dir. Clears any existing jsdoc there.
+
+rm -rf jsdoc
+echo Generating JSDoc...
+java -jar jsdoc-toolkit/jsrun.jar \
+  jsdoc-toolkit/app/run.js \
+  -d=jsdoc -t=jsdoc-toolkit/templates/jsdoc \
+  dygraph.js \
+| tee /tmp/dygraphs-jsdocerrors.txt
+
+if [ -s /tmp/dygraphs-jsdocerrors.txt ]; then
+  echo Please fix any jsdoc errors/warnings before sending patches.
+fi
+
+chmod -R a+rX jsdoc
+
+echo Done
diff --git a/scripts/push-to-web.sh b/scripts/push-to-web.sh
new file mode 100755 (executable)
index 0000000..0ea59ac
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/bash
+# This script generates the combined JS file, pushes all content to a web site
+# and then reverts the combined file.
+
+if [ "$1" == "" ] ; then
+  echo "usage: $0 destination"
+  exit 1
+fi
+
+set -x
+site=$1
+
+# Produce dygraph-combined.js and dygraph-combined-dev.js
+./generate-combined.sh
+./generate-combined.sh cat-dev > dygraph-combined-dev.js
+
+# Generate documentation.
+./generate-documentation.py > docs/options.html
+chmod a+r docs/options.html
+if [ -s docs/options.html ] ; then
+  ./generate-jsdoc.sh
+  ./generate-download.py > docs/download.html
+
+  temp_dir=$(mktemp -d /tmp/dygraphs-docs.XXXX)
+  cd docs
+  ./ssi_expander.py $temp_dir
+  cd ..
+
+  # Make sure everything will be readable on the web.
+  # This is like "chmod -R a+rX", but excludes the .git directory.
+  find . -path ./.git -prune -o -print | xargs chmod a+rX
+
+  # Copy everything to the site.
+  rsync -avzr gallery common tests jsdoc experimental plugins datahandler polyfills extras $site \
+  && \
+  rsync -avzr --copy-links dashed-canvas.js dygraph*.js gadget.xml thumbnail.png screenshot.png $temp_dir/* $site/
+else
+  echo "generate-documentation.py failed"
+fi
+
+# Revert changes to dygraph-combined.js and docs.
+make clean-combined-test
+rm dygraph-combined-dev.js
+git checkout docs/download.html
+rm docs/options.html
+rm -rf $temp_dir
diff --git a/scripts/release.sh b/scripts/release.sh
new file mode 100755 (executable)
index 0000000..21c0c34
--- /dev/null
@@ -0,0 +1,85 @@
+#!/bin/bash
+# This script "releases" a version of dygraphs.
+
+if [ $# -ne 1 ]; then
+  echo "Usage: $0 X.Y.Z" >&2
+  exit 1
+fi
+
+VERSION=$1
+echo $VERSION | egrep '\d+\.\d+\.\d+' > /dev/null
+if [ $? -ne 0 ]; then
+  echo "Version must be of the form 1.2.3 (got '$VERSION')" >&2
+  exit 1
+fi
+
+# Make sure this is being run from a release branch with the correct name.
+branch=$(git rev-parse --abbrev-ref HEAD)
+if [ $branch != "release-$VERSION" ]; then
+  echo "Must be on a branch named 'release-$VERSION' (found '$branch')" >&2
+  exit 1
+fi
+
+git status | grep 'working directory clean' > /dev/null
+if [ $? -ne 0 ]; then
+  echo "Must release with a clean working directory. Commit your changes." >&2
+  exit 1
+fi
+
+grep "$VERSION" package.json
+if [ $? -ne 0 ]; then
+  echo "Version in package.json doesn't match command line argument." >&2
+  exit 1
+fi
+
+grep "v$VERSION" bower.json
+if [ $? -ne 0 ]; then
+  echo "Version in bower.json doesn't match command line argument." >&2
+  exit 1
+fi
+
+grep "$VERSION" releases.json
+if [ $? -ne 0 ]; then
+  echo "Version $VERSION does not appear in releases.json." >&2
+  exit 1
+fi
+
+rm dygraph-combined.js  # changes to this will make the tests fail.
+make lint test test-combined
+if [ $? -ne 0 ]; then
+  echo "Tests failed. Won't release!" >&2
+  exit 1
+fi
+git reset --hard  # make test-combined deletes the source map
+
+# Push a permanent copy of documentation & generated files to a versioned copy
+# of the site. This is where the downloadable files are generated.
+# TODO(danvk): make sure this actually generates the downloadable files!
+echo "Pushing docs and generated files to dygraphs.com/$VERSION"
+./push-to-web.sh dygraphs.com:dygraphs.com/$VERSION
+if [ $? -ne 0 ]; then
+  echo "Push to web failed" >&2
+  exit 1
+fi
+
+# Everything is good.
+# Switch to the "releases" branch, merge this change and tag it.
+echo "Switching branches to do the release."
+git checkout releases
+git merge --no-ff $branch
+
+COMMIT=$(git rev-parse HEAD)
+echo "Tagging commit $COMMIT as version $VERSION"
+git tag -a "v$VERSION" -m "Release of version $VERSION"
+git push --tags
+
+echo "Release was successful!"
+echo "Pushing the new version to dygraphs.com..."
+./push-to-web.sh dygraphs.com:dygraphs.com
+
+echo "Success!\n"
+echo "Don't forget to merge changes on this branch back into master:"
+echo "git merge --no-ff $branch"
+
+# Discourage users from working on the "releases" branch.
+git checkout master
diff --git a/test.sh b/test.sh
deleted file mode 100755 (executable)
index 3194c72..0000000
--- a/test.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-which phantomjs > /dev/null
-if [ $? != 0 ]; then
-  echo You must install phantomjs to use command-line testing.
-  echo Visit http://www.phantomjs.org/ to get it.
-  echo
-  echo OR open auto_tests/misc/local.html in a browser.
-  echo OR follow the instructions in auto_tests/README
-  exit 1
-fi
-
-# Don't run tests if the documentation doesn't parse.
-./generate-documentation.py > /dev/null
-if [ $? != 0 ]; then
-  echo Failed to generate documentation. Fix this before running tests.
-  exit 1
-fi
-
-phantomjs phantom-driver.js $* | tee /tmp/test-results.txt
-trap "rm -f /tmp/test-results.txt" EXIT
-if grep -q 'FAIL' /tmp/test-results.txt; then
-  echo One or more tests failed.
-  exit 1
-fi