Refactors ezl to use CommonJS.
authordsc <david.schoonover@gmail.com>
Mon, 29 Nov 2010 04:12:48 +0000 (20:12 -0800)
committerdsc <david.schoonover@gmail.com>
Mon, 29 Nov 2010 04:12:48 +0000 (20:12 -0800)
60 files changed:
bin/cjs.py
doc/notes.md
index.php
lib/cjs/deps.js [new file with mode: 0644]
lib/cjs/dummy.js [new file with mode: 0644]
lib/cjs/module.js
lib/jquery.js [new symlink]
src/Y/class.cjs
src/Y/core.cjs
src/Y/index.cjs
src/Y/modules/old-y.event.js [moved from src/Y/modules/event.js with 100% similarity]
src/Y/modules/y.cookies.cjs [moved from src/Y/modules/y.cookies.js with 86% similarity]
src/Y/modules/y.event.cjs [moved from src/Y/modules/y.event.js with 94% similarity]
src/Y/modules/y.json.cjs [moved from src/Y/modules/y.json.js with 62% similarity]
src/Y/modules/y.kv.cjs [moved from src/Y/modules/y.kv.js with 88% similarity]
src/Y/op.cjs
src/Y/types/array.cjs
src/Y/types/collection.cjs
src/Y/types/function.cjs
src/Y/types/number.cjs
src/Y/types/object.cjs
src/Y/types/string.cjs
src/Y/utils.cjs [new file with mode: 0644]
src/Y/y.cjs
src/evt/evt.class.js
src/ezl/index.cjs [new file with mode: 0644]
src/ezl/layer.cjs [moved from src/ezl/layer.js with 96% similarity]
src/ezl/loc/boundingbox.cjs [new file with mode: 0644]
src/ezl/loc/index.cjs [new file with mode: 0644]
src/ezl/loc/loc.cjs [new file with mode: 0644]
src/ezl/loc/square.cjs [new file with mode: 0644]
src/ezl/loop/cooldown.cjs [moved from src/ezl/loop/cooldown.js with 92% similarity]
src/ezl/loop/eventloop.cjs [moved from src/ezl/loop/eventloop.js with 87% similarity]
src/ezl/loop/fps.cjs [moved from src/ezl/loop/fps.js with 92% similarity]
src/ezl/loop/index.cjs [new file with mode: 0644]
src/ezl/math/index.cjs [new file with mode: 0644]
src/ezl/math/line.cjs [moved from src/ezl/math/line.js with 70% similarity]
src/ezl/math/math.js [deleted file]
src/ezl/math/rect.cjs
src/ezl/math/vec.cjs [moved from src/ezl/math/vec.js with 78% similarity]
src/ezl/shape/circle.cjs [moved from src/ezl/shape/circle.js with 92% similarity]
src/ezl/shape/index.cjs [new file with mode: 0644]
src/ezl/shape/line.cjs [moved from src/ezl/shape/line.js with 98% similarity]
src/ezl/shape/polygon.cjs [moved from src/ezl/shape/polygon.js with 74% similarity]
src/ezl/shape/rect.cjs [moved from src/ezl/shape/rect.js with 74% similarity]
src/ezl/shape/shape.cjs [moved from src/ezl/shape/shape.js with 93% similarity]
src/ezl/util/astar.cjs [new file with mode: 0644]
src/ezl/util/astar.js [deleted file]
src/ezl/util/binaryheap.cjs [moved from src/ezl/util/binaryheap.js with 98% similarity]
src/ezl/util/graph.cjs [moved from src/ezl/util/graph.js with 76% similarity]
src/ezl/util/tree/pointquadtree.cjs [moved from src/ezl/util/tree/pointquadtree.js with 100% similarity]
src/ezl/util/tree/quadtree.cjs [moved from src/ezl/util/tree/quadtree.js with 97% similarity]
src/ezl/util/tree/rbtree.cjs [moved from src/ezl/util/tree/rbtree.js with 98% similarity]
src/tanks/map/level.cjs
src/tanks/map/loc/bbox.cjs [deleted file]
src/tanks/map/loc/index.cjs [deleted file]
src/tanks/map/loc/loc.cjs [deleted file]
src/tanks/map/loc/rect.cjs [deleted file]
src/tanks/ui/grid.cjs
tanks.php

index 250e42d..dbdbc72 100755 (executable)
@@ -7,7 +7,7 @@ __version__   = (0, 0, 1)
 __all__ = ('ResolutionError', 'Module', 'JSResolver',)
 
 
-import sys, re
+import sys, re, json
 from itertools import chain, repeat
 from collections import defaultdict
 from subprocess import Popen, check_output, STDOUT, PIPE
@@ -18,21 +18,30 @@ from path import path
 import pystache
 
 
+AT_REQUIRE_PAT   = re.compile(r'//@require\(\s*([\'"])(.*?)\1\s*\)')
 LINE_COMMENT_PAT = re.compile(r'(.*?)(//.*?)(?:\n|$)')
 PAIR_COMMENT_PAT = re.compile(r'(.*?)(/\*.*?\*/)')
 REQUIRE_PAT      = re.compile(r'\brequire\(\s*([\'"])(.*?)\1\s*\)')
+
 ROOT             = path(__file__).abspath().dirname().dirname()
 LIB              = ROOT/'lib/cjs'
 CWD              = path('.')
 BLANK            = path('')
+
 MODULE_TEMPLATE  = ''
+DUMMY_TEMPLATE   = ''
+DEPS_TEMPLATE    = ''
 
 try:
-    with (LIB/'module.js').open('rU') as f : 
+    with (LIB/'module.js').open('rU') as f:
         MODULE_TEMPLATE = f.read()
+    with (LIB/'dummy.js').open('rU') as f:
+        DUMMY_TEMPLATE = f.read()
+    with (LIB/'deps.js').open('rU') as f:
+        DEPS_TEMPLATE = f.read()
 except Exception as ex:
     print
-    print 'Error reading module template file!'
+    print 'Error reading template file!'
     print '  ROOT={ROOT}, LIB={LIB}, module.js={}'.format(LIB/'module.js', **locals())
     raise
 
@@ -86,55 +95,41 @@ def canonicalise(query, base=None):
     return str(id)
 
 
-class Repo(object):
-    def __init__(self, filepath, url):
-        self.path = filepath
-        self.url  = url
-    
-    def __repr__(self):
-        return 'Repo({}, url={})'.format(self.path, self.url)
-    
-    def __str__(self):
-        return repr(self)
-
-
 
 class Module(Bunch):
     DEFAULTS = {
-        'id'        : '',   # Canonical Module ID
-        'file'      : None, # path
-        'name'      : '',   # Module Name
-        'uri'       : '',   # Module URI (TODO)
-        'text'      : None, # Unmodified module text
-        'contents'  : None, # Compiled module text
-        '_requires' : None,
-        'outpath'   : None,
+        'id'           : '',   # Canonical Module ID
+        'file'         : None, # path
+        'name'         : '',   # Module Name
+        'uri'          : '',   # Module URI (TODO)
+        'text'         : None, # Unmodified module text
+        'contents'     : None, # Compiled module text
+        'outpath'      : None, # Build directory
+        '_requires'    : None,
+        '_at_requires' : None,
     }
     
     
-    def __init__(self, id, file, uri='', out=None, compile=True):
+    def __init__(self, id, file, uri=BLANK, out=None, compile=True):
         self.update(Module.DEFAULTS)
         
         self.id   = id
         self.file = path(file)
-        self.name = self.id.split('/').pop().capitalize()
+        self.name = re.subn(r'\W', '', self.id.split('/').pop().capitalize())[0]
         
         if out:
-            out = (path(out) / self.id) + '.js'
+            out = path(out)
+            outpath = (out / self.id) + '.js'
         else:
-            out = self.file.replace('.cjs', '.js')
-        self.outpath = path(out)
-        self.uri = uri
+            out = BLANK
+            outpath = self.file.replace('.cjs', '.js')
+        self.outpath = path(outpath)
+        self.uri = str(out/uri/self.id)+'.js'
         
-        if compile: self.compile()
+        self.compile()
         # print "new!", repr(self)
     
     def compile(self):
-        # if uri: self.uri = uri.format(**self) # TODO: calc uri
-        
-        if not self.file.endswith('.cjs'):
-            return self
-        
         outdir = self.outpath.dirname()
         if not outdir.exists():
             outdir.makedirs()
@@ -149,7 +144,8 @@ class Module(Bunch):
             with self.file.open('rU') as f:
                 txt = f.read()
             self['text'] = txt
-            self['contents'] = pystache.render(MODULE_TEMPLATE, self)
+            template = MODULE_TEMPLATE if self.file.endswith('.cjs') else DUMMY_TEMPLATE
+            self['contents'] = pystache.render(template,  self)
         return self[attr]
     
     @property
@@ -160,6 +156,10 @@ class Module(Bunch):
     def contents(self):
         return self.read('contents')
     
+    def findDirectives(self, text):
+        for areq in AT_REQUIRE_PAT.finditer(text):
+            yield canonicalise(areq.group(2), self)
+    
     def findRequires(self, text):
         text = LINE_COMMENT_PAT.subn(r'\1 ', text)[0]
         text = PAIR_COMMENT_PAT.subn(r'\1 ', text)[0]
@@ -167,11 +167,23 @@ class Module(Bunch):
         for mreq in REQUIRE_PAT.finditer(text):
             yield canonicalise(mreq.group(2), self)
     
+    def calcRequires(self, attr):
+        if self._requires is None or self._at_requires is None:
+            self._at_requires = list(self.findDirectives(self.text))
+            self._requires    = list(self.findRequires(self.text))
+        return getattr(self, attr)
+    
+    @property
+    def atRequires(self):
+        return self.calcRequires('_at_requires')
+    
+    @property
+    def nonAtRequires(self):
+        return self.calcRequires('_requires')
+    
     @property
     def requires(self):
-        if self._requires is None:
-            self._requires = list(self.findRequires(self.text))
-        return self._requires
+        return self.calcRequires('_at_requires') + self._requires
     
     def __hash__(self):
         return hash(self.id)
@@ -209,22 +221,30 @@ class CommonJS(object):
                 req = cjs.lookup(query, mod)
                 if req not in seen:
                     queue.append(req)
+        if cjs.deps_name:
+            cjs.genDepLoader()
         return cjs
     
     
     repos   = []
     modules = {} # id -> Module
-    _graph  = None # id -> set(dependants)
-    
-    
-    def __init__(self, repos, out='build', clean=True):
-        self.modules = {}
-        self.repos   = set( path(path(p).abspath()+'/') for p in repos)
-        self.out = None if out is '' else out
+    _graph    = None # id -> set(dependants)
+    _at_graph = None # id -> set(dependants)
+    
+    def __init__(self, repos, out='build', deps_name=None, clean=True, **options):
+        self.modules  = {}
+        self.options  = options
+        self.deps_name = deps_name
+        self.repos    = set( path(path(p).abspath()+'/') for p in repos)
+        self.out      = None if out is '' else out
         
         if self.out is not None and clean:
             out = path(self.out)
-            if out.exists(): out.rmtree()
+            for f in out.glob('*'):
+                if f.isdir():
+                    f.rmtree()
+                elif f.isfile():
+                    f.remove()
     
     def register(self, id, file):
         mod = self.modules[id] = Module(id=id, file=file, out=self.out)
@@ -254,32 +274,67 @@ class CommonJS(object):
         for k, mod in self.modules.iteritems():
             yield k, mod
     
-    @property
-    def graph(self):
+    def calcGraph(self, attr):
         if self._graph is None:
-            graph = self._graph = defaultdict(set)
+            graph   = self._graph    = defaultdict(set)
+            atgraph = self._at_graph = defaultdict(set)
             for mod in self.modules.values():
-                for req in mod.requires:
+                for req in mod.nonAtRequires:
                     graph[req].add(mod)
-        return self._graph
+                for req in mod.atRequires:
+                    atgraph[req].add(mod)
+        return getattr(self, attr)
     
     @property
-    def deplist(self):
-        return '\n'.join( '%s %s' % (id, dep.id) for (id, deps) in self.graph.iteritems() for dep in deps )
+    def graph(self):
+        return self.calcGraph('_graph')
     
     @property
-    def dependencies(self):
+    def atGraph(self):
+        return self.calcGraph('_at_graph')
+    
+    def tsort(self, graph):
         p = Popen(['tsort'], stderr=STDOUT, stdin=PIPE, stdout=PIPE)
-        p.stdin.write( self.deplist )
+        deps = '\n'.join( '%s %s' % (id, dep.id) for (id, deps) in graph.iteritems() for dep in deps )
+        p.stdin.write(deps)
         p.stdin.close()
         if p.wait() != 0:
             raise ResolutionError('Cannot resolve dependencies! Requirements must be acyclic!')
-        return p.stdout.read().strip().split('\n')
+        return p.stdout.read()
+    
+    @property
+    def dependencies(self):
+        deps = (self.tsort(self.atGraph)+self.tsort(self.graph)).strip().split('\n')
+        for i, dep in reversed(list(enumerate(deps[:]))):
+            try:
+                idx = deps.index(dep, 0, i-1)
+                if i != idx:
+                    del deps[idx]
+            except ValueError: pass
+        return deps
+    
+    def dumpDependencies(self):
+        column = Popen(['column', '-s', '\t', '-t'], stderr=sys.stderr, stdin=PIPE, stdout=PIPE)
+        mods = self.modules.values()
+        for mod in sorted(mods, key=lambda m: len(m.requires), reverse=True):
+            column.stdin.write('%s\t->\t%r\n' % (mod, sorted(mod.requires)))
+            # print '%s\t->\t%r' % (mod, sorted(mod.requires))
+        column.stdin.close()
+        if column.wait() != 0:
+            print >> sys.stderr, 'Some sort of error has occurred!'
+        return column.stdout.read()
     
-    def cat(self):
-        for dep in self.dependencies:
-            print self.modules[dep].contents
+    def genDepLoader(self):
+        with (path(self.out)/self.deps_name).open('w') as deps:
+            deps.write( pystache.render(DEPS_TEMPLATE, uris=json.dumps(self.uris)) )
     
+    @property
+    def uris(self):
+        return [ self.modules[d].uri for d in self.dependencies ]
+    
+    @property
+    def scriptTags(self):
+        return '\n'.join( '<script src="{}" type="text/javascript"></script>'.format(uri) for uri in self.uris )
 
 
 
@@ -292,15 +347,19 @@ def main():
         usage   = 'usage: %prog [options] file[...] [-- [repo_path[...]]]',
         description = 'Compiles CommonJS modules.',
         version = '%prog'+" %i.%i.%i" % __version__)
-    parser.add_option("-p", "--repo-paths", dest='repos', default='',
+    parser.add_option("-p", "--repo-paths", default='',
         help="Comma-seperated paths to search for unqualified modules. "
              "If a path contains :, the portion afterward will be taken as the repo URL. [default: .]")
     parser.add_option("-o", "--out", default='build',
         help="Root directory to write compiled JS files. Specify '' to write to module's dir. [default: build]")
     parser.add_option("-C", "--no-clean", dest='clean', default=True, action="store_false",
         help="Do not clean the out-dir of compiled files. [default: False if -o, True otherwise]")
-    parser.add_option("-d", "--print-deps", default=False, action="store_true", 
+    parser.add_option("--print-deps", default=False, action="store_true", 
         help="Prints module dependencies after compiling. [default: %default]")
+    parser.add_option("-g", "--gen-deps-script", dest='deps_name', default=None,
+        help="Generates a JS script with the given name which doc-writes tags for this set of modules.")
+    parser.add_option("-s", "--script-tags", default=False, action="store_true",
+        help="Emits script-tags for this set of modules in dependency order. [default: %default]")
     
     (options, args) = parser.parse_args()
     
@@ -310,21 +369,24 @@ def main():
     (files, sep, repos) = partition(sys.argv, '--')
     files = filter(lambda f: f in args, files)
     repos = filter(lambda f: f in args, repos)
-    repos.extend( filter(None, options.repos.split(',')) )
+    repos.extend( filter(None, options.repo_paths.split(',')) )
     
     # print 'files:', files, 'repos:', (repos or ['.'])
-    js = CommonJS.discover(files=files, repos=repos or ['.'], **options.__dict__)
+    try:
+        js = CommonJS.discover(files=files, repos=repos or ['.'], **options.__dict__)
+    except ResolutionError as ex:
+        print >> sys.stderr, str(ex)
+        return 1
+    
+    if options.script_tags:
+        print js.scriptTags
     
     if options.print_deps:
-        column = Popen(['column', '-s', '\t', '-t'], stderr=sys.stderr, stdin=PIPE, stdout=PIPE)
-        mods = js.modules.values()
-        for mod in sorted(mods, key=lambda m: len(m.requires), reverse=True):
-            column.stdin.write('%s\t->\t%r\n' % (mod, sorted(mod.requires)))
-            # print '%s\t->\t%r' % (mod, sorted(mod.requires))
-        
-        column.stdin.close()
-        if column.wait() != 0: print 'Some sort of error has occurred!'
-        print column.stdout.read()
+        print >> sys.stderr, 'All Dependencies:'
+        print >> sys.stderr, js.dumpDependencies()
+        print >> sys.stderr, ''
+        print >> sys.stderr, 'Resolution:'
+        print >> sys.stderr, '\n'.join(js.dependencies)
     
     return 0
 
index 3285ae5..38cd78e 100644 (file)
@@ -2,9 +2,8 @@
 - Tanks seem to get stuck on some corners
 
 # TODOs
-- How-to overlay
-- Restart level button
-- AI: Line-of-sight for dodging, firing
+- change @require to @ensure
+
 - AI: Don't shoot if it'll kill you or your friends
 - AI: Lead shots on moving targets
 - Config-driven unit-types (name, stats, properties; pointers to behavior scripts, assets)
index afcb361..3ac3f78 100644 (file)
--- a/index.php
+++ b/index.php
 </div></div>
 
 <div id="scripts">
-<?php require "tanks.php"; Tanks::writeTags( Tanks::ALL_SCRIPTS ); ?>
+<?php
+    /* require "tanks.php"; Tanks::writeTags( Tanks::ALL_SCRIPTS ); */
+    require "tanks.php"; Tanks::writeLoaders();
+?>
 </div>
 
 </body>
diff --git a/lib/cjs/deps.js b/lib/cjs/deps.js
new file mode 100644 (file)
index 0000000..3735e59
--- /dev/null
@@ -0,0 +1,3 @@
+{{{uris}}}.forEach(function(uri){
+    document.write('<scr'+'ipt src="'+uri+'" type="text/javascript"></scr'+'ipt>');
+});
diff --git a/lib/cjs/dummy.js b/lib/cjs/dummy.js
new file mode 100644 (file)
index 0000000..71c5a6b
--- /dev/null
@@ -0,0 +1,14 @@
+require.install(
+// Module ID
+'{{{id}}}',
+
+// Module metadata
+{
+    'name'  : '{{{name}}}',
+    'id'    : '{{{id}}}',
+    'uri'   : '{{{uri}}}'
+},
+// Dummy module: setup does nothing
+function setup{{{name}}}(require, exports, module){});
+
+{{{text}}}
index 7f225e1..167f51a 100644 (file)
@@ -1,17 +1,17 @@
 require.install(
 // Module ID
-'{{id}}',
+'{{{id}}}',
 
 // Module metadata
 {
-    'name'  : '{{name}}',
-    'id'    : '{{id}}',
-    'uri'   : '{{uri}}'
+    'name'  : '{{{name}}}',
+    'id'    : '{{{id}}}',
+    'uri'   : '{{{uri}}}'
 },
 
 // Module Setup function
-function setup{{name}}(require, exports, module){
+function setup{{{name}}}(require, exports, module){
 
-{{text}}
+{{{text}}}
 
 });
\ No newline at end of file
diff --git a/lib/jquery.js b/lib/jquery.js
new file mode 120000 (symlink)
index 0000000..0185856
--- /dev/null
@@ -0,0 +1 @@
+jquery-1.4.3.min.js
\ No newline at end of file
index 9ca59ab..dc1a4b5 100644 (file)
@@ -1,7 +1,10 @@
 // Inspired by John Resig's "Simple Class Inheritence" -- http://ejohn.org/blog/simple-javascript-inheritance/
 
-var Y = require('Y').Y
-,   type = require('Y/type')
+var type      = require('Y/type')
+,   YFunction = require('Y/types/function').YFunction
+,   core      = require('Y/core')
+,   slice     = core.slice
+
 ,   KNOWN_CLASSES = type.type.KNOWN_CLASSES
 ,   classToString = function toString(){ return this.className+"()"; }
 ;
@@ -113,7 +116,7 @@ function Class(className, Parent, members) {
             prototype[k] = members[k];
     }
     
-    if (prototype.init) NewClass.init = Y(prototype.init);
+    if (prototype.init) NewClass.init = YFunction(prototype.init);
     
     KNOWN_CLASSES[className] = NewClass;
     
@@ -171,59 +174,9 @@ var YBase = new Class("YBase", {
 
 
 
-
-/// Other Class Utilities ///
-
-function bindName(v, k){
-    if ( isFunction(v) )
-        this[k] = Y(v).bind(this);
-}
-// bindName = Y(bindName).curry();
-
-function bindAll(o, names){
-    var names = new Y(arguments, 1);
-    Y(names.size() ? names.generate(Y.op.get(o)) : o).forEach( bindName, o );
-    return o;
-}
-
-// Y.chainDelegates = chainDelegates;
-function chainDelegates(type, name){
-    var names = Y(arguments, 1),
-        proto = type.prototype;
-    names.forEach(function(name){
-        proto[name] = function(){
-            var o = this._o || this;
-            o[name].apply(o, Y(arguments));
-            return this;
-        };
-    });
-    return type;
-}
-
-// Y.mixinNames = mixinNames;
-function mixinNames(o, Donor, names, override, yWrap){
-    var target = ( isFunction(o)     ? o.prototype     : o)
-    ,   proto  = ( isFunction(Donor) ? Donor.prototype : Donor);
-    
-    // Need a closure to capture the name
-    names.forEach(function(name){
-        if ( isFunction(proto[name]) && (override || !target[name]) )
-            target[name] = function(){
-                var r = proto[name].apply(this._o || target, arguments);
-                return (yWrap ? Y(r) : r);
-