From: dsc Date: Fri, 26 Nov 2010 21:16:36 +0000 (-0800) Subject: Trying out friendly AI. Some code reorganization. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=135699021c2556cefbc582d0a80d20fd2be7d075;p=tanks.git Trying out friendly AI. Some code reorganization. --- diff --git a/bin/check-css.sh b/bin/check-css.sh new file mode 100755 index 0000000..4bc5456 --- /dev/null +++ b/bin/check-css.sh @@ -0,0 +1,3 @@ +#!/usr/local/bin/fish +echo "$_" (dirname "$_") +# ls -lia ~/dev/lang/css/reset* (dirname $_)/css/reset* | sort diff --git a/css/reset-old.css b/css/reset-old.css new file mode 100644 index 0000000..dd5886b --- /dev/null +++ b/css/reset-old.css @@ -0,0 +1,2 @@ +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 diff --git a/css/reset.css b/css/reset.css index dd5886b..0fbb22a 100644 --- a/css/reset.css +++ b/css/reset.css @@ -1,2 +1,47 @@ -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 diff --git a/css/reset.min.css b/css/reset.min.css new file mode 100644 index 0000000..3c93c02 --- /dev/null +++ b/css/reset.min.css @@ -0,0 +1 @@ +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 diff --git a/src/Y/y-object.js b/src/Y/y-object.js index b286350..2d6b497 100644 --- a/src/Y/y-object.js +++ b/src/Y/y-object.js @@ -27,12 +27,13 @@ YCollection.subclass('YObject', { /** * 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('.'); @@ -48,7 +49,10 @@ YCollection.subclass('YObject', { }; }, { value:this._o }); - return (ret && !meta ? ret.value : ret); + if (ret !== undefined) + return (meta ? ret : ret.value); + else + return (meta ? { value:def } : def); }, /** diff --git a/src/portal/layer.js b/src/easel/layer.js similarity index 100% rename from src/portal/layer.js rename to src/easel/layer.js diff --git a/src/portal/loop/cooldown.js b/src/easel/loop/cooldown.js similarity index 100% rename from src/portal/loop/cooldown.js rename to src/easel/loop/cooldown.js diff --git a/src/portal/loop/eventloop.js b/src/easel/loop/eventloop.js similarity index 100% rename from src/portal/loop/eventloop.js rename to src/easel/loop/eventloop.js diff --git a/src/portal/loop/fps.js b/src/easel/loop/fps.js similarity index 100% rename from src/portal/loop/fps.js rename to src/easel/loop/fps.js diff --git a/src/portal/math/line.js b/src/easel/math/line.js similarity index 100% rename from src/portal/math/line.js rename to src/easel/math/line.js diff --git a/src/portal/math/math.js b/src/easel/math/math.js similarity index 100% rename from src/portal/math/math.js rename to src/easel/math/math.js diff --git a/src/portal/math/vec.js b/src/easel/math/vec.js similarity index 100% rename from src/portal/math/vec.js rename to src/easel/math/vec.js diff --git a/src/portal/shape/circle.js b/src/easel/shape/circle.js similarity index 100% rename from src/portal/shape/circle.js rename to src/easel/shape/circle.js diff --git a/src/portal/shape/line.js b/src/easel/shape/line.js similarity index 100% rename from src/portal/shape/line.js rename to src/easel/shape/line.js diff --git a/src/portal/shape/polygon.js b/src/easel/shape/polygon.js similarity index 100% rename from src/portal/shape/polygon.js rename to src/easel/shape/polygon.js diff --git a/src/portal/shape/rect.js b/src/easel/shape/rect.js similarity index 100% rename from src/portal/shape/rect.js rename to src/easel/shape/rect.js diff --git a/src/portal/shape/shape.js b/src/easel/shape/shape.js similarity index 100% rename from src/portal/shape/shape.js rename to src/easel/shape/shape.js diff --git a/src/portal/util/astar.js b/src/easel/util/astar.js similarity index 100% rename from src/portal/util/astar.js rename to src/easel/util/astar.js diff --git a/src/portal/util/binaryheap.js b/src/easel/util/binaryheap.js similarity index 100% rename from src/portal/util/binaryheap.js rename to src/easel/util/binaryheap.js diff --git a/src/portal/util/graph.js b/src/easel/util/graph.js similarity index 100% rename from src/portal/util/graph.js rename to src/easel/util/graph.js diff --git a/src/portal/util/tree/pointquadtree.js b/src/easel/util/tree/pointquadtree.js similarity index 100% rename from src/portal/util/tree/pointquadtree.js rename to src/easel/util/tree/pointquadtree.js diff --git a/src/portal/util/tree/quadtree.js b/src/easel/util/tree/quadtree.js similarity index 100% rename from src/portal/util/tree/quadtree.js rename to src/easel/util/tree/quadtree.js diff --git a/src/portal/util/tree/rbtree.js b/src/easel/util/tree/rbtree.js similarity index 100% rename from src/portal/util/tree/rbtree.js rename to src/easel/util/tree/rbtree.js diff --git a/src/tanks/game.js b/src/tanks/game.js index e6cdbf0..8839f36 100644 --- a/src/tanks/game.js +++ b/src/tanks/game.js @@ -43,7 +43,7 @@ tanks.Game = Y.subclass('Game', { this.root.draw(); this.pathmap.removeOverlay(this.viewport); - if (tanks.config.pathing.overlayPathmap) + if (tanks.config.values.pathing.overlayPathmap) this.pathmap.overlay(this.viewport); }, @@ -59,7 +59,7 @@ tanks.Game = Y.subclass('Game', { 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); @@ -67,11 +67,11 @@ tanks.Game = Y.subclass('Game', { 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(); } diff --git a/src/tanks/map/level.js b/src/tanks/map/level.js index 9c050d2..2dc4857 100644 --- a/src/tanks/map/level.js +++ b/src/tanks/map/level.js @@ -26,6 +26,7 @@ Level = Rect.subclass('Level', { 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); diff --git a/src/tanks/map/pathmap.js b/src/tanks/map/pathmap.js index 5cdf075..7284034 100644 --- a/src/tanks/map/pathmap.js +++ b/src/tanks/map/pathmap.js @@ -207,7 +207,7 @@ PathMap = QuadTree.subclass('PathMap', { , 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 diff --git a/src/tanks/thing/bullet.js b/src/tanks/thing/bullet.js index 331aa9d..2dc640d 100644 --- a/src/tanks/thing/bullet.js +++ b/src/tanks/thing/bullet.js @@ -91,7 +91,7 @@ Bullet = Thing.subclass('Bullet', { 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) diff --git a/src/tanks/ui/main.js b/src/tanks/ui/main.js index e6c3598..7f697b2 100644 --- a/src/tanks/ui/main.js +++ b/src/tanks/ui/main.js @@ -124,7 +124,7 @@ function startGame(evt){ $('#welcome').hide(); - if ( tanks.config.ui.showCountdown ) + if ( tanks.config.values.ui.showCountdown ) countdownToStart(3, readyToStart); else readyToStart(); diff --git a/src/tanks/ui/config.js b/src/tanks/ui/ui-config.js similarity index 72% rename from src/tanks/ui/config.js rename to src/tanks/ui/ui-config.js index 9076096..c32d26b 100644 --- a/src/tanks/ui/config.js +++ b/src/tanks/ui/ui-config.js @@ -1,21 +1,5 @@ (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 @@ -33,11 +17,15 @@ Y.YString.prototype.splitCamel = // - 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); @@ -56,5 +44,47 @@ ns.update = function updateConfigUi(evt){ $('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords'); }; +// var templates = { +// 'label' : { html:'' }, +// 'input' : { html:'' }, +// '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(' '); + }; + + })(); diff --git a/src/tanks/util/config.js b/src/tanks/util/config.js index 2404089..822e579 100644 --- a/src/tanks/util/config.js +++ b/src/tanks/util/config.js @@ -1,5 +1,93 @@ -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 diff --git a/tanks.php b/tanks.php index 2a137d9..6488545 100644 --- a/tanks.php +++ b/tanks.php @@ -22,6 +22,8 @@ class Tanks { "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", @@ -35,7 +37,7 @@ class Tanks { "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" ); @@ -61,26 +63,26 @@ class Tanks { "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="") { diff --git a/test/jsc/bar.js b/test/jsc/bar.js new file mode 100644 index 0000000..38eaaa4 --- /dev/null +++ b/test/jsc/bar.js @@ -0,0 +1,3 @@ +require('./lol.js'); +require('./baz.js'); +var bar = true; \ No newline at end of file diff --git a/test/jsc/baz.js b/test/jsc/baz.js new file mode 100644 index 0000000..d12b446 --- /dev/null +++ b/test/jsc/baz.js @@ -0,0 +1,2 @@ +require('./lol.js'); +var baz = true; \ No newline at end of file diff --git a/test/jsc/foo.js b/test/jsc/foo.js new file mode 100644 index 0000000..514e202 --- /dev/null +++ b/test/jsc/foo.js @@ -0,0 +1,2 @@ +require('./bar.js'); +var foo = true; \ No newline at end of file diff --git a/test/jsc/jsc.py b/test/jsc/jsc.py new file mode 100755 index 0000000..0775b8d --- /dev/null +++ b/test/jsc/jsc.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python +# encoding: utf-8 +__author__ = 'David Schoonover ' +__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()) + diff --git a/test/jsc/lol.js b/test/jsc/lol.js new file mode 100644 index 0000000..832976f --- /dev/null +++ b/test/jsc/lol.js @@ -0,0 +1 @@ +var lol = true; diff --git a/test/math/index.php b/test/math/index.php index a96d3a3..830a32c 100644 --- a/test/math/index.php +++ b/test/math/index.php @@ -2,9 +2,6 @@ math -document.write(''); -?> + + +
+
+ +
+
+ + + ( , ) + ( , ) + +
+

+    
+ +
+

Trigonometry and Reflection Test

+
    +
  • The main line is pink (and the light-orange line is its tangent).
  • +
  • Click anywhere to add a point. It will be purple, and its reflection in the main line will be orange.
  • +
  • You can drag the blue control points of the main line to change it.
  • +
+
+
+ +
+ +
+ + \ No newline at end of file diff --git a/test/uki/test-uki.js b/test/uki/test-uki.js new file mode 100644 index 0000000..e69de29