--- /dev/null
+#!/usr/local/bin/fish
+echo "$_" (dirname "$_")
+# ls -lia ~/dev/lang/css/reset* (dirname $_)/css/reset* | sort
--- /dev/null
+html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym {border:0;font-variant:normal;}sup {vertical-align:text-top;}sub {vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}
+h1{font-size:138.5%;}h2{font-size:123.1%;}h3{font-size:108%;}h1,h2,h3{margin:1em 0;}h1,h2,h3,h4,h5,h6,strong{font-weight:bold;}abbr,acronym{border-bottom:1px dotted #000;cursor:help;} em{font-style:italic;}blockquote,ul,ol,dl{margin:1em;}ol,ul,dl{margin-left:2em;}ol li{list-style:decimal outside;}ul li{list-style:disc outside;}dl dd{margin-left:1em;}th,td{padding:.5em;}th{font-weight:bold;text-align:center;}caption{margin-bottom:.5em;text-align:center;}p,fieldset,table,pre{margin-bottom:1em;}input[type=text],input[type=password],textarea{width:12.25em;*width:11.9em;}
\ No newline at end of file
-html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym {border:0;font-variant:normal;}sup {vertical-align:text-top;}sub {vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}
-h1{font-size:138.5%;}h2{font-size:123.1%;}h3{font-size:108%;}h1,h2,h3{margin:1em 0;}h1,h2,h3,h4,h5,h6,strong{font-weight:bold;}abbr,acronym{border-bottom:1px dotted #000;cursor:help;} em{font-style:italic;}blockquote,ul,ol,dl{margin:1em;}ol,ul,dl{margin-left:2em;}ol li{list-style:decimal outside;}ul li{list-style:disc outside;}dl dd{margin-left:1em;}th,td{padding:.5em;}th{font-weight:bold;text-align:center;}caption{margin-bottom:.5em;text-align:center;}p,fieldset,table,pre{margin-bottom:1em;}input[type=text],input[type=password],textarea{width:12.25em;*width:11.9em;}
\ No newline at end of file
+html { color:#000; background:#fff; width:100%; height:100%; }
+body { position:absolute; width:100%; top:0; left:0; }
+html,body,div,
+ dl,dt,dd,ul,ol,li,
+ h1,h2,h3,h4,h5,h6,
+ pre,code,
+ form,fieldset,legend,input,button,textarea,
+ p,blockquote,
+ th,td { margin:0; padding:0; }
+
+address,caption,cite,code,dfn,em,strong,th,var,optgroup { font-style:inherit; font-weight:inherit; }
+fieldset,img { border:0; }
+caption,th { text-align:left; }
+caption { margin-bottom:.5em; text-align:center; }
+
+h1,h2,h3,h4,h5,h6 { font-size:100%; font-weight:normal; }
+h1,h2,h3,h4,h5,h6,strong { font-weight:bold; }
+h1 { font-size:138.5%; }
+h2 { font-size:123.1%; }
+h3 { font-size:108%; }
+h1,h2,h3 { margin:0.5em 0 1em; }
+
+q:before,q:after { content:''; }
+del,ins { text-decoration:none; }
+abbr,acronym { border:0; font-variant:normal; border-bottom:1px dotted #000; cursor:help; }
+sup, sub { vertical-align:baseline; }
+em { font-style:italic; }
+
+input,button,textarea,select,optgroup,option { font-family:inherit; font-size:inherit; font-style:inherit; font-weight:inherit; }
+input,button,textarea,select { *font-size:100%; }
+input[type=text],input[type=password],textarea { width:12.25em; *width:11.9em; }
+legend { color:#000; }
+
+ul,ol,dl,blockquote { margin:1em; }
+ol,ul,dl { margin-left:2em; }
+li { list-style:none; }
+ol li { list-style:decimal outside; }
+ul li { list-style:disc outside; }
+dl dd { margin-left:1em; }
+
+table { border-collapse:collapse; border-spacing:0; }
+th,td { padding:.5em; }
+th { font-weight:bold; text-align:center; }
+p,fieldset,table,pre { margin-bottom:1em; }
+
+.clearer { clear:both !important; float:none !important; margin:0 !important; padding:0 !important; }
+.rounded { border-radius:1em; -moz-border-radius:1em; -webkit-border-radius:1em; }
\ No newline at end of file
--- /dev/null
+html{color:#000;background:#fff;width:100%;height:100%;}body{position:absolute;width:100%;top:0;left:0;}html,body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}fieldset,img{border:0;}caption,th{text-align:left;}caption{margin-bottom:.5em;text-align:center;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}h1,h2,h3,h4,h5,h6,strong{font-weight:bold;}h1{font-size:138.5%;}h2{font-size:123.1%;}h3{font-size:108%;}h1,h2,h3{margin:.5em 0 1em;}q:before,q:after{content:'';}del,ins{text-decoration:none;}abbr,acronym{border:0;font-variant:normal;border-bottom:1px dotted #000;cursor:help;}sup,sub{vertical-align:baseline;}em{font-style:italic;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}input[type=text],input[type=password],textarea{width:12.25em;*width:11.9em;}legend{color:#000;}ul,ol,dl,blockquote{margin:1em;}ol,ul,dl{margin-left:2em;}li{list-style:none;}ol li{list-style:decimal outside;}ul li{list-style:disc outside;}dl dd{margin-left:1em;}table{border-collapse:collapse;border-spacing:0;}th,td{padding:.5em;}th{font-weight:bold;text-align:center;}p,fieldset,table,pre{margin-bottom:1em;}.clearer{clear:both!important;float:none!important;margin:0!important;padding:0!important;}.rounded{border-radius:1em;-moz-border-radius:1em;-webkit-border-radius:1em;}
\ No newline at end of file
/**
* Searches a heirarchical object for a given subkey specified in dotted-property syntax.
* @param {Array|String} chain The property-chain to lookup.
+ * @param {Any} [def] Default value should the key be `undefined`.
* @param {Boolean} [meta] If supplied return an object of the form
* `{ key: Qualified key name, obj: Parent object of key, value: Value at obj[key] }`
* if chain is found (and `undefined` otherwise).
- * @return {Any} The value at the path, or `undefined`.
+ * @return {Any} The value at the path, or `def` if `undefined`, otherwise `undefined`.
*/
- getNested : function getNested(chain, meta){
+ getNested : function getNested(chain, def, meta){
if ( !isArray(chain) )
chain = chain.toString().split('.');
};
}, { value:this._o });
- return (ret && !meta ? ret.value : ret);
+ if (ret !== undefined)
+ return (meta ? ret : ret.value);
+ else
+ return (meta ? { value:def } : def);
},
/**
this.root.draw();
this.pathmap.removeOverlay(this.viewport);
- if (tanks.config.pathing.overlayPathmap)
+ if (tanks.config.values.pathing.overlayPathmap)
this.pathmap.overlay(this.viewport);
},
SECONDTH = ELAPSED / 1000;
SQUARETH = REF_SIZE * SECONDTH
- if (!tanks.config.pathing.overlayAIPaths)
+ if (!tanks.config.values.pathing.overlayAIPaths)
this.pathmap.hidePaths();
this.active.invoke('updateCooldowns', NOW);
this.draw();
- if (this.player.dead) {
+ if ( this.player.dead ) {
this.fire('lose', this.player);
this.stop();
- } else if ( !this.units.filter('!_.dead'.lambda()).remove(this.player).size() ) {
+ } else if ( !this.units.filter('_.align !== 1 && !_.dead'.lambda()).size() ) {
this.fire('win', this.player);
this.stop();
}
P =
game.player = game.addUnit(new PlayerTank(1), 5,9);
+ game.addUnit(new Tank(1).colors('#4596FF', '#182B53', '#F25522'), 3,9);
E =
game.addUnit(new Tank(2), 0,1);
, path = Y(astar.search(grid, startN, endN))
;
- if (tanks.config.pathing.overlayAIPaths)
+ if (tanks.config.values.pathing.overlayAIPaths)
this.drawPath(id, startN, path);
return path
if (this.tline) { this.tline.remove(); delete this.tline; }
if (this.shape) this.shape.remove();
- if (tanks.config.pathing.traceTrajectories) {
+ if (tanks.config.values.pathing.traceTrajectories) {
var t = this.trajectory;
this.tline = Line.fromPoints(t.x1,t.y1, t.x2,t.y2)
.attr('drawDefinitionPoints', true)
$('#welcome').hide();
- if ( tanks.config.ui.showCountdown )
+ if ( tanks.config.values.ui.showCountdown )
countdownToStart(3, readyToStart);
else
readyToStart();
(function(){
-var upperPat = /^[A-Z]+$/
-, symbolPat = /^[^a-zA-Z]+$/
-;
-
-Y.YString.prototype.splitCamel =
- function splitCamel(s){
- return this.reduce(
- function(acc, ch, i){
- return acc + (
- symbolPat.test(ch) ? '' :
- (upperPat.test(ch) ? ' '+ch : ch) );
- }, '')
- .split(' ');
- };
-
-
// TODO
// ====
// - store at tanks.config.values, but clone at start to t.c.defaults
// - If different from default, set cookie
// - Update tanks.config.values
+
var ns = tanks.ui.config = {}
, c = tanks.config.values
, p = c.pathing
;
+
+
+
ns.init = function initConfigUi(){
$('#config [name=pathmap]').attr('checked', p.overlayPathmap);
$('#config [name=aipaths]').attr('checked', p.overlayAIPaths);
$('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords');
};
+// var templates = {
+// 'label' : { html:'<label for="{id}">{label}</label>' },
+// 'input' : { html:'<input id="{id}" type="{type}" name="{key}" value="{value}">' },
+// 'text' : { include:'input', type:'text' },
+// 'checkbox' : { include:'input', type:'checkbox', props:{ 'checked':false } },
+// 'radio' : { include:'input', type:'radio' }
+// // 'select' : '',
+// // 'textarea' : '',
+// // 'hidden' : '',
+// // 'button' : ''
+// };
+//
+// var type2el = {
+// 'Boolean'
+// 'Number' :
+// 'String' : 'text'
+// // 'Array'
+// // 'Date'
+// // 'RegExp'
+// // 'Function'
+// // 'Object'
+// };
+// ns.makeField = function makeField(key, value){
+//
+// };
+
+var upperPat = /^[A-Z]+$/
+, symbolPat = /^[^a-zA-Z]+$/
+;
+
+Y.YString.prototype.splitCamel =
+ function splitCamel(s){
+ return this.reduce(
+ function(acc, ch, i){
+ return acc + (
+ symbolPat.test(ch) ? '' :
+ (upperPat.test(ch) ? ' '+ch : ch) );
+ }, '')
+ .split(' ');
+ };
+
+
})();
-Config = Y.subclass('Config', {
-
+Config = Y.YObject.subclass('Config', function(){
+ Y.op.extend(this, {
+
+ init : function initConfig(defaults){
+ this._defaults = defaults;
+ this._o = Y({}, defaults);
+ this.__emitter__ = new Y.event.Emitter(this);
+ },
+
+ set : function set(key, value, def){
+ if ( key !== undefined ){
+ var meta = this.ensure(key).getNested(key, def, true)
+ , old = meta.value ;
+ def = (def === undefined ? old : def);
+ value = (value === undefined ? def : value);
+
+ meta.obj[meta.key] = value;
+ this._fireMutation('set', key, old, value, meta.obj);
+ }
+ return this;
+ },
+
+ remove : function remove(key){
+ if ( key !== undefined ){
+ var sentinel = {}
+ , meta = this.getNested(key, sentinel, true)
+ , old = (meta.value === sentinel ? undefined : old);
+
+ if ( meta.obj ) {
+ delete meta.obj[meta.key];
+ this._fireMutation('remove', key, old, undefined, meta.obj);
+ }
+ }
+ return this;
+ },
+
+ /**
+ * @private
+ */
+ _fireMutation : function _fireMutation(evt, key, oldval, newval, obj){
+ if (newval !== oldval){
+ var data = {
+ 'key' : key,
+ 'oldval' : oldval,
+ 'newval' : newval,
+ 'obj' : obj,
+ 'root' : this
+ };
+ this.fire(evt+':'+key, this, data);
+ this.fire(evt, this, data);
+ }
+ return this;
+ },
+
+ /**
+ * Recursively iterates the hierarchy of the Config, depth-first.
+ */
+ reduce : function reduce(fn, acc, context){
+ context = context || this;
+
+ var ret = Y.reduce(this._o, this._reducer, {
+ 'acc' : acc,
+ 'path' : new Y.YArray(),
+ 'cxt' : context
+ }, this);
+
+ return ret.acc;
+ },
+
+ /**
+ * @private
+ * XXX: Not going to call the iterator on root objects. ok?
+ * XXX: Always passing the Config object as obj to iterator. ok?
+ */
+ _reducer : function _reducer(state, v, k, o){
+ var chain = state.path.push(k).join('.');
+
+ // Nested object -- recurse
+ if ( Y.isPlainObject(v) )
+ state = Y.reduce(v, this._reducer, state, this);
+
+ // Normal value -- invoke iterator
+ else
+ state.acc = fn.call(state.cxt, state.acc, v, chain, this);
+
+ state.path.pop();
+ return state;
+ }
+
+ });
});
\ No newline at end of file
"src/tanks/config.js",
"src/tanks/calc.js",
+ "src/tanks/util/config.js",
+
"src/tanks/thing/thing.js",
"src/tanks/thing/bullet.js",
"src/tanks/thing/tank.js",
"src/tanks/ui/ui.js",
"src/tanks/ui/grid.js",
- "src/tanks/ui/config.js",
+ "src/tanks/ui/ui-config.js",
"src/tanks/game.js"
);
"src/evt/evt.class.js",
- "src/portal/math/math.js",
- "src/portal/math/vec.js",
- "src/portal/math/line.js",
+ "src/easel/math/math.js",
+ "src/easel/math/vec.js",
+ "src/easel/math/line.js",
- "src/portal/layer.js",
- "src/portal/shape/shape.js",
- "src/portal/shape/circle.js",
- "src/portal/shape/rect.js",
- "src/portal/shape/line.js",
- "src/portal/shape/polygon.js",
+ "src/easel/layer.js",
+ "src/easel/shape/shape.js",
+ "src/easel/shape/circle.js",
+ "src/easel/shape/rect.js",
+ "src/easel/shape/line.js",
+ "src/easel/shape/polygon.js",
- "src/portal/util/binaryheap.js",
- "src/portal/util/graph.js",
- "src/portal/util/astar.js",
+ "src/easel/util/binaryheap.js",
+ "src/easel/util/graph.js",
+ "src/easel/util/astar.js",
- "src/portal/util/tree/quadtree.js",
+ "src/easel/util/tree/quadtree.js",
- "src/portal/loop/eventloop.js",
- "src/portal/loop/fps.js",
- "src/portal/loop/cooldown.js",
+ "src/easel/loop/eventloop.js",
+ "src/easel/loop/fps.js",
+ "src/easel/loop/cooldown.js",
);
static function writeTags($scripts=null, $prefix="") {
--- /dev/null
+require('./lol.js');
+require('./baz.js');
+var bar = true;
\ No newline at end of file
--- /dev/null
+require('./lol.js');
+var baz = true;
\ No newline at end of file
--- /dev/null
+require('./bar.js');
+var foo = true;
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env python
+# encoding: utf-8
+__author__ = 'David Schoonover <dsc@less.ly>'
+__date__ = '2010-11-24'
+__version__ = (0, 0, 1)
+
+__all__ = ('ResolutionError', 'Module', 'JSResolver',)
+
+
+import sys, re
+from itertools import chain, repeat
+from collections import defaultdict
+from subprocess import Popen, check_output, STDOUT, PIPE
+from path import path
+
+
+class ResolutionError(Exception): pass
+
+REQUIRE_PAT = re.compile(r'require\(\s*([\'"])(.*?)\1\s*\)')
+CWD = path('.')
+BLANK = path('')
+
+class Module(object):
+ module_paths = []
+
+ @staticmethod
+ def canonicalise(module, basefile=BLANK):
+ if isinstance(basefile, Module):
+ basefile = path(basefile.key)
+
+ if module.endswith('/index.js'):
+ module = module[:-9]
+ elif module.endswith('.js'):
+ module = module[:-3]
+
+ if module.startswith('./') and basefile.dirname():
+ key = ( basefile.dirname() / module ).normpath()
+ if basefile.startswith('./'):
+ key = CWD / key
+ else:
+ key = module
+
+ return str(key)
+
+
+ key = ''
+ query = ''
+ basefile = BLANK
+ filepath = BLANK
+ _contents = None
+ _requires = None
+
+
+ def __init__(self, query, basefile=BLANK):
+ if isinstance(basefile, Module):
+ basefile = basefile.key
+
+ self.query = query
+ self.basefile = path(basefile)
+ self.key = Module.canonicalise(query, self.basefile)
+
+ self.filepath = self.lookup()
+
+ def lookup(self):
+ if self.key.startswith('./'):
+ search = [ CWD ]
+ else:
+ search = self.module_paths
+ for f in chain(*( (p+'.js', p/'index.js') for p in ((base / self.key).abspath() for base in search) )):
+ if f.isfile():
+ return f
+ raise ResolutionError('Unable to find %s' % self.key)
+
+ @property
+ def contents(self):
+ if self._contents is None:
+ with self.filepath.open('rU') as f:
+ self._contents = f.read()
+ return self._contents
+
+ @property
+ def requires(self):
+ if self._requires is None:
+ self._requires = [ Module.canonicalise(m.group(2), self) for m in REQUIRE_PAT.finditer(self.contents) ]
+ return self._requires
+
+ def __hash__(self):
+ return hash(self.key)
+
+ def __cmp__(self, other):
+ return cmp(self.key, other.key)
+
+ def __eq__(self, other):
+ return self.key == other.key
+
+ # def __str__(self):
+ # return str(self.key)
+
+ def __repr__(self):
+ return '{self.__class__.__name__}(key={self.key!r}, query={self.query!r}, basefile={self.basefile!r})'.format(**locals())
+
+
+
+
+class JSResolver(object):
+ """ Resolves JS imports. """
+
+ def __init__(self, files, module_paths=None):
+ Module.module_paths = set( path(p).abspath() for p in (module_paths or []) )
+
+ self.graph = defaultdict(set)
+ self.files = [ Module(CWD / path(f).normpath()) for f in files ]
+ self.modules = dict( (mod.key,mod) for mod in self.files )
+
+ def run(self):
+ queue = self.files[:]
+
+ while queue:
+ mod = queue.pop(0)
+
+ for req in mod.requires:
+
+ if req in self.modules:
+ m = self.modules[req]
+ else:
+ m = Module(req, mod)
+ self.modules[req] = m
+ queue.append(m)
+
+ # self.graph[mod.key].add(m)
+ self.graph[m.key].add(mod)
+
+ return self
+
+ @property
+ def deplist(self):
+ return '\n'.join( '%s %s' % (key, dep.key) for (key, deps) in self.graph.iteritems() for dep in deps )
+
+ @property
+ def dependencies(self):
+ # p = Process(['tsort', '-'], stdin=PIPE)
+ # p._process.stdin.write( self.deplist() )
+ # p._process.stdin.flush()
+ # return p()
+ p = Popen(['tsort'], stderr=STDOUT, stdin=PIPE, stdout=PIPE)
+ p.stdin.write( self.deplist )
+ p.stdin.close()
+ if p.wait() != 0:
+ raise ResolutionError('Cannot resolve dependencies! Requirements must be acyclic!')
+ return p.stdout.read().strip().split('\n')
+
+ # TODO wrap in closure; collect the `module.id` info; assign `exports` to the module definition
+ # TODO create a modules repo for handling require() calls at runtime
+
+ def cat(self):
+ for dep in self.dependencies:
+ print '// %s' % dep
+ print self.modules[dep].contents
+
+
+
+
+
+
+def main():
+ from optparse import OptionParser
+
+ parser = OptionParser(
+ usage = 'usage: %prog [options] file[...] [-- [module_path[...]]]',
+ description = 'Resolves imports in JS files',
+ version = '%prog'+" %i.%i.%i" % __version__)
+ parser.add_option("-p", "--module-paths", default='',
+ help="Comma-seperated paths to search for unqualified modules.")
+
+ (options, args) = parser.parse_args()
+
+ if not args:
+ parser.error("You must specify at least one JS file to resolve!")
+
+ js = JSResolver(args, module_paths=options.module_paths.split(','))
+ js.run()
+ print 'deplist:'
+ print js.deplist
+ print
+ print 'dependencies:'
+ print '\n'.join(js.dependencies)
+ # print js.dependencies
+ print
+ print 'cat:'
+ js.cat()
+ print '---'
+ print
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
+
--- /dev/null
+var lol = true;
<html>
<head>
<title>math</title>
-<?php
-//<script type="text/javascript">document.write('<base href="http://'+window.location.host+'/">');</script>
-?>
<link rel="stylesheet" href="../../css/reset.css" type="text/css" media="screen">
<style type="text/css" media="screen">
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<title>uki test</title>
+<link rel="stylesheet" href="../../css/reset.css" type="text/css" media="screen">
+<style type="text/css" media="screen">
+
+html { background-color:#3F3F3F; }
+body { position:absolute; width:100%; top:0; left:0; margin:0; padding:0;
+ font-family:Geogrotesque,Helvetica; font-size:12pt; color:#fff; }
+html, body { width:100%; height:100%;
+ font:12pt "Politica Light",Arial,Helvetica,sans-serif; color:#333; background-color:#7F7F7F; }
+h1 { position:fixed; top:0; right:0; margin:0; padding:0; font-size:3em; color:#000; opacity:0.25; z-index:100; }
+/* ul, ol, li { list-style: none ! important; margin:0; padding:0; } */
+
+#content { position:relative; width:1000px; margin:1em auto; }
+/* #content > div { margin:1em 0; } */
+
+#plot { width:1000px; height:600px; background-color:#fff; }
+/* #plot .circle { z-index:5; } */
+
+#info { position:absolute; top:0; left:1000px; margin-left:0.5em; width:275px; padding:0.5em;
+ color:#ddd; background-color:rgba(0,0,0, 0.25); line-height:2em; }
+#info fieldset { border:1px solid #444; padding:0.5em; }
+#info legend { color:#ccc; font-weight:bold; padding:0 0.5em; }
+#info input { width:3em; font-size:0.8em; padding:0.1em; text-align:center; background-color:rgba(0,0,0, 0.2); color:#ccc; border:0; }
+#info label { margin-right:1em; }
+#info pre { padding:0.5em; height:5em; font:12px monospace; background-color:rgba(0,0,0, 0.2); color:#ccc; border:0; }
+
+
+#howto { position:relative; width:600px; margin:1em auto; background-color:#D8D8D8; }
+#howto > * { padding:1em; margin-top:0; margin-bottom:0; }
+#howto h3 { margin:0; padding-bottom:0; }
+
+.handle { cursor:move; }
+.rounded { border-radius:1em; -moz-border-radius:1em; -webkit-border-radius:1em; }
+
+</style>
+</head>
+<body>
+<div id="content">
+ <div id="plot"></div>
+
+ <div id="info" class="rounded">
+ <form id="line" action="#" method="get">
+ <!--<fieldset id="line"><legend>Line</legend>-->
+ <label for="line_coords">Line:</label>
+ ( <input id="line_coords" name="x1" value="" type="text">, <input name="y1" value="" type="text"> )
+ ( <input name="x2" value="" type="text">, <input name="y2" value="" type="text"> )
+ <!--</fieldset>-->
+ </form>
+ <pre id="inter"></pre>
+ </div>
+
+ <div id="howto" class="rounded">
+ <h3>Trigonometry and Reflection Test</h3>
+ <ul>
+ <li>The main line is pink (and the light-orange line is its tangent).</li>
+ <li>Click anywhere to add a point. It will be purple, and its reflection in the main line will be orange.</li>
+ <li>You can drag the blue control points of the main line to change it.</li>
+ </ul>
+ </div>
+</div>
+
+<div id="scripts">
+<?php
+
+require "../../tanks.php";
+Tanks::writeTags(null, "../../");
+
+$scripts = array(
+ "math.test.js"
+);
+foreach ($scripts as $s) js($s);
+
+?>
+</div>
+</body>
+</html>
\ No newline at end of file