[introspection-doc-generator] Add Javascript Compressor, and new XObject base util class (will replace Object.js)
- From: Alan Knowles <alank src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [introspection-doc-generator] Add Javascript Compressor, and new XObject base util class (will replace Object.js)
- Date: Thu, 29 Apr 2010 23:04:55 +0000 (UTC)
commit 8b882e6abb3281639eea6cb3850054af2550f6ce
Author: Alan Knowles <alan akkbhome com>
Date: Fri Apr 30 06:59:07 2010 +0800
Add Javascript Compressor, and new XObject base util class (will replace Object.js)
pack.js - pack and rewrite JS files - similar to YUI Compressor
XObject.js - replacement for Object.js - more generic, no overwriting built in types etc.
ScopeParser.js - full scope parser
File.js | 48 +++-
JSDOC/CompressWhite.js | 284 ++++++++++++++++
JSDOC/Identifier.js | 25 ++
JSDOC/Packer.js | 449 +++++++++++++++++++++++++
JSDOC/Scope.js | 316 ++++++++++++++++++
JSDOC/ScopeParser.js | 859 ++++++++++++++++++++++++++++++++++++++++++++++++
JSDOC/Token.js | 29 ++-
JSDOC/TokenReader.js | 65 +++--
JSDOC/TokenStream.js | 38 ++-
XObject.js | 439 ++++++++++++++++++++++++
docs.js | 17 +-
docs/class.html | 8 +-
docs/index.html | 18 +-
pack.js | 83 +++++
14 files changed, 2630 insertions(+), 48 deletions(-)
---
diff --git a/File.js b/File.js
index f0da9ff..e788e59 100755
--- a/File.js
+++ b/File.js
@@ -4,6 +4,20 @@ Gio = imports.gi.Gio;
imports['String.js'].load(String);
+/**
+* @namespace File
+*
+* Library to wrap GLib and Gio basic File related methods
+*
+* usage:
+*
+* File = import.File.File;
+*
+* var contents = File.read("/tmp/test.txt");
+*
+*
+*
+*/
var File = {
SEPARATOR : '/',
@@ -74,14 +88,44 @@ var File = {
var can = f.resolve_relative_path('');
return can.get_path();
},
-
+ /**
+ * write
+ * @arg path {String} File to write to
+ * @arg string {String} Contents of file.
+ *
+ */
write : function (path, string) {
var f = Gio.file_new_for_path(String(path));
var data_out = new Gio.DataOutputStream({base_stream:f.replace(null, false, Gio.FileCreateFlags.NONE, null)});
data_out.put_string(string, null);
data_out.close(null);
},
-
+ /**
+ * append
+ * @arg path {String} File to write to
+ * @arg string {String} string to append to file.
+ *
+ */
+ append : function (path, string) {
+ var f = Gio.file_new_for_path(String(path));
+ var data_out = new Gio.DataOutputStream({
+ base_stream:f.append_to(Gio.FileCreateFlags.NONE, null)
+ });
+ data_out.put_string(string, null);
+ data_out.close(null);
+ },
+ /**
+ * remove
+ * Delete a file.
+ * @arg path {String} File to remove
+ *
+ *
+ */
+ remove : function (path)
+ {
+ var f = Gio.file_new_for_path(String(path));
+ return f['delete']();
+ },
// copy files recursively from fromDir, silently ignore them if they already exist in toDir
silentRecursiveCopy : function (fromDir, toDir) {
var filesToCopy = File.recursiveListing(fromDir);
diff --git a/JSDOC/CompressWhite.js b/JSDOC/CompressWhite.js
new file mode 100644
index 0000000..6b08e16
--- /dev/null
+++ b/JSDOC/CompressWhite.js
@@ -0,0 +1,284 @@
+ // <script type="text/javascript">
+/**
+ *
+ * pack a javascript file, and return a shorter version!
+ *
+ * a bit picky at present with ; and crlf reading...
+ * @arg ts {TokenStream}
+ @arg packer {Packer}
+ */
+
+CompressWhite = function (ts, packer)
+{
+
+ ts.rewind();
+ //var str = File.read(fn);
+ var rep_var = 1;
+
+ while (true) {
+ var tok = ts.next();
+ if (!tok) {
+ break;
+ }
+ if (tok.type == "WHIT") {
+ continue;
+ //if (tok._isDoc) {
+ // continue;
+ //}
+ // just spaces, not \n!
+ //if (tok.data.indexOf("\n") < 0) {
+ // continue;
+ // }
+
+
+ }
+ if (tok.data == "}") {
+
+ if (ts.lookTok(1).type == 'NAME' && ts.look(1,true).name == "NEWLINE") {
+
+ ts.look(0).outData = ts.look(0).data+"\n";
+ }
+ // restore..
+
+ continue;
+ }
+ // add semi-colon's where linebreaks are used... - not foolproof yet.!
+ if (tok.type == "NAME") {
+ //var tokident = ts.look(-1).data + tok.data + ts.look(1).data + ts.look(2).data;
+ // a = new function() {}
+ if (ts.lookTok(1).data == '=' && ts.lookTok(2).name == 'NEW' &&
+ ts.lookTok(3).name == 'FUNCTION') {
+ // freeze time..
+ var cu = ts.cursor;
+
+ ts.balance("(");
+
+
+ ts.balance("{");
+ // if next is not ';' -> make it so...
+
+ if (ts.lookTok(1).data != ';' && ts.lookTok(1).data != '}' && ts.lookTok(1,true).name == "NEWLINE") {
+ ts.look(0).outData = ts.cur().data +";";
+ }
+ // restore..
+ ts.cursor = cu;
+ continue;
+ }
+ // a = function() { ...
+
+ if (ts.lookTok(1).data == '=' && ts.lookTok(2).name == "FUNCTION") {
+ // freeze time..
+ //println("got = function() ");
+ var cu = ts.cursor;
+
+ ts.balance("(");
+ ts.balance("{");
+ // if next is not ';' -> make it so...
+ // although this var a=function(){},v,c; causes
+ if (ts.lookTok(1).data != ';' && ts.lookTok(1).data != '}' && ts.lookTok(1,true).name == "NEWLINE") {
+ ts.look(0).outData = ts.look(0).data+";";
+ }
+ // restore..
+ ts.cursor = cu;
+ continue;
+ }
+ // next item is a name..
+ if ((ts.lookTok(1).type == 'NAME' || ts.lookTok(1).type == 'KEYW' ) && ts.look(1,true).name == "NEWLINE") {
+ // preserve linebraek
+ ts.look(0).outData = ts.look(0).data+"\n";
+ }
+ // method call followed by name..
+ if (ts.lookTok(1).data == "(") {
+ var cu = ts.cursor;
+
+ ts.balance("(");
+ // although this var a=function(){},v,c; causes
+
+ if (ts.lookTok(1).type == 'NAME' && ts.look(1,true).name == "NEWLINE") {
+
+ ts.look(0).outData = ts.look(0).data+"\n";
+ }
+ // restore..
+ ts.cursor = cu;
+ continue;
+ }
+
+
+ // function a () { ... };
+ /*
+ if (ts.look(-1).isTypeN(Script.TOKfunction) && ts.look(1).isTypeN(Script.TOKlparen)) {
+ // freeze time..
+ //println("got = function() ");
+ var cu = ts.cursor;
+
+ ts.balance("lparen");
+ ts.balance("lbrace");
+ // if next is not ';' -> make it so...
+ // although this var a=function(){},v,c; causes
+ if (!ts.look(1).isData(';') && !ts.look(1).isData('}') && ts.look(1,true).isLineBreak()) {
+ ts.cur().outData = ts.cur().data+";";
+ }
+ // restore..
+ ts.cursor = cu;
+ continue;
+ }
+ */
+
+ // a = { ....
+
+ if (ts.lookTok(1).data == '=' && ts.lookTok(2).data == '{') {
+ // freeze time..
+ //println("----------*** 3 *** --------------");
+ var cu = ts.cursor;
+
+ if (!ts.balance("{") ){
+ throw "could not find end lbrace!!!";
+ }
+ // if next is not ';' -> make it so...
+
+ if (ts.lookTok(1).data != ';' && ts.lookTok(1).data != '}' && ts.look(1,true).name=="NEWLINE") {
+ ts.look(0).outData = ts.look(0).data +";";
+ }
+ // restore..
+ ts.cursor = cu;
+ continue;
+ }
+
+ // any more??
+ }
+
+
+
+
+ //println("got Token: " + tok.type);
+
+
+
+ switch(tok.data.toUpperCase()) {
+ // things that need space appending
+ case "FUNCTION":
+ case "BREAK":
+ case "CONTINUE":
+ // if next item is a identifier..
+ if (ts.lookTok(1).type == "NAME" || ts.lookTok(1).data.match(/^[a-z]+$/i) ) { // as include is a keyword for us!!
+ tok.outData = tok.data + " ";
+ }
+ continue;
+
+
+ case "RETURN": // if next item is not a semi; (or }
+ if (ts.lookTok(1).data == ';' || ts.lookTok(1).data == '}') {
+ continue;
+ }
+ tok.outData = tok.data + " ";
+
+ continue;
+
+
+ case "ELSE": // if next item is not a semi; (or }
+ if (!ts.lookTok(1).name == "IF") {
+ continue;
+ }
+
+ tok.outData = tok.data + " ";
+ continue;
+
+ case "++": // if previous was a plus or next is a + add a space..
+ case "--": // if previous was a - or next is a - add a space..
+
+ var p = (tok.data == "--" ? '-' : '+');
+
+ if (ts.lookTok(1).data == p) {
+ tok.outData = tok.data + " ";
+ }
+ if (ts.lookTok(-1).data == p) {
+ tok.outData = " " + tok.data;
+
+ }
+ continue;
+
+ case "IN": // before and after??
+ case "INSTANCEOF":
+
+ tok.outData = " " + tok.data + " ";
+ continue;
+
+ case "VAR": // always after..
+ case "NEW":
+ case "DELETE":
+ case "THROW":
+ case "CASE":
+
+ case "VOID":
+ tok.outData = tok.data + " ";
+
+ continue
+
+ case "TYPEOF": // what about typeof(
+ if (ts.lookTok(1).data != '(') {
+ tok.outData = tok.data + " ";
+ }
+ continue;
+ case ";":
+ //remove semicolon before brace --
+ //if(ts.look(1).isTypeN(Script.TOKrbrace)) {
+ // tok.outData = '';
+ // }
+ continue;
+
+ default:
+ continue;
+ }
+ }
+
+ ts.rewind();
+
+ // NOW OUTPUT THE THING.
+ //var f = new File(minfile, File.NEW);
+
+ var out = '';
+ var outoff = 0;
+ out.length = ts.slen; // prealloc.
+ out = '';
+ while (true) {
+ var tok = ts.nextTok();
+
+ if (!tok) {
+ break;
+ }
+
+
+ if (tok.type == "NAME" && tok.identifier && tok.identifier.mungedValue && tok.identifier.mungedValue.length) {
+ //f.write(tok.identifier.mungedValue);
+ out += tok.identifier.mungedValue;
+ continue;
+ }
+
+ // at this point we can apply a text translation kit...
+
+ if ((tok.type == 'STRN') && (tok.name== 'DOUBLE_QUOTE')) {
+ if (packer && packer.stringHandler) {
+ out += packer.stringHandler(tok);
+ continue;
+ }
+ }
+
+ out += tok.outData !== false ? tok.outData : tok.data;
+
+ if ((tok.outData == ';') && (out.length - outoff > 255)) {
+ outoff = out.length;
+ out += "\n";
+ }
+ }
+ //f.close();
+ /*
+ // remove the last ';' !!!
+ if (out.substring(out.length-1) == ';') {
+ return out.substring(0,out.length-1);
+ }
+ */
+ return out;
+
+}
+
+
\ No newline at end of file
diff --git a/JSDOC/Identifier.js b/JSDOC/Identifier.js
new file mode 100644
index 0000000..7cc82bd
--- /dev/null
+++ b/JSDOC/Identifier.js
@@ -0,0 +1,25 @@
+//<Script type="text/javascript">
+
+/**
+ * @class Identifier
+ * holds details about identifiers and their replacement values
+ * used by the packer..
+ *
+ */
+
+
+function Identifier(name, scope) {
+ // print("NEW IDENT: " + name);
+ this.name = name;
+ this.scope = scope;
+ this.identifiers = {};
+
+}
+Identifier.prototype = {
+ name: '',
+ refcount: 1,
+ mungedValue : '', // should be at least 1?!?!
+ scope : false, // script of fn scope..
+ toMunge : true
+};
+
diff --git a/JSDOC/Packer.js b/JSDOC/Packer.js
new file mode 100644
index 0000000..d04e630
--- /dev/null
+++ b/JSDOC/Packer.js
@@ -0,0 +1,449 @@
+// <script type="text/javascript">
+XObject = imports.XObject.XObject;
+File = imports.File.File;
+
+TextStream = imports['JSDOC/TextStream.js'].TextStream;
+TokenReader = imports['JSDOC/TokenReader.js'].TokenReader;
+ScopeParser = imports['JSDOC/ScopeParser.js'].ScopeParser;
+TokenStream = imports['JSDOC/TokenStream.js'].TokenStream;
+CompressWhite = imports['JSDOC/CompressWhite.js'].CompressWhite;
+
+GLib = imports.gi.GLib;
+/**
+ * @namespace JSDOC
+ * @class Packer
+ * Create a new packer
+ *
+ * Use with pack.js
+ *
+ *
+ * Usage:
+ * <code>
+ *
+Packer = imports['JSDOC/Packer.js'].Packer;
+var x = new Packer({
+
+ files : [ "/location/of/file1.js", "/location/of/file2.js", ... ],
+ target : "/tmp/output.js",
+ debugTarget : "/tmp/output.debug.js", // merged file without compression.
+ translateJSON: "/tmp/translate.json",
+
+
+);
+x.packFiles(
+ "/location/of/temp_batch_dir",
+ "/location/of/output-compacted-file.js",
+ "/location/of/output-debug-merged-file.js"
+);
+
+ *</code>
+ *
+ * Notes for improving compacting:
+ * if you add a jsdoc comment
+ * <code>
+ * /**
+ * eval:var:avarname
+ * eval:var:bvarname
+ * ....
+ * </code>
+ * directly before an eval statement, it will compress all the code around the eval,
+ * and not rename the variables 'avarname'
+ *
+ * Dont try running this on a merged uncompressed large file - it's used to be horrifically slow. not sure about now..
+ * Best to use lot's of small classes, and use it to merge, as it will cache the compaction
+ *
+ *
+ *
+ * Notes for translation
+ * - translation relies on you using double quotes for strings if they need translating
+ * - single quoted strings are ignored.
+ *
+ * Generation of indexFiles
+ * - translateIndex = the indexfile
+ *
+ *
+ *
+ *
+
+ */
+Packer = function(cfg)
+{
+
+ XObject.extend(this, cfg);
+
+ if (this.srcfile) {
+ this.loadSourceFile();
+ }
+
+ if (!this.files) {
+ throw "No Files";
+ }
+
+
+ this.timer = new Date() * 1;
+ this.packAll();
+
+
+}
+Packer.prototype = {
+ /**
+ * @prop srcfiles {String} file containing a list of files/or classes to use.
+ */
+ srcfiles : false,
+
+ /**
+ * @prop files {Array} list of files to compress (must be full path)
+ */
+ files : false,
+ /**
+ * @prop target {String} target to write files to - must be full path.
+ */
+ target : '',
+ /**
+ * @prop debugTarget {String} target to write files debug version to (uncompacted)- must be full path.
+ */
+ debugTarget : '', // merged file without compression.
+ /**
+ * @prop tmpDir {String} (optional) where to put the temporary files.
+ * if you set this, then files will not be cleaned up
+ */
+ tmpDir : '/tmp',
+
+ translateJSON : '', // json based list of strings in all files.
+
+ /**
+ * @prop cleanup {Boolean} (optional) clean up temp files after done -
+ * Defaults to false if you set tmpDir, otherwise true.
+ */
+ cleanup : true,
+
+ /**
+ * @prop prefix {String} (optional) prefix of directory to be stripped of when
+ * Calculating md5 of filename
+ */
+ prefix : '',
+ out : '', // if no target is specified - then this will contain the result
+
+
+ loadSourceFile : function()
+ {
+ var lines = File.read(this.srcfile).split("\n");
+ var _this = this;
+ lines.forEach(function(f) {
+
+ if (/^\s*\//.test(f) || !/[a-z]+/i.test(f)) { // skip comments..
+ return;
+ }
+ if (/\.js$/.test(f)) {
+ _this.files.push( f);
+ // js file..
+ return;
+ }
+
+ //println("ADD"+ f.replace(/\./g, '/'));
+ var add = f.replace(/\./g, '/').replace(/\s+/g,'')+'.js';
+ if (_this.files.indexOf(f) > -1) {
+ return;
+ }
+ _this.files.push( add );
+
+ })
+ },
+
+
+ packAll : function() // do the packing (run from constructor)
+ {
+
+ //this.transOrigFile= bpath + '/../lang.en.js'; // needs better naming...
+ //File.write(this.transfile, "");
+ if (this.target) {
+ File.write(this.target, "");
+ }
+
+ if (this.debugTarget) {
+ File.write(this.debugTarget, "");
+ }
+
+ for(var i=0; i < this.files.length; i++) {
+ var file = this.files[i];
+
+ print("reading " +file );
+ if (!File.isFile(file)) {
+ print("SKIP (is not a file) " + file);
+ continue;
+ }
+
+ if (this.debugTarget) {
+ File.append(this.debugTarget, File.read(file));
+ }
+ // it's a good idea to check with 0 compression to see if the code can parse!!
+
+ // debug file..
+ //File.append(dout, str +"\n");
+
+
+
+ var minfile = this.tmpDir + '/' +file.replace(/\//g, '.');
+
+
+ // let's see if we have a min file already?
+ // this might happen if tmpDir is set ..
+ if (true && File.exists(minfile)) {
+ var mt = File.mtime(minfile);
+ var ot = File.mtime(file);
+ print("compare : " + mt + "=>" + ot);
+ if (mt >= ot) {
+ continue;
+ /*
+ // then the min'files time is > than original..
+ var str = File.read(minfile);
+ print("using MIN FILE "+ minfile);
+ if (str.length) {
+ File.append(outpath, str + "\n");
+ }
+
+ continue;
+ */
+ }
+
+ }
+
+ print("COMPRESSING ");
+ //var codeComp = pack(str, 10, 0, 0);
+ if (File.exists(minfile)) {
+ File.remove(minfile);
+ }
+ var str = File.read(file);
+ var str = this.pack(str, file, minfile);
+ if (str.length) {
+ File.write(minfile, str);
+ }
+
+
+
+ }
+ if (this.translateJSON) {
+
+
+ print("MERGING LANGUAGE");
+ var out = "if (typeof(_T) == 'undefined') { _T={};}\n"
+ if (this.target) {
+ File.write(this.target, out);
+ } else {
+ this.out += out;
+ }
+
+
+
+ File.write(this.translateJSON, "");
+ for(var i=0; i < this.files.length; i++) {
+ var file = this.files[i];
+ var transfile= this.tmpDir + '/' +file.replace(/\//g, '.') +'.lang.trans';
+ var transmd5 = this.tmpDir + '/' +file.replace(/\//g, '.') +'.lang';
+ if (File.exists(transmd5)) {
+ var str = File.read(transmd5);
+ if (str.length) {
+ if (this.target) {
+ File.append(this.target, str + "\n");
+ } else {
+ this.out += str + "\n";
+ }
+
+ }
+ if (this.cleanup) {
+ File.remove(transmd5);
+ }
+ }
+ if (File.exists(transfile)) {
+ var str = File.read(transfile);
+ if (str.length) {
+ File.append(this.translateJSON, str);
+ }
+ if (this.cleanup) {
+ File.remove(transfile);
+ }
+ }
+
+
+ }
+ }
+
+ print("MERGING SOURCE");
+
+ for(var i=0; i < this.files.length; i++) {
+ var file = this.files[i];
+ var minfile = this.tmpDir + '/' + file.replace(/\//g, '.');
+
+
+ if (!File.exists(minfile)) {
+ continue;
+ }
+ var str = File.read(minfile);
+ print("using MIN FILE "+ minfile);
+ if (str.length) {
+ if (this.target) {
+ File.append(this.target, str + "\n");
+ } else {
+ this.out += str + "\n";
+ }
+
+ }
+ if (this.cleanup) {
+ File.remove(minfile);
+ }
+
+ }
+
+
+
+
+ },
+ /**
+ * Core packing routine for a file
+ *
+ * @param str - str source text..
+ * @param fn - filename (for reference?)
+ * @param minfile - min file location...
+ *
+ */
+
+ pack : function (str,fn,minfile)
+ {
+
+ var tr = new TokenReader( { keepDocs :true, keepWhite : true, keepComments : true, sepIdents : true });
+ this.timerPrint("START" + fn);
+
+ // we can load translation map here...
+
+ var toks = tr.tokenize(new TextStream(str)); // dont merge xxx + . + yyyy etc.
+
+ // at this point we can write a language file...
+ if (this.translateJSON) {
+
+ this.writeTranslateFile(fn, minfile, toks);
+ }
+
+ this.activeFile = fn;
+
+ // and replace if we are generating a different language..
+
+ this.timerPrint("Tokenized");
+ //return;//
+ var sp = new ScopeParser(new TokenStream(toks));
+ this.timerPrint("Converted to Parser");
+ sp.packer = this;
+ sp.buildSymbolTree();
+ this.timerPrint("Built Sym tree");
+ sp.mungeSymboltree();
+ this.timerPrint("Munged Sym tree");
+ print(sp.warnings.join("\n"));
+ var out = CompressWhite(sp.ts, this);
+ this.timerPrint("Compressed");
+ return out;
+
+
+
+ },
+
+ timerPrint: function (str) {
+ var ntime = new Date() * 1;
+ var tdif = ntime -this.timer;
+ this.timer = ntime;
+ print('['+tdif+']'+str);
+ },
+
+ /**
+ *
+ * Translation concept...
+ * -> replace text strings with _T....
+ * -> this file will need inserting at the start of the application....
+ * -> we need to generate 2 files,
+ * -> a reference used to do the translation, and the _T file..
+ *
+ */
+
+ writeTranslateFile : function(fn, minfile, toks)
+ {
+
+ var map = {};
+ var _this = this;
+ toks.forEach(function (t) {
+ if (t.type == 'STRN' && t.name == 'DOUBLE_QUOTE') {
+ var sval = t.data.substring(1,t.data.length-1);
+ var ffn = fn.substring(_this.prefix.length);
+ map[sval] = _this.md5(ffn + '-' + sval);
+ }
+ })
+
+ var transfile = minfile + '.lang.trans';
+ var transmd5 = minfile + '.lang';
+ print("writeTranslateFile " + transfile);
+ var i = 0;
+ var v = '';
+ if (File.exists(transfile)) {
+ File.remove(transfile);
+ }
+ if (File.exists(transmd5)) {
+ File.remove(transmd5);
+ }
+ for(v in map) { i++; break };
+ if (!i ) {
+ return; // no strings in file...
+ }
+ var ffn = fn.substring(this.prefix.length);
+
+
+ File.write(transfile, "\n'" + ffn + "' : {");
+ var l = '';
+ var _tout = {}
+
+ File.write(transmd5, '');
+ for(v in map) {
+ File.append(transfile, l + "\n\t \"" + v + '" : "' + v + '"');
+ l = ',';
+ // strings are raw... - as the where encoded to start with!!!
+ File.append(transmd5, '_T["' + this.md5(ffn + '-' + v) + '"]="'+v+"\";\n");
+ }
+ File.append(transfile, "\n},"); // always one trailing..
+
+
+ },
+ md5 : function (string)
+ {
+
+ return GLib.compute_checksum_for_string(GLib.ChecksumType.MD5, string, string.length);
+
+ },
+ stringHandler : function(tok)
+ {
+ //print("STRING HANDLER");
+ // callback when outputing compressed file,
+ var data = tok.data;
+ if (!this.translateJSON) {
+ // print("TURNED OFF");
+ return data;
+ }
+ if (tok.name == 'SINGLE_QUOTE') {
+ return data;
+ }
+
+ var sval = data.substring(1,data.length-1);
+ // we do not clean up... quoting here!??!!?!?!?!?
+
+
+ // blank with tabs or spaces..
+ //if (!sval.replace(new RegExp("(\\\\n|\\\\t| )+",'g'), '').length) {
+ // return tok.outData;
+ // }
+
+ var sval = tok.data.substring(1,data.length-1);
+ var fn = this.activeFile.substring(this.prefix.length);
+
+
+ return '_T["' + this.md5(fn + '-' + sval) + '"]';
+
+
+ }
+
+
+};
diff --git a/JSDOC/Scope.js b/JSDOC/Scope.js
new file mode 100644
index 0000000..84e86da
--- /dev/null
+++ b/JSDOC/Scope.js
@@ -0,0 +1,316 @@
+//<Script type="text/javascript">
+
+/**
+* Scope stuff
+*
+* // FIXME - I need this to do next() without doccomments..
+*/
+
+Identifier = imports['JSDOC/Identifier.js'].Identifier
+XObject = imports.XObject.XObject;
+
+
+function Scope(braceN, parent, startTokN, lastIdent)
+{
+ if (lastIdent.length) {
+ // println("NEW SCOPE: " + lastIdent);
+ }
+
+ this.braceN = braceN
+ this.parent = parent;
+ this.id = startTokN;
+ this.identifiers = { };
+ this.subScopes = [];
+ this.hints = { };
+ this.ident = lastIdent;
+
+
+ //println("ADD SCOPE(" + this.id + ") TO "+ (parent ? this.parent.id : 'TOP') + "<BR/>");
+
+ if (parent) {
+ this.parent.subScopes.push(this);
+ }
+
+}
+
+
+
+
+
+
+
+Scope.prototype = {
+
+ id : 0,
+ braceN : -1,
+ parent : false,
+ subScopes : false,
+ identifiers : false, // map of identifiers to {Identifier} objects
+ hints: false,
+ mungeM : true,
+ ident: '',
+
+ munged : false,
+ protectedVars : {}, // only used by to parent..
+ declareIdentifier : function(symbol, token) {
+
+ //println("ADD IDENT(" + this.id + "):<B>" + symbol+"</B><BR/>");
+
+ if (typeof(this.identifiers[symbol])== 'undefined') {
+
+ this.identifiers[symbol] = new Identifier(symbol, this);
+
+ }
+ if (typeof(token) != 'undefined') { // shoudl this happen?
+ token.identifier = this.identifiers[symbol];
+
+ }
+ if (this.braceN < 0) {
+ // then it's global...
+ this.identifiers[symbol].toMunge = false;
+ }
+ this.addToParentScope(symbol);
+ return this.identifiers[symbol];
+ },
+ getIdentifier : function(symbol) {
+ return (typeof(this.identifiers[symbol])== 'undefined') ? false : this.identifiers[symbol];
+ },
+
+ addHint : function(varName, varType) {
+ this.hint[varName] = varType;
+ },
+ preventMunging : function() {
+ this.mungeM = false;
+ },
+
+ usedsymcache : false,
+
+ getUsedSymbols : function() {
+
+ var result = [];
+ // if (this.usedsymcache !== false) {
+ // return this.usedsymcache;
+ //}
+
+ var idents = this.identifiers;
+ for(var i in idents) {
+ //println('<b>'+i+'</b>='+typeof(idents[i]) +'<br/>');
+ var identifier = this.identifiers[i];
+ var mungedValue = identifier.mungedValue
+ if (!mungedValue.length) {
+ //println(identifier.toSource());
+ mungedValue = identifier.name;
+ }
+ result.push(mungedValue);
+ }
+ //println("Symbols for ("+ this.id +"): <B>" + result.join(',') + "</B><BR/>");
+ //this.usedsymcache = result;
+ return result;
+ },
+
+ getAllUsedSymbols :function() {
+ var result = this.getUsedSymbols();
+ var scope = this.parent;
+ while (scope !== false) {
+ //println("addused:"+scope.id);
+ result = result.concat(scope.getUsedSymbols());
+ scope = scope.parent;
+ }
+ //println("Done - addused");
+ return result;
+ },
+ /** - we need to register short vairalbes so they never get munged into.. */
+ addToParentScope: function(ident)
+ {
+ if (ident.length > 2) {
+ return;
+ }
+ var scope = this.parent;
+ while (scope !== false) {
+ //println("addused:"+scope.id);
+ if (!scope.parent) {
+ scope.protectedVars[ident] = true;
+ }
+ scope = scope.parent;
+ }
+
+ },
+ isProtectedVar: function(ident)
+ {
+ if (ident.length > 2) {
+ return false;
+ }
+ var scope = this.parent;
+ while (scope !== false) {
+ //println("addused:"+scope.id);
+ if (!scope.parent) {
+ if (typeof(scope.protectedVars[ident]) != 'undefined') return true;
+ }
+ scope = scope.parent;
+ }
+ return false;
+ },
+
+ /**
+ * set's all the munged values on the identifiers.
+ *
+ *
+ */
+
+ munge :function()
+ {
+
+ if (!this.mungeM) {
+ // Stop right here if this scope was flagged as unsafe for munging.
+ // println("MUNGE: SKIP - Scope" + this.id+"</BR>");
+ return;
+ }
+ if (this.munged) {
+ return;
+ }
+
+
+
+
+ var pickFromSet = 1;
+
+ // Do not munge symbols in the global scope!
+ if (this.parent) {
+
+ var all = [];
+ for (var ii in this.identifiers) {
+ all.push(ii);
+ }
+ //print("MUNGE: " + all.join(', '));
+
+ //println("MUNGE: Building FreeSyms:" + this.id+"</BR>");
+
+ var freeSymbols = [];
+ var sy = this.getAllUsedSymbols();
+
+ var addSyms=function(batch)
+ {
+ for(var i =0;i<batch.length;i++) {
+ if (sy.indexOf(batch[i]) > -1) {
+ continue;
+ }
+ freeSymbols.push(batch[i]);
+ }
+ }
+
+ addSyms(Scope.ones);
+
+ var repsym = '';
+ //println(freeSymbols.toSource());
+
+ //println("MUNGE: Replacing " + this.id+"</BR>");
+ for (var i in this.identifiers) {
+
+ // is the identifer in the global scope!?!!?
+
+
+ if (!this.identifiers[i].toMunge) {
+ //print("SKIP toMunge==false : " + i)
+ continue;
+ }
+
+ if (this.isProtectedVar(i)) {
+ //print("SKIP PROTECTED: " + i)
+ continue; //
+ }
+
+
+
+ //if (this.identifiers[i].constructor != Identifier) {
+ // print("SKIP NOT IDENTIFIER : " + i)
+ // continue;
+ // }
+ // println("IDENT:" +i+'</BR>');
+
+ if (!repsym.length) {
+ if (!freeSymbols.length) {
+ addSyms(JSDOC.Scope.twos);
+ }
+ repsym = freeSymbols.shift(); // pop off beginngin???
+ }
+
+ var identifier = this.identifiers[i];
+ //println(typeof(identifier.name));
+ var mungedValue = identifier.name;
+
+ //println([ repsym,mungedValue ]);
+
+ if (this.mungeM && repsym.length < mungedValue.length) {
+ //print("REPLACE:"+ mungedValue +" with " + repsym );
+ mungedValue = repsym;
+ repsym = '';
+ }
+
+ identifier.mungedValue = mungedValue;
+ }
+ //println("MUNGE: Done " + this.id+"</BR>");
+ }
+ this.munged = true;
+ //println("Doing sub scopes");
+ for (var j = 0; j < this.subScopes.length; j++) {
+ var ss = this.subScopes[j];
+ ss.munge();
+ }
+ }
+
+
+};
+
+
+
+
+
+XObject.extend(Scope, {
+
+ builtin : ["NaN","top"],
+ skips : [ 'as', 'is', 'do', 'if', 'in', 'for', 'int', 'new', 'try', 'use', 'var', "NaN","top"],
+
+ ones : [],
+ twos : [],
+ threes : [],
+ init : function () {
+ /* cache it later?
+ if (File.exists('/tmp/var_list_ones.js')) {
+ eval("JSDOC.Scope.ones = " + File.read('/tmp/var_list_ones.js'));
+ eval("JSDOC.Scope.twos = " + File.read('/tmp/var_twos_ones.js'));
+ eval("JSDOC.Scope.threes = " + File.read('/tmp/var_threes_ones.js'));
+ }
+ */
+ this.ones = 'A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z'.split(',');
+ var a = this.ones;
+ var n = a.concat( '0,1,2,3,4,5,6,7,8,9'.split(','));
+ for(var i = 0; i < a.length; i++) {
+ for(var j = 0; j < n.length; j++) {
+ var tw = a[i] + n[j];
+ if (this.skips.indexOf(tw) < 0) {
+ this.twos.push(tw);
+ }
+
+ /*
+ for(var k = 0; k < n.length; k++) {
+ var thr = a[i] + n[j] + n[k];
+ //println("thr="+ thr + ":iOf="+this.skips.indexOf(thr) );
+ if (this.skips.indexOf(thr) < 0) {
+ //println("+"+thr);
+ this.threes.push(thr);
+ }
+
+ }
+ */
+ }
+ }
+ //println("done creating var list");
+ //println("threes="+ this.threes.toSource());
+ //throw "DONE";
+
+
+ }
+})
+// init the scope constants..
+Scope.init();
+
\ No newline at end of file
diff --git a/JSDOC/ScopeParser.js b/JSDOC/ScopeParser.js
new file mode 100644
index 0000000..1b315b0
--- /dev/null
+++ b/JSDOC/ScopeParser.js
@@ -0,0 +1,859 @@
+//<Script type="text/javascript">
+
+Scope = imports['JSDOC/Scope.js'].Scope;
+
+/**
+* Scope stuff
+*
+* // FIXME - I need this to do next() without doccomments..
+*/
+
+ScopeParser = function(ts) {
+ this.ts = ts; // {TokenStream}
+ this.warnings = [];
+ this.scopes = [];
+ this.indexedScopes = {};
+ this.timer = new Date() * 1;
+ this.debug = false;
+}
+
+// list of keywords that should not be used in object literals.
+ScopeParser.idents = [
+ "break",
+ "case",
+ "continue",
+ "default",
+ "delete",
+ "do",
+ "else",
+ "export",
+ "false",
+ "for",
+ "function",
+ "if",
+ "import",
+ "in",
+ "new",
+ "null",
+ "return",
+ "switch",
+ "this",
+ "true",
+ "typeof",
+ "var",
+ "void",
+ "while",
+ "with",
+
+ "catch",
+ "class",
+ "const",
+ "debugger",
+ "enum",
+ "extends",
+ "finally",
+ "super",
+ "throw",
+ "try",
+
+ "abstract",
+ "boolean",
+ "byte",
+ "char",
+ "double",
+ "final",
+ "float",
+ "goto",
+ "implements",
+ "instanceof",
+ "int",
+ "interface",
+ "long",
+ "native",
+ "package",
+ "private",
+ "protected",
+ "public",
+ "short",
+ "static",
+ "synchronized",
+ "throws",
+ "transient",
+ "include",
+ "undefined"
+];
+
+
+ScopeParser.prototype = {
+ timer: 0,
+ timerPrint: function (str) {
+ var ntime = new Date() * 1;
+ var tdif = ntime -this.timer;
+ this.timer = ntime;
+ var pref = '';
+ if (tdif > 100) { //slower ones..
+ pref = '***';
+ }
+ println(pref+'['+tdif+']'+str);
+
+ },
+ warn: function(s) {
+ //print('****************' + s);
+ this.warnings.push(s);
+ //println("WARNING:" + htmlescape(s) + "<BR>");
+ },
+ // defaults should not be initialized here =- otherwise they get duped on new, rather than initalized..
+ warnings : false,
+ ts : false,
+ scopes : false,
+ global : false,
+ mode : "", //"BUILDING_SYMBOL_TREE",
+ braceNesting : 0,
+ indexedScopes : false,
+ munge: true,
+
+
+
+
+
+ buildSymbolTree : function()
+ {
+ //println("<PRE>");
+
+ this.ts.rewind();
+ this.braceNesting = 0;
+ this.scopes = [];
+
+
+
+
+ this.globalScope = new Scope(-1, false, -1, '');
+ indexedScopes = { 0 : this.globalScope };
+
+ this.mode = 'BUILDING_SYMBOL_TREE';
+ this.parseScope(this.globalScope);
+
+ //print("---------------END PASS 1 ---------------- ");
+
+ },
+ mungeSymboltree : function()
+ {
+
+ if (!this.munge) {
+ return;
+ }
+
+ // One problem with obfuscation resides in the use of undeclared
+ // and un-namespaced global symbols that are 3 characters or less
+ // in length. Here is an example:
+ //
+ // var declaredGlobalVar;
+ //
+ // function declaredGlobalFn() {
+ // var localvar;
+ // localvar = abc; // abc is an undeclared global symbol
+ // }
+ //
+ // In the example above, there is a slim chance that localvar may be
+ // munged to 'abc', conflicting with the undeclared global symbol
+ // abc, creating a potential bug. The following code detects such
+ // global symbols. This must be done AFTER the entire file has been
+ // parsed, and BEFORE munging the symbol tree. Note that declaring
+ // extra symbols in the global scope won't hurt.
+ //
+ // Note: Since we go through all the tokens to do this, we also use
+ // the opportunity to count how many times each identifier is used.
+
+ this.ts.rewind();
+ this.braceNesting = 0;
+ this.scopes= [];
+ this.mode = 'PASS2_SYMBOL_TREE';
+
+ //println("MUNGING?");
+
+ this.parseScope(this.globalScope);
+ this.globalScope.munge();
+ },
+
+
+ log : function(str)
+ {
+ print (" ".substring(0, this.braceNesting*2) + str);
+
+ //println("<B>LOG:</B>" + htmlescape(str) + "<BR/>\n");
+ },
+ logR : function(str)
+ {
+ //println("<B>LOG:</B>" + str + "<BR/>");
+ },
+
+
+
+
+
+
+ parseScope : function(scope) // parse a token stream..
+ {
+ //this.timerPrint("parseScope EnterScope");
+ //this.log(">>> ENTER SCOPE" + this.scopes.length);
+ var symbol;
+ var token;
+
+ var identifier;
+
+ var expressionBraceNesting = this.braceNesting;
+
+ var parensNesting = 0;
+
+ var isObjectLitAr = [ false ];
+ var isInObjectLitAr;
+ this.scopes.push(scope);
+ token = this.ts.lookTok(1);
+ while (token) {
+ // this.timerPrint("parseScope AFTER lookT: " + token.toString());
+
+ //this.log(token.data);
+ if (token.type == 'NAME') {
+ // print('*' + token.data);
+ }
+ switch(token.type + '.' + token.name) {
+ case "KEYW.VAR":
+ case "KEYW.CONST": // not really relivant as it's only mozzy that does this.
+
+ //this.log("parseScope GOT VAR/CONST : " + token.toString());
+ while (true) {
+ token = this.ts.nextTok();
+ !this.debug|| print( token.toString());
+
+ if (!token) { // can return false at EOF!
+ break;
+ }
+ if (token.name == "VAR" || token.data == ',') { // kludge..
+ continue;
+ }
+ //this.logR("parseScope GOT VAR : <B>" + token.toString() + "</B>");
+ if (token.type !="NAME") {
+ for(var i = Math.max(this.ts.cursor-10,0); i < this.ts.cursor+1; i++) {
+ print(this.ts.tokens[i].toString());
+ }
+
+ print( "var without ident");
+ Seed.quit()
+ }
+
+
+ if (this.mode == "BUILDING_SYMBOL_TREE") {
+ identifier = scope.getIdentifier(token.data) ;
+
+ if (identifier == false) {
+ scope.declareIdentifier(token.data, token);
+ } else {
+ token.identifier = identifier;
+ this.warn("(SCOPE) The variable " + token.data + ' (line:' + token.line + ") has already been declared in the same scope...");
+ }
+ }
+
+ token = this.ts.nextTok();
+ !this.debug|| print(token.toString());
+ /*
+ assert token.getType() == Token.SEMI ||
+ token.getType() == Token.ASSIGN ||
+ token.getType() == Token.COMMA ||
+ token.getType() == Token.IN;
+ */
+ if (token.name == "IN") {
+ break;
+ } else {
+ //var bn = this.braceNesting;
+ this.parseExpression();
+ //this.braceNesting = bn;
+ //this.logR("parseScope DONE : <B>ParseExpression</B> - tok is:" + this.ts.lookT(0).toString());
+
+ token = this.ts.lookTok(1);
+ !this.debug|| print("AFTER EXP: " + token.toString());
+ if (token.data == ';') {
+ break;
+ }
+ }
+ }
+ break;
+ case "KEYW.FUNCTION":
+ //println("<i>"+token.data+"</i>");
+ var bn = this.braceNesting;
+ this.parseFunctionDeclaration();
+ this.braceNesting = bn;
+ break;
+
+ case "PUNC.LEFT_CURLY": // {
+ //println("<i>"+token.data+"</i>");
+ isObjectLitAr.push(false);
+ this.braceNesting++;
+
+ //print(">>>>>> OBJLIT PUSH(false)" + this.braceNesting);
+ break;
+
+ case "PUNC.RIGHT_CURLY": // }
+ //println("<i>"+token.data+"</i>");
+ this.braceNesting--;
+ isObjectLitAr.pop();
+ //print(">>>>>> OBJLIT POP"+ this.braceNesting);
+ //assert braceNesting >= scope.getBra ceNesting();
+
+ if (this.braceNesting < expressionBraceNesting) {
+ var ls = this.scopes.pop();
+ ls.getUsedSymbols();
+ // eat symbol if we are currently at {
+ if (this.ts.look(0).data == '{') {
+ this.ts.nextTok();
+ }
+
+ //print("<<<<<<<EXIT SCOPE" +this.scopes.length);
+ return;
+ }
+ break;
+
+ case "KEYW.WITH":
+ //println("<i>"+token.data+"</i>");
+ if (this.mode == "BUILDING_SYMBOL_TREE") {
+ // Inside a 'with' block, it is impossible to figure out
+ // statically whether a symbol is a local variable or an
+ // object member. As a consequence, the only thing we can
+ // do is turn the obfuscation off for the highest scope
+ // containing the 'with' block.
+ this.protectScopeFromObfuscation(scope);
+ this.warn("Using 'with' is not recommended." + (this.munge ? " Moreover, using 'with' reduces the level of compression!" : ""), true);
+ }
+ break;
+
+ case "KEYW.CATCH":
+ //println("<i>"+token.data+"</i>");
+ this.parseCatch();
+ break;
+ /*
+ case Token.SPECIALCOMMENT:
+ if (mode == BUILDING_SYMBOL_TREE) {
+ protectScopeFromObfuscation(scope);
+ this.warn("Using JScript conditional comments is not recommended." + (munge ? " Moreover, using JScript conditional comments reduces the level of compression." : ""), true);
+ }
+ break;
+ */
+
+ case "STRN.DOUBLE_QUOTE": // used for object lit detection..
+ case "STRN.SINGLE_QUOTE":
+ //println("<i>"+token.data+"</i>");
+ if (this.ts.lookTok(-1).data == '{' && this.ts.lookTok(1).data == ':') {
+ // then we are in an object lit.. -> we need to flag the brace as such...
+ isObjectLitAr.pop();
+ isObjectLitAr.push(true);
+ //print(">>>>>> OBJLIT REPUSH(true)");
+ }
+ isInObjectLitAr = isObjectLitAr[isObjectLitAr.length-1];
+
+ if (isInObjectLitAr && this.ts.lookTok(1).data == ':' &&
+ ( this.ts.lookTok(-1).data == '{' || this.ts.lookTok(-1).data == ':' )) {
+ // see if we can replace..
+ // remove the quotes..
+ // should do a bit more checking!!!! (what about wierd char's in the string..
+ var str = token.data.substring(1,token.data.length-1);
+ if (/^[a-z_]+$/i.test(str) && ScopeParser.idents.indexOf(str) < 0) {
+ token.outData = str;
+ }
+
+
+
+ }
+
+
+
+ break;
+
+ case "NAME.NAME":
+
+ //print("DEAL WITH NAME:");
+ // got identifier..
+
+ // look for { ** : <- indicates obj literal.. ** this could occur with numbers ..
+ if ((this.ts.lookTok(-1).data == "{") && (this.ts.lookTok(1).data == ":")) {
+ isObjectLitAr.pop();
+ isObjectLitAr.push(true);
+ //print(">>>>>> OBJLIT REPUSH(true)");
+ //println("<i>"+token.data+"</i>");
+ break;
+ }
+ // print("DEAL WITH obj lit:");
+ isInObjectLitAr = isObjectLitAr[isObjectLitAr.length-1];
+
+ if (isInObjectLitAr && (this.ts.lookTok(1).data == ":") && (this.ts.lookTok(-1).data == ",")) {
+ // skip, it's an object lit key..
+ //println("<i>"+token.data+"</i>");
+ break;
+ }
+
+
+ // skip anyting with "." before it..!!
+
+ if (this.ts.lookTok(-1).data == ".") {
+ // skip, it's an object prop.
+ //println("<i>"+token.data+"</i>");
+ break;
+ }
+ symbol = token.data;
+ if (this.mode == 'PASS2_SYMBOL_TREE') {
+
+ //println("GOT IDENT: -2 : " + this.ts.lookT(-2).toString() + " <BR> ..... -1 : " + this.ts.lookT(-1).toString() + " <BR> ");
+
+ //print ("MUNGE?" + symbol);
+
+ //println("GOT IDENT: <B>" + symbol + "</B><BR/>");
+
+ //println("GOT IDENT (2): <B>" + symbol + "</B><BR/>");
+ identifier = this.getIdentifier(symbol, scope);
+
+ if (identifier == false) {
+// BUG!find out where builtin is defined...
+ if (symbol.length <= 3 && Scope.builtin.indexOf(symbol) < 0) {
+ // Here, we found an undeclared and un-namespaced symbol that is
+ // 3 characters or less in length. Declare it in the global scope.
+ // We don't need to declare longer symbols since they won't cause
+ // any conflict with other munged symbols.
+ this.globalScope.declareIdentifier(symbol, token);
+ this.warn("Found an undeclared symbol: " + symbol + ' (line:' + token.line + ')', true);
+ }
+
+ //println("GOT IDENT IGNORE(3): <B>" + symbol + "</B><BR/>");
+ } else {
+ token.identifier = identifier;
+ identifier.refcount++;
+ }
+ }
+
+ break;
+ //println("<B>SID</B>");
+ default:
+ if (token.type != 'KEYW') {
+ break;
+ }
+ // print("Check eval:");
+
+ symbol = token.data;
+
+ if (this.mode == 'BUILDING_SYMBOL_TREE') {
+
+ if (symbol == "eval") {
+ // look back one and see if we can find a comment!!!
+ if (this.ts.look(-1).type == "COMM") {
+ // look for eval:var:noreplace\n
+ var _t = this;
+ this.ts.look(-1).data.replace(/eval:var:([a-z_]+)/ig, function(m, a) {
+
+ var hi = _t.getIdentifier(a, scope);
+ // println("PROTECT "+a+" from munge" + (hi ? "FOUND" : "MISSING"));
+ if (hi) {
+ // println("PROTECT "+a+" from munge");
+ hi.toMunge = false;
+ }
+
+ });
+
+
+ } else {
+
+
+ this.protectScopeFromObfuscation(scope);
+ this.warn("Using 'eval' is not recommended. (use eval:var:noreplace in comments to optimize) " + (this.munge ? " Moreover, using 'eval' reduces the level of compression!" : ""), true);
+ }
+
+ }
+
+ }
+ break;
+
+
+ } // end switch
+
+
+ //this.timerPrint("parseScope TOK : " + token.toString());
+ token = this.ts.nextTok();
+ //if (this.ts.nextT()) break;
+
+ }
+ //print("<<<<<<<EXIT SCOPE ERR?" +this.scopes.length);
+ },
+
+
+ parseExpression : function() {
+
+ // Parse the expression until we encounter a comma or a semi-colon
+ // in the same brace nesting, bracket nesting and paren nesting.
+ // Parse functions if any...
+ //println("<i>EXP</i><BR/>");
+ !this.debug || print("PARSE EXPR");
+ var symbol;
+ var token;
+ var currentScope;
+ var identifier;
+
+ var expressionBraceNesting = this.braceNesting;
+ var bracketNesting = 0;
+ var parensNesting = 0;
+ var isInObjectLitAr;
+ var isObjectLitAr = [ false ];
+ while (token = this.ts.lookTok()) {
+
+
+
+ currentScope = this.scopes[this.scopes.length-1];
+
+ //println("<i>"+token.data+"</i>");
+ //this.log("EXP:" + token.data);
+ switch (token.type) {
+ case 'PUNC':
+ switch(token.data) {
+
+ case ';':
+ case ',':
+ if (this.braceNesting == expressionBraceNesting &&
+ bracketNesting == 0 &&
+ parensNesting == 0) {
+
+ return;
+ }
+ break;
+
+
+
+ case '{': //Token.LC:
+ isObjectLitAr.push(false);
+
+ this.braceNesting++;
+ ///print(">>>>> EXP PUSH(false)"+this.braceNesting);
+ break;
+
+ case '}': //Token.RC:
+ this.braceNesting--;
+ isObjectLitAr.pop();
+ //print(">>>>> EXP POP" + this.braceNesting);
+ // assert braceNesting >= expressionBraceNesting;
+ break;
+
+ case '[': //Token.LB:
+ bracketNesting++;
+ break;
+
+ case ']': //Token.RB:
+ bracketNesting--;
+ break;
+
+ case '(': //Token.LP:
+ parensNesting++;
+ break;
+
+ case ')': //Token.RP:
+ parensNesting--;
+ break;
+ }
+ break;
+
+ case 'STRN': // used for object lit detection..
+ if (this.ts.lookTok(-1).data == "{" && this.ts.lookTok(1).data == ":" ) {
+ // then we are in an object lit.. -> we need to flag the brace as such...
+ isObjectLitAr.pop();
+ isObjectLitAr.push(true);
+ //print(">>>>> EXP PUSH(true)");
+ }
+
+
+
+ isInObjectLitAr = isObjectLitAr[isObjectLitAr.length-1];
+ if (isInObjectLitAr && this.ts.lookTok(1).data == ":" &&
+ ( this.ts.lookTok(-1).data == "{" || this.ts.lookTok(-1).data == "," )) {
+ // see if we can replace..
+ // remove the quotes..
+ var str = token.data.substring(1,token.data.length-1);
+ if (/^[a-z_]+$/i.test(str) && ScopeParser.idents.indexOf(str) < 0) {
+ token.outData = str;
+ }
+
+
+
+ }
+
+ break;
+
+
+
+ case 'NAME':
+
+ symbol = token.data;
+
+ if (this.ts.look(0).data == "{" && this.ts.lookTok(2).data == ":") {
+ // then we are in an object lit.. -> we need to flag the brace as such...
+ isObjectLitAr.pop();
+ isObjectLitAr.push(true);
+ //print(">>>>> EXP PUSH(true)");
+ break;
+ }
+ isInObjectLitAr = isObjectLitAr[isObjectLitAr.length-1];
+ if (isInObjectLitAr && this.ts.lookTok(0).data == "," && this.ts.lookTok(2).data == ":") {
+ break;
+ }
+ //print(this.ts.lookTok(0).data);
+ if (this.ts.lookTok(0).data == ".") {
+ //skip '.'
+ break;
+ }
+
+ if (this.mode == 'PASS2_SYMBOL_TREE') {
+
+ identifier = this.getIdentifier(symbol, currentScope);
+ //println("<B>??</B>");
+ if (identifier == false) {
+
+ if (symbol.length <= 3 && Scope.builtin.indexOf(symbol) < 0) {
+ // Here, we found an undeclared and un-namespaced symbol that is
+ // 3 characters or less in length. Declare it in the global scope.
+ // We don't need to declare longer symbols since they won't cause
+ // any conflict with other munged symbols.
+ this.globalScope.declareIdentifier(symbol, token);
+ this.warn("Found an undeclared symbol: " + symbol + ' (line:' + token.line + ')', true);
+ } else {
+ //println("undeclared")
+ }
+
+
+ } else {
+ //println("<B>++</B>");
+ token.identifier = identifier;
+ identifier.refcount++;
+ }
+
+ }
+ break;
+
+
+
+
+ //println("<B>EID</B>");
+ case 'KEYW':
+
+ if (token.name == "FUNCTION") {
+
+ this.parseFunctionDeclaration();
+ break;
+ }
+
+
+
+ symbol = token.data;
+ if (this.mode == 'BUILDING_SYMBOL_TREE') {
+
+ if (symbol == "eval") {
+ if (this.ts.look(-1).type == 'COMM') {
+ // look for eval:var:noreplace\n
+ var _t = this;
+ this.ts.look(-1).data.replace(/eval:var:([a-z]+)/ig, function(m, a) {
+ var hi = _t.getIdentifier(a, currentScope);
+ //println("PROTECT "+a+" from munge" + (hi ? "FOUND" : "MISSING"));
+ if (hi) {
+ // println("PROTECT "+a+" from munge");
+ hi.toMunge = false;
+ }
+
+
+ });
+
+ } else {
+ this.protectScopeFromObfuscation(currentScope);
+ this.warn("Using 'eval' is not recommended." + (this.munge ? " Moreover, using 'eval' reduces the level of compression!" : ""), true);
+ }
+
+
+ }
+ break;
+ }
+
+ }
+ if (!this.ts.nextTok()) break;
+ }
+ },
+
+
+ parseCatch : function() {
+
+ var symbol;
+ var token;
+ var currentScope;
+ var identifier;
+
+ //token = getToken(-1);
+ //assert token.getType() == Token.CATCH;
+ token = this.ts.nextTok();
+ //assert token.getType() == Token.LP; (
+ token = this.ts.nextTok();
+ //assert token.getType() == Token.NAME;
+
+ symbol = token.data;
+ currentScope = this.scopes[this.scopes.length-1];
+
+ if (this.mode == 'BUILDING_SYMBOL_TREE') {
+ // We must declare the exception identifier in the containing function
+ // scope to avoid errors related to the obfuscation process. No need to
+ // display a warning if the symbol was already declared here...
+ currentScope.declareIdentifier(symbol, token);
+ } else {
+ //?? why inc the refcount?? - that should be set when building the tree???
+ identifier = this.getIdentifier(symbol, currentScope);
+ identifier.refcount++;
+ }
+
+ token = this.ts.nextTok();
+ //assert token.getType() == Token.RP; // )
+ },
+
+ parseFunctionDeclaration : function()
+ {
+ // print("PARSE FUNCTION");
+ var symbol;
+ var token;
+ var currentScope = false;
+ var fnScope = false;
+ var identifier;
+ //this.logR("<B>PARSING FUNCTION</B>");
+ currentScope = this.scopes[this.scopes.length-1];
+
+ token = this.ts.nextTok();
+ if (token.type == "NAME") {
+ if (this.mode == 'BUILDING_SYMBOL_TREE') {
+ // Get the name of the function and declare it in the current scope.
+ symbol = token.data;
+ if (currentScope.getIdentifier(symbol) != false) {
+ this.warn("The function " + symbol + " has already been declared in the same scope...", true);
+ }
+ currentScope.declareIdentifier(symbol,token);
+ }
+ token = this.ts.nextTok();
+ }
+
+ //assert token.getType() == Token.LP;
+ if (this.mode == 'BUILDING_SYMBOL_TREE') {
+ fnScope = new Scope(this.braceNesting, currentScope, token.n, '');
+
+ //println("STORING SCOPE" + this.ts.cursor);
+
+ this.indexedScopes[this.ts.cursor] = fnScope;
+
+ } else {
+ //qln("FETCHING SCOPE" + this.ts.cursor);
+ fnScope = this.indexedScopes[this.ts.cursor];
+
+ }
+
+ // Parse function arguments.
+ var argpos = 0;
+ while (this.ts.lookTok().data != ')') { //(token = consumeToken()).getType() != Token.RP) {
+ token = this.ts.nextTok();
+ // print ("FUNC ARGS: " + token.toString())
+ //assert token.getType() == Token.NAME ||
+ // token.getType() == Token.COMMA;
+ if (token.type == 'NAME' && this.mode == 'BUILDING_SYMBOL_TREE') {
+ symbol = token.data;
+ identifier = fnScope.declareIdentifier(symbol,token);
+ if (symbol == "$super" && argpos == 0) {
+ // Exception for Prototype 1.6...
+ identifier.preventMunging();
+ }
+ argpos++;
+ }
+ }
+
+ token = this.ts.nextTok();
+ // assert token.getType() == Token.LC;
+ this.braceNesting++;
+
+ token = this.ts.nextTok();
+ if (token.type == "STRN" && this.ts.lookTok(1).data == ';') {
+ /*
+
+ NOT SUPPORTED YET!?!!?!
+
+ // This is a hint. Hints are empty statements that look like
+ // "localvar1:nomunge, localvar2:nomunge"; They allow developers
+ // to prevent specific symbols from getting obfuscated (some heretic
+ // implementations, such as Prototype 1.6, require specific variable
+ // names, such as $super for example, in order to work appropriately.
+ // Note: right now, only "nomunge" is supported in the right hand side
+ // of a hint. However, in the future, the right hand side may contain
+ // other values.
+ consumeToken();
+ String hints = token.getValue();
+ // Remove the leading and trailing quotes...
+ hints = hints.substring(1, hints.length() - 1).trim();
+ StringTokenizer st1 = new StringTokenizer(hints, ",");
+ while (st1.hasMoreTokens()) {
+ String hint = st1.nextToken();
+ int idx = hint.indexOf(':');
+ if (idx <= 0 || idx >= hint.length() - 1) {
+ if (mode == BUILDING_SYMBOL_TREE) {
+ // No need to report the error twice, hence the test...
+ this.warn("Invalid hint syntax: " + hint, true);
+ }
+ break;
+ }
+ String variableName = hint.substring(0, idx).trim();
+ String variableType = hint.substring(idx + 1).trim();
+ if (mode == BUILDING_SYMBOL_TREE) {
+ fnScope.addHint(variableName, variableType);
+ } else if (mode == CHECKING_SYMBOL_TREE) {
+ identifier = fnScope.getIdentifier(variableName);
+ if (identifier != null) {
+ if (variableType.equals("nomunge")) {
+ identifier.preventMunging();
+ } else {
+ this.warn("Unsupported hint value: " + hint, true);
+ }
+ } else {
+ this.warn("Hint refers to an unknown identifier: " + hint, true);
+ }
+ }
+ }
+ */
+ }
+
+ this.parseScope(fnScope);
+ // now pop it off the stack!!!
+
+
+
+ },
+
+ protectScopeFromObfuscation : function(scope) {
+ //assert scope != null;
+
+ if (scope == this.globalScope) {
+ // The global scope does not get obfuscated,
+ // so we don't need to worry about it...
+ return;
+ }
+
+ // Find the highest local scope containing the specified scope.
+ while (scope && scope.parent != this.globalScope) {
+ scope = scope.parent;
+ }
+
+ //assert scope.getParentScope() == globalScope;
+ scope.preventMunging();
+ },
+
+ getIdentifier: function(symbol, scope) {
+ var identifier;
+ while (scope != false) {
+ identifier = scope.getIdentifier(symbol);
+ //println("ScopeParser.getIdentgetUsedSymbols("+symbol+")=" + scope.getUsedSymbols().join(','));
+ if (identifier) {
+ return identifier;
+ }
+ scope = scope.parent;
+ }
+ return false;
+ }
+};
\ No newline at end of file
diff --git a/JSDOC/Token.js b/JSDOC/Token.js
index cad3547..0486c68 100644
--- a/JSDOC/Token.js
+++ b/JSDOC/Token.js
@@ -4,18 +4,41 @@ imports['Object.js'].load(Object);
JSDOC = imports['JSDOC.js'].JSDOC;
console = imports['console.js'].console;
/**
- @constructor
+ * @class Token
+ *
+ * @prop data {String} raw value of token
+ * @prop type {String} type of token
+ * TOKN (unknown) - name is UNKNOWN_TOKEN
+ * KEYW (keyword) - name is upper case version of keyword
+ * NAME (name/identifier) - name is NAME
+ * COMM (comment) - name is MULTI_LINE_COMM, JSDOC, SINGLE_LINE_COMM
+ * PUNC (puctuation) - name is String description of punctionan (eg LEFTPARAM)
+ * WHIT (white space) - name is SPACE,NEWLINE
+ * STRN (string) - name is DOBULE_QUOTE, SINGLE_QUOTE
+ * NUMB (number) - name is OCTAL,DECIMAL,HEC_DEC
+ * REGX (reg.expression) - name is REGX
+ * @prop name {String} see type details above
+ * @prop indentifier {Identifier} identifier class if relivant
+ *
*/
Token = Object.define(
- function(data, type, name) {
+ function(data, type, name, line) {
this.data = data;
this.type = type;
this.name = name;
- this.prefix = '';
+ this.line = line;
+ this.prefix = '';
+ this.outData = false; // used by packer/scopeparser
+ this.identifier = false; // used by scope
},
Object,
{
+ toString: function()
+ {
+ return 'line:' + this.line + ', type:' + this.type + ', name:' + this.name + ', data:' + this.data;
+ },
+
toRaw : function(lvl)
{
diff --git a/JSDOC/TokenReader.js b/JSDOC/TokenReader.js
index 6f5d959..4d795cf 100644
--- a/JSDOC/TokenReader.js
+++ b/JSDOC/TokenReader.js
@@ -1,22 +1,24 @@
//<script type="text/javascript">
-imports['Object.js'].load(Object);
+//imports['Object.js'].load(Object);
+XObject = imports.XObject.XObject;
console = imports['console.js'].console;
JSDOC = imports['JSDOC.js'].JSDOC;
Token = imports['JSDOC/Token.js'].Token;
-Lang = imports['JSDOC/Token.js'].Lang;
+Lang = imports['JSDOC/Lang.js'].Lang;
/**
@class Search a { link JSDOC.TextStream} for language tokens.
*/
-TokenReader = Object.define(
+TokenReader = XObject.define(
function(o) {
this.keepDocs = true;
this.keepWhite = false;
this.keepComments = false;
- Roo.apply(this, o || {});
+ this.sepIdents = false; // seperate '.' in identifiers..
+ XObject.extend(this, o || {});
},
Object,
@@ -29,6 +31,7 @@ TokenReader = Object.define(
tokenize : function(/**JSDOC.TextStream*/stream) {
+ this.line =1;
var tokens = [];
/** ignore*/ tokens.last = function() { return tokens[tokens.length-1]; }
/** ignore*/ tokens.lastSym = function() {
@@ -50,7 +53,7 @@ TokenReader = Object.define(
if (this.read_word(stream, tokens)) continue;
// if execution reaches here then an error has happened
- tokens.push(new Token(stream.next(), "TOKN", "UNKNOWN_TOKEN"));
+ tokens.push(new Token(stream.next(), "TOKN", "UNKNOWN_TOKEN", this.line));
}
@@ -72,9 +75,25 @@ TokenReader = Object.define(
}
else {
var name;
- if ((name = Lang.keyword(found))) tokens.push(new Token(found, "KEYW", name));
- else tokens.push(new Token(found, "NAME", "NAME"));
+ if ((name = Lang.keyword(found))) {
+ tokens.push(new Token(found, "KEYW", name, this.line));
+ return true;
+ }
+ if (!this.sepIdents || found.indexOf('.') < 0 ) {
+ tokens.push(new Token(found, "NAME", "NAME", this.line));
+ return true;
+ }
+ var n = found.split('.');
+ var p = false;
+ n.forEach(function(nm) {
+ if (p) {
+ tokens.push(new Token('.', "PUNC", "DOT", this.line));
+ }
+ p=true;
+ tokens.push(new Token(nm, "NAME", "NAME", this.line));
+ });
return true;
+
}
},
@@ -92,7 +111,7 @@ TokenReader = Object.define(
return false;
}
else {
- tokens.push(new Token(found, "PUNC", Lang.punc(found)));
+ tokens.push(new Token(found, "PUNC", Lang.punc(found), this.line));
return true;
}
},
@@ -112,7 +131,7 @@ TokenReader = Object.define(
}
else {
if (this.collapseWhite) found = " ";
- if (this.keepWhite) tokens.push(new Token(found, "WHIT", "SPACE"));
+ if (this.keepWhite) tokens.push(new Token(found, "WHIT", "SPACE", this.line));
return true;
}
},
@@ -124,6 +143,7 @@ TokenReader = Object.define(
var found = "";
while (!stream.look().eof && Lang.isNewline(stream.look())) {
+ this.line++;
found += stream.next();
}
@@ -132,7 +152,7 @@ TokenReader = Object.define(
}
else {
if (this.collapseWhite) found = "\n";
- if (this.keepWhite) tokens.push(new Token(found, "WHIT", "NEWLINE"));
+ if (this.keepWhite) tokens.push(new Token(found, "WHIT", "NEWLINE", this.line));
return true;
}
},
@@ -143,14 +163,16 @@ TokenReader = Object.define(
read_mlcomment : function(/**JSDOC.TokenStream*/stream, tokens) {
if (stream.look() == "/" && stream.look(1) == "*") {
var found = stream.next(2);
-
+ var c = '';
while (!stream.look().eof && !(stream.look(-1) == "/" && stream.look(-2) == "*")) {
- found += stream.next();
+ c = stream.next();
+ if (c == "\n") this.line++;
+ found += c;
}
// to start doclet we allow /** or /*** but not /**/ or /****
- if (/^\/\*\*([^\/]|\*[^*])/.test(found) && this.keepDocs) tokens.push(new Token(found, "COMM", "JSDOC"));
- else if (this.keepComments) tokens.push(new Token(found, "COMM", "MULTI_LINE_COMM"));
+ if (/^\/\*\*([^\/]|\*[^*])/.test(found) && this.keepDocs) tokens.push(new Token(found, "COMM", "JSDOC", this.line));
+ else if (this.keepComments) tokens.push(new Token(found, "COMM", "MULTI_LINE_COMM", this.line));
return true;
}
return false;
@@ -172,8 +194,9 @@ TokenReader = Object.define(
}
if (this.keepComments) {
- tokens.push(new Token(found, "COMM", "SINGLE_LINE_COMM"));
+ tokens.push(new Token(found, "COMM", "SINGLE_LINE_COMM", this.line));
}
+ this.line++;
return true;
}
return false;
@@ -201,7 +224,7 @@ TokenReader = Object.define(
}
else if (stream.look() == "\"") {
string += stream.next();
- tokens.push(new Token(string, "STRN", "DOUBLE_QUOTE"));
+ tokens.push(new Token(string, "STRN", "DOUBLE_QUOTE", this.line));
return true;
}
else {
@@ -226,7 +249,7 @@ TokenReader = Object.define(
}
else if (stream.look() == "'") {
string += stream.next();
- tokens.push(new Token(string, "STRN", "SINGLE_QUOTE"));
+ tokens.push(new Token(string, "STRN", "SINGLE_QUOTE", this.line));
return true;
}
else {
@@ -255,8 +278,8 @@ TokenReader = Object.define(
return false;
}
else {
- if (/^0[0-7]/.test(found)) tokens.push(new Token(found, "NUMB", "OCTAL"));
- else tokens.push(new Token(found, "NUMB", "DECIMAL"));
+ if (/^0[0-7]/.test(found)) tokens.push(new Token(found, "NUMB", "OCTAL", this.line));
+ else tokens.push(new Token(found, "NUMB", "DECIMAL", this.line));
return true;
}
},
@@ -293,7 +316,7 @@ TokenReader = Object.define(
while (!stream.look().eof) {
if (Lang.isHexDec(found) && !Lang.isHexDec(found+stream.look())) { // done
- tokens.push(new Token(found, "NUMB", "HEX_DEC"));
+ tokens.push(new Token(found, "NUMB", "HEX_DEC", this.line));
return true;
}
else {
@@ -338,7 +361,7 @@ TokenReader = Object.define(
regex += stream.next();
}
- tokens.push(new Token(regex, "REGX", "REGX"));
+ tokens.push(new Token(regex, "REGX", "REGX", this.line));
return true;
}
else {
diff --git a/JSDOC/TokenStream.js b/JSDOC/TokenStream.js
index cc3f6af..b5385a6 100644
--- a/JSDOC/TokenStream.js
+++ b/JSDOC/TokenStream.js
@@ -21,6 +21,7 @@ TokenStream = Object.define(
},
Object,
{
+ cursor : -1, // where are we in the stream.
rewind : function() {
this.cursor = -1;
@@ -33,7 +34,9 @@ TokenStream = Object.define(
if (typeof n == "undefined") n = 0;
if (considerWhitespace == true) {
- if (this.cursor+n < 0 || this.cursor+n > this.tokens.length) return {};
+ if (this.cursor+n < 0 || this.cursor+n > (this.tokens.length -1)) {
+ return new Token("", "VOID", "START_OF_STREAM");
+ }
return this.tokens[this.cursor+n];
}
else {
@@ -91,8 +94,14 @@ TokenStream = Object.define(
var i = this.cursor;
while (true) {
- if (i < 0) return false;
- else if (i > this.tokens.length) return false;
+ // print(i);
+ if (i < 0) {
+ if (n > -1) {
+ i = 0; continue;
+ }
+ return new Token("", "VOID", "END_OF_STREAM");
+ }
+ else if (i > this.tokens.length) return new Token("", "VOID", "END_OF_STREAM");
if (i != this.cursor && (this.tokens[i] === undefined || this.tokens[i].is("WHIT") || this.tokens[i].is("COMM"))) {
if (n < 0) i--; else i++;
@@ -105,7 +114,7 @@ TokenStream = Object.define(
count++;
(n < 0)? i-- : i++;
}
-
+ // should never get here..
return false; // because null isn't an object and caller always expects an object;
},
@@ -133,7 +142,9 @@ TokenStream = Object.define(
},
// what about comments after 'function'...
// is this used ???
-
+ nextTok : function() {
+ return this.nextNonSpace();
+ },
nextNonSpace : function ()
{
@@ -149,9 +160,14 @@ TokenStream = Object.define(
}
},
/**
- @type JSDOC.Token[]
- */
+ * @type JSDOC.Token[]
+ * @param start {String} token name or data (eg. '{'
+ * @param stop {String} (Optional) token name or data (eg. '}'
+ */
balance : function(/**String*/start, /**String*/stop) {
+
+ start = typeof(Lang.matching(start)) == 'undefined' ? Lang.punc(start) : start;
+
if (!stop) stop = Lang.matching(start);
var depth = 0;
@@ -218,10 +234,16 @@ TokenStream = Object.define(
arrayToString : function(ar) {
console.log(typeof(ar));
var ret = [];
- Roo.each(ar, function(e) {
+ ar.forEach(function(e) {
ret.push(e.data);
})
return ret.join('');
+ },
+ dump: function()
+ {
+ this.tokens.forEach(function(t) {
+ print(t.toString());
+ });
}
});
\ No newline at end of file
diff --git a/XObject.js b/XObject.js
new file mode 100644
index 0000000..424db53
--- /dev/null
+++ b/XObject.js
@@ -0,0 +1,439 @@
+//<script type="text/javascript">
+
+/**
+ * XObject
+ * Yet another attempt to create a usable object construction library for seed..
+ *
+ * Why is this useful?
+ * A) It turns rather messy code into a tree structure, making it easy to find code relating to
+ * an interface element
+ * B) In theory it should be gjs/Seed compatible..
+ * C) It provides getElementById style lookups for elements.
+ * D) It provides classic OO constructors for Javascript (extend/define)
+ * E) It does not modify any buildin prototypes..
+ *
+ * Extend this.. to use it's wonderful features..
+ *
+ * normal usage:
+ * XObject = imports.XObject.XObject;
+ *
+ * Xyz = new XObject({
+ * xtype: Gtk.Window,
+ * id : 'window',
+ * items : [
+ *
+ * ]
+ * });
+ * Xyz.init(); // create and show.
+ *
+ *
+ *
+ * @arg xtype {String|Function} constructor or string.
+ * @arg id {String} (optional) id for registry
+ * @arg xns {String|Object} (optional) namespace eg. Gtk or 'Gtk' - used with xtype.
+ * @arg items {Array} (optional) list of child elements which will be constructed.. using XObject
+ * @arg listeners {Object} (optional) map Gobject signals to functions
+ * @arg pack {Function|String|Array} (optional) how this object gets added to it's parent
+ * @arg el {Object} (optional) premade GObject
+ *
+ * --- needs a xdebug option!
+ *
+ *
+ * He's some questions.
+ * - should we generate ID's for all elements? (if so we probably need to garbage collect)
+ * - should we have a special property to use as the constructor / gobject.properties rather
+ * than sending all basic types to this?
+ *
+ *
+ */
+
+function XObject (cfg) {
+ // first apply cfg if set.
+ this.config = cfg;
+ if (cfg.init) {
+ this.init = cfg.init; // override!
+ }
+
+
+}
+
+
+
+XObject.prototype = {
+ /**
+ * @property el {GObject} the Gtk / etc. element.
+ */
+ el : false,
+ /*
+ * @property items {Array} list of sub elements
+ */
+ /**
+ * @property parent {XObject} parent Element
+ */
+
+ /**
+ * @property config {Object} the construction configuration.
+ */
+ /**
+ * @method init
+ * Initializes the Element (el) hooks up all the listeners
+ * and packs the children.
+ * you can override this, in child objects, then
+ * do this to do thi initaliztion.
+ *
+ * XObject.prototype.init.call(this);
+ *
+ */
+ init : function()
+ {
+ var cfg = this.config;
+
+ print("new xobj?" + XObject.keys(cfg).join(','));
+ //print(cfg);
+ o = {};
+
+ cfg.items = cfg.items || [];
+
+ XObject.extend(o, cfg); // copy everything into o.
+
+ o.pack = typeof(o.pack) == 'undefined' ? 'add' : o.pack;
+
+ XObject.extend(this, o);
+
+ // remove items.
+
+ this.listeners = this.listeners || {};
+ this.items = [];
+
+ // remove objects/functions from o, so they can be sent to the contructor.
+ for (var i in o) {
+ if ((typeof(o[i]) == 'object') ||
+ (typeof(o[i]) == 'function') ||
+ i == 'pack' ||
+ i == 'id' ||
+ i == 'xtype' ||
+ i == 'xdebug' ||
+ i == 'xns'
+ ) {
+ delete o[i];
+ }
+ }
+
+ // do we need to call 'beforeInit here?'
+
+ // handle include?
+ //if ((this.xtype == 'Include')) {
+ // o = this.pre_registry[cls];
+ //}
+ var isSeed = typeof(Seed) != 'undefined';
+
+ // xtype= Gtk.Menu ?? what about c_new stuff?
+ print(this.xtype);
+ if (typeof(this.xtype) == 'function') {
+ print("func?" + XObject.keys(o).join(','));
+ this.el = this.el || this.xtype(o);
+ }
+ if (typeof(this.xtype) == 'object') {
+ print("obj?" + XObject.keys(o).join(','));
+ this.el = this.el || new this.xtype(o);
+ }
+ //print(this.el);
+ if (!this.el && o.xns) {
+
+ var NS = imports.gi[o.xns];
+ if (!NS) {
+ Seed.print('Invalid xns: ' + o.xns);
+ }
+ constructor = NS[o.xtype];
+ if (!constructor) {
+ Seed.print('Invalid xtype: ' + o.xns + '.' + o.xtype);
+ }
+ this.el = isSeed ? new constructor(o) : new constructor();
+
+ }
+ // always overlay props..
+ for (var i in o) {
+ this.el[i] = o[i];
+ }
+ // register it!
+ //if (o.xnsid && o.id) {
+ // XObject.registry = XObject.registry || { };
+ // XObject.registry[o.xnsid] = XObject.registry[o.xnsid] || {};
+ // XObject.registry[o.xnsid][o.id] = this;
+ //}
+
+ cfg.items.forEach(this.addItem, this);
+
+ for (var i in this.listeners) {
+ this.addListener(i, this.listeners[i]);
+ }
+ // delete this.listeners ?
+
+
+ // do we need to call 'init here?'
+ },
+
+
+ /**
+ * @method addItem
+ * Adds an item to the object using a new XObject
+ * uses pack property to determine how to add it.
+ * @arg cfg {Object} same as XObject constructor.
+ */
+ addItem : function(o) {
+
+
+ var item = (o.constructor == XObject) ? o : new XObject(o);
+ item.init();
+ item.parent = this;
+ this.items.push(item);
+
+ if (item.pack===false) { // no
+ return;
+ }
+ if (typeof(item.pack) == 'function') {
+ // parent, child
+ item.pack.apply(o, [ o , o.items[i] ]);
+ item.parent = this;
+ return;
+ }
+ var args = [];
+ var pack_m = false;
+ if (typeof(item.pack) == 'string') {
+ pack_m = item.pack;
+ } else {
+ pack_m = item.pack.shift();
+ args = item.pack;
+ }
+
+ // handle error.
+ if (pack_m && typeof(this.el[pack_m]) == 'undefined') {
+ Seed.print('pack method not available : ' + this.xtype + '.' + pack_m);
+ return;
+ }
+
+
+ //Seed.print('Pack ' + this.el + '.'+ pack_m + '(' + item.el + ')');
+
+ args.unshift(item.el);
+ print('[' + args.join(',') +']');
+ //Seed.print('args: ' + args.length);
+ if (pack_m) {
+ this.el[pack_m].apply(this.el, args);
+ }
+
+
+
+ },
+ /**
+ * @method addListener
+ * Connects a method to a signal. (gjs/Seed aware)
+ *
+ * @arg sig {String} name of signal
+ * @arg fn {Function} handler.
+ */
+ addListener : function(sig, fn)
+ {
+
+ Seed.print("Add signal " + sig);
+
+ var _li = XObject.createDelegate(fn,this);
+ // private listeners that are not copied to GTk.
+
+ if (typeof(Seed) != 'undefined') {
+ // Seed.print(typeof(_li));
+ this.el.signal[sig].connect(_li);
+ } else {
+ this.el.connect( sig, _li);
+ }
+
+
+ },
+ /**
+ * @method get
+ * Finds an object in the child elements using xid of object.
+ * prefix with '.' to look up the tree.. multiple '..' to look further up..
+ *
+ * @arg name {String} name of signal
+ * @return {XObject|false} the object if found.
+ */
+ get : function(xid)
+ {
+ var ret= false;
+ if (xid[0] == '.') {
+ return this.parent.get(xid.substring(1));
+ }
+
+
+ this.items.forEach(function(ch) {
+ if (ch.id == xid) {
+ ret = ch;
+ return true;
+ }
+ })
+ if (ret) {
+ return ret;
+ }
+ // iterate children.
+ this.items.forEach(function(ch) {
+ ret = ch.get(xid);
+ if (ret) {
+ return true;
+ }
+ })
+ return ret;
+ }
+
+
+}
+
+
+/**
+ * Copies all the properties of config to obj.
+ *
+ * Pretty much the same as JQuery/Prototype..
+ * @param {Object} obj The receiver of the properties
+ * @param {Object} config The source of the properties
+ * @param {Object} defaults A different object that will also be applied for default values
+ * @return {Object} returns obj
+ * @member XObject extend
+ */
+
+
+XObject.extend = function(o, c, defaults){
+ if(defaults){
+ // no "this" reference for friendly out of scope calls
+ XObject.extend(o, defaults);
+ }
+ if(o && c && typeof c == 'object'){
+ for(var p in c){
+ o[p] = c[p];
+ }
+ }
+ return o;
+};
+
+XObject.extend(XObject,
+{
+ /**
+ * Copies all the properties of config to obj, if the do not exist.
+ * @param {Object} obj The receiver of the properties
+ * @param {Object} config The source of the properties
+ * @return {Object} returns obj
+ * @member Object extendIf
+ */
+
+
+ extendIf : function(o, c){
+
+ if(!o || !c || typeof c != 'object'){
+ return o;
+ }
+ for(var p in c){
+ if (typeof(o[p]) != 'undefined') {
+ continue;
+ }
+ o[p] = c[p];
+ }
+ return o;
+ },
+
+
+
+ /**
+ * Extends one class with another class and optionally overrides members with the passed literal. This class
+ * also adds the function "override()" to the class that can be used to override
+ * members on an instance.
+ *
+ * usage:
+ * MyObject = Object.define(
+ * function(...) {
+ * ....
+ * },
+ * parentClass, // or Object
+ * {
+ * ... methods and properties.
+ * }
+ * });
+ * @param {Function} constructor The class inheriting the functionality
+ * @param {Object} superclass The class being extended
+ * @param {Object} overrides (optional) A literal with members
+ * @return {Function} constructor (eg. class
+ * @method define
+ */
+ define : function(){
+ // inline overrides
+ var io = function(o){
+ for(var m in o){
+ this[m] = o[m];
+ }
+ };
+ return function(sb, sp, overrides) {
+ if (typeof(sp) == 'undefined') {
+ // error condition - try and dump..
+ throw "Missing superclass: when applying: " + sb
+ }
+
+ var F = function(){}, sbp, spp = sp.prototype;
+ F.prototype = spp;
+ sbp = sb.prototype = new F();
+ sbp.constructor=sb;
+ sb.superclass=spp;
+
+ // extends Object.
+ if(spp.constructor == Object.prototype.constructor){
+ spp.constructor=sp;
+ }
+
+ sb.override = function(o){
+ Object.extend(sb.prototype, o);
+ };
+ sbp.override = io;
+ XObject.extend(sb.prototype, overrides);
+ return sb;
+ };
+ }(),
+
+
+ /**
+ * returns a list of keys of the object.
+ * @param {Object} obj object to inspect
+ * @return {Array} returns list of kyes
+ * @member XObject keys
+ */
+ keys : function(o)
+ {
+ var ret = [];
+ for(var i in o) {
+ ret.push(i);
+ }
+ return ret;
+ },
+
+ /**
+ * @member XObject createDelegate
+ * creates a delage metdhod
+ * @param {Function} method to wrap
+ * @param {Object} scope
+ * @param {Array} args to add
+ * @param {Boolean|Number} append arguments or replace after N arguments.
+ * @return {Function} returns the delegate
+ */
+
+ createDelegate : function(method, obj, args, appendArgs){
+
+ return function() {
+ var callArgs = args || arguments;
+ if(appendArgs === true){
+ callArgs = Array.prototype.slice.call(arguments, 0);
+ callArgs = callArgs.concat(args);
+ }else if(typeof appendArgs == "number"){
+ callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
+ var applyArgs = [appendArgs, 0].concat(args); // create method call params
+ Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
+ }
+ return method.apply(obj || window, callArgs);
+ };
+ }
+
+});
\ No newline at end of file
diff --git a/docs.js b/docs.js
index 10f1f11..9755ebe 100644
--- a/docs.js
+++ b/docs.js
@@ -37,16 +37,16 @@ if (typeof(Seed.argv[3]) == 'string') {
ns_list = Seed.argv[3].split(',');
}
-ns_list = ns_list .sort();
-var cls_list = [];
+ns_list = ns_list.sort();
+
var cls_template = new JSDOC.Template(__script_path__ + '/docs/class.html');
var cls_ix_template = new JSDOC.Template(__script_path__ + '/docs/class_ix.html');
var reference_template = new JSDOC.Template(__script_path__ + '/docs/references.html');
-
+var ns_idx = [];
ns_list.map(function(ns_name)
{
var core = imports.gi[ns_name];
-
+ var idx = { name: ns_name};
console.log("START:" + ns_name);
var ns = Introspect.ns(ns_name);
@@ -54,10 +54,6 @@ ns_list.map(function(ns_name)
Gio.simple_write(outputdir + '/'+ ns_name + '.html', cls_template.process(ns));
- cls_list.push({
- alias : ns_name
- });
-
// left bar index of elements in namespace...
Gio.simple_write(outputdir + '/_ix_'+ ns_name + '.shtml', cls_ix_template.process(ns));
@@ -71,6 +67,7 @@ ns_list.map(function(ns_name)
};
for (var i in actions) {
+ idx[i]= ns[i].length;
ns[i].map( function(n) {
Gio.simple_write(outputdir + '/'+ ns_name + '.' + n + '.html',
cls_template.process(
@@ -84,7 +81,7 @@ ns_list.map(function(ns_name)
console.log(ns_name + '.' +n);
});
}
-
+ ns_idx.push(idx);
});
@@ -119,7 +116,7 @@ for (var i in Introspect.references) {
}
var ix_template = new JSDOC.Template(__script_path__ + '/docs/index.html');
-Gio.simple_write(outputdir + '/index.html', ix_template.process(ns_list));
+Gio.simple_write(outputdir + '/index.html', ix_template.process(ns_idx));
File.silentRecursiveCopy(__script_path__ + '/docs/resources/', outputdir);
diff --git a/docs/class.html b/docs/class.html
index 33f5218..4d0f5e7 100644
--- a/docs/class.html
+++ b/docs/class.html
@@ -112,7 +112,13 @@
<td class="hd-info">{+data.ns+} = imports.gi.{+data.ns+}; </td>
</tr>
</if>
-
+ <tr>
+ <td class="label">Note:</td>
+ <td class="hd-info"><b style="color:red">This documentation is generated from the HEAD of most
+ libraries (and some experimental libs at times) - and may not be exactly the same as your
+ installed .gir files - you may want to download the generator and install on your machine
+ to find out what you have.<b/></td>
+ </tr>
<tr>
<td class="label">C documentation:</td>
<td class="hd-info">{+new Link().toGnome(data.alias)+}</td>
diff --git a/docs/index.html b/docs/index.html
index aae2b1a..0a852cf 100755
--- a/docs/index.html
+++ b/docs/index.html
@@ -18,15 +18,27 @@
</div>
</div>
-
+ <h2>GObject Libraries</h2>
<for each="thisClass" in="data">
-
+ {!
+ if (!thisClass.objects) { continue; }
+ !}
<div>
- <h2 class="classTitle">{+ (new Link().toSymbol(thisClass)) +}</h2>
+ <h2 class="classTitle">{+ (new Link().toSymbol(thisClass.name)) +}</h2>
</div>
+ </for>
+ <h2>Non - GObject Libraries</h2>
+ <for each="thisClass" in="data">
+ {!
+ if (thisClass.objects) { continue; }
+ !}
+ <div>
+ <h2 class="classTitle">{+ (new Link().toSymbol(thisClass.name)) +}</h2>
+ </div>
</for>
+
</div>
</body>
</html>
diff --git a/pack.js b/pack.js
new file mode 100755
index 0000000..3a17a4c
--- /dev/null
+++ b/pack.js
@@ -0,0 +1,83 @@
+#!/usr/bin/seed
+//<script type="text/javascript">
+/**
+ * packer command line
+ *
+ * -o Output
+ * -O Output debug file here.
+ * -t Translate json file.
+ * -w Cache / working dir.
+ * -f File to read with a list of source paths / or class names.
+ * -C no cleanup (use with -w if you need are using a cache directory.)
+ * -p prefix for translation md5 generator (directory that files are in, and is removed
+ * from path when generating an md5 for the translated name.
+ *
+ * compresses files listed as arguments and outputs result
+ */
+
+Packer = imports['JSDOC/Packer.js'].Packer;
+File = imports.File.File;
+
+
+
+var args = Array.prototype.slice.call(Seed.argv);
+args.shift(); //seed
+args.shift(); // pack.js
+var cfg = {
+ files : [],
+ target : false,
+}
+
+
+for(var i =0; i < args.length;i++) {
+ if (args[i] == '-o') {
+ cfg.target = args[i+1];
+ i++;
+ continue;
+ }
+ if (args[i] == '-O') {
+ cfg.debugTarget = args[i+1];
+ i++;
+ continue;
+ }
+ if (args[i] == '-t') {
+ cfg.translateJSON = args[i+1];
+ i++;
+ continue;
+ }
+ if (args[i] == '-w') {
+ cfg.tmpDir = args[i+1];
+ i++;
+ continue;
+ }
+ if (args[i] == '-p') {
+ cfg.prefix = args[i+1];
+ i++;
+ continue;
+ }
+ if (args[i] == '-C') {
+ cfg.cleanup = false;
+ continue;
+ }
+ if (args[i] == '-f') {
+ cfg.srcfile = args[i+1];
+ i++;
+ continue;
+ }
+ if (cfg.files.indexOf(args[i]) > -1) {
+ continue; // remove dupes.
+ }
+ cfg.files.push(args[i]);
+}
+var pack;
+try {
+ pack = new Packer(cfg)
+} catch (e) {
+ print("ERROR " + e.toString());
+ throw e;
+}
+if (!pack.target) {
+ print(pack.out);
+}
+
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]