Index: /CKReleaser/trunk/_dev/jslint/lint.bat
===================================================================
--- /CKReleaser/trunk/_dev/jslint/lint.bat	(revision 2890)
+++ /CKReleaser/trunk/_dev/jslint/lint.bat	(revision 2890)
@@ -0,0 +1,45 @@
+@ECHO OFF
+
+::
+:: CKEditor - The text editor for Internet - http://ckeditor.com
+:: Copyright (C) 2003-2008 Frederico Caldeira Knabben
+::
+:: == BEGIN LICENSE ==
+::
+:: Licensed under the terms of any of the following licenses at your
+:: choice:
+::
+::  - GNU General Public License Version 2 or later (the "GPL")
+::    http://www.gnu.org/licenses/gpl.html
+::
+::  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+::    http://www.gnu.org/licenses/lgpl.html
+::
+::  - Mozilla Public License Version 1.1 or later (the "MPL")
+::    http://www.mozilla.org/MPL/MPL-1.1.html
+::
+:: == END LICENSE ==
+::
+:: Calls the JavaScript Lint (jsl) with the predefined configurations.
+:: If a file name is passed as a parameter it writes there the results,
+:: otherwise it simply outputs it.
+::
+
+IF "%1"=="" GOTO NoParam
+
+ECHO Generating %1...
+bin\jsl -conf lint.conf -nofilelisting -nologo > %1
+
+ECHO.
+ECHO Process completed.
+ECHO.
+
+GOTO End
+
+:NoParam
+
+"../_thirdparty/jsl/jsl.exe" -conf lint.conf -nofilelisting -nologo
+
+ECHO.
+
+:End
Index: /CKReleaser/trunk/_dev/jslint/lint.conf
===================================================================
--- /CKReleaser/trunk/_dev/jslint/lint.conf	(revision 2890)
+++ /CKReleaser/trunk/_dev/jslint/lint.conf	(revision 2890)
@@ -0,0 +1,180 @@
+#
+# Configuration File for JavaScript Lint 0.3.0
+# Developed by Matthias Miller (http://www.JavaScriptLint.com)
+#
+# This configuration file can be used to lint a collection of scripts, or to enable
+# or disable warnings for scripts that are linted via the command line.
+#
+
+### Warnings
+# Enable or disable warnings based on requirements.
+# Use "+WarningName" to display or "-WarningName" to suppress.
+#
++no_return_value              # function {0} does not always return a value
++duplicate_formal             # duplicate formal argument {0}
++equal_as_assign              # test for equality (==) mistyped as assignment (=)?{0}
++var_hides_arg                # variable {0} hides argument
++redeclared_var               # redeclaration of {0} {1}
++anon_no_return_value         # anonymous function does not always return a value
++missing_semicolon            # missing semicolon
++meaningless_block            # meaningless block; curly braces have no impact
++comma_separated_stmts        # multiple statements separated by commas (use semicolons?)
++unreachable_code             # unreachable code
++missing_break                # missing break statement
+-missing_break_for_last_case  # missing break statement for last case in switch
++comparison_type_conv         # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)
+-inc_dec_within_stmt          # increment (++) and decrement (--) operators used as part of greater statement
++useless_void                 # use of the void type may be unnecessary (void is always undefined)
++multiple_plus_minus          # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs
++use_of_label                 # use of label
+-block_without_braces         # block statement without curly braces
++leading_decimal_point        # leading decimal point may indicate a number or an object member
++trailing_decimal_point       # trailing decimal point may indicate a number or an object member
++octal_number                 # leading zeros make an octal number
++nested_comment               # nested comment
++misplaced_regex              # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma
+-ambiguous_newline            # unexpected end of line; it is ambiguous whether these lines are part of the same statement
++empty_statement              # empty statement or extra semicolon
+-missing_option_explicit      # the "option explicit" control comment is missing
++partial_option_explicit      # the "option explicit" control comment, if used, must be in the first script tag
++dup_option_explicit          # duplicate "option explicit" control comment
++useless_assign               # useless assignment
++ambiguous_nested_stmt        # block statements containing block statements should use curly braces to resolve ambiguity
++ambiguous_else_stmt          # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent)
+-missing_default_case         # missing default case in switch statement
++duplicate_case_in_switch     # duplicate case in switch statements
++default_not_at_end           # the default case is not at the end of the switch statement
+-legacy_cc_not_understood     # couldn't understand control comment using /*@keyword@*/ syntax
++jsl_cc_not_understood        # couldn't understand control comment using /*jsl:keyword*/ syntax
++useless_comparison           # useless comparison; comparing identical expressions
++with_statement               # with statement hides undeclared variables; use temporary variable instead
++trailing_comma_in_array      # extra comma is not recommended in array initializers
++assign_to_function_call      # assignment to a function call
++parseint_missing_radix       # parseInt missing radix parameter
+
+### Output format
+# Customize the format of the error message.
+#    __FILE__ indicates current file path
+#    __FILENAME__ indicates current file name
+#    __LINE__ indicates current line
+#    __ERROR__ indicates error message
+#
+# Visual Studio syntax (default):
++output-format __FILE__(__LINE__): __ERROR__
+# Alternative syntax:
+#+output-format __FILE__:__LINE__: __ERROR__
+
+
+### Context
+# Show the in-line position of the error.
+# Use "+context" to display or "-context" to suppress.
+#
++context
+
+
+### Semicolons
+# By default, assignments of an anonymous function to a variable or
+# property (such as a function prototype) must be followed by a semicolon.
+#
++lambda_assign_requires_semicolon
+
+
+### Control Comments
+# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for
+# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is
+# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason,
+# although legacy control comments are enabled by default for backward compatibility.
+#
++legacy_control_comments
+
+
+### JScript Function Extensions
+# JScript allows member functions to be defined like this:
+#     function MyObj() { /*constructor*/ }
+#     function MyObj.prototype.go() { /*member function*/ }
+#
+# It also allows events to be attached like this:
+#     function window::onload() { /*init page*/ }
+#
+# This is a Microsoft-only JavaScript extension. Enable this setting to allow them.
+#
+-jscript_function_extensions
+
+
+### Defining identifiers
+# By default, "option explicit" is enabled on a per-file basis.
+# To enable this for all files, use "+always_use_option_explicit"
++always_use_option_explicit
+
+# Define certain identifiers of which the lint is not aware.
+# (Use this in conjunction with the "undeclared identifier" warning.)
+
+# Rhino globals
++define arguments
++define importClass
++define importPackage
++define load
++define print
++define readFile
++define runCommand
++define quit
+
+# Rhino Classes
++define Context
+
+# Java Packages
++define org
++define java
++define javax
++define com
+
+# Java Classes
++define BufferedWriter
++define DocumentBuilderFactory
++define DOMSource
++define Integer
++define InputStreamReader
++define InputStreamWriter
++define File
++define FileInputStream
++define FileOutputStream
++define FileWriter
++define OutputKeys
++define OutputStreamWriter
++define Packages
++define Pattern
++define StreamResult
++define StringBuffer
++define StringWriter
++define System
++define TarEntry
++define TarGzOutputStream
++define TransformerFactory
++define ZipEntry
++define ZipOutputStream
+
+# JSON
++define JSON
+
+# FIXME - CKRELEASER should not be here...
++define CKRELEASER
++define CKPACKAGER
+
+### Interactive
+# Prompt for a keystroke before exiting.
+#+pauseatend
+
+
+### Files
+# Specify which files to lint
+# Use "+recurse" to enable recursion (disabled by default).
+# To add a set of files, use "+process FileName", "+process Folder\Path\*.js",
+# or "+process Folder\Path\*.htm".
+#
+#+process jsl-test.js
++recurse
+
++process ../../includes/ckreleaser.js
++process ../../releaser.js
++process ../../includes/*.js
++process ../../test/*.js
Index: /CKReleaser/trunk/_dev/jslint/lint.sh
===================================================================
--- /CKReleaser/trunk/_dev/jslint/lint.sh	(revision 2890)
+++ /CKReleaser/trunk/_dev/jslint/lint.sh	(revision 2890)
@@ -0,0 +1,34 @@
+#
+# CKEditor - The text editor for Internet - http://ckeditor.com
+# Copyright (C) 2003-2008 Frederico Caldeira Knabben
+#
+# == BEGIN LICENSE ==
+#
+# Licensed under the terms of any of the following licenses at your
+# choice:
+#
+#  - GNU General Public License Version 2 or later (the "GPL")
+#    http://www.gnu.org/licenses/gpl.html
+#
+#  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+#    http://www.gnu.org/licenses/lgpl.html
+#
+#  - Mozilla Public License Version 1.1 or later (the "MPL")
+#    http://www.mozilla.org/MPL/MPL-1.1.html
+#
+# == END LICENSE ==
+#
+# Calls the JavaScript Lint (jsl) with the predefined configurations.
+# If a file name is passed as a parameter it writes there the results,
+# otherwise it simply outputs it.
+#
+
+if [ "$1" = "" ]
+then
+	../_thirdparty/jsl/jsl -conf lint.conf -nofilelisting -nologo
+else
+	echo Generating $1 ...
+	../_thirdparty/jsl/jsl -conf lint.conf -nofilelisting -nologo > $1
+	echo
+	echo Process completed.
+fi
Index: /CKReleaser/trunk/ckeditor.release
===================================================================
--- /CKReleaser/trunk/ckeditor.release	(revision 2890)
+++ /CKReleaser/trunk/ckeditor.release	(revision 2890)
@@ -0,0 +1,64 @@
+fixLineEnds : true,
+
+ignore : 
+[
+	'_dev',
+	'_docs/api',
+	'_source/adapters',
+ 	'ckeditor.js',
+ 	'ckeditor_basic.js',
+	'.svn',
+	'.settings',
+	'.project'
+],
+
+copy :
+[
+	{
+		source : '_source/lang',
+		target : 'lang',
+		minify : true
+	},
+
+	{
+		source : '_source/plugins',
+		target : 'plugins',
+		minify : true
+	},
+
+	{
+		source : '_source/tests',
+		target : 'tests',
+		minify : false
+	},
+
+	{
+		source : '_source/themes',
+		target : 'themes',
+		minify : true
+	}
+],
+
+packages :
+[
+	'../ckeditor/ckeditor.pack'
+],
+
+documentation :
+{
+	config: '_dev/docs_build/docs_build.conf',
+	target: '_docs/api'
+},
+
+samples :
+{
+	source : '_samples',
+	target : '_samples',
+	template : '_samples/sample.html',
+},
+
+skins :
+{
+	source : '_source/skins',
+	target : 'skins'
+}
Index: /CKReleaser/trunk/ckreleaser.js
===================================================================
--- /CKReleaser/trunk/ckreleaser.js	(revision 2890)
+++ /CKReleaser/trunk/ckreleaser.js	(revision 2890)
@@ -0,0 +1,64 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+importPackage( org.mozilla.javascript );
+importClass( java.lang.System );
+importClass( java.io.File );
+
+var jarPath = JavaAdapter(org.mozilla.javascript.Parser)["class"].getResource("/org/mozilla/javascript").toString();
+var releaserPath = jarPath.replaceFirst( "^jar:", '' ).replaceFirst( "\/js\.jar\!\/org\/mozilla\/javascript$", '');
+
+load( releaserPath + "/includes/ckreleaser.js" );
+CKRELEASER.path = releaserPath;
+CKRELEASER.loadCode( "includes/releaser.js" );
+	
+function error( msg )
+{
+	print( msg );
+	print( '' );
+	quit();
+}
+
+if (arguments.length > 1 && arguments[arguments.length-1] == '-v')
+	CKRELEASER.verbose = 1; 
+
+if ( arguments[0] == '-test' )
+{
+	CKRELEASER.loadCode( 'test/test.js' );
+	quit();
+}
+
+CKRELEASER.releaseFile = arguments[0];
+CKRELEASER.sourceDir = arguments[1];
+CKRELEASER.targetDir = arguments[2];
+CKRELEASER.version = arguments[3];
+CKRELEASER.zipName = arguments[4];
+CKRELEASER.targzName = arguments[5];
+
+if ( !CKRELEASER.releaseFile || !CKRELEASER.sourceDir || !CKRELEASER.targetDir 
+		|| !CKRELEASER.version || !CKRELEASER.zipName || !CKRELEASER.targzName)
+{
+	error( 'Usage:java -jar js.jar ckreleaser.js [release_file] [source_dir] [target_dir] [version] [zip_name] [targz_name]' );
+}
+
+CKRELEASER.releaseDir = CKRELEASER.targetDir + "/" + "release";
+CKRELEASER.loadCode( 'tools/CKPackager/includes/ckpackager.js' );
+CKPACKAGER.packDir = CKRELEASER.sourceDir;
+CKPACKAGER.dir = 'tools/CKPackager/';
+CKPACKAGER.outputDir = CKRELEASER.releaseDir;
+CKPACKAGER.loadCode( 'includes/packager.js' );
+
+(function()
+{
+	try {
+		var releaser = new CKRELEASER.releaser();
+		releaser.loadDefinitionFile( CKRELEASER.releaseFile );
+		releaser.run();
+	}
+	catch(e)
+	{
+		error(e);
+	}
+})();
Index: /CKReleaser/trunk/includes/ckreleaser.js
===================================================================
--- /CKReleaser/trunk/includes/ckreleaser.js	(revision 2890)
+++ /CKReleaser/trunk/includes/ckreleaser.js	(revision 2890)
@@ -0,0 +1,25 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+ */
+
+importClass( java.lang.Integer );
+
+var CKRELEASER = {
+	verbose : 0,
+
+	timestamp :( function()
+	{
+		var time = new Date();
+		
+		var timestamp = Integer.toString( time.getUTCFullYear() % 1000, 36 ) + Integer.toString( time.getUTCMonth(), 36 )
+				+ Integer.toString( time.getUTCDate(), 36 ) + Integer.toString( time.getUTCHours(), 36 );
+
+		return timestamp.toUpperCase();
+	})(),
+
+	loadCode : function( path )
+	{
+		load( CKRELEASER.path + "/" + path );
+	}
+};
Index: /CKReleaser/trunk/includes/docsprocessor.js
===================================================================
--- /CKReleaser/trunk/includes/docsprocessor.js	(revision 2890)
+++ /CKReleaser/trunk/includes/docsprocessor.js	(revision 2890)
@@ -0,0 +1,52 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+ */
+
+CKRELEASER.loadCode( 'tools/json/json2.js' );
+
+CKRELEASER.docsProcessor = function( )
+{
+};
+
+( function()
+{
+	CKRELEASER.docsProcessor.prototype = {
+		createDocumentation : function( sourceDir, targetDir )
+		{
+			var confLocation = new File( sourceDir, CKRELEASER.release.documentation.config );
+			var targetLocation = new File( targetDir, CKRELEASER.release.documentation.target );
+
+			if ( !confLocation.exists() )
+				throw "Missing configuration file (" + confLocation.getAbsolutePath() + ")";
+
+			var jsdocLocation = new File( 'tools/jsdoc' );
+			var jsdocPath = jsdocLocation.getAbsolutePath();
+
+			var o = { output : "" };
+
+			var configurationCode = 'var configuration = ' + CKRELEASER.io.readFile( confLocation.getAbsolutePath() ) + '\n;';
+			var cx = Context.enter(), scope = cx.initStandardObjects();
+			cx.evaluateString( scope, configurationCode, confLocation.getName(), 1, null );
+
+			var confDir = confLocation.getParent();
+
+			scope.configuration.t = confDir.replace( "\\", "/" ) + "/" + scope.configuration.t;
+			scope.configuration.d = confDir.replace( "\\", "/" ) + "/" + scope.configuration.d;
+
+			for ( var i = 0 ; i < scope.configuration._.length ; i++ )
+			{
+				scope.configuration._[i] = confDir.replace( "\\", "/" ) + "/" + scope.configuration._[i];
+			}
+
+			var tempFile = File.createTempFile( "ckreleaser_jsdoc", ".conf" );
+			CKRELEASER.io.saveFile( tempFile, JSON.stringify( scope.configuration ) );
+
+			runCommand( "java", "-jar", jsdocPath + "/jsrun.jar", jsdocPath + "/app/run.js", "-c=" + tempFile.getAbsolutePath(), "-d="
+					+ targetLocation.getAbsolutePath(), o );
+
+			if ( CKRELEASER.verbose )
+				print( o.output );
+		}
+	};
+})();
Index: /CKReleaser/trunk/includes/io.js
===================================================================
--- /CKReleaser/trunk/includes/io.js	(revision 2890)
+++ /CKReleaser/trunk/includes/io.js	(revision 2890)
@@ -0,0 +1,275 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+ */
+
+importPackage( javax.activation );
+importPackage( com.ice.tar );
+
+importClass( java.io.BufferedWriter );
+importClass( java.io.FileWriter );
+importClass( java.io.FileOutputStream );
+importClass( java.io.FileInputStream );
+importClass( java.io.InputStreamReader );
+importClass( java.io.FileOutputStream );
+importClass( java.io.OutputStreamWriter );
+importClass( java.util.zip.ZipOutputStream );
+importClass( java.util.zip.ZipEntry );
+importClass( java.util.zip.GZIPInputStream );
+
+( function()
+{
+	var txtExtensions = {
+		'' :0,
+		'txt' :1,
+		'js' :1,
+		'html' :1,
+		'css' :1,
+		'xml' :1
+	};
+
+	function compressDirectory( sourcePath, outStream, compressMethod )
+	{
+		if ( CKRELEASER.verbose )
+			print( "    " + compressMethod + ": " + sourcePath );
+
+		var r = new File( CKRELEASER.releaseDir );
+
+		try
+		{
+			var compressedDir = new File( sourcePath );
+
+			var dirList = compressedDir.list();
+			var readBuffer = new Packages.java.lang.reflect.Array.newInstance( java.lang.Byte.TYPE, 2056 );
+			var bytesIn = 0;
+			var anEntry, fis;
+
+			for ( var i = 0 ; i < dirList.length ; i++ )
+			{
+				var f = new File( compressedDir, dirList[i] );
+
+				if ( f.isDirectory() )
+				{
+					compressDirectory( f.getPath(), outStream, compressMethod );
+					continue;
+				}
+
+				fis = new FileInputStream( f );
+				
+				switch ( compressMethod )
+				{
+					case 'tar.gz':
+						anEntry = new TarEntry( f.getCanonicalPath().replace( r.getCanonicalPath(), "" ) );
+						break;
+					case 'zip':
+						anEntry = new ZipEntry( f.getCanonicalPath().replace( r.getCanonicalPath(), "" ) );
+						break;
+					default:
+						throw "Unknown compression method: " + compressMethod;
+						break;
+				}
+
+				outStream.putNextEntry( anEntry );
+
+				while ( (bytesIn = fis.read( readBuffer )) != -1 )
+				{
+					outStream.write( readBuffer, 0, bytesIn );
+				}
+				outStream.closeEntry();
+
+				fis.close();
+			}
+		}
+		catch ( e )
+		{
+			throw "An error occured during (" + compressMethod + ") compression of " + sourcePath + ": " + e;
+		}
+	}
+
+	function copyFile( sourceLocation, targetLocation )
+	{
+		var inStream = new FileInputStream( sourceLocation );
+		var outStream = new FileOutputStream( targetLocation );
+
+		var len, buf = new Packages.java.lang.reflect.Array.newInstance( java.lang.Byte.TYPE, 1024 );
+
+		while ( (len = inStream.read( buf )) != -1 )
+		{
+			outStream.write( buf, 0, len );
+		}
+		inStream.close();
+		outStream.close();
+	}
+
+	CKRELEASER.io = {
+		deleteDirectory : function( path )
+		{
+			var dir = new File( path );
+
+			if ( dir.isDirectory() )
+			{
+				var children = dir.list();
+				for ( var i = 0 ; i < children.length ; i++ )
+				{
+					if ( !this.deleteDirectory( new File( dir, children[i] ) ) )
+					{
+						return false;
+					}
+				}
+			}
+
+			return dir["delete"]();
+		},
+
+		deleteFile : function( path )
+		{
+			var f = new File( path );
+
+			if ( !f.exists() )
+				return true;
+
+			if ( !f.canWrite() )
+				throw "Cannot delete file: " + f.getAbsolutePath();
+
+			return f["delete"]();
+		},
+
+		saveFile : function( file, text )
+		{
+			var stream = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( file ), "UTF-8" ) );
+			stream.write( text );
+			stream.flush();
+			stream.close();
+		},
+
+		copy : function( sourceLocation, targetLocation )
+		{
+			if ( CKRELEASER.release.isIgnoredPath( sourceLocation.getAbsolutePath() ) )
+				return;
+
+			if ( CKRELEASER.verbose )
+				print( "    Copy -> " + targetLocation.toString().replaceFirst( ".*?release(/|\\\\)?", '' ) );
+
+			if ( sourceLocation.isDirectory() )
+			{
+				if ( !targetLocation.exists() )
+				{
+					targetLocation.mkdir();
+				}
+
+				var children = sourceLocation.list();
+				for ( var i = 0 ; i < children.length ; i++ )
+				{
+					this.copy( new File( sourceLocation, children[i] ), new File( targetLocation, children[i] ) );
+				}
+			}
+			else
+			{
+				copyFile( sourceLocation, targetLocation );
+			}
+		},
+
+		zipDirectory : function( sourcePath, targetFile )
+		{
+			var outStream = new ZipOutputStream( new FileOutputStream( targetFile ) );
+			compressDirectory( sourcePath, outStream, "zip" );
+			outStream.close();
+		},
+
+		targzDirectory : function( sourcePath, targetFile )
+		{
+			var outStream = new TarGzOutputStream( new FileOutputStream( targetFile ) );
+			compressDirectory( sourcePath, outStream, "tar.gz" );
+			outStream.close();
+		},
+
+		readFile : function( file )
+		{
+			var inStream = new InputStreamReader( new FileInputStream( file ), "UTF-8" );
+
+			var buffer = new StringBuffer();
+			var chars = new Packages.java.lang.reflect.Array.newInstance( java.lang.Character.TYPE, 8192 );
+			var count;
+
+			try
+			{
+				while ( (count = inStream.read( chars, 0, 8192 )) != -1 )
+				{
+					if ( count > 0 )
+					{
+						buffer.append( chars, 0, count );
+					}
+				}
+			}
+			catch ( e )
+			{
+				throw 'An I/O error occurred reading the ' + file.getAbsolutePath() + ' file.';
+			}
+			finally
+			{
+				inStream.close();
+			}
+
+			/* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 */
+			if ( buffer.length() && buffer.charAt( 0 ) == 65279 )
+				buffer.deleteCharAt( 0 );
+
+			return String( buffer.toString() );
+		},
+
+		getDirectoryInfo : function( file )
+		{
+			var path_iterator, current_file, files, result = { files: 0, size : 0 };
+
+			if (!file.exists())
+				return result;
+			
+			files = file.listFiles();
+			
+			if (!files)
+				return result;
+			
+			path_iterator = (java.util.Arrays.asList( files )).iterator();
+
+			while ( path_iterator.hasNext() )
+			{
+				current_file = path_iterator.next();
+				if ( current_file.isFile() )
+				{
+					result.size += current_file.length();
+					result.files++;
+				}
+				else
+				{
+					var info = this.getDirectoryInfo( current_file );
+					result.size += info.size;
+					result.files += info.files;
+				}
+			}
+
+			return result;
+		},
+		
+		getFileName : function( filePath )
+		{
+			var file = new File( filePath );
+			return file.getName();
+		},
+		getExtension : function( fileName )
+		{
+			var pos = fileName.lastIndexOf( "." );
+			if ( pos == -1 )
+				return "";
+			else
+				return fileName.substring( pos + 1 ).toLowerCase();
+		},
+
+		isTextFile : function( fileName )
+		{
+			var ext = this.getExtension( fileName );
+
+			return txtExtensions[ext] === 1;
+		}
+
+	};
+})();
Index: /CKReleaser/trunk/includes/releaser.js
===================================================================
--- /CKReleaser/trunk/includes/releaser.js	(revision 2890)
+++ /CKReleaser/trunk/includes/releaser.js	(revision 2890)
@@ -0,0 +1,327 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+ */
+
+importClass( java.io.BufferedWriter );
+importClass( java.io.FileWriter );
+importPackage( java.util.regex );
+
+CKRELEASER.loadCode( 'includes/io.js' );
+CKRELEASER.loadCode( 'includes/skinprocessor.js' );
+CKRELEASER.loadCode( 'includes/samplesprocessor.js' );
+CKRELEASER.loadCode( 'includes/docsprocessor.js' );
+CKRELEASER.loadCode( 'includes/tools.js' );
+
+( function()
+{
+	CKRELEASER.releaser = function()
+	{
+		CKRELEASER.release = new release();
+	};
+
+	function release()
+	{
+		this.fixLineEnds = false;
+		this.ignore = [];
+		this.copy = [];
+		this.packages = [];
+		this.documentation = {};
+		this.samples = {};
+		this.skins = {};
+	}
+
+	release.prototype.isIgnoredPath = function( path )
+	{
+		if ( !this.ignore )
+			return false;
+
+		for ( var i = 0 ; i < this.ignore.length ; i++ )
+		{
+			if ( path.replace( "\\", "/" ).endsWith( this.ignore[i] ) )
+				return true;
+		}
+
+		return false;
+	};
+
+	function moveFiles()
+	{
+		for ( var i = 0 ; i < CKRELEASER.release.copy.length ; i++ )
+		{
+			var sourceLocation = new File( CKRELEASER.releaseDir, CKRELEASER.release.copy[i].source );
+			var targetLocation = new File( CKRELEASER.releaseDir, CKRELEASER.release.copy[i].target );
+			CKRELEASER.io.copy( sourceLocation, targetLocation );
+			/**
+			 * TODO: Delete, really?
+			 */
+			CKRELEASER.io.deleteDirectory( sourceLocation );
+		}
+	}
+
+	function minifyFile( file )
+	{
+		var o = {
+			output :""
+		};
+		var ext = CKRELEASER.io.getExtension( file.getName() );
+
+		if ( ext == "css" )
+		{
+			runCommand( "java", "-jar", "yuicompressor.jar", file.getAbsolutePath(), "--line-break", "4096", "-v", "--charset", "UTF-8",
+					"-o", file.getAbsolutePath(), o );
+
+			print( o.output );
+		}
+		else if ( ext == "js" )
+		{
+			var source = CKRELEASER.io.readFile( file );
+			var compressed = CKPACKAGER.scriptCompressor.compress( source, false, null, false, false );
+			CKRELEASER.io.saveFile( file, compressed );
+		}
+	}
+
+	 function minifyFiles( file )
+	{
+		if ( file.isDirectory() )
+		{
+			var children = file.list();
+			for ( var i = 0 ; i < children.length ; i++ )
+				minifyFiles( new File( file, children[i] ) );
+		}
+		else if ( CKRELEASER.io.isTextFile( file.getName() ) )
+			minifyFile( file );
+	}
+
+	/**
+	 * FIXME: this function shouldn't exist at all
+	 */
+	function cleanUp()
+	{
+		var file, text;
+
+		file = new File( CKRELEASER.releaseDir, 'ckeditor.js' );
+		if ( !file.exists() )
+			throw "ckeditor.js not found";
+
+		text = CKRELEASER.io.readFile( file );
+		CKRELEASER.io.saveFile( file, text.replace( /_source\//g, '' ) );
+	}
+
+	var regexLib = {
+		packagerRemove :Pattern.compile( '(?m-s:^.*?%REMOVE_START%).*?(?m-s:%REMOVE_END%.*?$)', Pattern.DOTALL ),
+		packagerRemoveLine :Pattern.compile( '.*%REMOVE_LINE%.*(?:\\r\\n|\\r||\n)?' )
+	};
+
+	/*
+	 * %VERSION%: 
+	 *     the "version" string passed to the CKReleaser execution command.
+	 * %TIMESTAMP%: 
+	 *     a four characters string containing the
+	 *     concatenation of the "Base 36" value of each of the following components
+	 *     of the program execution date and time: year + month + day + hour.
+	 * %REMOVE_LINE%: 
+	 *     removes the line. 
+	 * %REMOVE_START% and %REMOVE_END%: 
+	 *     removes all lines starting from %REMOVE_START% to %REMOVE_END%, 
+	 *     declaration line inclusive.
+	 */
+	function replaceDirectives( file )
+	{
+		var text = CKRELEASER.io.readFile( file );
+		var replaced = false;
+
+		if ( text.indexOf( "%VERSION%" ) != -1 )
+		{
+			text = text.replace( /%VERSION%/g, CKRELEASER.version );
+			replaced = true;
+		}
+		if ( text.indexOf( "%TIMESTAMP%" ) != -1 )
+		{
+			text = text.replace( /%TIMESTAMP%/g, CKRELEASER.timestamp );
+			replaced = true;
+		}
+		if ( text.indexOf( "%REMOVE_START%" ) != -1 && text.indexOf( "%REMOVE_END%" ) != -1 )
+		{
+			text = String( regexLib.packagerRemove.matcher( text ).replaceAll( '%REMOVE_LINE%' ) );
+			text = String( regexLib.packagerRemoveLine.matcher( text ).replaceAll( '' ) );
+			replaced = true;
+		}
+		else if ( text.indexOf( "%REMOVE_LINE%" ) != -1 )
+		{
+			text = String( regexLib.packagerRemoveLine.matcher( text ).replaceAll( '' ) );
+			replaced = true;
+		}
+
+		if ( replaced )
+		{
+			if ( CKRELEASER.verbose )
+				print( "\r    Replacing directives in " + file );
+
+			CKRELEASER.io.saveFile( file, text );
+		}
+	}
+
+	CKRELEASER.releaser.prototype = {
+		loadDefinitionFile : function( filePath )
+		{
+			var file = new File( filePath );
+
+			var releaseCode = 'var release = { ' + CKRELEASER.io.readFile( filePath ) + '\n};';
+
+			var cx = Context.enter(), scope = cx.initStandardObjects();
+
+			cx.evaluateString( scope, releaseCode, file.getName(), 1, null );
+
+			this.loadDefinition( scope.release );
+		},
+
+		loadDefinition : function( definitionObject )
+		{
+			CKRELEASER.release = new release();
+
+			if ( definitionObject.fixLineEnds )
+				CKRELEASER.release.fixLineEnds = definitionObject.fixLineEnds;
+
+			if ( definitionObject.ignore )
+				CKRELEASER.release.ignore = definitionObject.ignore;
+
+			for ( var i = 0 ; i < CKRELEASER.release.ignore.length ; i++ )
+				CKRELEASER.release.ignore[i] = CKRELEASER.release.ignore[i].replace( "\\", "/" );
+
+			if ( definitionObject.copy )
+				CKRELEASER.release.copy = definitionObject.copy;
+
+			if ( definitionObject.packages )
+				CKRELEASER.release.packages = definitionObject.packages;
+
+			if ( definitionObject.documentation )
+				CKRELEASER.release.documentation = definitionObject.documentation;
+
+			if ( definitionObject.samples )
+				CKRELEASER.release.samples = definitionObject.samples;
+
+			if ( definitionObject.skins )
+				CKRELEASER.release.skins = definitionObject.skins;
+
+			if ( !CKRELEASER.release.packages.length )
+				throw "Nothing to release: no packages found in the release file (" + CKRELEASER.releaseFile + ")";
+		},
+
+		processDirectives : function( file )
+		{
+			System.out.print( "." );
+			if ( file.isDirectory() )
+			{
+				var children = file.list();
+				for ( var i = 0 ; i < children.length ; i++ )
+					this.processDirectives( new File( file, children[i] ) );
+			}
+			else if ( CKRELEASER.io.isTextFile( file.getName() ) )
+				replaceDirectives( file );
+		},
+
+		run : function()
+		{
+			var i, info, time = new Date();
+
+			var targetDir = new File( CKRELEASER.targetDir );
+			var releaseDir = new File( CKRELEASER.releaseDir );
+			var sourceDir = new File( CKRELEASER.sourceDir );
+
+			if ( !sourceDir.exists() )
+				throw 'Source directory does not exist (' + CKRELEASER.sourceDir + ')';
+			if ( targetDir.exists() )
+				throw 'Target directory already exists (' + CKRELEASER.targetDir + ')';
+			else if ( !releaseDir.mkdirs() )
+				throw "Cannot create release directory (" + releaseDir + ")";
+
+			print( "\nCopying files...\n" );
+			CKRELEASER.io.copy( sourceDir, releaseDir );
+
+			print( "\nMoving files to special destination...\n" );
+			moveFiles();
+
+			print( "\nPackaging CKEditor...\n" );
+			for ( i = 0 ; i < CKRELEASER.release.packages.length ; i++ )
+			{
+				var packager = new CKPACKAGER.packager();
+				packager.loadDefinitionFile( CKRELEASER.release.packages[i] );
+				packager.run();
+			}
+
+			print( "\nCreating skins...\n" );
+			var skinProcessor = new CKRELEASER.skinProcessor();
+			skinProcessor.createSkins( CKRELEASER.sourceDir, CKRELEASER.releaseDir );
+
+			print( "\nCreating documentation...\n" );
+			var docsProcessor = new CKRELEASER.docsProcessor();
+			docsProcessor.createDocumentation( CKRELEASER.sourceDir, CKRELEASER.releaseDir );
+
+			var docsDir = new File( CKRELEASER.releaseDir, CKRELEASER.release.documentation.target );
+			info = CKRELEASER.io.getDirectoryInfo( docsDir );
+			print( "    Number of created files: " + info.files );
+			print( "    Total size.............: " + info.size + " bytes" );
+
+			print( "\nCreating samples...\n" );
+			var samplesProcessor = new CKRELEASER.samplesProcessor();
+			samplesProcessor.createSamples( CKRELEASER.sourceDir, CKRELEASER.releaseDir );
+
+			print( "\nMinifying files...\n" );
+			for ( i = 0 ; i < CKRELEASER.release.copy.length ; i++ )
+			{
+				if ( CKRELEASER.release.copy[i].minify )
+				{
+					var targetLocation = new File( CKRELEASER.releaseDir, CKRELEASER.release.copy[i].target );
+					minifyFiles( targetLocation );
+				}
+			}
+
+			print( "\nProcessing directives in files...\n" );
+			this.processDirectives( releaseDir );
+
+			print( "\n\nCleaning up...\n" );
+			cleanUp();
+
+			print( "\nFixing line endings...\n" );
+			if ( CKRELEASER.release.fixLineEnds )
+			{
+				var o = {
+					output :""
+				};
+
+				runCommand( "php", "tools/fixlineends/fixlineends.php", "--eolstripwhite", "--eofnewline", "--eofstripwhite", "--nohidden",
+						"--nosystem", CKRELEASER.releaseDir, o );
+
+				if ( CKRELEASER.verbose )
+					print( o.output );
+			}
+
+			// get information about release directory
+			info = CKRELEASER.io.getDirectoryInfo( releaseDir );
+
+			print( "\nCreating compressed files...\n" );
+
+			var zipFile = new File( CKRELEASER.targetDir, CKRELEASER.zipName );
+			CKRELEASER.io.zipDirectory( CKRELEASER.releaseDir, zipFile );
+			print( "    Created " + zipFile.getName() + "...: " + zipFile.length() + " bytes ("
+					+ Math.round( zipFile.length() / info.size * 100 ) + "% of original)" );
+
+			var tarFile = new File( CKRELEASER.targetDir, CKRELEASER.targzName );
+			CKRELEASER.io.targzDirectory( CKRELEASER.releaseDir, tarFile );
+			print( "    Created " + tarFile.getName() + ": " + tarFile.length() + " bytes ("
+					+ Math.round( tarFile.length() / info.size * 100 ) + "% of original)" );
+
+			print( "\n==========================" );
+
+			time = (new Date()) - time;
+			time = time / 1000;
+
+			print( "Release process completed:" );
+			print( "    Number of files: " + info.files );
+			print( "    Total size.....: " + info.size + " bytes" );
+			print( "    Time taken:....: " + time + " seconds" );
+			print( "" );
+		}
+	};
+})();
Index: /CKReleaser/trunk/includes/samplesprocessor.js
===================================================================
--- /CKReleaser/trunk/includes/samplesprocessor.js	(revision 2890)
+++ /CKReleaser/trunk/includes/samplesprocessor.js	(revision 2890)
@@ -0,0 +1,152 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+ */
+
+CKRELEASER.loadCode( 'includes/xml.js' );
+
+CKRELEASER.samplesProcessor = function( release )
+{
+};
+
+( function()
+{
+	var templateFile, templateLocation, templateDocument, processedFiles = {}, copiedFiles = {};
+
+	var regexLib = {
+		head :Pattern.compile( '<head.*?<\\/head>', Pattern.DOTALL ),
+		body :Pattern.compile( '<body.*?<\\/body>', Pattern.DOTALL ),
+		script :Pattern.compile( '<script ([^>]+)/>' )
+	};
+
+	function processXmlFile( sourceLocation, targetLocation )
+	{
+		var newDocument = templateDocument.cloneNode( true );
+		var document = CKRELEASER.xml.loadDocument( sourceLocation );
+		var result = new java.lang.String( templateFile );
+		var node = {
+			code :null,
+			html :null,
+			headscript :null,
+			styles :null
+		};
+		var replacement = {
+			code :"",
+			html :"",
+			headscript :"",
+			styles :"",
+			head :"",
+			body :""
+		};
+
+		node.html = document.getElementById( 'html' );
+		node.code = document.getElementById( 'code' );
+
+		if ( !node.html || !node.code )
+		{
+			//Not a xml template, probably index.html or documentation.
+			if ( CKRELEASER.verbose )
+				print( "    WARNING: File with .html extension does not contain all necessary elements (html, code): "
+						+ sourceLocation.getName() );
+			CKRELEASER.io.copy( sourceLocation, targetLocation );
+			return;
+		}
+
+		node.html = newDocument.importNode( node.html, true );
+		node.code = newDocument.importNode( node.code, true );
+
+		CKRELEASER.xml.replaceNodeWithNodes( newDocument, newDocument.getElementById( 'html' ), node.html.getChildNodes() );
+		CKRELEASER.xml.replaceNodeWithNodes( newDocument, newDocument.getElementById( 'code' ), node.code.getChildNodes() );
+
+		node.styles = document.getElementById( 'styles' );
+		node.headscript = document.getElementById( 'headscript' );
+
+		if ( node.styles )
+		{
+			node.styles = newDocument.importNode( node.styles, true );
+			CKRELEASER.xml.replaceNode( newDocument, newDocument.getElementById( 'styles' ), node.styles );
+		}
+		else
+		{
+			newDocument.getElementById( 'styles' ).parentNode.removeChild( newDocument.getElementById( 'styles' ) );
+			if ( CKRELEASER.verbose )
+				print( "    XML file does not contain element with id 'styles': " + sourceLocation.getName() );
+		}
+
+		if ( node.headscript )
+		{
+			node.headscript = newDocument.importNode( node.headscript, true );
+			CKRELEASER.xml.replaceNode( newDocument, newDocument.getElementById( 'headscript' ), node.headscript );
+		}
+		else
+		{
+			newDocument.getElementById( 'headscript' ).parentNode.removeChild( newDocument.getElementById( 'headscript' ) );
+			if ( CKRELEASER.verbose )
+				print( "    XML file does not contain element with id 'headscript': " + sourceLocation.getName() );
+		}
+
+		replacement.head = CKRELEASER.xml.asXml( newDocument, newDocument.getElementsByTagName( 'head' ).item( 0 ) );
+		replacement.body = CKRELEASER.xml.asXml( newDocument, newDocument.getElementsByTagName( 'body' ).item( 0 ) );
+
+		result = String( regexLib.head.matcher( result ).replaceAll( replacement.head ) );
+		result = String( regexLib.body.matcher( result ).replaceAll( replacement.body ) );
+		result = String( regexLib.script.matcher( result ).replaceAll( "<script $1></script>" ) );
+		// FIXME: dirt hack, sample.html template includes ckeditor_source.js
+		result = result.replace( /_source\.js/g, ".js" );
+
+		print( "    Created sample " + targetLocation.getName() );
+		CKRELEASER.io.saveFile( targetLocation, result );
+	}
+
+	function processDirectory( sourceLocation, targetLocation )
+	{
+		if ( CKRELEASER.release.isIgnoredPath( sourceLocation.getAbsolutePath() ) )
+			return;
+
+		if ( sourceLocation.isDirectory() )
+		{
+			if ( !targetLocation.exists() )
+			{
+				targetLocation.mkdir();
+			}
+
+			var children = sourceLocation.list();
+			for ( var i = 0 ; i < children.length ; i++ )
+			{
+				processDirectory( new File( sourceLocation, children[i] ), new File( targetLocation, children[i] ) );
+			}
+		}
+		else
+		{
+			if ( sourceLocation.equals( templateLocation ) )
+				return;
+
+			if ( sourceLocation.getAbsolutePath().toLowerCase().endsWith( ".html" ) )
+				processXmlFile( sourceLocation, targetLocation );
+			else
+				CKRELEASER.io.copy( sourceLocation, targetLocation );
+
+			copiedFiles[sourceLocation] = targetLocation;
+		}
+	}
+
+	CKRELEASER.samplesProcessor.prototype = {
+		createSamples : function( sourceDir, targetDir )
+		{
+			var sourceLocation = new File( sourceDir, CKRELEASER.release.samples.source );
+			if ( !sourceLocation.exists() )
+				throw "Invalid source (" + sourceLocation.getAbsolutePath() + ")";
+
+			var targetLocation = new File( targetDir, CKRELEASER.release.samples.target );
+			
+			templateLocation = new File( sourceDir, CKRELEASER.release.samples.template );
+			if ( !templateLocation.exists() )
+				throw "Missing template file (" + templateLocation.getAbsolutePath() + ")";
+
+			templateDocument = CKRELEASER.xml.loadDocument( templateLocation );
+			templateFile = CKRELEASER.io.readFile( templateLocation );
+
+			processDirectory( sourceLocation, targetLocation );
+		}
+	};
+})();
Index: /CKReleaser/trunk/includes/skinprocessor.js
===================================================================
--- /CKReleaser/trunk/includes/skinprocessor.js	(revision 2890)
+++ /CKReleaser/trunk/includes/skinprocessor.js	(revision 2890)
@@ -0,0 +1,155 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+ */
+
+importClass( java.io.BufferedWriter );
+importClass( java.io.FileWriter );
+importClass( java.io.FileOutputStream );
+importClass( java.io.FileInputStream );
+
+CKRELEASER.skinProcessor = function()
+{
+};
+
+( function()
+{
+	var importedFiles = {}, copiedFiles = {};
+
+	function processCssFile( sourceLocation, parentLocation )
+	{
+		var parentPath, path = sourceLocation.getCanonicalPath();
+		var lines = CKRELEASER.io.readFile( path ).split( /\r\n|\n|\r/ );
+		var out = [];
+		var isImported = false;
+
+		if ( !parentLocation )
+		{
+			parentLocation = sourceLocation;
+			parentPath = sourceLocation.getCanonicalPath();
+		}
+		else
+		{
+			isImported = true;
+			parentPath = parentLocation.getCanonicalPath();
+			if ( path == parentPath )
+				throw "Invalid @import statements, file including itself: " + path;
+
+			if ( importedFiles[parentPath][path] )
+				throw "Invalid @import statement in " + parentPath + ", file " + path + " was already imported.";
+
+			importedFiles[parentPath][path] = true;
+		}
+
+		for ( var i = 0 ; i < lines.length ; i++ )
+		{
+			if ( lines[i].indexOf( "@import" ) == -1 )
+				out.push( lines[i] );
+			else
+			{
+				var matches = lines[i].match( /^\s*@import\s+url\("(.*?)"\)/ );
+
+				if ( matches[1] )
+				{
+					var file = new File( sourceLocation.getParent(), matches[1] );
+					if ( !file.exists() )
+						throw "Importing of CSS file failed, file does not exist (" + file.getPath() + ")";
+					else
+					{
+						if ( !importedFiles[parentPath] )
+							importedFiles[parentPath] = {};
+
+						out.push( processCssFile( file, parentLocation ) );
+					}
+				}
+				else
+					out.push( lines[i] );
+			}
+		}
+
+		if ( isImported )
+			return CKRELEASER.tools.removeComments( out.join( "\r\n" ) );
+		else
+			return out.join( "\r\n" );
+	}
+
+	function processSkins( sourceLocation, targetLocation )
+	{
+		if ( !sourceLocation.isDirectory() )
+			throw "Skins source is not a directory (" + sourceLocation.getAbsolutePath() + ");";
+
+		if ( !targetLocation.exists() )
+			targetLocation.mkdir();
+
+		var children = sourceLocation.list();
+		for ( var i = 0 ; i < children.length ; i++ )
+		{
+			if (children[i] == ".svn" || children[i] == "CVS")
+				continue;
+			
+			importedFiles = {};
+			copiedFiles = {};
+
+			if ( false !== processDirectory( new File( sourceLocation, children[i] ), new File( targetLocation, children[i] ) ) )
+				print( "    Created skin " + children[i] + "." );
+
+			for ( var parentPath in importedFiles )
+			{
+				for ( var path in importedFiles[parentPath] )
+				{
+					if ( !importedFiles[path] )
+					{
+						if ( CKRELEASER.verbose )
+							print( "    CSS file was imported, removing: " + path );
+						CKRELEASER.io.deleteFile( copiedFiles[path] );
+					}
+					else
+					{
+						if ( CKRELEASER.verbose )
+							print( "    CSS file was imported, but is also a root CSS file for another file: " + path );
+					}
+				}
+			}
+		}
+	}
+
+	function processDirectory( sourceLocation, targetLocation )
+	{
+		if ( CKRELEASER.release.isIgnoredPath( sourceLocation.getAbsolutePath() ) )
+			return;
+
+		if ( sourceLocation.isDirectory() )
+		{
+			if ( !targetLocation.exists() )
+				targetLocation.mkdir();
+
+			var children = sourceLocation.list();
+			for ( var i = 0 ; i < children.length ; i++ )
+			{
+				processDirectory( new File( sourceLocation, children[i] ), new File( targetLocation, children[i] ) );
+			}
+		}
+		else
+		{
+			if ( sourceLocation.getAbsolutePath().toLowerCase().endsWith( ".css" ) )
+			{
+				CKRELEASER.io.saveFile( targetLocation, processCssFile( sourceLocation, false ) );
+				if ( CKRELEASER.verbose )
+					print( "    Created CSS file: " + sourceLocation );
+			}
+			else
+				CKRELEASER.io.copy( sourceLocation, targetLocation );
+
+			copiedFiles[sourceLocation.getCanonicalPath()] = targetLocation;
+		}
+	}
+
+	CKRELEASER.skinProcessor.prototype.createSkins = function( sourceDir, targetDir )
+	{
+		var sourceLocation = new File( sourceDir, CKRELEASER.release.skins.source );
+		var targetLocation = new File( targetDir, CKRELEASER.release.skins.target );
+
+		processSkins( sourceLocation, targetLocation );
+	};
+
+})();
Index: /CKReleaser/trunk/includes/tools.js
===================================================================
--- /CKReleaser/trunk/includes/tools.js	(revision 2890)
+++ /CKReleaser/trunk/includes/tools.js	(revision 2890)
@@ -0,0 +1,68 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+ */
+
+importClass( java.lang.StringBuffer );
+
+( function()
+{
+	CKRELEASER.tools = {
+		removeComments : function( text )
+		{
+			var endIndex, startIndex = 0;
+			var iemac = false;
+			var preserve = false;
+
+			var sb = new StringBuffer( text );
+			while ( (startIndex = sb.indexOf( "/*", startIndex )) >= 0 )
+			{
+				preserve = sb.length() > startIndex + 2 && sb.charAt( startIndex + 2 ) == '!';
+				endIndex = sb.indexOf( "*/", startIndex + 2 );
+				if ( endIndex < 0 )
+				{
+					if ( !preserve )
+					{
+						sb["delete"]( startIndex, sb.length() );
+					}
+				}
+				else if ( endIndex >= startIndex + 2 )
+				{
+					if ( sb.charAt( endIndex - 1 ) == '\\' )
+					{
+						// Looks like a comment to hide rules from IE Mac.
+						// Leave this comment, and the following one, alone...
+						startIndex = endIndex + 2;
+						iemac = true;
+					}
+					else if ( iemac )
+					{
+						startIndex = endIndex + 2;
+						iemac = false;
+					}
+					else if ( !preserve )
+					{
+						//Remove new line character if there is nothing else after a comment
+						if (sb.charAt(endIndex+2)==13 && sb.charAt(endIndex+3)==10)
+							endIndex +=2;
+						else if (sb.charAt(endIndex+2)==10 && sb.charAt(endIndex+3)==13)
+							endIndex +=2;
+						else if (sb.charAt(endIndex+2)==13 && sb.charAt(endIndex+3)==13)
+							endIndex +=1;
+						else if (sb.charAt(endIndex+2)==10 && sb.charAt(endIndex+3)==10)
+							endIndex +=1;
+						
+						sb["delete"]( startIndex, endIndex + 2 );
+					}
+					else
+					{
+						startIndex = endIndex + 2;
+					}
+				}
+			}
+			
+			return sb.toString();
+		}
+	};
+
+})();
Index: /CKReleaser/trunk/includes/xml.js
===================================================================
--- /CKReleaser/trunk/includes/xml.js	(revision 2890)
+++ /CKReleaser/trunk/includes/xml.js	(revision 2890)
@@ -0,0 +1,72 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+ */
+
+importClass( java.io.StringWriter );
+importClass( java.io.FileInputStream );
+
+importClass( javax.xml.parsers.DocumentBuilderFactory );
+importClass( javax.xml.transform.dom.DOMSource );
+importClass( javax.xml.transform.OutputKeys );
+importClass( javax.xml.transform.stream.StreamResult );
+importClass( javax.xml.transform.TransformerFactory );
+
+( function()
+{
+	var docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+	
+	CKRELEASER.xml = {
+		
+		replaceNodeWithNodes : function( document, oldNode, nodeList )
+		{
+			for ( var i = 0 ; i < nodeList.getLength() ; i++ )
+				oldNode.getParentNode().insertBefore( document.importNode( nodeList.item( i ), true ), oldNode );
+
+			oldNode.getParentNode().removeChild( oldNode );
+		},
+
+		replaceNode : function( document, oldNode, newNode )
+		{
+			oldNode.getParentNode().insertBefore( document.importNode( newNode, true ), oldNode );
+			oldNode.getParentNode().removeChild( oldNode );
+		},
+
+		loadDocument : function( location )
+		{
+			return docBuilder.parse( location );
+		},
+
+		asXml : function( document, node )
+		{
+			var transformer = TransformerFactory.newInstance().newTransformer();
+			transformer.setOutputProperty( OutputKeys.INDENT, "no" );
+			transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" );
+
+			var result = new StreamResult( new StringWriter() );
+			var source = new DOMSource( document );
+			if ( node )
+				source.setNode( node );
+			transformer.transform( source, result );
+
+			return result.getWriter().toString();
+		},
+
+		/**
+		 * FIXME: New line characters in comments are changed. x0Dx0A is changed into x0A.
+		 */
+		saveXml : function( document, file )
+		{
+			var docType = document.getDoctype();
+			var transformer = TransformerFactory.newInstance().newTransformer();
+			transformer.setOutputProperty( OutputKeys.DOCTYPE_PUBLIC, docType.getPublicId() );
+			transformer.setOutputProperty( OutputKeys.DOCTYPE_SYSTEM, docType.getSystemId() );
+			transformer.setOutputProperty( OutputKeys.INDENT, "no" );
+			transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" );
+
+			var result = new StreamResult( file );
+			var source = new DOMSource( document );
+			transformer.transform( source, result );
+		}
+	};
+})();
Index: /CKReleaser/trunk/run.bat
===================================================================
--- /CKReleaser/trunk/run.bat	(revision 2890)
+++ /CKReleaser/trunk/run.bat	(revision 2890)
@@ -0,0 +1,11 @@
+:: Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+:: For licensing, see LICENSE.html or http://ckeditor.com/license
+
+@ECHO OFF
+
+CLS
+ECHO.
+
+:: rmdir /S /Q targetdir
+
+java -cp js.jar;tools/javatar/tar.jar;tools/tartool/tartool.jar;tools/jaf/activation.jar org.mozilla.javascript.tools.shell.Main ckreleaser.js ckeditor.release ../ckeditor targetdir 0.1 ckeditor_0.1.zip ckeditor_0.1.tar.gz
Index: /CKReleaser/trunk/test/_assets/directives/remove_line.correct.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/directives/remove_line.correct.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/directives/remove_line.correct.txt	(revision 2890)
@@ -0,0 +1,7 @@
+﻿aa bb
+
+ee
+ff
+REMOVE_LINE
+ii
+
Index: /CKReleaser/trunk/test/_assets/directives/remove_line.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/directives/remove_line.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/directives/remove_line.txt	(revision 2890)
@@ -0,0 +1,9 @@
+﻿aa bb
+
+cc %REMOVE_LINE% dd
+ee
+%REMOVE_LINE%
+ff
+REMOVE_LINE
+ii
+
Index: /CKReleaser/trunk/test/_assets/directives/remove_range.correct.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/directives/remove_range.correct.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/directives/remove_range.correct.txt	(revision 2890)
@@ -0,0 +1,3 @@
+﻿aa bb
+ii jj
+mm
Index: /CKReleaser/trunk/test/_assets/directives/remove_range.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/directives/remove_range.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/directives/remove_range.txt	(revision 2890)
@@ -0,0 +1,7 @@
+﻿aa bb
+cc %REMOVE_START% dd
+ee ff
+gg %REMOVE_END%hh
+ii jj
+kk %REMOVE_START% iii %REMOVE_END% ll
+mm
Index: /CKReleaser/trunk/test/_assets/directives/timestamp.correct.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/directives/timestamp.correct.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/directives/timestamp.correct.txt	(revision 2890)
@@ -0,0 +1,2 @@
+﻿aa AB89 cc
+bb %TIMESTAMP Редакторdd AB89
Index: /CKReleaser/trunk/test/_assets/directives/timestamp.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/directives/timestamp.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/directives/timestamp.txt	(revision 2890)
@@ -0,0 +1,2 @@
+﻿aa %TIMESTAMP% cc
+bb %TIMESTAMP Редакторdd %TIMESTAMP%
Index: /CKReleaser/trunk/test/_assets/directives/version.correct.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/directives/version.correct.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/directives/version.correct.txt	(revision 2890)
@@ -0,0 +1,2 @@
+﻿aa 3.1beta cc
+3.1beta bb %VERSION dd
Index: /CKReleaser/trunk/test/_assets/directives/version.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/directives/version.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/directives/version.txt	(revision 2890)
@@ -0,0 +1,2 @@
+﻿aa %VERSION% cc
+%VERSION% bb %VERSION dd
Index: /CKReleaser/trunk/test/_assets/samples/ajax.correct.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/samples/ajax.correct.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/samples/ajax.correct.txt	(revision 2890)
@@ -0,0 +1,139 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+	<title>Sample - CKEditor</title>
+	<script src="../ckeditor.js" type="text/javascript" xml:space="preserve"></script>
+<!--
+	## Uncomment this if loading the "basic" version.
+
+	<script type="text/javascript" src="../_source/core/loader.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+// Loaded dependencies of sample.js.
+CKEDITOR.loader.load( 'core/ajax' );
+CKEDITOR.loader.load( 'core/env' );
+
+	//]]>
+	</script>
+-->
+	<script src="sample.js" type="text/javascript" xml:space="preserve"></script>
+	<link href="sample.css" rel="stylesheet" type="text/css"/>
+	
+	<script id="headscript" type="text/javascript">
+	//<![CDATA[
+
+var editor;
+
+function createEditor()
+{
+	if ( editor )
+		return;
+
+	var html = document.getElementById( 'editorcontents' ).innerHTML;
+
+	// Create a new editor inside the <div id="editor">
+	editor = CKEDITOR.appendTo( 'editor' );
+	editor.setData( html );
+
+	// This sample may break here if the ckeditor_basic.js is used. In such case, the following code should be used instead:
+	/*
+	if ( editor.setData )
+		editor.setData( html );
+	else
+		CKEDITOR.on( 'loaded', function()
+			{
+				editor.setData( html );
+			});
+	*/
+}
+
+function removeEditor()
+{
+	if ( !editor )
+		return;
+
+	// Retrieve the editor contents. In an Ajax application, this data would be
+	// sent to the server or used in any other way.
+	document.getElementById( 'editorcontents' ).innerHTML = editor.getData();
+	document.getElementById( 'contents' ).style.display = '';
+
+	// Destroy the editor.
+	editor.destroy();
+	editor = null;
+}
+
+	//]]>
+	</script>
+</head>
+<body>
+	<h1>
+		CKEditor Sample
+	</h1>
+	<!-- This <div> holds alert messages to be display in the sample page. -->
+	<div id="alerts">
+		<noscript>
+			<p>
+				<strong>CKEditor requires JavaScript to run</strong>. In a browser with no JavaScript
+				support, like yours, you should still see the contents (HTML data) and you should
+				be able to edit it normally, without a rich editor interface.
+			</p>
+		</noscript>
+	</div>
+	<!-- This <fieldset> holds the HTML that you will usually find in your
+	     pages. -->
+	<fieldset title="Output">
+		<legend>Output</legend>
+		
+		<p>
+			<input onclick="createEditor();" type="button" value="Create Editor"/>
+			<input onclick="removeEditor();" type="button" value="Remove Editor"/>
+		</p>
+		<div id="editor">
+		</div>
+		<div id="contents" style="display: none">
+			<p>Edited Contents:</p>
+			<div id="editorcontents"/>
+		</div>
+	
+	</fieldset>
+	<!-- This <fieldset> contains the output readable code that illustrates
+	     how to use the editor, having the results shown in this sample. -->
+	<fieldset title="Code">
+		<legend>Code</legend>
+		
+		<pre/>
+	
+	</fieldset>
+	<div id="footer">
+		<hr/>
+		<p>
+			CKEditor - The text editor for Internet - <a href="http://ckeditor.com/" shape="rect">http://ckeditor.com</a>
+		</p>
+		<p id="copy">
+			Copyright © 2003-2008 Frederico Caldeira Knabben (<a href="http://www.fredck.com/" shape="rect">FredCK.com</a>)
+		</p>
+	</div>
+</body>
+</html>
Index: /CKReleaser/trunk/test/_assets/samples/ajax.html
===================================================================
--- /CKReleaser/trunk/test/_assets/samples/ajax.html	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/samples/ajax.html	(revision 2890)
@@ -0,0 +1,89 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>Ajax - CKEditor Sample</title>
+	<script type="text/javascript" src="sample.js"></script>
+	<script id="headscript" type="text/javascript">
+	//<![CDATA[
+
+var editor;
+
+function createEditor()
+{
+	if ( editor )
+		return;
+
+	var html = document.getElementById( 'editorcontents' ).innerHTML;
+
+	// Create a new editor inside the <div id="editor">
+	editor = CKEDITOR.appendTo( 'editor' );
+	editor.setData( html );
+
+	// This sample may break here if the ckeditor_basic.js is used. In such case, the following code should be used instead:
+	/*
+	if ( editor.setData )
+		editor.setData( html );
+	else
+		CKEDITOR.on( 'loaded', function()
+			{
+				editor.setData( html );
+			});
+	*/
+}
+
+function removeEditor()
+{
+	if ( !editor )
+		return;
+
+	// Retrieve the editor contents. In an Ajax application, this data would be
+	// sent to the server or used in any other way.
+	document.getElementById( 'editorcontents' ).innerHTML = editor.getData();
+	document.getElementById( 'contents' ).style.display = '';
+
+	// Destroy the editor.
+	editor.destroy();
+	editor = null;
+}
+
+	//]]>
+	</script>
+</head>
+<body>
+	<div id="html">
+		<p>
+			<input type="button" value="Create Editor" onclick="createEditor();" />
+			<input type="button" value="Remove Editor" onclick="removeEditor();" />
+		</p>
+		<div id="editor">
+		</div>
+		<div id="contents" style="display: none">
+			<p>Edited Contents:</p>
+			<div id="editorcontents"></div>
+		</div>
+	</div>
+	<div id="code">
+		<pre></pre>
+	</div>
+</body>
+</html>
Index: /CKReleaser/trunk/test/_assets/samples/divreplace.correct.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/samples/divreplace.correct.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/samples/divreplace.correct.txt	(revision 2890)
@@ -0,0 +1,180 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+	<title>Sample - CKEditor</title>
+	<script src="../ckeditor.js" type="text/javascript" xml:space="preserve"></script>
+<!--
+	## Uncomment this if loading the "basic" version.
+
+	<script type="text/javascript" src="../_source/core/loader.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+// Loaded dependencies of sample.js.
+CKEDITOR.loader.load( 'core/ajax' );
+CKEDITOR.loader.load( 'core/env' );
+
+	//]]>
+	</script>
+-->
+	<script src="sample.js" type="text/javascript" xml:space="preserve"></script>
+	<link href="sample.css" rel="stylesheet" type="text/css"/>
+	<style id="styles" type="text/css">
+
+		div
+		{
+			border: solid 2px Transparent;
+			padding-left: 15px;
+			padding-right: 15px;
+		}
+
+		div:hover
+		{
+			border-color: black;
+		}
+
+	</style>
+	<script id="headscript" type="text/javascript">
+	//<![CDATA[
+
+// Uncomment the following code to test the "Timeout Loading Method".
+// CKEDITOR.loadFullCoreTimeout = 5;
+
+window.onload = function()
+{
+	// Listen to the double click event.
+	if ( window.addEventListener )
+		document.body.addEventListener( 'dblclick', onDoubleClick, false );
+	else if ( window.attachEvent )
+		document.body.attachEvent( 'ondblclick', onDoubleClick );
+
+};
+
+function onDoubleClick( ev )
+{
+	// Get the element which fired the event. This is not necessarily the
+	// element to which the event has been attached.
+	var element = ev.target || ev.srcElement;
+
+	// Find out the div that holds this element.
+	while( element.parentNode && element.parentNode.nodeName.toLowerCase() != 'fieldset' )
+		element = element.parentNode;
+
+	if ( element.nodeName.toLowerCase() == 'div' )
+		replaceDiv( element );
+}
+
+var editor;
+
+function replaceDiv( div )
+{
+	if ( editor )
+		editor.destroy();
+
+	editor = CKEDITOR.replace( div );
+}
+
+	//]]>
+	</script>
+</head>
+<body>
+	<h1>
+		CKEditor Sample
+	</h1>
+	<!-- This <div> holds alert messages to be display in the sample page. -->
+	<div id="alerts">
+		<noscript>
+			<p>
+				<strong>CKEditor requires JavaScript to run</strong>. In a browser with no JavaScript
+				support, like yours, you should still see the contents (HTML data) and you should
+				be able to edit it normally, without a rich editor interface.
+			</p>
+		</noscript>
+	</div>
+	<!-- This <fieldset> holds the HTML that you will usually find in your
+	     pages. -->
+	<fieldset title="Output">
+		<legend>Output</legend>
+		
+		<p>
+			Double-click on any of the following DIVs to transform them into editor instances.</p>
+		<div>
+			<h3>
+				Part 1</h3>
+			<p>
+				Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et ipsum quis mi
+				semper accumsan. Integer pretium dui id massa. Suspendisse in nisl sit amet urna
+				rutrum imperdiet. Nulla eu tellus. Donec ante nisi, ullamcorper quis, fringilla
+				nec, sagittis eleifend, pede. Nulla commodo interdum massa. Donec id metus. Fusce
+				eu ipsum. Suspendisse auctor. Phasellus fermentum porttitor risus.
+			</p>
+		</div>
+		<div>
+			<h3>
+				Part 2</h3>
+			<p>
+				Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et ipsum quis mi
+				semper accumsan. Integer pretium dui id massa. Suspendisse in nisl sit amet urna
+				rutrum imperdiet. Nulla eu tellus. Donec ante nisi, ullamcorper quis, fringilla
+				nec, sagittis eleifend, pede. Nulla commodo interdum massa. Donec id metus. Fusce
+				eu ipsum. Suspendisse auctor. Phasellus fermentum porttitor risus.
+			</p>
+			<p>
+				Donec velit. Mauris massa. Vestibulum non nulla. Nam suscipit arcu nec elit. Phasellus
+				sollicitudin iaculis ante. Ut non mauris et sapien tincidunt adipiscing. Vestibulum
+				vitae leo. Suspendisse nec mi tristique nulla laoreet vulputate.
+			</p>
+		</div>
+		<div>
+			<h3>
+				Part 3</h3>
+			<p>
+				Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et ipsum quis mi
+				semper accumsan. Integer pretium dui id massa. Suspendisse in nisl sit amet urna
+				rutrum imperdiet. Nulla eu tellus. Donec ante nisi, ullamcorper quis, fringilla
+				nec, sagittis eleifend, pede. Nulla commodo interdum massa. Donec id metus. Fusce
+				eu ipsum. Suspendisse auctor. Phasellus fermentum porttitor risus.
+			</p>
+		</div>
+	
+	</fieldset>
+	<!-- This <fieldset> contains the output readable code that illustrates
+	     how to use the editor, having the results shown in this sample. -->
+	<fieldset title="Code">
+		<legend>Code</legend>
+		
+		<pre/>
+	
+	</fieldset>
+	<div id="footer">
+		<hr/>
+		<p>
+			CKEditor - The text editor for Internet - <a href="http://ckeditor.com/" shape="rect">http://ckeditor.com</a>
+		</p>
+		<p id="copy">
+			Copyright © 2003-2008 Frederico Caldeira Knabben (<a href="http://www.fredck.com/" shape="rect">FredCK.com</a>)
+		</p>
+	</div>
+</body>
+</html>
Index: /CKReleaser/trunk/test/_assets/samples/divreplace.html
===================================================================
--- /CKReleaser/trunk/test/_assets/samples/divreplace.html	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/samples/divreplace.html	(revision 2890)
@@ -0,0 +1,131 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>Replace DIV - CKEditor Sample</title>
+	<style id="styles" type="text/css">
+
+		div
+		{
+			border: solid 2px Transparent;
+			padding-left: 15px;
+			padding-right: 15px;
+		}
+
+		div:hover
+		{
+			border-color: black;
+		}
+
+	</style>
+	<script type="text/javascript" src="sample.js"></script>
+	<script id="headscript" type="text/javascript">
+	//<![CDATA[
+
+// Uncomment the following code to test the "Timeout Loading Method".
+// CKEDITOR.loadFullCoreTimeout = 5;
+
+window.onload = function()
+{
+	// Listen to the double click event.
+	if ( window.addEventListener )
+		document.body.addEventListener( 'dblclick', onDoubleClick, false );
+	else if ( window.attachEvent )
+		document.body.attachEvent( 'ondblclick', onDoubleClick );
+
+};
+
+function onDoubleClick( ev )
+{
+	// Get the element which fired the event. This is not necessarily the
+	// element to which the event has been attached.
+	var element = ev.target || ev.srcElement;
+
+	// Find out the div that holds this element.
+	while( element.parentNode && element.parentNode.nodeName.toLowerCase() != 'fieldset' )
+		element = element.parentNode;
+
+	if ( element.nodeName.toLowerCase() == 'div' )
+		replaceDiv( element );
+}
+
+var editor;
+
+function replaceDiv( div )
+{
+	if ( editor )
+		editor.destroy();
+
+	editor = CKEDITOR.replace( div );
+}
+
+	//]]>
+	</script>
+</head>
+<body>
+	<div id="html">
+		<p>
+			Double-click on any of the following DIVs to transform them into editor instances.</p>
+		<div>
+			<h3>
+				Part 1</h3>
+			<p>
+				Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et ipsum quis mi
+				semper accumsan. Integer pretium dui id massa. Suspendisse in nisl sit amet urna
+				rutrum imperdiet. Nulla eu tellus. Donec ante nisi, ullamcorper quis, fringilla
+				nec, sagittis eleifend, pede. Nulla commodo interdum massa. Donec id metus. Fusce
+				eu ipsum. Suspendisse auctor. Phasellus fermentum porttitor risus.
+			</p>
+		</div>
+		<div>
+			<h3>
+				Part 2</h3>
+			<p>
+				Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et ipsum quis mi
+				semper accumsan. Integer pretium dui id massa. Suspendisse in nisl sit amet urna
+				rutrum imperdiet. Nulla eu tellus. Donec ante nisi, ullamcorper quis, fringilla
+				nec, sagittis eleifend, pede. Nulla commodo interdum massa. Donec id metus. Fusce
+				eu ipsum. Suspendisse auctor. Phasellus fermentum porttitor risus.
+			</p>
+			<p>
+				Donec velit. Mauris massa. Vestibulum non nulla. Nam suscipit arcu nec elit. Phasellus
+				sollicitudin iaculis ante. Ut non mauris et sapien tincidunt adipiscing. Vestibulum
+				vitae leo. Suspendisse nec mi tristique nulla laoreet vulputate.
+			</p>
+		</div>
+		<div>
+			<h3>
+				Part 3</h3>
+			<p>
+				Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et ipsum quis mi
+				semper accumsan. Integer pretium dui id massa. Suspendisse in nisl sit amet urna
+				rutrum imperdiet. Nulla eu tellus. Donec ante nisi, ullamcorper quis, fringilla
+				nec, sagittis eleifend, pede. Nulla commodo interdum massa. Donec id metus. Fusce
+				eu ipsum. Suspendisse auctor. Phasellus fermentum porttitor risus.
+			</p>
+		</div>
+	</div>
+	<div id="code">
+		<pre></pre>
+	</div>
+</body>
+</html>
Index: /CKReleaser/trunk/test/_assets/samples/index.correct.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/samples/index.correct.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/samples/index.correct.txt	(revision 2890)
@@ -0,0 +1,55 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>Samples List - CKEditor</title>
+	<link type="text/css" rel="stylesheet" href="sample.css" />
+</head>
+<body>
+	<h1>
+		CKEditor Samples List
+	</h1>
+	<h2>
+		Basic Samples
+	</h2>
+	<ul>
+		<li><a href="replacebyclass.html">Replace textareas by class name</a></li>
+		<li><a href="replacebycode.html">Replace textareas by code</a></li>
+	</ul>
+	<h2>
+		Advanced Samples
+	</h2>
+	<ul>
+		<li><a href="divreplace.html">Replace DIV elements on the fly</a>&nbsp; </li>
+		<li><a href="ajax.html">Create and destroy editor instances for Ajax applications</a></li>
+	</ul>
+	<div id="footer">
+		<hr />
+		<p>
+			CKEditor - The text editor for Internet - <a href="http://ckeditor.com/">http://ckeditor.com</a>
+		</p>
+		<p id="copy">
+			Copyright &copy; 2003-2008 Frederico Caldeira Knabben (<a href="http://www.fredck.com/">FredCK.com</a>)
+		</p>
+	</div>
+</body>
+</html>
Index: /CKReleaser/trunk/test/_assets/samples/index.html
===================================================================
--- /CKReleaser/trunk/test/_assets/samples/index.html	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/samples/index.html	(revision 2890)
@@ -0,0 +1,55 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>Samples List - CKEditor</title>
+	<link type="text/css" rel="stylesheet" href="sample.css" />
+</head>
+<body>
+	<h1>
+		CKEditor Samples List
+	</h1>
+	<h2>
+		Basic Samples
+	</h2>
+	<ul>
+		<li><a href="replacebyclass.html">Replace textareas by class name</a></li>
+		<li><a href="replacebycode.html">Replace textareas by code</a></li>
+	</ul>
+	<h2>
+		Advanced Samples
+	</h2>
+	<ul>
+		<li><a href="divreplace.html">Replace DIV elements on the fly</a>&nbsp; </li>
+		<li><a href="ajax.html">Create and destroy editor instances for Ajax applications</a></li>
+	</ul>
+	<div id="footer">
+		<hr />
+		<p>
+			CKEditor - The text editor for Internet - <a href="http://ckeditor.com/">http://ckeditor.com</a>
+		</p>
+		<p id="copy">
+			Copyright &copy; 2003-2008 Frederico Caldeira Knabben (<a href="http://www.fredck.com/">FredCK.com</a>)
+		</p>
+	</div>
+</body>
+</html>
Index: /CKReleaser/trunk/test/_assets/samples/sample.css
===================================================================
--- /CKReleaser/trunk/test/_assets/samples/sample.css	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/samples/sample.css	(revision 2890)
@@ -0,0 +1,65 @@
+﻿/*
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ */
+
+fieldset
+{
+	margin: 0;
+	padding: 10px;
+}
+
+form
+{
+	margin: 0;
+	padding: 0;
+}
+
+pre
+{
+	background-color: #F7F7F7;
+	border: 1px solid #D7D7D7;
+	overflow: auto;
+	margin: 0;
+	padding: 0.25em;
+}
+
+#alerts
+{
+	color: Red;
+}
+
+#footer hr
+{
+	margin: 10px 0 15px 0;
+	height: 1px;
+	border: solid 1px gray;
+	border-bottom: none;
+}
+
+#footer p
+{
+	margin: 0 10px 10px 10px;
+	float: left;
+}
+
+#footer #copy
+{
+	float: right;
+}
Index: /CKReleaser/trunk/test/_assets/samples/sample.html
===================================================================
--- /CKReleaser/trunk/test/_assets/samples/sample.html	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/samples/sample.html	(revision 2890)
@@ -0,0 +1,98 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>Sample - CKEditor</title>
+	<script type="text/javascript" src="../ckeditor_source.js"></script>
+<!--
+	## Uncomment this if loading the "basic" version.
+
+	<script type="text/javascript" src="../_source/core/loader.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+// Loaded dependencies of sample.js.
+CKEDITOR.loader.load( 'core/ajax' );
+CKEDITOR.loader.load( 'core/env' );
+
+	//]]>
+	</script>
+-->
+	<script type="text/javascript" src="sample.js"></script>
+	<link type="text/css" rel="stylesheet" href="sample.css" />
+	<script id="styles" type="text/javascript">
+	//<![CDATA[
+		document.write( CKEDITOR.samples.styles );
+	//]]>
+	</script>
+	<script id="headscript" type="text/javascript">
+	//<![CDATA[
+		document.write( CKEDITOR.samples.headScript );
+	//]]>
+	</script>
+</head>
+<body>
+	<h1>
+		CKEditor Sample
+	</h1>
+	<!-- This <div> holds alert messages to be display in the sample page. -->
+	<div id="alerts">
+		<noscript>
+			<p>
+				<strong>CKEditor requires JavaScript to run</strong>. In a browser with no JavaScript
+				support, like yours, you should still see the contents (HTML data) and you should
+				be able to edit it normally, without a rich editor interface.
+			</p>
+		</noscript>
+	</div>
+	<!-- This <fieldset> holds the HTML that you will usually find in your
+	     pages. -->
+	<fieldset title="Output">
+		<legend>Output</legend>
+		<script id="html" type="text/javascript">
+		//<![CDATA[
+			document.write( CKEDITOR.samples.htmlData );
+		//]]>
+		</script>
+	</fieldset>
+	<!-- This <fieldset> contains the output readable code that illustrates
+	     how to use the editor, having the results shown in this sample. -->
+	<fieldset title="Code">
+		<legend>Code</legend>
+		<script id="code" type="text/javascript">
+		//<![CDATA[
+			document.write( CKEDITOR.samples.codeData );
+		//]]>
+		</script>
+	</fieldset>
+	<div id="footer">
+		<hr />
+		<p>
+			CKEditor - The text editor for Internet - <a href="http://ckeditor.com/">http://ckeditor.com</a>
+		</p>
+		<p id="copy">
+			Copyright &copy; 2003-2008 Frederico Caldeira Knabben (<a href="http://www.fredck.com/">FredCK.com</a>)
+		</p>
+	</div>
+</body>
+</html>
Index: /CKReleaser/trunk/test/_assets/skins/good/dummy.correct.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/skins/good/dummy.correct.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/skins/good/dummy.correct.txt	(revision 2890)
@@ -0,0 +1,6 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+p {color:#222}
Index: /CKReleaser/trunk/test/_assets/skins/good/dummy.css
===================================================================
--- /CKReleaser/trunk/test/_assets/skins/good/dummy.css	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/skins/good/dummy.css	(revision 2890)
@@ -0,0 +1,6 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+p {color:#222}
Index: /CKReleaser/trunk/test/_assets/skins/good/editor.correct.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/skins/good/editor.correct.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/skins/good/editor.correct.txt	(revision 2890)
@@ -0,0 +1,11 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+
+a {color:red}
+b {top:1em}
+
+p {color:#666}
+u {top:5px}
Index: /CKReleaser/trunk/test/_assets/skins/good/editor.css
===================================================================
--- /CKReleaser/trunk/test/_assets/skins/good/editor.css	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/skins/good/editor.css	(revision 2890)
@@ -0,0 +1,7 @@
+﻿/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+@import url("sub/reset.css");
+@import url("mainui.css");
Index: /CKReleaser/trunk/test/_assets/skins/good/mainui.css
===================================================================
--- /CKReleaser/trunk/test/_assets/skins/good/mainui.css	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/skins/good/mainui.css	(revision 2890)
@@ -0,0 +1,7 @@
+﻿/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+p {color:#666}
+u {top:5px}
Index: /CKReleaser/trunk/test/_assets/skins/good/style.correct.txt
===================================================================
--- /CKReleaser/trunk/test/_assets/skins/good/style.correct.txt	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/skins/good/style.correct.txt	(revision 2890)
@@ -0,0 +1,10 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+ul{color:black}
+
+p {color:#666}
+u {top:5px}
+div{float:left}
Index: /CKReleaser/trunk/test/_assets/skins/good/style.css
===================================================================
--- /CKReleaser/trunk/test/_assets/skins/good/style.css	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/skins/good/style.css	(revision 2890)
@@ -0,0 +1,8 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+ul{color:black}
+@import url("mainui.css");
+div{float:left}
Index: /CKReleaser/trunk/test/_assets/skins/good/sub/reset.css
===================================================================
--- /CKReleaser/trunk/test/_assets/skins/good/sub/reset.css	(revision 2890)
+++ /CKReleaser/trunk/test/_assets/skins/good/sub/reset.css	(revision 2890)
@@ -0,0 +1,7 @@
+﻿/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+a {color:red}
+b {top:1em}
Index: /CKReleaser/trunk/test/test.bat
===================================================================
--- /CKReleaser/trunk/test/test.bat	(revision 2890)
+++ /CKReleaser/trunk/test/test.bat	(revision 2890)
@@ -0,0 +1,8 @@
+:: Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+:: For licensing, see LICENSE.html or http://ckeditor.com/license
+
+@ECHO OFF
+CLS
+ECHO.
+
+java -cp ../js.jar;../tar.jar;../tartool.jar;../activation.jar org.mozilla.javascript.tools.shell.Main ../ckreleaser.js -test %1
Index: /CKReleaser/trunk/test/test.js
===================================================================
--- /CKReleaser/trunk/test/test.js	(revision 2890)
+++ /CKReleaser/trunk/test/test.js	(revision 2890)
@@ -0,0 +1,228 @@
+/*
+Copyright (c) 2003-2008, Frederico Caldeira Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+( function()
+{
+	var releaser = new CKRELEASER.releaser();
+
+	// Run tests.
+	var passCount = 0, failCount = 0;
+
+	function assertEquals( expected, actual, title )
+	{
+		if ( expected != actual )
+		{
+			var error = {
+				expected :expected,
+				actual :actual
+			};
+
+			print( 'FAILED: ' + (title ? title : "") );
+
+			if ( !error.expected )
+				throw error;
+
+			print( '  Expected: ' + error.expected );
+			print( '  Actual  : ' + error.actual );
+
+			failCount++;
+		}
+		else
+			passCount++;
+	}
+
+	function error( msg )
+	{
+		print( msg );
+		quit();
+	}
+
+	CKRELEASER.release.ignore = [ '.svn', '.settings', '.project' ];
+
+	function prepareTempDirs()
+	{
+		var tempDir = new File( 'tmp' );
+
+		if ( tempDir.exists() && !CKRELEASER.io.deleteDirectory( 'tmp' ) )
+			error( "Can't delete temp directory" );
+
+		if ( !tempDir.mkdir() )
+			error( "Can't create temp directory: " + tempDir );
+
+		var tests = [ 'directives', 'skins', 'samples' ];
+
+		for ( var i = 0 ; i < tests.length ; i++ )
+		{
+			tempDir = new File( 'tmp/' + tests[i] );
+			if ( !tempDir.mkdir() )
+				error( "Can't create temp directory: " + tempDir );
+		}
+	}
+
+	function testDirectives()
+	{
+		print( "\nTesting directives\n" );
+
+		CKRELEASER.version = "3.1beta";
+		CKRELEASER.timestamp = "AB89";
+
+		var name = 'directives';
+		var testName, tempFile, correctFile, sampleFile;
+
+		var dir = new File( '_assets/directives' );
+		var dirList = dir.list();
+
+		for ( var i = 0 ; i < dirList.length ; i++ )
+		{
+			var f = new File( dir, dirList[i] );
+
+			if ( dirList[i].indexOf( ".correct." ) == -1 )
+				continue;
+
+			testName = dirList[i].replace( ".correct.txt", "" );
+
+			sampleFile = new File( '_assets/' + name + '/' + testName + '.txt' );
+			correctFile = new File( '_assets/' + name + '/' + testName + '.correct.txt' );
+			tempFile = new File( 'tmp/' + name + '/' + testName + '.out.txt' );
+
+			CKRELEASER.io.copy( sampleFile, tempFile );
+			releaser.processDirectives( tempFile );
+
+			assertEquals( CKRELEASER.io.readFile( correctFile ), CKRELEASER.io.readFile( tempFile ),
+					'releaser.directives[' + testName + ']' );
+		}
+	}
+
+	function testSkins()
+	{
+		print( "\nTesting skins\n" );
+
+		CKRELEASER.release.skins.source = '_assets/skins';
+		CKRELEASER.release.skins.target = 'tmp/skins';
+
+		var name = 'skins';
+		var correctFile, dir, dirList, test, tempFile;
+
+		var skinProcessor = new CKRELEASER.skinProcessor();
+		skinProcessor.createSkins( '.', '.' );
+
+		var sourceDir = new File( CKRELEASER.release.skins.source );
+		var sourceDirList = sourceDir.list();
+
+		for ( var i = 0 ; i < sourceDirList.length ; i++ )
+		{
+			if (sourceDirList[i] == ".svn" || sourceDirList[i] == "CVS")
+				continue;
+			
+			dir = new File( CKRELEASER.release.skins.target, sourceDirList[i] );
+			assertEquals( true, dir.exists(), dir + " exists?" );
+
+			dirList = dir.list();
+			assertEquals( true, dirList.length > 0, dir + " not empty?" );
+
+			var foundCorrect = 0;
+			var foundCss = 0;
+			/**
+			 * Loop through files in the target directory and search for valid
+			 * CSS files
+			 */
+			for ( var j = 0 ; j < dirList.length ; j++ )
+			{
+				if ( dirList[j].indexOf( ".css" ) != -1 )
+					foundCss++;
+
+				if ( dirList[j].indexOf( "correct.txt" ) != -1 )
+				{
+					foundCorrect++;
+					test = dirList[j].replace( ".correct.txt", "" );
+
+					correctFile = new File( dir, dirList[j] );
+					tempFile = new File( dir, test + '.css' );
+
+					assertEquals( true, tempFile.exists(), tempFile + " exists?" );
+
+					assertEquals( md5( CKRELEASER.io.readFile( correctFile ) ), md5( CKRELEASER.io.readFile( tempFile ) ),
+							'Checking md5 of created file [' + dir.getName() + "/" + test + '.css]' );
+				}
+			}
+			if ( foundCorrect )
+				assertEquals( foundCorrect, foundCss, 'The number of created and correct css files must be equal in skin ' + dir.getName() );
+
+			/**
+			 * TODO: Loop through files and subdirectories in the source
+			 * directory and make sure that all files != .css have been copied.
+			 */
+		}
+	}
+
+	function md5( s )
+	{
+		s = new java.lang.String( s );
+		var m = java.security.MessageDigest.getInstance( "MD5" );
+		m.update( s.getBytes(), 0, s.length() );
+		return new java.math.BigInteger( 1, m.digest() ).toString( 16 );
+	}
+
+	function testSamples()
+	{
+		print( "\nTesting samples\n" );
+
+		CKRELEASER.release.samples.source = '_assets/samples';
+		CKRELEASER.release.samples.target = 'tmp/samples';
+		CKRELEASER.release.samples.template = '_assets/samples/sample.html';
+
+		var name = 'skins';
+		var test, tempFile, correctFile;
+
+		var samplesProcessor = new CKRELEASER.samplesProcessor();
+		samplesProcessor.createSamples( '.', '.' );
+
+		var dir = new File( CKRELEASER.release.samples.target );
+		var dirList = dir.list();
+
+		assertEquals( true, dirList.length > 0, dir + " not empty?" );
+
+		for ( var j = 0 ; j < dirList.length ; j++ )
+		{
+			if ( dirList[j].indexOf( "correct.txt" ) != -1 )
+			{
+				test = dirList[j].replace( ".correct.txt", "" );
+				
+				correctFile = new File( dir, dirList[j] );
+				
+				if ( !correctFile.isDirectory() )
+				{
+					tempFile = new File( dir, test + '.html' );
+
+					assertEquals( true, tempFile.exists(), tempFile + " exists?" );
+
+					assertEquals( md5( CKRELEASER.io.readFile( correctFile ) ), md5( CKRELEASER.io.readFile( tempFile ) ),
+							'Checking md5 of created file [' + test + '.html]' );
+				}
+			}
+		}
+	}
+
+	function testRemoveComments()
+	{
+		var tests = [ "/* comment \r\n\r\n something */\r\n\r\nword", "/* comment \r\n\r\n something */word",
+				"/* comment \r\n\r\n something */\r\nword", "/* comment \r\n\r\n something */\n\nword",
+				"/* comment \r\n\r\n something */\r\rword", "aa/* comment \r\n\r\n something */bb\n" ];
+		var results = [ "\r\nword", "word", "word", "\nword", "\rword", "aabb\n" ];
+
+		for ( var i = 0 ; i < tests.length ; i++ )
+			assertEquals( results[i], CKRELEASER.tools.removeComments( tests[i] ) );
+	}
+
+	prepareTempDirs();
+	testDirectives();
+	testSkins();
+	testSamples();
+	testRemoveComments();
+
+	print( '' );
+	print( 'Finished: ' + passCount + ' passed / ' + failCount + ' failed' );
+
+})();
Index: /CKReleaser/trunk/tools/fixlineends/console_getopt/Getopt.php
===================================================================
--- /CKReleaser/trunk/tools/fixlineends/console_getopt/Getopt.php	(revision 2890)
+++ /CKReleaser/trunk/tools/fixlineends/console_getopt/Getopt.php	(revision 2890)
@@ -0,0 +1,290 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Andrei Zmievski <andrei@php.net>                             |
+// +----------------------------------------------------------------------+
+//
+// $Id: Getopt.php,v 1.4 2007/06/12 14:58:56 cellog Exp $
+
+require_once 'PEAR.php';
+
+/**
+ * Command-line options parsing class.
+ *
+ * @author Andrei Zmievski <andrei@php.net>
+ *
+ */
+class Console_Getopt {
+    /**
+     * Parses the command-line options.
+     *
+     * The first parameter to this function should be the list of command-line
+     * arguments without the leading reference to the running program.
+     *
+     * The second parameter is a string of allowed short options. Each of the
+     * option letters can be followed by a colon ':' to specify that the option
+     * requires an argument, or a double colon '::' to specify that the option
+     * takes an optional argument.
+     *
+     * The third argument is an optional array of allowed long options. The
+     * leading '--' should not be included in the option name. Options that
+     * require an argument should be followed by '=', and options that take an
+     * option argument should be followed by '=='.
+     *
+     * The return value is an array of two elements: the list of parsed
+     * options and the list of non-option command-line arguments. Each entry in
+     * the list of parsed options is a pair of elements - the first one
+     * specifies the option, and the second one specifies the option argument,
+     * if there was one.
+     *
+     * Long and short options can be mixed.
+     *
+     * Most of the semantics of this function are based on GNU getopt_long().
+     *
+     * @param array  $args           an array of command-line arguments
+     * @param string $short_options  specifies the list of allowed short options
+     * @param array  $long_options   specifies the list of allowed long options
+     *
+     * @return array two-element array containing the list of parsed options and
+     * the non-option arguments
+     *
+     * @access public
+     *
+     */
+    function getopt2($args, $short_options, $long_options = null)
+    {
+        return Console_Getopt::doGetopt(2, $args, $short_options, $long_options);
+    }
+
+    /**
+     * This function expects $args to start with the script name (POSIX-style).
+     * Preserved for backwards compatibility.
+     * @see getopt2()
+     */
+    function getopt($args, $short_options, $long_options = null)
+    {
+        return Console_Getopt::doGetopt(1, $args, $short_options, $long_options);
+    }
+
+    /**
+     * The actual implementation of the argument parsing code.
+     */
+    function doGetopt($version, $args, $short_options, $long_options = null)
+    {
+        // in case you pass directly readPHPArgv() as the first arg
+        if (PEAR::isError($args)) {
+            return $args;
+        }
+        if (empty($args)) {
+            return array(array(), array());
+        }
+        $opts     = array();
+        $non_opts = array();
+
+        settype($args, 'array');
+
+        if ($long_options) {
+            sort($long_options);
+        }
+
+        /*
+         * Preserve backwards compatibility with callers that relied on
+         * erroneous POSIX fix.
+         */
+        if ($version < 2) {
+            if (isset($args[0]{0}) && $args[0]{0} != '-') {
+                array_shift($args);
+            }
+        }
+
+        reset($args);
+        while (list($i, $arg) = each($args)) {
+
+            /* The special element '--' means explicit end of
+               options. Treat the rest of the arguments as non-options
+               and end the loop. */
+            if ($arg == '--') {
+                $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
+                break;
+            }
+
+            if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
+                $non_opts = array_merge($non_opts, array_slice($args, $i));
+                break;
+            } elseif (strlen($arg) > 1 && $arg{1} == '-') {
+                $error = Console_Getopt::_parseLongOption(substr($arg, 2), $long_options, $opts, $args);
+                if (PEAR::isError($error))
+                    return $error;
+            } elseif ($arg == '-') {
+                // - is stdin
+                $non_opts = array_merge($non_opts, array_slice($args, $i));
+                break;
+            } else {
+                $error = Console_Getopt::_parseShortOption(substr($arg, 1), $short_options, $opts, $args);
+                if (PEAR::isError($error))
+                    return $error;
+            }
+        }
+
+        return array($opts, $non_opts);
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _parseShortOption($arg, $short_options, &$opts, &$args)
+    {
+        for ($i = 0; $i < strlen($arg); $i++) {
+            $opt = $arg{$i};
+            $opt_arg = null;
+
+            /* Try to find the short option in the specifier string. */
+            if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':')
+            {
+                return PEAR::raiseError("Console_Getopt: unrecognized option -- $opt");
+            }
+
+            if (strlen($spec) > 1 && $spec{1} == ':') {
+                if (strlen($spec) > 2 && $spec{2} == ':') {
+                    if ($i + 1 < strlen($arg)) {
+                        /* Option takes an optional argument. Use the remainder of
+                           the arg string if there is anything left. */
+                        $opts[] = array($opt, substr($arg, $i + 1));
+                        break;
+                    }
+                } else {
+                    /* Option requires an argument. Use the remainder of the arg
+                       string if there is anything left. */
+                    if ($i + 1 < strlen($arg)) {
+                        $opts[] = array($opt,  substr($arg, $i + 1));
+                        break;
+                    } else if (list(, $opt_arg) = each($args)) {
+                        /* Else use the next argument. */;
+                        if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) {
+                            return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
+                        }
+                    } else {
+                        return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
+                    }
+                }
+            }
+
+            $opts[] = array($opt, $opt_arg);
+        }
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _isShortOpt($arg)
+    {
+        return strlen($arg) == 2 && $arg[0] == '-' && preg_match('/[a-zA-Z]/', $arg[1]);
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _isLongOpt($arg)
+    {
+        return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
+            preg_match('/[a-zA-Z]+$/', substr($arg, 2));
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _parseLongOption($arg, $long_options, &$opts, &$args)
+    {
+        @list($opt, $opt_arg) = explode('=', $arg, 2);
+        $opt_len = strlen($opt);
+
+        for ($i = 0; $i < count($long_options); $i++) {
+            $long_opt  = $long_options[$i];
+            $opt_start = substr($long_opt, 0, $opt_len);
+            $long_opt_name = str_replace('=', '', $long_opt);
+
+            /* Option doesn't match. Go on to the next one. */
+            if ($long_opt_name != $opt) {
+                continue;
+            }
+
+            $opt_rest  = substr($long_opt, $opt_len);
+
+            /* Check that the options uniquely matches one of the allowed
+               options. */
+            if ($i + 1 < count($long_options)) {
+                $next_option_rest = substr($long_options[$i + 1], $opt_len);
+            } else {
+                $next_option_rest = '';
+            }
+            if ($opt_rest != '' && $opt{0} != '=' &&
+                $i + 1 < count($long_options) &&
+                $opt == substr($long_options[$i+1], 0, $opt_len) &&
+                $next_option_rest != '' &&
+                $next_option_rest{0} != '=') {
+                return PEAR::raiseError("Console_Getopt: option --$opt is ambiguous");
+            }
+
+            if (substr($long_opt, -1) == '=') {
+                if (substr($long_opt, -2) != '==') {
+                    /* Long option requires an argument.
+                       Take the next argument if one wasn't specified. */;
+                    if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
+                        return PEAR::raiseError("Console_Getopt: option --$opt requires an argument");
+                    }
+                    if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) {
+                        return PEAR::raiseError("Console_Getopt: option requires an argument --$opt");
+                    }
+                }
+            } else if ($opt_arg) {
+                return PEAR::raiseError("Console_Getopt: option --$opt doesn't allow an argument");
+            }
+
+            $opts[] = array('--' . $opt, $opt_arg);
+            return;
+        }
+
+        return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
+    }
+
+    /**
+    * Safely read the $argv PHP array across different PHP configurations.
+    * Will take care on register_globals and register_argc_argv ini directives
+    *
+    * @access public
+    * @return mixed the $argv PHP array or PEAR error if not registered
+    */
+    function readPHPArgv()
+    {
+        global $argv;
+        if (!is_array($argv)) {
+            if (!@is_array($_SERVER['argv'])) {
+                if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
+                    return PEAR::raiseError("Console_Getopt: Could not read cmd args (register_argc_argv=Off?)");
+                }
+                return $GLOBALS['HTTP_SERVER_VARS']['argv'];
+            }
+            return $_SERVER['argv'];
+        }
+        return $argv;
+    }
+
+}
+
+?>
Index: /CKReleaser/trunk/tools/fixlineends/console_getopt/License.html
===================================================================
--- /CKReleaser/trunk/tools/fixlineends/console_getopt/License.html	(revision 2890)
+++ /CKReleaser/trunk/tools/fixlineends/console_getopt/License.html	(revision 2890)
@@ -0,0 +1,7 @@
+	<p>
+		<a href="http://pear.php.net/package/Console_Getopt">Console_Getopt</a> and <a href="http://pear.php.net/">
+			PEAR</a>: At _dev/_thirdparty/console_getopt can be found the source code of
+		Console_Getopt and part of the source code of PEAR, which are licensed under the
+		terms of the PHP License Version 3.0 (<a href="http://www.php.net/license/3_0.txt">http://www.php.net/license/3_0.txt</a>).
+		Console_Getopt and PEAR are Copyright &copy; 1997-2004 The PHP Group.
+	</p>
Index: /CKReleaser/trunk/tools/fixlineends/console_getopt/PEAR.php
===================================================================
--- /CKReleaser/trunk/tools/fixlineends/console_getopt/PEAR.php	(revision 2890)
+++ /CKReleaser/trunk/tools/fixlineends/console_getopt/PEAR.php	(revision 2890)
@@ -0,0 +1,1118 @@
+<?php
+/**
+ * PEAR, the PHP Extension and Application Repository
+ *
+ * PEAR class and PEAR_Error class
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Sterling Hughes <sterling@php.net>
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2008 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    CVS: $Id: PEAR.php,v 1.104 2008/01/03 20:26:34 cellog Exp $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**#@+
+ * ERROR constants
+ */
+define('PEAR_ERROR_RETURN',     1);
+define('PEAR_ERROR_PRINT',      2);
+define('PEAR_ERROR_TRIGGER',    4);
+define('PEAR_ERROR_DIE',        8);
+define('PEAR_ERROR_CALLBACK',  16);
+/**
+ * WARNING: obsolete
+ * @deprecated
+ */
+define('PEAR_ERROR_EXCEPTION', 32);
+/**#@-*/
+define('PEAR_ZE2', (function_exists('version_compare') &&
+                    version_compare(zend_version(), "2-dev", "ge")));
+
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+    define('OS_WINDOWS', true);
+    define('OS_UNIX',    false);
+    define('PEAR_OS',    'Windows');
+} else {
+    define('OS_WINDOWS', false);
+    define('OS_UNIX',    true);
+    define('PEAR_OS',    'Unix'); // blatant assumption
+}
+
+// instant backwards compatibility
+if (!defined('PATH_SEPARATOR')) {
+    if (OS_WINDOWS) {
+        define('PATH_SEPARATOR', ';');
+    } else {
+        define('PATH_SEPARATOR', ':');
+    }
+}
+
+$GLOBALS['_PEAR_default_error_mode']     = PEAR_ERROR_RETURN;
+$GLOBALS['_PEAR_default_error_options']  = E_USER_NOTICE;
+$GLOBALS['_PEAR_destructor_object_list'] = array();
+$GLOBALS['_PEAR_shutdown_funcs']         = array();
+$GLOBALS['_PEAR_error_handler_stack']    = array();
+
+@ini_set('track_errors', true);
+
+/**
+ * Base class for other PEAR classes.  Provides rudimentary
+ * emulation of destructors.
+ *
+ * If you want a destructor in your class, inherit PEAR and make a
+ * destructor method called _yourclassname (same name as the
+ * constructor, but with a "_" prefix).  Also, in your constructor you
+ * have to call the PEAR constructor: $this->PEAR();.
+ * The destructor method will be called without parameters.  Note that
+ * at in some SAPI implementations (such as Apache), any output during
+ * the request shutdown (in which destructors are called) seems to be
+ * discarded.  If you need to get any debug information from your
+ * destructor, use error_log(), syslog() or something similar.
+ *
+ * IMPORTANT! To use the emulated destructors you need to create the
+ * objects by reference: $obj =& new PEAR_child;
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V.V. Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2006 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    Release: 1.7.1
+ * @link       http://pear.php.net/package/PEAR
+ * @see        PEAR_Error
+ * @since      Class available since PHP 4.0.2
+ * @link        http://pear.php.net/manual/en/core.pear.php#core.pear.pear
+ */
+class PEAR
+{
+    // {{{ properties
+
+    /**
+     * Whether to enable internal debug messages.
+     *
+     * @var     bool
+     * @access  private
+     */
+    var $_debug = false;
+
+    /**
+     * Default error mode for this object.
+     *
+     * @var     int
+     * @access  private
+     */
+    var $_default_error_mode = null;
+
+    /**
+     * Default error options used for this object when error mode
+     * is PEAR_ERROR_TRIGGER.
+     *
+     * @var     int
+     * @access  private
+     */
+    var $_default_error_options = null;
+
+    /**
+     * Default error handler (callback) for this object, if error mode is
+     * PEAR_ERROR_CALLBACK.
+     *
+     * @var     string
+     * @access  private
+     */
+    var $_default_error_handler = '';
+
+    /**
+     * Which class to use for error objects.
+     *
+     * @var     string
+     * @access  private
+     */
+    var $_error_class = 'PEAR_Error';
+
+    /**
+     * An array of expected errors.
+     *
+     * @var     array
+     * @access  private
+     */
+    var $_expected_errors = array();
+
+    // }}}
+
+    // {{{ constructor
+
+    /**
+     * Constructor.  Registers this object in
+     * $_PEAR_destructor_object_list for destructor emulation if a
+     * destructor object exists.
+     *
+     * @param string $error_class  (optional) which class to use for
+     *        error objects, defaults to PEAR_Error.
+     * @access public
+     * @return void
+     */
+    function PEAR($error_class = null)
+    {
+        $classname = strtolower(get_class($this));
+        if ($this->_debug) {
+            print "PEAR constructor called, class=$classname\n";
+        }
+        if ($error_class !== null) {
+            $this->_error_class = $error_class;
+        }
+        while ($classname && strcasecmp($classname, "pear")) {
+            $destructor = "_$classname";
+            if (method_exists($this, $destructor)) {
+                global $_PEAR_destructor_object_list;
+                $_PEAR_destructor_object_list[] = &$this;
+                if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
+                    register_shutdown_function("_PEAR_call_destructors");
+                    $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
+                }
+                break;
+            } else {
+                $classname = get_parent_class($classname);
+            }
+        }
+    }
+
+    // }}}
+    // {{{ destructor
+
+    /**
+     * Destructor (the emulated type of...).  Does nothing right now,
+     * but is included for forward compatibility, so subclass
+     * destructors should always call it.
+     *
+     * See the note in the class desciption about output from
+     * destructors.
+     *
+     * @access public
+     * @return void
+     */
+    function _PEAR() {
+        if ($this->_debug) {
+            printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
+        }
+    }
+
+    // }}}
+    // {{{ getStaticProperty()
+
+    /**
+    * If you have a class that's mostly/entirely static, and you need static
+    * properties, you can use this method to simulate them. Eg. in your method(s)
+    * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
+    * You MUST use a reference, or they will not persist!
+    *
+    * @access public
+    * @param  string $class  The calling classname, to prevent clashes
+    * @param  string $var    The variable to retrieve.
+    * @return mixed   A reference to the variable. If not set it will be
+    *                 auto initialised to NULL.
+    */
+    function &getStaticProperty($class, $var)
+    {
+        static $properties;
+        if (!isset($properties[$class])) {
+            $properties[$class] = array();
+        }
+        if (!array_key_exists($var, $properties[$class])) {
+            $properties[$class][$var] = null;
+        }
+        return $properties[$class][$var];
+    }
+
+    // }}}
+    // {{{ registerShutdownFunc()
+
+    /**
+    * Use this function to register a shutdown method for static
+    * classes.
+    *
+    * @access public
+    * @param  mixed $func  The function name (or array of class/method) to call
+    * @param  mixed $args  The arguments to pass to the function
+    * @return void
+    */
+    function registerShutdownFunc($func, $args = array())
+    {
+        // if we are called statically, there is a potential
+        // that no shutdown func is registered.  Bug #6445
+        if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
+            register_shutdown_function("_PEAR_call_destructors");
+            $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
+        }
+        $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
+    }
+
+    // }}}
+    // {{{ isError()
+
+    /**
+     * Tell whether a value is a PEAR error.
+     *
+     * @param   mixed $data   the value to test
+     * @param   int   $code   if $data is an error object, return true
+     *                        only if $code is a string and
+     *                        $obj->getMessage() == $code or
+     *                        $code is an integer and $obj->getCode() == $code
+     * @access  public
+     * @return  bool    true if parameter is an error
+     */
+    function isError($data, $code = null)
+    {
+        if (is_a($data, 'PEAR_Error')) {
+            if (is_null($code)) {
+                return true;
+            } elseif (is_string($code)) {
+                return $data->getMessage() == $code;
+            } else {
+                return $data->getCode() == $code;
+            }
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ setErrorHandling()
+
+    /**
+     * Sets how errors generated by this object should be handled.
+     * Can be invoked both in objects and statically.  If called
+     * statically, setErrorHandling sets the default behaviour for all
+     * PEAR objects.  If called in an object, setErrorHandling sets
+     * the default behaviour for that object.
+     *
+     * @param int $mode
+     *        One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+     *        PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
+     *        PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
+     *
+     * @param mixed $options
+     *        When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
+     *        of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+     *
+     *        When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
+     *        to be the callback function or method.  A callback
+     *        function is a string with the name of the function, a
+     *        callback method is an array of two elements: the element
+     *        at index 0 is the object, and the element at index 1 is
+     *        the name of the method to call in the object.
+     *
+     *        When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
+     *        a printf format string used when printing the error
+     *        message.
+     *
+     * @access public
+     * @return void
+     * @see PEAR_ERROR_RETURN
+     * @see PEAR_ERROR_PRINT
+     * @see PEAR_ERROR_TRIGGER
+     * @see PEAR_ERROR_DIE
+     * @see PEAR_ERROR_CALLBACK
+     * @see PEAR_ERROR_EXCEPTION
+     *
+     * @since PHP 4.0.5
+     */
+
+    function setErrorHandling($mode = null, $options = null)
+    {
+        if (isset($this) && is_a($this, 'PEAR')) {
+            $setmode     = &$this->_default_error_mode;
+            $setoptions  = &$this->_default_error_options;
+        } else {
+            $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
+            $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
+        }
+
+        switch ($mode) {
+            case PEAR_ERROR_EXCEPTION:
+            case PEAR_ERROR_RETURN:
+            case PEAR_ERROR_PRINT:
+            case PEAR_ERROR_TRIGGER:
+            case PEAR_ERROR_DIE:
+            case null:
+                $setmode = $mode;
+                $setoptions = $options;
+                break;
+
+            case PEAR_ERROR_CALLBACK:
+                $setmode = $mode;
+                // class/object method callback
+                if (is_callable($options)) {
+                    $setoptions = $options;
+                } else {
+                    trigger_error("invalid error callback", E_USER_WARNING);
+                }
+                break;
+
+            default:
+                trigger_error("invalid error mode", E_USER_WARNING);
+                break;
+        }
+    }
+
+    // }}}
+    // {{{ expectError()
+
+    /**
+     * This method is used to tell which errors you expect to get.
+     * Expected errors are always returned with error mode
+     * PEAR_ERROR_RETURN.  Expected error codes are stored in a stack,
+     * and this method pushes a new element onto it.  The list of
+     * expected errors are in effect until they are popped off the
+     * stack with the popExpect() method.
+     *
+     * Note that this method can not be called statically
+     *
+     * @param mixed $code a single error code or an array of error codes to expect
+     *
+     * @return int     the new depth of the "expected errors" stack
+     * @access public
+     */
+    function expectError($code = '*')
+    {
+        if (is_array($code)) {
+            array_push($this->_expected_errors, $code);
+        } else {
+            array_push($this->_expected_errors, array($code));
+        }
+        return sizeof($this->_expected_errors);
+    }
+
+    // }}}
+    // {{{ popExpect()
+
+    /**
+     * This method pops one element off the expected error codes
+     * stack.
+     *
+     * @return array   the list of error codes that were popped
+     */
+    function popExpect()
+    {
+        return array_pop($this->_expected_errors);
+    }
+
+    // }}}
+    // {{{ _checkDelExpect()
+
+    /**
+     * This method checks unsets an error code if available
+     *
+     * @param mixed error code
+     * @return bool true if the error code was unset, false otherwise
+     * @access private
+     * @since PHP 4.3.0
+     */
+    function _checkDelExpect($error_code)
+    {
+        $deleted = false;
+
+        foreach ($this->_expected_errors AS $key => $error_array) {
+            if (in_array($error_code, $error_array)) {
+                unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
+                $deleted = true;
+            }
+
+            // clean up empty arrays
+            if (0 == count($this->_expected_errors[$key])) {
+                unset($this->_expected_errors[$key]);
+            }
+        }
+        return $deleted;
+    }
+
+    // }}}
+    // {{{ delExpect()
+
+    /**
+     * This method deletes all occurences of the specified element from
+     * the expected error codes stack.
+     *
+     * @param  mixed $error_code error code that should be deleted
+     * @return mixed list of error codes that were deleted or error
+     * @access public
+     * @since PHP 4.3.0
+     */
+    function delExpect($error_code)
+    {
+        $deleted = false;
+
+        if ((is_array($error_code) && (0 != count($error_code)))) {
+            // $error_code is a non-empty array here;
+            // we walk through it trying to unset all
+            // values
+            foreach($error_code as $key => $error) {
+                if ($this->_checkDelExpect($error)) {
+                    $deleted =  true;
+                } else {
+                    $deleted = false;
+                }
+            }
+            return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+        } elseif (!empty($error_code)) {
+            // $error_code comes alone, trying to unset it
+            if ($this->_checkDelExpect($error_code)) {
+                return true;
+            } else {
+                return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+            }
+        } else {
+            // $error_code is empty
+            return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
+        }
+    }
+
+    // }}}
+    // {{{ raiseError()
+
+    /**
+     * This method is a wrapper that returns an instance of the
+     * configured error class with this object's default error
+     * handling applied.  If the $mode and $options parameters are not
+     * specified, the object's defaults are used.
+     *
+     * @param mixed $message a text error message or a PEAR error object
+     *
+     * @param int $code      a numeric error code (it is up to your class
+     *                  to define these if you want to use codes)
+     *
+     * @param int $mode      One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+     *                  PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
+     *                  PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
+     *
+     * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
+     *                  specifies the PHP-internal error level (one of
+     *                  E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+     *                  If $mode is PEAR_ERROR_CALLBACK, this
+     *                  parameter specifies the callback function or
+     *                  method.  In other error modes this parameter
+     *                  is ignored.
+     *
+     * @param string $userinfo If you need to pass along for example debug
+     *                  information, this parameter is meant for that.
+     *
+     * @param string $error_class The returned error object will be
+     *                  instantiated from this class, if specified.
+     *
+     * @param bool $skipmsg If true, raiseError will only pass error codes,
+     *                  the error message parameter will be dropped.
+     *
+     * @access public
+     * @return object   a PEAR error object
+     * @see PEAR::setErrorHandling
+     * @since PHP 4.0.5
+     */
+    function &raiseError($message = null,
+                         $code = null,
+                         $mode = null,
+                         $options = null,
+                         $userinfo = null,
+                         $error_class = null,
+                         $skipmsg = false)
+    {
+        // The error is yet a PEAR error object
+        if (is_object($message)) {
+            $code        = $message->getCode();
+            $userinfo    = $message->getUserInfo();
+            $error_class = $message->getType();
+            $message->error_message_prefix = '';
+            $message     = $message->getMessage();
+        }
+
+        if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
+            if ($exp[0] == "*" ||
+                (is_int(reset($exp)) && in_array($code, $exp)) ||
+                (is_string(reset($exp)) && in_array($message, $exp))) {
+                $mode = PEAR_ERROR_RETURN;
+            }
+        }
+        // No mode given, try global ones
+        if ($mode === null) {
+            // Class error handler
+            if (isset($this) && isset($this->_default_error_mode)) {
+                $mode    = $this->_default_error_mode;
+                $options = $this->_default_error_options;
+            // Global error handler
+            } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
+                $mode    = $GLOBALS['_PEAR_default_error_mode'];
+                $options = $GLOBALS['_PEAR_default_error_options'];
+            }
+        }
+
+        if ($error_class !== null) {
+            $ec = $error_class;
+        } elseif (isset($this) && isset($this->_error_class)) {
+            $ec = $this->_error_class;
+        } else {
+            $ec = 'PEAR_Error';
+        }
+        if (intval(PHP_VERSION) < 5) {
+            // little non-eval hack to fix bug #12147
+            include 'PEAR/FixPHP5PEARWarnings.php';
+            return $a;
+        }
+        if ($skipmsg) {
+            $a = new $ec($code, $mode, $options, $userinfo);
+        } else {
+            $a = new $ec($message, $code, $mode, $options, $userinfo);
+        }
+        return $a;
+    }
+
+    // }}}
+    // {{{ throwError()
+
+    /**
+     * Simpler form of raiseError with fewer options.  In most cases
+     * message, code and userinfo are enough.
+     *
+     * @param string $message
+     *
+     */
+    function &throwError($message = null,
+                         $code = null,
+                         $userinfo = null)
+    {
+        if (isset($this) && is_a($this, 'PEAR')) {
+            $a = &$this->raiseError($message, $code, null, null, $userinfo);
+            return $a;
+        } else {
+            $a = &PEAR::raiseError($message, $code, null, null, $userinfo);
+            return $a;
+        }
+    }
+
+    // }}}
+    function staticPushErrorHandling($mode, $options = null)
+    {
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+        $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
+        $def_options = &$GLOBALS['_PEAR_default_error_options'];
+        $stack[] = array($def_mode, $def_options);
+        switch ($mode) {
+            case PEAR_ERROR_EXCEPTION:
+            case PEAR_ERROR_RETURN:
+            case PEAR_ERROR_PRINT:
+            case PEAR_ERROR_TRIGGER:
+            case PEAR_ERROR_DIE:
+            case null:
+                $def_mode = $mode;
+                $def_options = $options;
+                break;
+
+            case PEAR_ERROR_CALLBACK:
+                $def_mode = $mode;
+                // class/object method callback
+                if (is_callable($options)) {
+                    $def_options = $options;
+                } else {
+                    trigger_error("invalid error callback", E_USER_WARNING);
+                }
+                break;
+
+            default:
+                trigger_error("invalid error mode", E_USER_WARNING);
+                break;
+        }
+        $stack[] = array($mode, $options);
+        return true;
+    }
+
+    function staticPopErrorHandling()
+    {
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+        $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
+        $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
+        array_pop($stack);
+        list($mode, $options) = $stack[sizeof($stack) - 1];
+        array_pop($stack);
+        switch ($mode) {
+            case PEAR_ERROR_EXCEPTION:
+            case PEAR_ERROR_RETURN:
+            case PEAR_ERROR_PRINT:
+            case PEAR_ERROR_TRIGGER:
+            case PEAR_ERROR_DIE:
+            case null:
+                $setmode = $mode;
+                $setoptions = $options;
+                break;
+
+            case PEAR_ERROR_CALLBACK:
+                $setmode = $mode;
+                // class/object method callback
+                if (is_callable($options)) {
+                    $setoptions = $options;
+                } else {
+                    trigger_error("invalid error callback", E_USER_WARNING);
+                }
+                break;
+
+            default:
+                trigger_error("invalid error mode", E_USER_WARNING);
+                break;
+        }
+        return true;
+    }
+
+    // {{{ pushErrorHandling()
+
+    /**
+     * Push a new error handler on top of the error handler options stack. With this
+     * you can easily override the actual error handler for some code and restore
+     * it later with popErrorHandling.
+     *
+     * @param mixed $mode (same as setErrorHandling)
+     * @param mixed $options (same as setErrorHandling)
+     *
+     * @return bool Always true
+     *
+     * @see PEAR::setErrorHandling
+     */
+    function pushErrorHandling($mode, $options = null)
+    {
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+        if (isset($this) && is_a($this, 'PEAR')) {
+            $def_mode    = &$this->_default_error_mode;
+            $def_options = &$this->_default_error_options;
+        } else {
+            $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
+            $def_options = &$GLOBALS['_PEAR_default_error_options'];
+        }
+        $stack[] = array($def_mode, $def_options);
+
+        if (isset($this) && is_a($this, 'PEAR')) {
+            $this->setErrorHandling($mode, $options);
+        } else {
+            PEAR::setErrorHandling($mode, $options);
+        }
+        $stack[] = array($mode, $options);
+        return true;
+    }
+
+    // }}}
+    // {{{ popErrorHandling()
+
+    /**
+    * Pop the last error handler used
+    *
+    * @return bool Always true
+    *
+    * @see PEAR::pushErrorHandling
+    */
+    function popErrorHandling()
+    {
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+        array_pop($stack);
+        list($mode, $options) = $stack[sizeof($stack) - 1];
+        array_pop($stack);
+        if (isset($this) && is_a($this, 'PEAR')) {
+            $this->setErrorHandling($mode, $options);
+        } else {
+            PEAR::setErrorHandling($mode, $options);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ loadExtension()
+
+    /**
+    * OS independant PHP extension load. Remember to take care
+    * on the correct extension name for case sensitive OSes.
+    *
+    * @param string $ext The extension name
+    * @return bool Success or not on the dl() call
+    */
+    function loadExtension($ext)
+    {
+        if (!extension_loaded($ext)) {
+            // if either returns true dl() will produce a FATAL error, stop that
+            if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
+                return false;
+            }
+            if (OS_WINDOWS) {
+                $suffix = '.dll';
+            } elseif (PHP_OS == 'HP-UX') {
+                $suffix = '.sl';
+            } elseif (PHP_OS == 'AIX') {
+                $suffix = '.a';
+            } elseif (PHP_OS == 'OSX') {
+                $suffix = '.bundle';
+            } else {
+                $suffix = '.so';
+            }
+            return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
+        }
+        return true;
+    }
+
+    // }}}
+}
+
+// {{{ _PEAR_call_destructors()
+
+function _PEAR_call_destructors()
+{
+    global $_PEAR_destructor_object_list;
+    if (is_array($_PEAR_destructor_object_list) &&
+        sizeof($_PEAR_destructor_object_list))
+    {
+        reset($_PEAR_destructor_object_list);
+        if (PEAR::getStaticProperty('PEAR', 'destructlifo')) {
+            $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
+        }
+        while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
+            $classname = get_class($objref);
+            while ($classname) {
+                $destructor = "_$classname";
+                if (method_exists($objref, $destructor)) {
+                    $objref->$destructor();
+                    break;
+                } else {
+                    $classname = get_parent_class($classname);
+                }
+            }
+        }
+        // Empty the object list to ensure that destructors are
+        // not called more than once.
+        $_PEAR_destructor_object_list = array();
+    }
+
+    // Now call the shutdown functions
+    if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
+        foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
+            call_user_func_array($value[0], $value[1]);
+        }
+    }
+}
+
+// }}}
+/**
+ * Standard PEAR error class for PHP 4
+ *
+ * This class is supserseded by {@link PEAR_Exception} in PHP 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V.V. Cox <cox@idecnet.com>
+ * @author     Gregory Beaver <cellog@php.net>
+ * @copyright  1997-2006 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    Release: 1.7.1
+ * @link       http://pear.php.net/manual/en/core.pear.pear-error.php
+ * @see        PEAR::raiseError(), PEAR::throwError()
+ * @since      Class available since PHP 4.0.2
+ */
+class PEAR_Error
+{
+    // {{{ properties
+
+    var $error_message_prefix = '';
+    var $mode                 = PEAR_ERROR_RETURN;
+    var $level                = E_USER_NOTICE;
+    var $code                 = -1;
+    var $message              = '';
+    var $userinfo             = '';
+    var $backtrace            = null;
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * PEAR_Error constructor
+     *
+     * @param string $message  message
+     *
+     * @param int $code     (optional) error code
+     *
+     * @param int $mode     (optional) error mode, one of: PEAR_ERROR_RETURN,
+     * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
+     * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
+     *
+     * @param mixed $options   (optional) error level, _OR_ in the case of
+     * PEAR_ERROR_CALLBACK, the callback function or object/method
+     * tuple.
+     *
+     * @param string $userinfo (optional) additional user/debug info
+     *
+     * @access public
+     *
+     */
+    function PEAR_Error($message = 'unknown error', $code = null,
+                        $mode = null, $options = null, $userinfo = null)
+    {
+        if ($mode === null) {
+            $mode = PEAR_ERROR_RETURN;
+        }
+        $this->message   = $message;
+        $this->code      = $code;
+        $this->mode      = $mode;
+        $this->userinfo  = $userinfo;
+        if (!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
+            $this->backtrace = debug_backtrace();
+            if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
+                unset($this->backtrace[0]['object']);
+            }
+        }
+        if ($mode & PEAR_ERROR_CALLBACK) {
+            $this->level = E_USER_NOTICE;
+            $this->callback = $options;
+        } else {
+            if ($options === null) {
+                $options = E_USER_NOTICE;
+            }
+            $this->level = $options;
+            $this->callback = null;
+        }
+        if ($this->mode & PEAR_ERROR_PRINT) {
+            if (is_null($options) || is_int($options)) {
+                $format = "%s";
+            } else {
+                $format = $options;
+            }
+            printf($format, $this->getMessage());
+        }
+        if ($this->mode & PEAR_ERROR_TRIGGER) {
+            trigger_error($this->getMessage(), $this->level);
+        }
+        if ($this->mode & PEAR_ERROR_DIE) {
+            $msg = $this->getMessage();
+            if (is_null($options) || is_int($options)) {
+                $format = "%s";
+                if (substr($msg, -1) != "\n") {
+                    $msg .= "\n";
+                }
+            } else {
+                $format = $options;
+            }
+            die(sprintf($format, $msg));
+        }
+        if ($this->mode & PEAR_ERROR_CALLBACK) {
+            if (is_callable($this->callback)) {
+                call_user_func($this->callback, $this);
+            }
+        }
+        if ($this->mode & PEAR_ERROR_EXCEPTION) {
+            trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
+            eval('$e = new Exception($this->message, $this->code);throw($e);');
+        }
+    }
+
+    // }}}
+    // {{{ getMode()
+
+    /**
+     * Get the error mode from an error object.
+     *
+     * @return int error mode
+     * @access public
+     */
+    function getMode() {
+        return $this->mode;
+    }
+
+    // }}}
+    // {{{ getCallback()
+
+    /**
+     * Get the callback function/method from an error object.
+     *
+     * @return mixed callback function or object/method array
+     * @access public
+     */
+    function getCallback() {
+        return $this->callback;
+    }
+
+    // }}}
+    // {{{ getMessage()
+
+
+    /**
+     * Get the error message from an error object.
+     *
+     * @return  string  full error message
+     * @access public
+     */
+    function getMessage()
+    {
+        return ($this->error_message_prefix . $this->message);
+    }
+
+
+    // }}}
+    // {{{ getCode()
+
+    /**
+     * Get error code from an error object
+     *
+     * @return int error code
+     * @access public
+     */
+     function getCode()
+     {
+        return $this->code;
+     }
+
+    // }}}
+    // {{{ getType()
+
+    /**
+     * Get the name of this error/exception.
+     *
+     * @return string error/exception name (type)
+     * @access public
+     */
+    function getType()
+    {
+        return get_class($this);
+    }
+
+    // }}}
+    // {{{ getUserInfo()
+
+    /**
+     * Get additional user-supplied information.
+     *
+     * @return string user-supplied information
+     * @access public
+     */
+    function getUserInfo()
+    {
+        return $this->userinfo;
+    }
+
+    // }}}
+    // {{{ getDebugInfo()
+
+    /**
+     * Get additional debug information supplied by the application.
+     *
+     * @return string debug information
+     * @access public
+     */
+    function getDebugInfo()
+    {
+        return $this->getUserInfo();
+    }
+
+    // }}}
+    // {{{ getBacktrace()
+
+    /**
+     * Get the call backtrace from where the error was generated.
+     * Supported with PHP 4.3.0 or newer.
+     *
+     * @param int $frame (optional) what frame to fetch
+     * @return array Backtrace, or NULL if not available.
+     * @access public
+     */
+    function getBacktrace($frame = null)
+    {
+        if (defined('PEAR_IGNORE_BACKTRACE')) {
+            return null;
+        }
+        if ($frame === null) {
+            return $this->backtrace;
+        }
+        return $this->backtrace[$frame];
+    }
+
+    // }}}
+    // {{{ addUserInfo()
+
+    function addUserInfo($info)
+    {
+        if (empty($this->userinfo)) {
+            $this->userinfo = $info;
+        } else {
+            $this->userinfo .= " ** $info";
+        }
+    }
+
+    // }}}
+    // {{{ toString()
+    function __toString()
+    {
+        return $this->getMessage();
+    }
+    // }}}
+    // {{{ toString()
+
+    /**
+     * Make a string representation of this object.
+     *
+     * @return string a string with an object summary
+     * @access public
+     */
+    function toString() {
+        $modes = array();
+        $levels = array(E_USER_NOTICE  => 'notice',
+                        E_USER_WARNING => 'warning',
+                        E_USER_ERROR   => 'error');
+        if ($this->mode & PEAR_ERROR_CALLBACK) {
+            if (is_array($this->callback)) {
+                $callback = (is_object($this->callback[0]) ?
+                    strtolower(get_class($this->callback[0])) :
+                    $this->callback[0]) . '::' .
+                    $this->callback[1];
+            } else {
+                $callback = $this->callback;
+            }
+            return sprintf('[%s: message="%s" code=%d mode=callback '.
+                           'callback=%s prefix="%s" info="%s"]',
+                           strtolower(get_class($this)), $this->message, $this->code,
+                           $callback, $this->error_message_prefix,
+                           $this->userinfo);
+        }
+        if ($this->mode & PEAR_ERROR_PRINT) {
+            $modes[] = 'print';
+        }
+        if ($this->mode & PEAR_ERROR_TRIGGER) {
+            $modes[] = 'trigger';
+        }
+        if ($this->mode & PEAR_ERROR_DIE) {
+            $modes[] = 'die';
+        }
+        if ($this->mode & PEAR_ERROR_RETURN) {
+            $modes[] = 'return';
+        }
+        return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
+                       'prefix="%s" info="%s"]',
+                       strtolower(get_class($this)), $this->message, $this->code,
+                       implode("|", $modes), $levels[$this->level],
+                       $this->error_message_prefix,
+                       $this->userinfo);
+    }
+
+    // }}}
+}
+
+/*
+ * Local Variables:
+ * mode: php
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+?>
Index: /CKReleaser/trunk/tools/fixlineends/fixlineends.bat
===================================================================
--- /CKReleaser/trunk/tools/fixlineends/fixlineends.bat	(revision 2890)
+++ /CKReleaser/trunk/tools/fixlineends/fixlineends.bat	(revision 2890)
@@ -0,0 +1,23 @@
+@ECHO OFF
+::
+:: CKEditor - The text editor for Internet - http://ckeditor.com
+:: Copyright (C) 2003-2008 Frederico Caldeira Knabben
+::
+:: == BEGIN LICENSE ==
+::
+:: Licensed under the terms of any of the following licenses at your
+:: choice:
+::
+::  - GNU General Public License Version 2 or later (the "GPL")
+::    http://www.gnu.org/licenses/gpl.html
+::
+::  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+::    http://www.gnu.org/licenses/lgpl.html
+::
+::  - Mozilla Public License Version 1.1 or later (the "MPL")
+::    http://www.mozilla.org/MPL/MPL-1.1.html
+::
+:: == END LICENSE ==
+::
+
+php fixlineends.php --excluderegex=/(?:_dev[\\\/]_thirdparty)/ --eolstripwhite --eofnewline --eofstripwhite --nohidden --nosystem ../../
Index: /CKReleaser/trunk/tools/fixlineends/fixlineends.php
===================================================================
--- /CKReleaser/trunk/tools/fixlineends/fixlineends.php	(revision 2890)
+++ /CKReleaser/trunk/tools/fixlineends/fixlineends.php	(revision 2890)
@@ -0,0 +1,622 @@
+#!/usr/bin/php -q
+<?php
+/*
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+*
+* Script for automatic line-ending corrections.
+* Requires PHP5 to run: http://www.gophp5.org/ ;)
+*/
+
+error_reporting(E_ALL);
+
+/**
+ * Carriage return
+ *
+ */
+define('CR', "\r");
+/**
+ * Line feed
+ *
+ */
+define('LF', "\n");
+/**
+ * Carriage return + Line feed
+ *
+ */
+define('CRLF', "\r\n");
+
+/**
+ * Array, where:
+ *  file extension is the key
+ *  value is the new line character
+ */
+$list = array();
+$list["readme"] = CRLF;
+$list["afp"] = CRLF;
+$list["afpa"] = CRLF;
+$list["ascx"] = CRLF;
+$list["asp"] = CRLF;
+$list["aspx"] = CRLF;
+$list["bat"] = CRLF;
+$list["cfc"] = CRLF;
+$list["cfm"] = CRLF;
+$list["cgi"] = LF;
+$list["code"] = CRLF;
+$list["command"] = CRLF;
+$list["conf"] = CRLF;
+$list["css"] = CRLF;
+$list["dtd"] = CRLF;
+$list["htaccess"] = CRLF;
+$list["htc"] = CRLF;
+$list["htm"] = CRLF;
+$list["html"] = CRLF;
+$list["js"] = CRLF;
+$list["jsp"] = CRLF;
+$list["lasso"] = CRLF;
+$list["php"] = CRLF;
+$list["pl"] = LF;
+$list["py"] = CRLF;
+$list["sample"] = CRLF;
+$list["sh"] = LF;
+$list["txt"] = CRLF;
+$list["xml"] = CRLF;
+
+/**
+ * Do not modify anything below
+ * use command line arguments to modify script's behaviour
+ */
+
+/**
+ * Strip whitespace from the end of file
+ * @var boolean $eofstripwhite
+ */
+$eofstripwhite = false;
+/**
+ * Strip whitespace from the end of line
+ * @var boolean $eolstripwhite
+ */
+$eolstripwhite = false;
+/**
+ * Force new line character at the end of file
+ * @var boolean $eofnewline
+ */
+$eofnewline = false;
+/**
+ * Windows only
+ * If set to true, archive files/folders are skipped
+ * @var boolean $noarchive
+ */
+$noarchive = false;
+/**
+ * Windows only
+ * If set to true, hidden files/folders are skipped
+ * @var boolean $nohidden
+ */
+$nohidden = false;
+/**
+ * Windows only
+ * If set to true, system files/folders are skipped
+ * @var boolean $nosystem
+ */
+$nosystem = false;
+/**
+ * If set to true, dot files are skipped
+ * @var boolean $nodotfiles
+ */
+$nodotfiles = false;
+/**
+ * How deep to recurse into subdirectories
+ * -1 to disable
+ *  0 to fix only current directory
+ *
+ * @var integer $maxdepth
+ */
+$maxdepth = -1;
+/**
+ * If set, regex is used to exclude files
+ * Warning: preg_match format expected
+ * example:
+ * --excluderegex = "/(\.\w+)$/"
+ *
+ * @var string $excluderegex
+ */
+$excluderegex = "";
+
+/**
+ * Set to true if script is launched in Windows
+ * @var boolean $windows
+ */
+$windows = (strtolower(substr(PHP_OS, 0, 3)) == "win");
+
+/**
+ * Count saved bytes
+ * @var integer $saved_bytes
+ */
+$saved_bytes = 0;
+
+/**
+ * Filter file list using regular expression (negative result)
+ *
+ */
+class NegRegexFilter extends FilterIterator
+{
+    protected $regex;
+    public function __construct(Iterator $it, $regex)
+    {
+        parent::__construct($it);
+        $this->regex=$regex;
+    }
+    public function accept()
+    {
+        return !preg_match($this->regex, $this->current());
+    }
+}
+
+/**
+ * Filter file list using regular expression
+ *
+ */
+class RegexFilter extends FilterIterator
+{
+    protected $regex;
+    public function __construct(Iterator $it, $regex)
+    {
+        parent::__construct($it);
+        $this->regex=$regex;
+    }
+    public function accept()
+    {
+        return preg_match($this->regex, $this->current());
+    }
+}
+
+/**
+ * Filter file list by depth
+ *
+ */
+class DepthFilter extends FilterIterator
+{
+    protected $depth;
+    public function __construct(Iterator $it, $depth)
+    {
+        parent::__construct($it);
+        $this->depth=$depth;
+    }
+    public function accept()
+    {
+        return $this->getInnerIterator()->getDepth()<$this->depth;
+    }
+}
+
+/**
+ * Fix new line characters in given file
+ * Returns true if file was changed
+ *
+ * @param string $path relative or absolute path name to file
+ * @param string $nl name of a constant that holds new line character (CRLF|CR|LF)
+ * @return bool
+ */
+function fixFile($path, $nl) {
+
+    $contents = file($path);
+    $size = filesize($path);
+    if ($contents === false) {
+        echo "\rERROR: couldn't read the " . $path . " file". "\n";
+        return false;
+    }
+
+    $modified = false;
+    $new_content = "";
+    $contents_len = sizeof($contents);
+
+    if ($GLOBALS['eofstripwhite']) {
+        $lines_processed=0;
+        //iterate through lines, from the end of file
+        for ($i=$contents_len-1; $i>=0; $i--) {
+            $old_line = $contents[$i];
+            $contents[$i] = rtrim($contents[$i]);
+            if ($old_line !== $contents[$i]) {
+                if (!$GLOBALS['eofnewline'] || $old_line !== $contents[$i] . constant($nl) || $lines_processed>0) {
+                    $modified = true;
+                }
+            }
+
+            if (empty($contents[$i])) {
+                //we have an empty line at the end of file, just skip it
+                unset($contents[$contents_len--]);
+            }
+            else {
+                if ($GLOBALS['eofnewline']) {
+                    $contents[$i] .= constant($nl);
+                    if ($old_line !== $contents[$i]) {
+                        $modified = true;
+                    }
+                }
+                //we have found non-empty line, there is no need to go further
+                break;
+            }
+            $lines_processed++;
+        }
+    }
+
+    for ($i=0; $i<$contents_len; $i++) {
+        $is_last_line = ($i == $contents_len-1);
+        $line = $contents[$i];
+
+        switch ($nl)
+        {
+            case 'CRLF':
+                if (substr($line, -2) !== CRLF) {
+                    if (substr($line, -1) === LF || substr($line, -1) === CR) {
+                        $line = substr($line, 0, -1) . CRLF;
+                        $modified = true;
+                    }
+                    elseif(strlen($line)) {
+                        if (!$is_last_line) {
+                            echo "\rERROR: wrong line ending: " . $path . "@line " . ($i+1) . "\n";
+                            return false;
+                        }
+                        elseif(!$GLOBALS['eofstripwhite']) {
+                            $line = $line . CRLF;
+                            $modified = true;
+                        }
+                    }
+                }
+                break;
+
+            case 'CR':
+                if (substr($line, -1) !== CR) {
+                    if (substr($line, -1) === LF) {
+                        $line = substr($line, 0, -1) . CR;
+                        $modified = true;
+                    }
+                    elseif(strlen($line)) {
+                        if (!$is_last_line) {
+                            echo "\rERROR: wrong line ending: " . $path . "@line " . ($i+1) . "\n";
+                            return false;
+                        }
+                        elseif(!$GLOBALS['eofstripwhite']) {
+                            $line = $line . CR;
+                            $modified = true;
+                        }
+                    }
+                }
+                break;
+
+            case 'LF':
+                if(substr($line, -2) === CRLF) {
+                    $line = substr($line, 0, -2) . LF;
+                    $modified = true;
+                }
+                elseif (substr($line, -1) !== LF) {
+                    if (substr($line, -1) === CR) {
+                        $line = substr($line, 0, -1) . LF;
+                        $modified = true;
+                    }
+                    elseif(strlen($line)) {
+                        if (!$is_last_line) {
+                            echo "\rERROR: wrong line ending: " . $path . "@line " . ($i+1) . "\n";
+                            return false;
+                        }
+                        elseif(!$GLOBALS['eofstripwhite']) {
+                            $line = $line . LF;
+                            $modified = true;
+                        }
+                    }
+                }
+                break;
+        }
+        if ($GLOBALS['eolstripwhite']) {
+            $before = strlen($line);
+            $line = preg_replace("/(?:\x09|\x20)+((?:\r|\n)+)$/", "$1", $line);
+            if (strlen($line) != $before) {
+                $modified = true;
+            }
+        }
+        $new_content .= $line;
+    }
+
+    if ($modified) {
+        $fp = fopen($path, "wb");
+        if (!$fp) {
+            echo "\rERROR: couldn't open the " . $path . " file". "\n";
+            return false;
+        }
+        else {
+            if (flock($fp, LOCK_EX)) {
+                fwrite($fp, $new_content);
+                flock($fp, LOCK_UN);
+                echo "\rMODIFIED to " . $nl . ": " . $path ;
+                if ($GLOBALS['eolstripwhite']) {
+                    $saved = $size - strlen($new_content);
+                    $GLOBALS['saved_bytes'] += $saved;
+                    if ($saved>0) {
+                        echo " (saved " . $saved . "B)";
+                    }
+                    else if ($saved<0) {
+                        echo " (" . abs($saved) . "B added)";
+                    }
+                }
+                echo "\n";
+            } else {
+                echo "\rERROR: couldn't lock the " . $path . " file". "\n";
+                return false;
+            }
+            fclose($fp);
+        }
+    }
+
+    return $modified;
+}
+
+/**
+ * Fix ending lines in all files at given path
+ *
+ * @param string $path
+ */
+function fixPath($path)
+{
+    if (is_file($path)) {
+        $ext = strtolower(substr($path, strrpos($path, ".")));
+        foreach (array('CRLF', 'LF', 'CR') as $nl) {
+            //find out what's the correct line ending and fix file
+            //no need to process further
+            if (in_array($ext, $GLOBALS['extList'][$nl])) {
+                echo "Fixing single file:\n";
+                fixFile($path, $nl);
+                break;
+            }
+        }
+
+    }
+    else {
+        $dir = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), true);
+
+        if ($GLOBALS['maxdepth'] > -1) {
+            $dir = new DepthFilter($dir, $GLOBALS['maxdepth']+1);
+        }
+
+        $dir = new NegRegexFilter($dir, "/\/(\.svn|CVS)/");
+
+        if ($GLOBALS['excluderegex']) {
+            $dir = new NegRegexFilter($dir, $GLOBALS['excluderegex']);
+        }
+
+        foreach (array('CRLF', 'LF', 'CR') as $nl) {
+
+            $filtered_dir = new RegexFilter($dir, "/\.(".implode("|", $GLOBALS['extList'][$nl]).")$/i");
+
+            $extensions = array();
+            $j = 0;
+            $progressbar = "|/-\\";
+            foreach ($filtered_dir as $file) {
+                if (!is_dir($file)) {
+                    $basename = basename($file);
+
+                    //skip dot files
+                    if ($GLOBALS['nodotfiles']) {
+                        if (strpos($basename, ".") === 0) {
+                            continue;
+                        }
+                    }
+
+                    if ($GLOBALS['windows']) {
+                        $attribs = trim(substr(shell_exec("attrib " . $file), 0, 5));
+                        //skip archive files
+                        if ($GLOBALS['noarchive'] && false !== strpos($attribs, "A")) {
+                            print "\r ".$progressbar[$j++ % 4]. " ". str_pad(basename($file), 35, " ", STR_PAD_RIGHT)." SKIPPED";
+                            continue;
+                        }
+                        //skip hidden files
+                        if ($GLOBALS['nohidden'] && false !== strpos($attribs, "H")) {
+                            print "\r ".$progressbar[$j++ % 4]. " ". str_pad(basename($file), 35, " ", STR_PAD_RIGHT)." SKIPPED";
+                            continue;
+                        }
+                        //skip system files
+                        if ($GLOBALS['nosystem'] && false !== strpos($attribs, "S")) {
+                            print "\r ".$progressbar[$j++ % 4]. " ". str_pad(basename($file), 35, " ", STR_PAD_RIGHT)." SKIPPED";
+                            continue;
+                        }
+                    }
+
+                    fixFile($file, $nl);
+                    print "\r ".$progressbar[$j++ % 4]. " ". str_pad(basename($file), 35, " ", STR_PAD_RIGHT);
+                }
+            }
+        }
+    }
+}
+
+function printHelp() {
+    $help = <<<HELP
+
+SYNOPSIS
+       php fixlineends.php [options] PATH [PATH2...]
+
+DESCRIPTION
+       Traverse recursively all the paths given and fix line endings
+       in each file.
+
+OPTIONS
+       --eofnewline
+            force new line character at the end of file
+
+       --eofstripwhite
+            strip whitespace from the end of file
+
+       --eolstripwhite
+            strip whitespace from the end of line (spaces, tabs)
+
+       --excluderegex=regex
+            use regex to exclude files, preg_match() format expected
+
+       --help
+            display this help and exit
+
+       --noarchive
+            if set to true, archive files are skipped (Windows only)
+
+       --nodotfiles
+            if set to true, dot files are skipped
+
+       --nohidden
+            if set to true, hidden files are skipped (Windows only)
+
+       --nosystem
+            if set to true, system files are skipped (Windows only)
+
+       --maxdepth
+            fix line ends only if file is N or fewer levels below
+            the command line argument; Use --max-depth=0 to omit
+            subdirectories
+
+EXAMPLES
+            php fixlineends.php --eofstripwhite --eofnewline --maxdepth=1
+                --nodotfiles --excluderegex=/\_private/ .
+
+       This command fixes line endings in current directory and in
+       subdirectories placed one level below. Dot files are skipped.
+       Paths that match "_private" are skipped. White chars are stripped
+       at the end of file. New line character is added to the end of file
+       (if required).
+
+
+HELP;
+    echo $help;
+}
+
+function translateCommandArgs($args) {
+    $paths = $args[1];
+
+    foreach ($paths as $path) {
+        if (!is_dir($path) && !is_file($path)) {
+            die("Entered path is invalid: " . $path);
+        }
+    }
+
+    foreach ($args[0] as $arg) {
+        if (!isset($arg[0])) {
+            continue;
+        }
+        switch ($arg[0]) {
+            case '--noarchive':
+                $GLOBALS['noarchive'] = true;
+                break;
+
+            case '--help':
+                printHelp();
+                die();
+                break;
+
+            case '--maxdepth':
+                $GLOBALS['maxdepth'] = intval($arg[1]);
+                break;
+
+            case '--nohidden':
+                $GLOBALS['nohidden'] = true;
+                break;
+
+            case '--nosystem':
+                $GLOBALS['nosystem'] = true;
+                break;
+
+            case '--nodotfiles':
+                $GLOBALS['nodotfiles'] = true;
+                break;
+
+            case '--excluderegex':
+                $GLOBALS['excluderegex'] = $arg[1];
+                break;
+
+            case '--eofnewline':
+                $GLOBALS['eofnewline'] = true;
+                break;
+
+            case '--eofstripwhite':
+                $GLOBALS['eofstripwhite'] = true;
+                break;
+
+            case '--eolstripwhite':
+                $GLOBALS['eolstripwhite'] = true;
+                break;
+        }
+    }
+
+    return $paths;
+}
+
+//$extList holds the associative array of extensions
+//key is the name of a constant that holds the line character
+$extList = array();
+$extList['CRLF'] = $extList['CR'] = $extList['LF'] = array();
+
+foreach ($list as $ext => $nl) {
+    $extRegex = preg_quote($ext);
+    switch ($nl) {
+        case CRLF:
+            $extList['CRLF'][] = $extRegex;
+            break;
+        case LF:
+            $extList['LF'][] = $extRegex;
+            break;
+        case CR:
+            $extList['CR'][] = $extRegex;
+            break;
+        default:
+            die("Unknown line ending");
+            break;
+    }
+}
+
+if ($_SERVER['argc']>1) {
+	include dirname(__FILE__).DIRECTORY_SEPARATOR."console_getopt/Getopt.php";
+
+    if ($windows) {
+        $longoptions = array("eofstripwhite", "eofnewline", "eolstripwhite", "help", "noarchive", "nohidden", "nosystem", "nodotfiles", "maxdepth=", "excluderegex=");
+    }
+    else {
+        $longoptions = array("eofstripwhite", "eofnewline", "eolstripwhite", "help", "nodotfiles", "maxdepth=", "excluderegex=");
+    }
+
+    $con  = new Console_Getopt;
+    $args = $con->readPHPArgv();
+    $options = $con->getopt($args, null, $longoptions);
+
+    if (PEAR::isError($options)) {
+        die($options->getMessage());
+    }
+
+    $paths = translateCommandArgs($options);
+    foreach ($paths as $path) {
+        fixPath($path);
+    }
+}
+else {
+    printHelp();
+    die();
+}
+
+print "\rDone!".str_repeat(" ",40)."\n";
+if ($saved_bytes>0) {
+    echo "saved " . $saved_bytes . "B";
+}
+else if ($saved_bytes<0) {
+    echo abs($saved_bytes) . "B added";
+}
Index: /CKReleaser/trunk/tools/jaf/LICENSE.txt
===================================================================
--- /CKReleaser/trunk/tools/jaf/LICENSE.txt	(revision 2890)
+++ /CKReleaser/trunk/tools/jaf/LICENSE.txt	(revision 2890)
@@ -0,0 +1,306 @@
+A. Sun Microsystems, Inc. ("Sun") ENTITLEMENT for SOFTWARE
+
+Licensee/Company: Entity receiving Software.
+
+Effective Date: Date of delivery of the Software to You.
+
+Software: JavaBeans Activation Framework 1.1.1.
+
+License Term:  Perpetual (subject to termination under the SLA).
+
+Licensed Unit: Software Copy.
+
+Licensed unit Count: Unlimited.
+
+Permitted Uses:
+
+1. You may reproduce and use the Software for Individual, Commercial,
+or Research and Instructional Use for the purposes of designing,
+developing, testing, and running Your applets and
+application("Programs").
+
+2. Subject to the terms and conditions of this Agreement and
+restrictions and exceptions set forth in the Software's documentation,
+You may reproduce and distribute portions of Software identified as a
+redistributable in the documentation ("Redistributable"), provided
+that:
+
+(a) you distribute Redistributable complete and unmodified and only
+bundled as part of Your Programs,
+
+(b) your Programs add significant and primary functionality to the
+Redistributable,
+
+(c) you distribute Redistributable for the sole purpose of running your
+Programs,
+
+(d) you do not distribute additional software intended to replace any
+component(s) of the Redistributable,
+
+(e) you do not remove or alter any proprietary legends or notices
+contained in or on the Redistributable.
+
+(f) you only distribute the Redistributable subject to a license
+agreement that protects Sun's interests consistent with the terms
+contained in this Agreement, and
+
+(g) you agree to defend and indemnify Sun and its licensors from and
+against any damages, costs, liabilities, settlement amounts and/or
+expenses  (including attorneys' fees) incurred in connection with any
+claim, lawsuit or action by any third party that arises or results from
+the use or distribution of any and all Programs and/or
+Redistributable.
+
+3. Java Technology Restrictions.  You may not create, modify, or change
+the behavior of, or authorize your licensees to create, modify, or
+change the behavior of, classes, interfaces, or subpackages that are in
+any way identified as "java", "javax", "sun" or similar convention as
+specified by Sun in any naming convention designation.
+
+B. Sun Microsystems, Inc. ("Sun")
+SOFTWARE LICENSE AGREEMENT
+
+READ THE TERMS OF THIS AGREEMENT ("AGREEMENT") CAREFULLY BEFORE OPENING
+SOFTWARE MEDIA PACKAGE. BY OPENING SOFTWARE MEDIA PACKAGE, YOU AGREE TO
+THE TERMS OF THIS AGREEMENT. IF YOU ARE ACCESSING SOFTWARE
+ELECTRONICALLY, INDICATE YOUR ACCEPTANCE OF THESE TERMS BY SELECTING
+THE "ACCEPT" BUTTON AT THE END OF THIS AGREEMENT. IF YOU DO NOT AGREE
+TO ALL OF THE TERMS, PROMPTLY RETURN THE UNUSED SOFTWARE TO YOUR PLACE
+OF PURCHASE FOR A REFUND OR, IF SOFTWARE IS ACCESSED ELECTRONICALLY,
+SELECT THE "DECLINE" (OR "EXIT") BUTTON AT THE END OF THIS AGREEMENT.
+IF YOU HAVE SEPARATELY AGREED TO LICENSE TERMS ("MASTER TERMS") FOR
+YOUR LICENSE TO THIS SOFTWARE, THEN SECTIONS 1-5 OF THIS AGREEMENT
+("SUPPLEMENTAL LICENSE TERMS") SHALL SUPPLEMENT AND SUPERSEDE THE
+MASTER TERMS IN RELATION TO THIS SOFTWARE.
+
+1.      Definitions.
+
+(a)     "Entitlement" means the collective set of applicable documents
+authorized by Sun evidencing your obligation to pay associated fees (if
+any) for the license, associated Services, and the authorized scope of
+use of Software under this Agreement.
+
+(b)     "Licensed Unit" means the unit of measure by which your use of
+Software and/or Service is licensed, as described in your Entitlement.
+
+(c)     "Permitted Use" means the licensed Software use(s) authorized
+in this Agreement as specified in your Entitlement. The Permitted Use
+for any bundled Sun software not specified in your Entitlement will be
+evaluation use as provided in Section 3.
+
+(d)     "Service" means the service(s) that Sun or its delegate will
+provide, if any, as selected in your Entitlement and as further
+described in the applicable service listings at
+www.sun.com/service/servicelist.
+
+(e)     "Software" means the Sun software described in your
+Entitlement. Also, certain software may be included for evaluation use
+under Section 3.
+
+(f)     "You" and "Your" means the individual or legal entity specified
+in the Entitlement, or for evaluation purposes, the entity performing
+the evaluation.
+
+2.      License Grant and Entitlement.
+
+Subject to the terms of your Entitlement, Sun grants you a
+nonexclusive, nontransferable limited license to use Software for its
+Permitted Use for the license term. Your Entitlement will specify (a)
+Software licensed, (b) the Permitted Use, (c) the license term, and (d)
+the Licensed Units.
+
+Additionally, if your Entitlement includes Services, then it will also
+specify the (e) Service and (f) service term.
+
+If your rights to Software or Services are limited in duration and the
+date such rights begin is other than the purchase date, your
+Entitlement will provide that beginning date(s).
+
+The Entitlement may be delivered to you in various ways depending on
+the manner in which you obtain Software and Services, for example, the
+Entitlement may be provided in your receipt, invoice or your contract
+with Sun or authorized Sun reseller. It may also be in electronic
+format if you download Software.
+
+3.      Permitted Use.
+
+As selected in your Entitlement, one or more of the following Permitted
+Uses will apply to your use of Software. Unless you have an Entitlement
+that expressly permits it, you may not use Software for any of the
+other Permitted Uses. If you don't have an Entitlement, or if your
+Entitlement doesn't cover additional software delivered to you, then
+such software is for your Evaluation Use.
+
+(a) Evaluation Use. You may evaluate Software internally for a period
+of 90 days from your first use.
+
+(b) Research and Instructional Use. You may use Software internally to
+design, develop and test, and also to provide instruction on such
+uses.
+
+(c) Individual Use. You may use Software internally for personal,
+individual use.
+
+(d) Commercial Use. You may use Software internally for your own
+commercial purposes.
+
+(e) Service Provider Use. You may make Software functionality
+accessible (but not by providing Software itself or through outsourcing
+services) to your end users in an extranet deployment, but not to your
+affiliated companies or to government agencies.
+
+4.      Licensed Units.
+
+Your Permitted Use is limited to the number of Licensed Units stated in
+your Entitlement. If you require additional Licensed Units, you will
+need additional Entitlement(s).
+
+5.	Restrictions.
+
+(a) The copies of Software provided to you under this Agreement are
+licensed, not sold, to you by Sun. Sun reserves all rights not
+expressly granted. (b) You may make a single archival copy of Software,
+but otherwise may not copy, modify, or distribute Software. However if
+the Sun documentation accompanying Software lists specific portions of
+Software, such as header files, class libraries, reference source code,
+and/or redistributable files, that may be handled differently, you may
+do so only as provided in the Sun documentation. (c) You may not rent,
+lease, lend or encumber Software. (d) Unless enforcement is prohibited
+by applicable law, you may not decompile, or reverse engineer
+Software.  (e) The terms and conditions of this Agreement will apply to
+any Software updates, provided to you at Sun's discretion, that replace
+and/or supplement the original Software, unless such update contains a
+separate license. (f) You may not publish or provide the results of any
+benchmark or comparison tests run on Software to any third party
+without the prior written consent of Sun. (g) Software is confidential
+and copyrighted. (h) Unless otherwise specified, if Software is
+delivered with embedded or bundled software that enables functionality
+of Software, you may not use such software on a stand-alone basis or
+use any portion of such software to interoperate with any program(s)
+other than Software.  (i) Software may contain programs that perform
+automated collection of system data and/or automated software updating
+services. System data collected through such programs may be used by
+Sun, its subcontractors, and its service delivery partners for the
+purpose of providing you with remote system services and/or improving
+Sun's software and systems. (j) Software is not designed, licensed or
+intended for use in the design, construction, operation or maintenance
+of any nuclear facility and Sun and its licensors disclaim any express
+or implied warranty of fitness for such uses. (k) No right, title or
+interest in or to any trademark, service mark, logo or trade name of
+Sun or its licensors is granted under this Agreement.
+
+6.	Term and Termination. 
+
+The license and service term are set forth in your Entitlement(s). Your
+rights under this Agreement will terminate immediately without notice
+from Sun if you materially breach it or take any action in derogation
+of Sun's and/or its licensors' rights to Software. Sun may terminate
+this Agreement should any Software become, or in Sun's reasonable
+opinion likely to become, the subject of a claim of intellectual
+property infringement or trade secret misappropriation. Upon
+termination, you will cease use of, and destroy, Software and confirm
+compliance in writing to Sun. Sections 1, 5, 6, 7, and 9-15 will
+survive termination of the Agreement.
+
+7.      Java Compatibility and Open Source.
+
+Software may contain Java technology. You may not create additional
+classes to, or modifications of, the Java technology, except under
+compatibility requirements available under a separate agreement
+available at www.java.net.
+
+Sun supports and benefits from the global community of open source
+developers, and thanks the community for its important contributions
+and open standards-based technology, which Sun has adopted into many of
+its products.
+
+Please note that portions of Software may be provided with notices and
+open source licenses from such communities and third parties that
+govern the use of those portions, and any licenses granted hereunder do
+not alter any rights and obligations you may have under such open
+source licenses, however, the disclaimer of warranty and limitation of
+liability provisions in this Agreement will apply to all Software in
+this distribution.
+
+8.      Limited Warranty.
+
+Sun warrants to you that for a period of 90 days from the date of
+purchase, as evidenced by a copy of the receipt, the media on which
+Software is furnished (if any) will be free of defects in materials and
+workmanship under normal use. Except for the foregoing, Software is
+provided "AS IS". Your exclusive remedy and Sun's entire liability
+under this limited warranty will be at Sun's option to replace Software
+media or refund the fee paid for Software. Some states do not allow
+limitations on certain implied warranties, so the above may not apply
+to you. This limited warranty gives you specific legal rights. You may
+have others, which vary from state to state.
+
+9.      Disclaimer of Warranty.
+
+UNLESS SPECIFIED IN THIS AGREEMENT, ALL EXPRESS OR IMPLIED CONDITIONS,
+REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT
+ARE DISCLAIMED, EXCEPT TO THE EXTENT THAT THESE DISCLAIMERS ARE HELD TO
+BE LEGALLY INVALID.
+
+10.     Limitation of Liability.
+
+TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT WILL SUN OR ITS
+LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES,
+HOWEVER CAUSED REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF OR
+RELATED TO THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. In no event will Sun's
+liability to you, whether in contract, tort (including negligence), or
+otherwise, exceed the amount paid by you for Software under this
+Agreement. The foregoing limitations will apply even if the above
+stated warranty fails of its essential purpose. Some states do not
+allow the exclusion of incidental or consequential damages, so some of
+the terms above may not be applicable to you.
+
+11.     Export Regulations.
+
+All Software, documents, technical data, and any other materials
+delivered under this Agreement are subject to U.S. export control laws
+and may be subject to export or import regulations in other countries.
+You agree to comply strictly with these laws and regulations and
+acknowledge that you have the responsibility to obtain any licenses to
+export, re-export, or import as may be required after delivery to you.
+
+12.     U.S. Government Restricted Rights.
+
+If Software is being acquired by or on behalf of the U.S. Government or
+by a U.S. Government prime contractor or subcontractor (at any tier),
+then the Government's rights in Software and accompanying documentation
+will be only as set forth in this Agreement; this is in accordance with
+48 CFR 227.7201 through 227.7202-4 (for Department of Defense (DOD)
+acquisitions) and with 48 CFR 2.101 and 12.212 (for non-DOD
+acquisitions).
+
+13.     Governing Law.
+
+Any action related to this Agreement will be governed by California law
+and controlling U.S. federal law. No choice of law rules of any
+jurisdiction will apply.
+
+14.     Severability.
+
+If any provision of this Agreement is held to be unenforceable, this
+Agreement will remain in effect with the provision omitted, unless
+omission would frustrate the intent of the parties, in which case this
+Agreement will immediately terminate.
+
+15.     Integration.
+
+This Agreement, including any terms contained in your Entitlement, is
+the entire agreement between you and Sun relating to its subject
+matter. It supersedes all prior or contemporaneous oral or written
+communications, proposals, representations and warranties and prevails
+over any conflicting or additional terms of any quote, order,
+acknowledgment, or other communication between the parties relating to
+its subject matter during the term of this Agreement. No modification
+of this Agreement will be binding, unless in writing and signed by an
+authorized representative of each party.
+
+Please contact Sun Microsystems, Inc. 4150 Network Circle, Santa Clara,
+California 95054 if you have questions.
Index: /CKReleaser/trunk/tools/javatar/LICENSE.txt
===================================================================
--- /CKReleaser/trunk/tools/javatar/LICENSE.txt	(revision 2890)
+++ /CKReleaser/trunk/tools/javatar/LICENSE.txt	(revision 2890)
@@ -0,0 +1,15 @@
+
+    ----  Public Domain  ----
+
+This work was autored by Timothy Gerard Endres, time@gjt.org.
+
+This work has been placed into the public domain.
+
+You are free to use this work in any way you wish.
+
+DISCLAIMER
+
+THIS SOFTWARE IS PROVIDED AS-IS, WITH ABSOLUTELY NO WARRANTY.
+YOU ASSUME ALL RESPONSIBILITY FOR ANY AND ALL CONSEQUENCES
+THAT MAY RESULT FROM THE USE OF THIS SOFTWARE!
+
Index: /CKReleaser/trunk/tools/jsdoc/README.txt
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/README.txt	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/README.txt	(revision 2890)
@@ -0,0 +1,151 @@
+======================================================================
+
+DESCRIPTION:
+
+** NOTICE ** THIS VERSION OF THE SOFTWARE IS "BETA," MEANING IT IS IS
+NOT YET READY FOR USE IN A PRODUCTION ENVIRONEMNT. IT IS MADE
+AVAILABLE FOR PREVIEW AND TESTING PURPOSES ONLY.
+
+This is Version 2 of JsDoc Toolkit, an automatic documentation
+generation tool for JavaScript. It is written in JavaScript and is run
+from a command line (or terminal) using the Java runtime engine.
+
+Using this tool you can automatically turn JavaDoc-like comments in
+your JavaScript source code into published output files, such as HTML
+or XML.
+
+For more information, to report a bug, or to browse the technical
+documentation for this tool please visit the official JsDoc Toolkit
+project homepage at http://code.google.com/p/jsdoc-toolkit/
+
+For the most up-to-date documentation on Version 2 of JsDoc Toolkit
+see the Verion 2 wiki at http://jsdoctoolkit.org/wiki
+
+
+======================================================================
+
+REQUIREMENTS:
+
+JsDoc Toolkit is known to work with:
+java version "1.6.0_03"
+Java(TM) SE Runtime Environment (build 1.6.0_03-b05)
+on Windows XP,
+and java version "1.5.0_13"
+Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_13-b05-241)
+on Mac OS X 10.4.
+
+Other versions of java may or may not work with JsDoc Toolkit.
+
+======================================================================
+
+USAGE:
+
+Running JsDoc Toolkit requires you to have Java installed on your
+computer. For more information see http://www.java.com/getjava/
+
+Before running the JsDoc Toolkit app you should change your current
+working directory to the jsdoc-toolkit folder. Then follow the
+examples below, or as shown on the project wiki.
+
+On a computer running Windows a valid command line to run JsDoc
+Toolkit might look like this:
+
+> java -jar jsrun.jar app\run.js -a -t=templates\jsdoc mycode.js
+
+On Mac OS X or Linux the same command would look like this:
+
+$ java -jar jsrun.jar app/run.js -a -t=templates/jsdoc mycode.js
+
+The above assumes your current working directory contains jsrun.jar,
+the "app" and "templates" subdirectories from the standard JsDoc
+Toolkit distribution and that the relative path to the code you wish
+to document is "mycode.js".
+
+The output documentation files will be saved to a new directory named
+"out" (by default) in the current directory, or if you specify a
+-d=somewhere_else option, to the somewhere_else directory.
+
+For help (usage notes) enter this on the command line:
+
+$ java -jar jsrun.jar app/run.js --help
+
+More information about the various command line options used by JsDoc
+Toolkit are available on the project wiki.
+
+======================================================================
+
+TESTING:
+
+To run the suite of unit tests included with JsDoc Toolkit enter this
+on the command line:
+
+$ java -jar jsrun.jar app/run.js -T
+
+To see a dump of the internal data structure that JsDoc Toolkit has
+built from your source files use this command:
+
+$ java -jar jsrun.jar app/run.js mycode.js -Z
+
+
+======================================================================
+
+LICENSE:
+
+JSDoc.pm
+
+This project is based on the JSDoc.pm tool, created by Michael
+Mathews and Gabriel Reid. More information on JsDoc.pm can
+be found on the JSDoc.pm homepage: http://jsdoc.sourceforge.net/
+
+Complete documentation on JsDoc Toolkit can be found on the project
+wiki at http://code.google.com/p/jsdoc-toolkit/w/list
+
+Rhino
+
+Rhino (JavaScript in Java) is open source and licensed by Mozilla
+under the MPL 1.1 or later/GPL 2.0 or later licenses, the text of
+which is available at http://www.mozilla.org/MPL/
+
+You can obtain the source code for Rhino from the Mozilla web site at
+http://www.mozilla.org/rhino/download.html
+
+JsDoc Toolkit is a larger work that uses the Rhino JavaScript engine
+but is not derived from it in any way. The Rhino library is used 
+without modification and without any claims whatsoever.
+
+The Rhino Debugger
+
+You can obtain more information about the Rhino Debugger from the 
+Mozilla web site at http://www.mozilla.org/rhino/debugger.html
+
+JsDoc Toolkit is a larger work that uses the Rhino Debugger but
+is not derived from it in any way. The Rhino Debugger is used
+without modification and without any claims whatsoever.
+
+JsDoc Toolkit
+
+All code specific to JsDoc Toolkit are free, open source and licensed
+for use under the X11/MIT License.
+
+JsDoc Toolkit is Copyright (c)2008 Michael Mathews <micmath@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms below.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions: The above copyright notice and this
+permission notice must be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Index: /CKReleaser/trunk/tools/jsdoc/app/frame.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/frame.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/frame.js	(revision 2890)
@@ -0,0 +1,33 @@
+IO.include("frame/Opt.js");
+IO.include("frame/Chain.js");
+IO.include("frame/Link.js");
+IO.include("frame/String.js");
+IO.include("frame/Hash.js");
+IO.include("frame/Namespace.js");
+IO.include("frame/Reflection.js");
+
+/** A few helper functions to make life a little easier. */
+
+function defined(o) {
+	return (o !== undefined);
+}
+
+function copy(o) { // todo check for circular refs
+	if (o == null || typeof(o) != 'object') return o;
+	var c = new o.constructor();
+	for(var p in o)	c[p] = copy(o[p]);
+	return c;
+}
+
+function isUnique(arr) {
+	var l = arr.length;
+	for(var i = 0; i < l; i++ ) {
+		if (arr.lastIndexOf(arr[i]) > i) return false;
+	}
+	return true;
+}
+
+/** Returns the given string with all regex meta characters backslashed. */
+RegExp.escapeMeta = function(str) {
+	return str.replace(/([$^\\\/()|?+*\[\]{}.-])/g, "\\$1");
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/frame/Chain.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/frame/Chain.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/frame/Chain.js	(revision 2890)
@@ -0,0 +1,102 @@
+/**@constructor*/
+function ChainNode(object, link) {
+	this.value = object;
+	this.link = link; // describes this node's relationship to the previous node
+}
+
+/**@constructor*/
+function Chain(valueLinks) {
+	this.nodes = [];
+	this.cursor = -1;
+	
+	if (valueLinks && valueLinks.length > 0) {
+		this.push(valueLinks[0], "//");
+		for (var i = 1, l = valueLinks.length; i < l; i+=2) {
+			this.push(valueLinks[i+1], valueLinks[i]);
+		}
+	}
+}
+
+Chain.prototype.push = function(o, link) {
+	if (this.nodes.length > 0 && link) this.nodes.push(new ChainNode(o, link));
+	else this.nodes.push(new ChainNode(o));
+}
+
+Chain.prototype.unshift = function(o, link) {
+	if (this.nodes.length > 0 && link) this.nodes[0].link = link;
+	this.nodes.unshift(new ChainNode(o));
+	this.cursor++;
+}
+
+Chain.prototype.get = function() {
+	if (this.cursor < 0 || this.cursor > this.nodes.length-1) return null;
+	return this.nodes[this.cursor];
+}
+
+Chain.prototype.first = function() {
+	this.cursor = 0;
+	return this.get();
+}
+
+Chain.prototype.last = function() {
+	this.cursor = this.nodes.length-1;
+	return this.get();
+}
+
+Chain.prototype.next = function() {
+	this.cursor++;
+	return this.get();
+}
+
+Chain.prototype.prev = function() {
+	this.cursor--;
+	return this.get();
+}
+
+Chain.prototype.toString = function() {
+	var string = "";
+	for (var i = 0, l = this.nodes.length; i < l; i++) {
+		if (this.nodes[i].link) string += " -("+this.nodes[i].link+")-> ";
+		string += this.nodes[i].value.toString();
+	}
+	return string;
+}
+
+Chain.prototype.joinLeft = function() {
+	var result = "";
+	for (var i = 0, l = this.cursor; i < l; i++) {
+		if (result && this.nodes[i].link) result += this.nodes[i].link;
+		result += this.nodes[i].value.toString();
+	}
+	return result;
+}
+
+
+/* USAGE:
+
+var path = "one/two/three.four/five-six";
+var pathChain = new Chain(path.split(/([\/.-])/));
+print(pathChain);
+
+var lineage = new Chain();
+lineage.push("Port");
+lineage.push("Les", "son");
+lineage.push("Dawn", "daughter");
+lineage.unshift("Purdie", "son");
+
+print(lineage);
+
+// walk left
+for (var node = lineage.last(); node !== null; node = lineage.prev()) {
+	print("< "+node.value);
+}
+
+// walk right
+var node = lineage.first()
+while (node !== null) {
+	print(node.value);
+	node = lineage.next();
+	if (node && node.link) print("had a "+node.link+" named");
+}
+
+*/
Index: /CKReleaser/trunk/tools/jsdoc/app/frame/Dumper.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/frame/Dumper.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/frame/Dumper.js	(revision 2890)
@@ -0,0 +1,144 @@
+/**
+ * @class
+<pre>
+This is a lightly modified version of Kevin Jones' JavaScript
+library Data.Dump. To download the original visit:
+    <a href="http://openjsan.org/doc/k/ke/kevinj/Data/Dump/">http://openjsan.org/doc/k/ke/kevinj/Data/Dump/</a>
+
+AUTHORS
+
+The Data.Dump JavaScript module is written by Kevin Jones 
+(kevinj@cpan.org), based on Data::Dump by Gisle Aas (gisle@aas.no),
+based on Data::Dumper by Gurusamy Sarathy (gsar@umich.edu).
+
+COPYRIGHT
+
+Copyright 2007 Kevin Jones. Copyright 1998-2000,2003-2004 Gisle Aas.
+Copyright 1996-1998 Gurusamy Sarathy.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the Perl Artistic License
+
+See http://www.perl.com/perl/misc/Artistic.html
+</pre>
+ * @static
+ */
+Dumper = {
+	/** @param [...] The objects to dump. */
+	dump: function () {
+	    if (arguments.length > 1)
+	        return this._dump(arguments);
+	    else if (arguments.length == 1)
+	        return this._dump(arguments[0]);
+	    else
+	        return "()";
+	},
+	
+	_dump: function (obj) {
+		if (typeof obj == 'undefined') return 'undefined';
+		var out;
+		if (obj.serialize) { return obj.serialize(); }
+		var type = this._typeof(obj);
+		if (obj.circularReference) obj.circularReference++;
+		switch (type) {
+			case 'circular':
+				out = "{ //circularReference\n}";
+				break;
+			case 'object':
+				var pairs = new Array;
+				
+				for (var prop in obj) {
+					if (prop != "circularReference" && obj.hasOwnProperty(prop)) { //hide inherited properties
+						pairs.push(prop + ': ' + this._dump(obj[prop]));
+					}
+				}
+	
+				out = '{' + this._format_list(pairs) + '}';
+				break;
+	
+			case 'string':
+				for (var prop in Dumper.ESC) {
+					if (Dumper.ESC.hasOwnProperty(prop)) {
+						obj = obj.replace(prop, Dumper.ESC[prop]);
+					}
+				}
+	
+			// Escape UTF-8 Strings
+				if (obj.match(/^[\x00-\x7f]*$/)) {
+					out = '"' + obj.replace(/\"/g, "\\\"").replace(/([\n\r]+)/g, "\\$1") + '"';
+				}
+				else {
+					out = "unescape('"+escape(obj)+"')";
+				}
+				break;
+	
+			case 'array':
+				var elems = new Array;
+	
+				for (var i=0; i<obj.length; i++) {
+					elems.push( this._dump(obj[i]) );
+				}
+	
+				out = '[' + this._format_list(elems) + ']';
+				break;
+	
+			case 'date':
+			// firefox returns GMT strings from toUTCString()...
+			var utc_string = obj.toUTCString().replace(/GMT/,'UTC');
+				out = 'new Date("' + utc_string + '")';
+				break;
+	
+			case 'element':
+				// DOM element
+				out = this._dump_dom(obj);
+				break;
+		
+				default:
+					out = obj;
+		}
+	
+		out = String(out).replace(/\n/g, '\n    ');
+		out = out.replace(/\n    (.*)$/,"\n$1");
+	
+		return out;
+	},
+	
+	_format_list: function (list) {
+		if (!list.length) return '';
+		var nl = list.toString().length > 60 ? '\n' : ' ';
+		return nl + list.join(',' + nl) + nl;
+    },
+    
+    _typeof: function (obj) {
+    	if (obj && obj.circularReference && obj.circularReference > 1) return 'circular';
+		if (Array.prototype.isPrototypeOf(obj)) return 'array';
+		if (Date.prototype.isPrototypeOf(obj)) return 'date';
+		if (typeof obj.nodeType != 'undefined') return 'element';
+		return typeof(obj);
+	},
+	
+	_dump_dom: function (obj) {
+		return '"' + Dumper.nodeTypes[obj.nodeType] + '"';
+	}
+};
+
+Dumper.ESC = {
+    "\t": "\\t",
+    "\n": "\\n",
+    "\f": "\\f"
+};
+
+Dumper.nodeTypes = {
+    1: "ELEMENT_NODE",
+    2: "ATTRIBUTE_NODE",
+    3: "TEXT_NODE",
+    4: "CDATA_SECTION_NODE",
+    5: "ENTITY_REFERENCE_NODE",
+    6: "ENTITY_NODE",
+    7: "PROCESSING_INSTRUCTION_NODE",
+    8: "COMMENT_NODE",
+    9: "DOCUMENT_NODE",
+    10: "DOCUMENT_TYPE_NODE",
+    11: "DOCUMENT_FRAGMENT_NODE",
+    12: "NOTATION_NODE"
+};
Index: /CKReleaser/trunk/tools/jsdoc/app/frame/Hash.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/frame/Hash.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/frame/Hash.js	(revision 2890)
@@ -0,0 +1,47 @@
+/**@constructor*/
+function Hash() {
+	this.reset();
+}
+
+Hash.prototype.reset = function() {
+	this.elements = {};
+}
+
+Hash.prototype.put = function() {
+	for (var i = 0, l = arguments.length; i < l; i++) {
+		this.elements[arguments[i]] = arguments[++i];
+	}
+}
+
+Hash.prototype.has = function(key) {
+	return this.elements.hasOwnProperty(key);
+}
+
+Hash.prototype.get = function(key) {
+	return (this.has(key)) ? this.elements[key] : undefined;
+}
+
+Hash.prototype.drop = function(key) {
+	if (this.has(key)) {
+		delete this.elements[key];
+	}
+}
+
+Hash.prototype.rename = function(oldKey, newKey) {
+	if (oldKey != newKey && this.has(oldKey)) {
+		this.elements[newKey] = this.elements[oldKey];
+		delete this.elements[oldKey];
+	}
+}
+
+Hash.prototype.keys = function() {
+	var keys = [];
+	for (var key in this.elements) if (this.has(key)) keys.push(key);
+	return keys;
+}
+
+Hash.prototype.values = function() {
+	var values = [];
+	for (var key in this.elements) if (this.has(key)) values.push(this.get(key));
+	return values;
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/frame/Link.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/frame/Link.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/frame/Link.js	(revision 2890)
@@ -0,0 +1,142 @@
+/** Handle the creation of HTML links to documented symbols.
+	@constructor
+*/
+function Link() {
+	this.alias = "";
+	this.src = "";
+	this.file = "";
+	this.text = "";
+	this.innerName = "";
+	this.classLink = false;
+	this.targetName = "";
+	
+	this.target = function(targetName) {
+		if (defined(targetName)) this.targetName = targetName;
+		return this;
+	}
+	this.inner = function(inner) {
+		if (defined(inner)) this.innerName = inner;
+		return this;
+	}
+	this.withText = function(text) {
+		if (defined(text)) this.text = text;
+		return this;
+	}
+	this.toSrc = function(filename) {
+		if (defined(filename)) this.src = filename;
+		return this;
+	}
+	this.toSymbol = function(alias) {
+		if (defined(alias)) this.alias = new String(alias);
+		return this;
+	}
+	this.toClass = function(alias) {
+		this.classLink = true;
+		return this.toSymbol(alias);
+	}
+	this.toFile = function(file) {
+		if (defined(file)) this.file = file;
+		return this;
+	}
+	
+	this.toString = function() {
+		var linkString;
+		var thisLink = this;
+
+		if (this.alias) {
+			linkString = this.alias.replace(/(^|[^a-z$0-9_#.:^-])([|a-z$0-9_#.:^-]+)($|[^a-z$0-9_#.:^-])/i,
+				function(match, prematch, symbolName, postmatch) {
+					var symbolNames = symbolName.split("|");
+					var links = [];
+					for (var i = 0, l = symbolNames.length; i < l; i++) {
+						thisLink.alias = symbolNames[i];
+						links.push(thisLink._makeSymbolLink(symbolNames[i]));
+					}
+					return prematch+links.join("|")+postmatch;
+				}
+			);
+		}
+		else if (this.src) {
+			linkString = thisLink._makeSrcLink(this.src);
+		}
+		else if (this.file) {
+			linkString = thisLink._makeFileLink(this.file);
+		}
+
+		return linkString;
+	}
+}
+
+/** prefixed for hashes */
+Link.hashPrefix = "";
+
+/** Appended to the front of relative link paths. */
+Link.base = "";
+
+Link.symbolNameToLinkName = function(symbol) {
+	var linker = "";
+	if (symbol.isStatic) linker = ".";
+	else if (symbol.isInner) linker = "-";
+	
+	return Link.hashPrefix+linker+symbol.name;
+}
+
+/** Create a link to a snother symbol. */
+Link.prototype._makeSymbolLink = function(alias) {
+	var linkBase = Link.base+publish.conf.symbolsDir;
+	var linkTo = Link.symbolSet.getSymbol(alias);
+	var linkPath;
+	var target = (this.targetName)? " target=\""+this.targetName+"\"" : "";
+
+	// is it an internal link?
+	if (alias.charAt(0) == "#") var linkPath = alias;
+	
+	// if there is no symbol by that name just return the name unaltered
+	else if (!linkTo) return this.text || alias;
+	
+	// it's a symbol in another file
+	else {
+
+		if (!linkTo.is("CONSTRUCTOR") && !linkTo.isNamespace) { // it's a method or property
+			linkPath = escape(linkTo.memberOf) || "_global_";
+			linkPath += publish.conf.ext + "#" + Link.symbolNameToLinkName(linkTo);
+		}
+		else {
+			linkPath = escape(linkTo.alias);
+			linkPath += publish.conf.ext + (this.classLink? "":"#" + Link.hashPrefix + "constructor");
+		}
+		linkPath = linkBase + linkPath
+	}
+	
+	var linkText = this.text || alias;
+	
+	var link = {linkPath: linkPath, linkText: linkText};
+	
+	if (typeof JSDOC.PluginManager != "undefined") {
+		JSDOC.PluginManager.run("onSymbolLink", link);
+	}
+	
+	return "<a href=\""+link.linkPath+"\""+target+">"+link.linkText+"</a>";
+}
+
+/** Create a link to a source file. */
+Link.prototype._makeSrcLink = function(srcFilePath) {
+	var target = (this.targetName)? " target=\""+this.targetName+"\"" : "";
+		
+	// transform filepath into a filename
+	var srcFile = srcFilePath.replace(/\.\.?[\\\/]/g, "").replace(/[:\\\/]/g, "_");
+	var outFilePath = Link.base + publish.conf.srcDir + srcFile + publish.conf.ext;
+
+	if (!this.text) this.text = FilePath.fileName(srcFilePath);
+	return "<a href=\""+outFilePath+"\""+target+">"+this.text+"</a>";
+}
+
+/** Create a link to a source file. */
+Link.prototype._makeFileLink = function(filePath) {
+	var target = (this.targetName)? " target=\""+this.targetName+"\"" : "";
+		
+	var outFilePath =  Link.base + filePath;
+
+	if (!this.text) this.text = filePath;
+	return "<a href=\""+outFilePath+"\""+target+">"+this.text+"</a>";
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/frame/Namespace.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/frame/Namespace.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/frame/Namespace.js	(revision 2890)
@@ -0,0 +1,10 @@
+_global_ = this;
+
+function Namespace(name, f) {
+	var n = name.split(".");
+	for (var o = _global_, i = 0, l = n.length; i < l; i++) {
+		o = o[n[i]] = o[n[i]] || {};
+	}
+	
+	if (f) f();
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/frame/Opt.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/frame/Opt.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/frame/Opt.js	(revision 2890)
@@ -0,0 +1,134 @@
+/** @namespace */
+Opt = {
+	/**
+	 * Get commandline option values.
+	 * @param {Array} args Commandline arguments. Like ["-a=xml", "-b", "--class=new", "--debug"]
+	 * @param {object} optNames Map short names to long names. Like {a:"accept", b:"backtrace", c:"class", d:"debug"}.
+	 * @return {object} Short names and values. Like {a:"xml", b:true, c:"new", d:true}
+	 */
+	get: function(args, optNames) {
+		var opt = {"_": []}; // the unnamed option allows multiple values
+		for (var i = 0; i < args.length; i++) {
+			var arg = new String(args[i]);
+			var name;
+			var value;
+			if (arg.charAt(0) == "-") {
+				if (arg.charAt(1) == "-") { // it's a longname like --foo
+					arg = arg.substring(2);
+					var m = arg.split("=");
+					name = m.shift();
+					value = m.shift();
+					if (typeof value == "undefined") value = true;
+					
+					for (var n in optNames) { // convert it to a shortname
+						if (name == optNames[n]) {
+							name = n;
+						}
+					}
+				}
+				else { // it's a shortname like -f
+					arg = arg.substring(1);
+					var m = arg.split("=");
+					name = m.shift();
+					value = m.shift();
+					if (typeof value == "undefined") value = true;
+					
+					for (var n in optNames) { // find the matching key
+						if (name == n || name+'[]' == n) {
+							name = n;
+							break;
+						}
+					}
+				}
+				if (name.match(/(.+)\[\]$/)) { // it's an array type like n[]
+					name = RegExp.$1;
+					if (!opt[name]) opt[name] = [];
+				}
+				
+				if (opt[name] && opt[name].push) {
+					opt[name].push(value);
+				}
+				else {
+					opt[name] = value;
+				}
+			}
+			else { // not associated with any optname
+				opt._.push(args[i]);
+			}
+		}
+		return opt;
+	}
+}
+
+/*t:
+	plan(11, "Testing Opt.");
+	
+	is(
+		typeof Opt,
+		"object",
+		"Opt is an object."
+	);
+	
+	is(
+		typeof Opt.get,
+		"function",
+		"Opt.get is a function."
+	);
+	
+	var optNames = {a:"accept", b:"backtrace", c:"class", d:"debug", "e[]":"exceptions"};
+	var t_options = Opt.get(["-a=xml", "-b", "--class=new", "--debug", "-e=one", "-e=two", "foo", "bar"], optNames);
+	
+	is(
+		t_options.a,
+		"xml",
+		"an option defined with a short name can be accessed by its short name."
+	);
+	
+	is(
+		t_options.b,
+		true,
+		"an option defined with a short name and no value are true."
+	);
+	
+	is(
+		t_options.c,
+		"new",
+		"an option defined with a long name can be accessed by its short name."
+	);
+	
+	is(
+		t_options.d,
+		true,
+		"an option defined with a long name and no value are true."
+	);
+	
+	is(
+		typeof t_options.e,
+		"object",
+		"an option that can accept multiple values is defined."
+	);
+	
+	is(
+		t_options.e.length,
+		2,
+		"an option that can accept multiple values can have more than one value."
+	);
+	
+	is(
+		t_options.e[1],
+		"two",
+		"an option that can accept multiple values can be accessed as an array."
+	);
+	
+	is(
+		typeof t_options._,
+		"object",
+		"the property '_' is defined for unnamed options."
+	);
+	
+	is(
+		t_options._[0],
+		"foo",
+		"the property '_' can be accessed as an array."
+	);
+ */
Index: /CKReleaser/trunk/tools/jsdoc/app/frame/Reflection.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/frame/Reflection.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/frame/Reflection.js	(revision 2890)
@@ -0,0 +1,26 @@
+/**@constructor*/
+function Reflection(obj) {
+	this.obj = obj;
+}
+
+Reflection.prototype.getConstructorName = function() {
+	if (this.obj.constructor.name) return this.obj.constructor.name;
+	var src = this.obj.constructor.toSource();
+	var name = src.substring(name.indexOf("function")+8, src.indexOf('(')).replace(/ /g,'');
+	return name;
+}
+
+Reflection.prototype.getMethod = function(name) {
+	for (var p in this.obj) {
+		if (p == name && typeof(this.obj[p]) == "function") return this.obj[p];
+	}
+	return null;
+}
+
+Reflection.prototype.getParameterNames = function() {
+	var src = this.obj.toSource();
+	src = src.substring(
+		src.indexOf("(", 8)+1, src.indexOf(")")
+	);
+	return src.split(/, ?/);
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/frame/String.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/frame/String.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/frame/String.js	(revision 2890)
@@ -0,0 +1,93 @@
+/**
+	@name String
+	@class Additions to the core string object.
+*/
+
+/** @author Steven Levithan, released as public domain. */
+String.prototype.trim = function() {
+	var str = this.replace(/^\s+/, '');
+	for (var i = str.length - 1; i >= 0; i--) {
+		if (/\S/.test(str.charAt(i))) {
+			str = str.substring(0, i + 1);
+			break;
+		}
+	}
+	return str;
+}
+/*t:
+	plan(6, "Testing String.prototype.trim.");
+	
+	var s = "   a bc   ".trim();
+	is(s, "a bc", "multiple spaces front and back are trimmed.");
+
+	s = "a bc\n\n".trim();
+	is(s, "a bc", "newlines only in back are trimmed.");
+	
+	s = "\ta bc".trim();
+	is(s, "a bc", "tabs only in front are trimmed.");
+	
+	s = "\n \t".trim();
+	is(s, "", "an all-space string is trimmed to empty.");
+	
+	s = "a b\nc".trim();
+	is(s, "a b\nc", "a string with no spaces in front or back is trimmed to itself.");
+	
+	s = "".trim();
+	is(s, "", "an empty string is trimmed to empty.");
+
+*/
+
+String.prototype.balance = function(open, close) {
+	var i = 0;
+	while (this.charAt(i) != open) {
+		if (i == this.length) return [-1, -1];
+		i++;
+	}
+	
+	var j = i+1;
+	var balance = 1;
+	while (j < this.length) {
+		if (this.charAt(j) == open) balance++;
+		if (this.charAt(j) == close) balance--;
+		if (balance == 0) break;
+		j++;
+		if (j == this.length) return [-1, -1];
+	}
+	
+	return [i, j];
+}
+/*t:
+	plan(16, "Testing String.prototype.balance.");
+	
+	var s = "{abc}".balance("{","}");
+	is(s[0], 0, "opener in first is found.");
+	is(s[1], 4, "closer in last is found.");
+	
+	s = "ab{c}de".balance("{","}");
+	is(s[0], 2, "opener in middle is found.");
+	is(s[1], 4, "closer in middle is found.");
+	
+	s = "a{b{c}de}f".balance("{","}");
+	is(s[0], 1, "nested opener is found.");
+	is(s[1], 8, "nested closer is found.");
+	
+	s = "{}".balance("{","}");
+	is(s[0], 0, "opener with no content is found.");
+	is(s[1], 1, "closer with no content is found.");
+	
+	s = "".balance("{","}");
+	is(s[0], -1, "empty string opener is -1.");
+	is(s[1], -1, "empty string closer is -1.");
+	
+	s = "{abc".balance("{","}");
+	is(s[0], -1, "opener with no closer returns -1.");
+	is(s[1], -1, "no closer returns -1.");
+	
+	s = "abc".balance("{","}");
+	is(s[0], -1, "no opener or closer returns -1 for opener.");
+	is(s[1], -1, "no opener or closer returns -1 for closer.");
+	
+	s = "a<bc}de".balance("<","}");
+	is(s[0], 1, "unmatching opener is found.");
+	is(s[1], 4, "unmatching closer is found.");
+*/
Index: /CKReleaser/trunk/tools/jsdoc/app/frame/Testrun.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/frame/Testrun.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/frame/Testrun.js	(revision 2890)
@@ -0,0 +1,129 @@
+/**
+ * @fileOverview
+ * @name JsTestrun
+ * @author Michael Mathews micmath@gmail.com
+ * @url $HeadURL: https://jsdoc-toolkit.googlecode.com/svn/tags/jsdoc_toolkit-2.0.2/jsdoc-toolkit/app/frame/Testrun.js $
+ * @revision $Id: Testrun.js 418 2008-01-15 21:40:33Z micmath $
+ * @license <a href="http://en.wikipedia.org/wiki/MIT_License">X11/MIT License</a>
+ *          (See the accompanying README file for full details.)
+ */
+ 
+/**
+	Yet another unit testing tool for JavaScript.
+	@author Michael Mathews <a href="mailto:micmath@gmail.com">micmath@gmail.com</a>
+	@param {object} testCases Properties are testcase names, values are functions to execute as tests.
+*/
+function testrun(testCases) {
+	var ran = 0;
+	for (t in testCases) {
+		var result = testCases[t]();
+		ran++;
+	}
+	
+	return testrun.reportOut+"-------------------------------\n"+((testrun.fails>0)? ":( Failed "+testrun.fails+"/" : ":) Passed all ")+testrun.count+" test"+((testrun.count == 1)? "":"s")+".\n";
+}
+
+
+testrun.count = 0;
+testrun.current = null;
+testrun.passes = 0;
+testrun.fails = 0;
+testrun.reportOut = "";
+
+/** @private */
+testrun.report = function(text) {
+	testrun.reportOut += text+"\n";
+}
+
+/**
+	Check if test evaluates to true.
+	@param {string} test To be evaluated.
+	@param {string} message Optional. To be displayed in the report.
+	@return {boolean} True if the string test evaluates to true.
+*/
+ok = function(test, message) {
+	testrun.count++;
+	
+	var result;
+	try {
+		result = eval(test);
+		
+		if (result) {
+			testrun.passes++;
+			testrun.report("    OK "+testrun.count+" - "+((message != null)? message : ""));
+		}
+		else {
+			testrun.fails++;
+			testrun.report("NOT OK "+testrun.count+" - "+((message != null)? message : ""));
+		}
+	}
+	catch(e) {
+		testrun.fails++
+		testrun.report("NOT OK "+testrun.count+" - "+((message != null)? message : ""));
+
+	}
+}
+
+/**
+	Check if test is same as expected.
+	@param {string} test To be evaluated.
+	@param {string} expected
+	@param {string} message Optional. To be displayed in the report. 
+	@return {boolean} True if (test == expected). Note that the comparison is not a strict equality check.
+*/
+is = function(test, expected, message) {
+	testrun.count++;
+	
+	var result;
+	try {
+		result = eval(test);
+		
+		if (result == expected) {
+			testrun.passes++
+			testrun.report("    OK "+testrun.count+" - "+((message != null)? message : ""));
+		}
+		else {
+			testrun.fails++
+			testrun.report("NOT OK "+testrun.count+" - "+((message != null)? message : ""));
+			testrun.report("expected: "+expected);
+			testrun.report("     got: "+result);
+		}
+	}
+	catch(e) {
+		testrun.fails++
+		testrun.report("NOT OK "+testrun.count+" - "+((message != null)? message : ""));
+		testrun.report("expected: "+expected);
+		testrun.report("     got: "+result);}
+}
+
+/**
+	Check if test matches pattern.
+	@param {string} test To be evaluated.
+	@param {string} pattern Used to create a RegExp.
+	@param {string} message Optional. To be displayed in the report.
+	@return {boolean} True if test matches pattern.
+*/
+like = function(test, pattern, message) {
+	testrun.count++;
+
+	var result;
+	try {
+		result = eval(test);
+		var rgx = new RegExp(pattern);
+		
+		if (rgx.test(result)) {
+			testrun.passes++
+			testrun.report("    OK "+testrun.count+" - "+((message != null)? message : ""));
+		}
+		else {
+			testrun.fails++
+			testrun.report("NOT OK "+testrun.count+" - "+((message != null)? message : ""));
+			testrun.report("       this: "+result);
+			testrun.report("is not like: "+pattern);
+		}
+	}
+	catch(e) {
+		testrun.fails++
+		testrun.report("NOT OK "+testrun.count+" - "+((message != null)? message : ""));
+	}
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/handlers/FOODOC.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/handlers/FOODOC.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/handlers/FOODOC.js	(revision 2890)
@@ -0,0 +1,26 @@
+/**
+   This is the main container for the FOODOC handler.
+   @namespace
+*/
+FOODOC = {
+};
+
+/** The current version string of this application. */
+FOODOC.VERSION = "1.0";
+
+FOODOC.handle = function(srcFile, src) {
+	LOG.inform("Handling file '" + srcFile + "'");
+	
+	return [
+		new JSDOC.Symbol(
+			"foo",
+			[],
+			"VIRTUAL",
+			new JSDOC.DocComment("/** This is a foo. */")
+		)
+	];
+};
+
+FOODOC.publish = function(symbolgroup) {
+	LOG.inform("Publishing symbolgroup.");
+};
Index: /CKReleaser/trunk/tools/jsdoc/app/handlers/XMLDOC.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/handlers/XMLDOC.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/handlers/XMLDOC.js	(revision 2890)
@@ -0,0 +1,26 @@
+/**
+ * This is the main container for the XMLDOC handler.
+ * @namespace
+ * @author Brett Fattori (bfattori@fry.com)
+ * @version $Revision: 498 $
+ */
+XMLDOC = {
+	
+};
+
+/** The current version string of this application. */
+XMLDOC.VERSION = "1.0";
+
+/** Include the library necessary to handle XML files */
+IO.includeDir("handlers/XMLDOC/");
+
+/**
+ * @type Symbol[]
+ */
+XMLDOC.handle = function(srcFile, src) {
+	
+};
+
+XMLDOC.publish = function(symbolgroup) {
+	
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/handlers/XMLDOC/DomReader.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/handlers/XMLDOC/DomReader.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/handlers/XMLDOC/DomReader.js	(revision 2890)
@@ -0,0 +1,159 @@
+LOG.inform("XMLDOC.DomReader loaded");
+
+XMLDOC.DomReader = function(root) {
+
+   this.dom = root;
+
+   /**
+    * The current node the reader is on
+    */
+   this.node = root;
+
+   /**
+    * Get the current node the reader is on
+    * @type XMLDOC.Parser.node
+    */
+   XMLDOC.DomReader.prototype.getNode = function() {
+      return this.node;
+   };
+
+   /**
+    * Set the node the reader should be positioned on.
+    * @param node {XMLDOC.Parser.node}
+    */
+   XMLDOC.DomReader.prototype.setNode = function(node) {
+      this.node = node;
+   };
+
+   /**
+    * A helper method to make sure the current node will
+    * never return null, unless null is passed as the root.
+    * @param step {String} An expression to evaluate - should return a node or null
+    */
+   XMLDOC.DomReader.prototype.navigate = function(step) {
+      var n;
+      if ((n = step) != null)
+      {
+         this.node = n;
+         return this.node;
+      }
+      return null;
+   };
+
+   /**
+    * Get the root node of the current node's document.
+    */
+   XMLDOC.DomReader.prototype.root = function() {
+      this.navigate(this.dom);
+   };
+
+   /**
+    * Get the parent of the current node.
+    */
+   XMLDOC.DomReader.prototype.parent = function() {
+      return this.navigate(this.node.parentNode());
+   };
+
+   /**
+    * Get the first child of the current node.
+    */
+   XMLDOC.DomReader.prototype.firstChild = function() {
+      return this.navigate(this.node.firstChild());
+   };
+
+   /**
+    * Get the last child of the current node.
+    */
+   XMLDOC.DomReader.prototype.lastChild = function() {
+      return this.navigate(this.node.lastChild());
+   };
+
+   /**
+    * Get the next sibling of the current node.
+    */
+   XMLDOC.DomReader.prototype.nextSibling = function() {
+      return this.navigate(this.node.nextSibling());
+   };
+
+   /**
+    * Get the previous sibling of the current node.
+    */
+   XMLDOC.DomReader.prototype.prevSibling = function() {
+      return this.navigate(this.node.prevSibling());
+   };
+
+   //===============================================================================================
+   // Support methods
+
+   /**
+    * Walk the tree starting with the current node, calling the plug-in for
+    * each node visited.  Each time the plug-in is called, the DomReader
+    * is passed as the only parameter.  Use the {@link XMLDOC.DomReader#getNode} method
+    * to access the current node.   <i>This method uses a depth first traversal pattern.</i>
+    *
+    * @param srcFile {String} The source file being evaluated
+    */
+   XMLDOC.DomReader.prototype.getSymbols = function(srcFile)
+   {
+      XMLDOC.DomReader.symbols = [];
+      XMLDOC.DomReader.currentFile = srcFile;
+      JSDOC.Symbol.srcFile = (srcFile || "");
+
+      if (defined(JSDOC.PluginManager)) {
+         JSDOC.PluginManager.run("onDomGetSymbols", this);
+      }
+
+      return XMLDOC.DomReader.symbols;
+   };
+
+   /**
+    * Find the node with the given name using a depth first traversal.
+    * Does not modify the DomReader's current node.
+    *
+    * @param name {String} The name of the node to find
+    * @return the node that was found, or null if not found
+    */
+   XMLDOC.DomReader.prototype.findNode = function(name)
+   {
+      var findNode = null;
+
+      // Start at the current node and move into the subtree,
+      // looking for the node with the given name
+      function deeper(node, find)
+      {
+         var look = null;
+
+         if (node) {
+            if (node.name == find)
+            {
+               return node;
+            }
+
+            if (node.firstChild())
+            {
+               look = deeper(node.firstChild(), find);
+            }
+
+            if (!look && node.nextSibling())
+            {
+               look = deeper(node.nextSibling(), find);
+            }
+         }
+
+         return look;
+      }
+
+      return deeper(this.getNode().firstChild(), name);
+   };
+
+   /**
+    * Find the next node with the given name using a depth first traversal.
+    *
+    * @param name {String} The name of the node to find
+    */
+   XMLDOC.DomReader.prototype.findPreviousNode = function(name)
+   {
+   };
+
+};
+
Index: /CKReleaser/trunk/tools/jsdoc/app/handlers/XMLDOC/XMLDoc.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/handlers/XMLDOC/XMLDoc.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/handlers/XMLDOC/XMLDoc.js	(revision 2890)
@@ -0,0 +1,16 @@
+LOG.inform("XMLDOC.symbolize loaded");
+
+/**
+ * Convert the source file to a set of symbols
+ */
+XMLDOC.symbolize = function(srcFile, src) {
+
+   LOG.inform("Symbolizing file '" + srcFile + "'");
+
+   // XML files already have a defined structure, so we don't need to
+   // do anything but parse them.  The DOM reader can create a symbol
+   // table from the parsed XML.
+   var dr = new XMLDOC.DomReader(XMLDOC.Parser.parse(src));
+   return dr.getSymbols(srcFile);
+
+};
Index: /CKReleaser/trunk/tools/jsdoc/app/handlers/XMLDOC/XMLParse.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/handlers/XMLDOC/XMLParse.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/handlers/XMLDOC/XMLParse.js	(revision 2890)
@@ -0,0 +1,292 @@
+LOG.inform("XMLDOC.Parser loaded");
+
+/**
+ * XML Parser object.  Returns an {@link #XMLDOC.Parser.node} which is
+ * the root element of the parsed document.
+ * <p/>
+ * By default, this parser will only handle well formed XML.  To
+ * allow the parser to handle HTML, set the <tt>XMLDOC.Parser.strictMode</tt>
+ * variable to <tt>false</tt> before calling <tt>XMLDOC.Parser.parse()</tt>.
+ * <p/>
+ * <i>Note: If you pass poorly formed XML, it will cause the parser to throw
+ * an exception.</i>
+ *
+ * @author Brett Fattori (bfattori@fry.com)
+ * @author $Author: micmath $
+ * @version $Revision: 497 $
+ */
+XMLDOC.Parser = {};
+
+/**
+ * Strict mode setting.  Setting this to false allows HTML-style source to
+ * be parsed.  Normally, well formed XML has defined end tags, or empty tags
+ * are properly formed.  Default: <tt>true</tt>
+ * @type Boolean
+ */
+XMLDOC.Parser.strictMode = true;
+
+/**
+ * A node in an XML Document.  Node types are ROOT, ELEMENT, COMMENT, PI, and TEXT.
+ * @param parent {XMLDOC.Parser.node} The parent node
+ * @param name {String} The node name
+ * @param type {String} One of the types
+ */
+XMLDOC.Parser.node = function(parent, name, type)
+{
+   this.name = name;
+   this.type = type || "ELEMENT";
+   this.parent = parent;
+   this.charData = "";
+   this.attrs = {};
+   this.nodes = [];
+   this.cPtr = 0;
+
+   XMLDOC.Parser.node.prototype.getAttributeNames = function() {
+      var a = [];
+      for (var o in this.attrs)
+      {
+         a.push(o);
+      }
+
+      return a;
+   };
+
+   XMLDOC.Parser.node.prototype.getAttribute = function(attr) {
+      return this.attrs[attr];
+   };
+
+   XMLDOC.Parser.node.prototype.setAttribute = function(attr, val) {
+      this.attrs[attr] = val;
+   };
+
+   XMLDOC.Parser.node.prototype.getChild = function(idx) {
+      return this.nodes[idx];
+   };
+
+   XMLDOC.Parser.node.prototype.parentNode = function() {
+      return this.parent;
+   };
+
+   XMLDOC.Parser.node.prototype.firstChild = function() {
+      return this.nodes[0];
+   };
+
+   XMLDOC.Parser.node.prototype.lastChild = function() {
+      return this.nodes[this.nodes.length - 1];
+   };
+
+   XMLDOC.Parser.node.prototype.nextSibling = function() {
+      var p = this.parent;
+      if (p && (p.nodes.indexOf(this) + 1 != p.nodes.length))
+      {
+         return p.getChild(p.nodes.indexOf(this) + 1);
+      }
+      return null;
+   };
+
+   XMLDOC.Parser.node.prototype.prevSibling = function() {
+      var p = this.parent;
+      if (p && (p.nodes.indexOf(this) - 1 >= 0))
+      {
+         return p.getChild(p.nodes.indexOf(this) - 1);
+      }
+      return null;
+   };
+};
+
+/**
+ * Parse an XML Document from the specified source.  The XML should be
+ * well formed, unless strict mode is disabled, then the parser will
+ * handle HTML-style XML documents.
+ * @param src {String} The source to parse
+ */
+XMLDOC.Parser.parse = function(src)
+{
+   var A = [];
+
+   // Normailize whitespace
+   A = src.split("\r\n");
+   src = A.join("\n");
+   A = src.split("\r");
+   src = A.join("\n");
+
+   // Remove XML and DOCTYPE specifier
+   src.replace(/<\?XML .*\?>/i, "");
+   src.replace(/<!DOCTYPE .*\>/i, "");
+
+   // The document is the root node and cannot be modified or removed
+   var doc = new XMLDOC.Parser.node(null, "ROOT", "DOCUMENT");
+
+   // Let's break it down
+   XMLDOC.Parser.eat(doc, src);
+
+   return doc;
+};
+
+/**
+ * The XML fragment processing routine.  This method is private and should not be called
+ * directly.
+ * @param parentNode {XMLDOC.Parser.node} The node which is the parent of this fragment
+ * @param src {String} The source within the fragment to process
+ * @private
+ */
+XMLDOC.Parser.eat = function(parentNode, src)
+{
+   // A simple tag def
+   var reTag = new RegExp("<(!|)(\\?|--|)((.|\\s)*?)\\2>","g");
+
+   // Special tag types
+   var reCommentTag = /<!--((.|\s)*?)-->/;
+   var rePITag = /<\?((.|\s)*?)\?>/;
+
+   // A start tag (with potential empty marker)
+   var reStartTag = /<(.*?)( +([\w_\-]*)=(\"|')(.*)\4)*(\/)?>/;
+
+   // An empty HTML style tag (not proper XML, but we'll accept it so we can process HTML)
+   var reHTMLEmptyTag = /<(.*?)( +([\w_\-]*)=(\"|')(.*)\4)*>/;
+
+   // Fully enclosing tag with nested tags
+   var reEnclosingTag = /<(.*?)( +([\w_\-]*)=(\"|')(.*?)\4)*>((.|\s)*?)<\/\1>/;
+
+   // Breaks down attributes
+   var reAttributes = new RegExp(" +([\\w_\\-]*)=(\"|')(.*?)\\2","g");
+
+   // Find us a tag
+   var tag;
+   while ((tag = reTag.exec(src)) != null)
+   {
+      if (tag.index > 0)
+      {
+         // The next tag has some text before it
+         var text = src.substring(0, tag.index).replace(/^[ \t\n]+((.|\n)*?)[ \t\n]+$/, "$1");
+
+         if (text.length > 0 && (text != "\n"))
+         {
+            var txtnode = new XMLDOC.Parser.node(parentNode, "", "TEXT");
+            txtnode.charData = text;
+
+            // Append the new text node
+            parentNode.nodes.push(txtnode);
+         }
+
+         // Reset the lastIndex of reTag
+         reTag.lastIndex -= src.substring(0, tag.index).length;
+
+         // Eat the text
+         src = src.substring(tag.index);
+      }
+
+      if (reCommentTag.test(tag[0]))
+      {
+         // Is this a comment?
+         var comment = new XMLDOC.Parser.node(parentNode, "", "COMMENT");
+         comment.charData = reCommentTag.exec(tag[0])[1];
+
+         // Append the comment
+         parentNode.nodes.push(comment);
+
+         // Move the lastIndex of reTag
+         reTag.lastIndex -= tag[0].length;
+
+         // Eat the tag
+         src = src.replace(reCommentTag, "");
+      }
+      else if (rePITag.test(tag[0]))
+      {
+         // Is this a processing instruction?
+         var pi = new XMLDOC.Parser.node(parentNode, "", "PI");
+         pi.charData = rePITag.exec(tag[0])[1];
+
+         // Append the processing instruction
+         parentNode.nodes.push(pi);
+
+         // Move the lastIndex of reTag
+         reTag.lastIndex -= tag[0].length;
+
+         // Eat the tag
+         src = src.replace(rePITag, "");
+      }
+      else if (reStartTag.test(tag[0]))
+      {
+         // Break it down
+         var e = reStartTag.exec(tag[0]);
+         var elem = new XMLDOC.Parser.node(parentNode, e[1], "ELEMENT");
+
+         // Get attributes from the tag
+         var a;
+         while ((a = reAttributes.exec(e[2])) != null )
+         {
+            elem.attrs[a[1]] = a[3];
+         }
+
+         // Is this an empty XML-style tag?
+         if (e[6] == "/")
+         {
+            // Append the empty element
+            parentNode.nodes.push(elem);
+
+            // Move the lastIndex of reTag (include the start tag length)
+            reTag.lastIndex -= e[0].length;
+
+            // Eat the tag
+            src = src.replace(reStartTag, "");
+         }
+         else
+         {
+            // Check for malformed XML tags
+            var htmlParsed = false;
+            var htmlStartTag = reHTMLEmptyTag.exec(src);
+
+            // See if there isn't an end tag within this block
+            var reHTMLEndTag = new RegExp("</" + htmlStartTag[1] + ">");
+            var htmlEndTag = reHTMLEndTag.exec(src);
+
+            if (XMLDOC.Parser.strictMode && htmlEndTag == null)
+            {
+               // Poorly formed XML fails in strict mode
+               var err = new Error("Malformed XML passed to XMLDOC.Parser... Error contains malformed 'src'");
+               err.src = src;
+               throw err;
+            }
+            else if (htmlEndTag == null)
+            {
+               // This is an HTML-style empty tag, store the element for it in non-strict mode
+               parentNode.nodes.push(elem);
+
+               // Eat the tag
+               src = src.replace(reHTMLEmptyTag, "");
+               htmlParsed = true;
+            }
+
+            // If we didn't parse HTML-style, it must be an enclosing tag
+            if (!htmlParsed)
+            {
+               var enc = reEnclosingTag.exec(src);
+
+               // Go deeper into the document
+               XMLDOC.Parser.eat(elem, enc[6]);
+
+               // Append the new element node
+               parentNode.nodes.push(elem);
+
+               // Eat the tag
+               src = src.replace(reEnclosingTag, "");
+            }
+         }
+
+         // Reset the lastIndex of reTag
+         reTag.lastIndex = 0;
+      }
+   }
+
+   // No tag was found... append the text if there is any
+   src = src.replace(/^[ \t\n]+((.|\n)*?)[ \t\n]+$/, "$1");
+   if (src.length > 0 && (src != "\n"))
+   {
+      var txtNode = new XMLDOC.Parser.node(parentNode, "", "TEXT");
+      txtNode.charData = src;
+
+      // Append the new text node
+      parentNode.nodes.push(txtNode);
+   }
+};
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC.js	(revision 2890)
@@ -0,0 +1,98 @@
+/**
+	@overview
+	@date $Date: 2008-08-02 09:25:54 +0100 (Sat, 02 Aug 2008) $
+	@version $Revision: 668 $ 
+	@location $HeadURL: https://jsdoc-toolkit.googlecode.com/svn/tags/jsdoc_toolkit-2.0.2/jsdoc-toolkit/app/lib/JSDOC.js $
+	@name whateverFilename.js
+ */
+
+/**
+	This is the main container for the JSDOC application.
+	@namespace
+*/
+JSDOC = {
+};
+
+/**
+	@requires Opt
+ */
+if (typeof arguments == "undefined") arguments = [];
+JSDOC.opt = Opt.get(
+	arguments, 
+	{
+		d: "directory",
+		c: "conf",
+		t: "template",
+		r: "recurse",
+		x: "ext",
+		p: "private",
+		a: "allfunctions", 
+		e: "encoding",
+		n: "nocode",
+		o: "out",
+		s: "suppress",
+		T: "testmode",
+		h: "help",
+		v: "verbose",
+		"D[]": "define",
+		"H[]": "handler"
+	}
+);
+
+/** The current version string of this application. */
+JSDOC.VERSION = "2.0.2";
+
+/** Print out usage information and quit. */
+JSDOC.usage = function() {
+	print("USAGE: java -jar jsrun.jar app/run.js [OPTIONS] <SRC_DIR> <SRC_FILE> ...");
+	print("");
+	print("OPTIONS:");
+	print("  -a or --allfunctions\n          Include all functions, even undocumented ones.\n");
+	print("  -c or --conf\n          Load a configuration file.\n");
+	print("  -d=<PATH> or --directory=<PATH>\n          Output to this directory (defaults to \"out\").\n");
+	print("  -D=\"myVar:My value\" or --define=\"myVar:My value\"\n          Multiple. Define a variable, available in JsDoc as JSDOC.opt.D.myVar\n");
+	print("  -e=<ENCODING> or --encoding=<ENCODING>\n          Use this encoding to read and write files.\n");
+	print("  -h or --help\n          Show this message and exit.\n");
+	//print("  -H=ext:handler or --handler=ext:handler\n          Multiple. Load handlers/handler.js to handle files with .ext names.\n");
+	print("  -n or --nocode\n          Ignore all code, only document comments with @name tags.\n");
+	print("  -o=<PATH> or --out=<PATH>\n          Print log messages to a file (defaults to stdout).\n");
+	print("  -p or --private\n          Include symbols tagged as private, underscored and inner symbols.\n");
+	print("  -r=<DEPTH> or --recurse=<DEPTH>\n          Descend into src directories.\n");
+	print("  -s or --suppress\n          Suppress source code output.\n");
+	print("  -t=<PATH> or --template=<PATH>\n          Required. Use this template to format the output.\n");
+	print("  -T or --test\n          Run all unit tests and exit.\n");
+	print("  -v or --verbose\n          Provide verbose feedback about what is happening.\n");
+	print("  -x=<EXT>[,EXT]... or --ext=<EXT>[,EXT]...\n          Scan source files with the given extension/s (defaults to js).\n");
+	
+	quit();
+}
+
+/*t:
+	plan(4, "Testing JSDOC namespace.");
+	
+	is(
+		typeof JSDOC,
+		"object",
+		"JSDOC.usage is a function."
+	);
+	
+	is(
+		typeof JSDOC.VERSION,
+		"string",
+		"JSDOC.VERSION is a string."
+	);
+	
+	is(
+		typeof JSDOC.usage,
+		"function",
+		"JSDOC.usage is a function."
+	);
+	
+	is(
+		typeof JSDOC.opt,
+		"object",
+		"JSDOC.opt is a object."
+	);
+ */
+
+if (this.IO) IO.includeDir("lib/JSDOC/");
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/DocComment.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/DocComment.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/DocComment.js	(revision 2890)
@@ -0,0 +1,200 @@
+if (typeof JSDOC == "undefined") JSDOC = {};
+
+/**
+	Create a new DocComment. This takes a raw documentation comment,
+	and wraps it in useful accessors.
+	@class Represents a documentation comment object.
+ */ 
+JSDOC.DocComment = function(/**String*/comment) {
+	this.init();
+	if (typeof comment != "undefined") {
+		this.parse(comment);
+	}
+}
+
+JSDOC.DocComment.prototype.init = function() {
+	this.isUserComment = true;
+	this.src           = "";
+	this.meta          = "";
+	this.tagTexts      = [];
+	this.tags          = [];
+}
+
+/**
+	@requires JSDOC.DocTag
+ */
+JSDOC.DocComment.prototype.parse = function(/**String*/comment) {
+	if (comment == "") {
+		comment = "/** @desc */";
+		this.isUserComment = false;
+	}
+	
+	this.src = JSDOC.DocComment.unwrapComment(comment);
+	
+	this.meta = "";
+	if (this.src.indexOf("#") == 0) {
+		this.src.match(/#(.+[+-])([\s\S]*)$/);
+		if (RegExp.$1) this.meta = RegExp.$1;
+		if (RegExp.$2) this.src = RegExp.$2;
+	}
+	
+	this.fixDesc();
+	
+	if (typeof JSDOC.PluginManager != "undefined") {
+		JSDOC.PluginManager.run("onDocCommentSrc", this);
+	}
+	
+	this.src = JSDOC.DocComment.shared+"\n"+this.src;
+	
+	this.tagTexts = 
+		this.src
+		.split(/(^|[\r\n])\s*@/)
+		.filter(function($){return $.match(/\S/)});
+	
+	/**
+		The tags found in the comment.
+		@type JSDOC.DocTag[]
+	 */
+	this.tags = this.tagTexts.map(function($){return new JSDOC.DocTag($)});
+	
+	if (typeof JSDOC.PluginManager != "undefined") {
+		JSDOC.PluginManager.run("onDocCommentTags", this);
+	}
+}
+
+/*t:
+	plan(5, "testing JSDOC.DocComment");
+	requires("../frame/String.js");
+	requires("../lib/JSDOC/DocTag.js");
+	
+	var com = new JSDOC.DocComment("/**@foo some\n* comment here*"+"/");
+	is(com.tagTexts[0], "foo some\ncomment here", "first tag text is found.");
+	is(com.tags[0].title, "foo", "the title is found in a comment with one tag.");
+	
+	var com = new JSDOC.DocComment("/** @foo first\n* @bar second*"+"/");
+	is(com.getTag("bar").length, 1, "getTag() returns one tag by that title.");
+	
+	JSDOC.DocComment.shared = "@author John Smith";
+	var com = new JSDOC.DocComment("/**@foo some\n* comment here*"+"/");
+	is(com.tags[0].title, "author", "shared comment is added.");
+	is(com.tags[1].title, "foo", "shared comment is added to existing tag.");
+*/
+
+/**
+	If no @desc tag is provided, this function will add it.
+ */
+JSDOC.DocComment.prototype.fixDesc = function() {
+	if (this.meta && this.meta != "@+") return;
+	if (/^\s*[^@\s]/.test(this.src)) {				
+		this.src = "@desc "+this.src;
+	}
+}
+
+/*t:
+	plan(5, "testing JSDOC.DocComment#fixDesc");
+	
+	var com = new JSDOC.DocComment();
+	
+	com.src = "this is a desc\n@author foo";
+	com.fixDesc();
+	is(com.src, "@desc this is a desc\n@author foo", "if no @desc tag is provided one is added.");
+
+	com.src = "x";
+	com.fixDesc();
+	is(com.src, "@desc x", "if no @desc tag is provided one is added to a single character.");
+
+	com.src = "\nx";
+	com.fixDesc();
+	is(com.src, "@desc \nx", "if no @desc tag is provided one is added to return and character.");
+	
+	com.src = " ";
+	com.fixDesc();
+	is(com.src, " ", "if no @desc tag is provided one is not added to just whitespace.");
+
+	com.src = "";
+	com.fixDesc();
+	is(com.src, "", "if no @desc tag is provided one is not added to empty.");
+*/
+
+/**
+	Remove slash-star comment wrapper from a raw comment string.
+	@type String
+ */
+JSDOC.DocComment.unwrapComment = function(/**String*/comment) {
+	if (!comment) return "";
+	var unwrapped = comment.replace(/(^\/\*\*|\*\/$)/g, "").replace(/^\s*\* ?/gm, "");
+	return unwrapped;
+}
+
+/*t:
+	plan(5, "testing JSDOC.DocComment.unwrapComment");
+	
+	var com = "/**x*"+"/";
+	var unwrapped = JSDOC.DocComment.unwrapComment(com);
+	is(unwrapped, "x", "a single character jsdoc is found.");
+	
+	com = "/***x*"+"/";
+	unwrapped = JSDOC.DocComment.unwrapComment(com);
+	is(unwrapped, "x", "three stars are allowed in the opener.");
+	
+	com = "/****x*"+"/";
+	unwrapped = JSDOC.DocComment.unwrapComment(com);
+	is(unwrapped, "*x", "fourth star in the opener is kept.");
+	
+	com = "/**x\n * y\n*"+"/";
+	unwrapped = JSDOC.DocComment.unwrapComment(com);
+	is(unwrapped, "x\ny\n", "leading stars and spaces are trimmed.");
+	
+	com = "/**x\n *   y\n*"+"/";
+	unwrapped = JSDOC.DocComment.unwrapComment(com);
+	is(unwrapped, "x\n  y\n", "only first space after leading stars are trimmed.");
+*/
+
+/**
+	Provides a printable version of the comment.
+	@type String
+ */
+JSDOC.DocComment.prototype.toString = function() {
+	return this.src;
+}
+
+/*t:
+	plan(1, "testing JSDOC.DocComment#fixDesc");
+	var com = new JSDOC.DocComment();
+	com.src = "foo";
+	is(""+com, "foo", "stringifying a comment returns the unwrapped src.");
+*/
+
+/**
+	Given the title of a tag, returns all tags that have that title.
+	@type JSDOC.DocTag[]
+ */
+JSDOC.DocComment.prototype.getTag = function(/**String*/tagTitle) {
+	return this.tags.filter(function($){return $.title == tagTitle});
+}
+
+/*t:
+	plan(1, "testing JSDOC.DocComment#getTag");
+	requires("../frame/String.js");
+	requires("../lib/JSDOC/DocTag.js");
+	
+	var com = new JSDOC.DocComment("/**@foo some\n* @bar\n* @bar*"+"/");
+	is(com.getTag("bar").length, 2, "getTag returns expected number of tags.");
+*/
+
+/**
+	Used to store the currently shared tag text.
+*/
+JSDOC.DocComment.shared = "";
+
+/*t:
+	plan(2, "testing JSDOC.DocComment.shared");
+	requires("../frame/String.js");
+	requires("../lib/JSDOC/DocTag.js");
+	
+	JSDOC.DocComment.shared = "@author Michael";
+	
+	var com = new JSDOC.DocComment("/**@foo\n* @foo*"+"/");
+	is(com.getTag("author").length, 1, "getTag returns shared tag.");
+	is(com.getTag("foo").length, 2, "getTag returns unshared tags too.");
+*/
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/DocTag.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/DocTag.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/DocTag.js	(revision 2890)
@@ -0,0 +1,294 @@
+if (typeof JSDOC == "undefined") JSDOC = {};
+
+/**
+	@constructor
+ */
+JSDOC.DocTag = function(src) {
+	this.init();
+	if (typeof src != "undefined") {
+		this.parse(src);
+	}
+}
+
+/**
+	Create and initialize the properties of this.
+ */
+JSDOC.DocTag.prototype.init = function() {
+	this.title        = "";
+	this.type         = "";
+	this.name         = "";
+	this.isOptional   = false;
+	this.defaultValue = "";
+	this.desc         = "";
+	
+	return this;
+}
+
+/**
+	Populate the properties of this from the given tag src.
+	@param {string} src
+ */
+JSDOC.DocTag.prototype.parse = function(src) {
+	if (typeof src != "string") throw "src must be a string not "+(typeof src);
+
+	try {
+		src = this.nibbleTitle(src);
+		if (JSDOC.PluginManager) {
+			JSDOC.PluginManager.run("onDocTagSynonym", this);
+		}
+		
+		src = this.nibbleType(src);
+		
+		// only some tags are allowed to have names.
+		if (this.title == "param" || this.title == "property" || this.title == "config") { // @config is deprecated
+			src = this.nibbleName(src);
+		}
+	}
+	catch(e) {
+		if (LOG) LOG.warn(e);
+		else throw e;
+	}
+	this.desc = src; // whatever is left
+	
+	// example tags need to have whitespace preserved
+	if (this.title != "example") this.desc = this.desc.trim();
+	
+	if (JSDOC.PluginManager) {
+		JSDOC.PluginManager.run("onDocTag", this);
+	}
+}
+
+/**
+	Automatically called when this is stringified.
+ */
+JSDOC.DocTag.prototype.toString = function() {
+	return this.desc;
+}
+
+/*t:
+	plan(1, "testing JSDOC.DocTag#toString");
+	
+	var tag = new JSDOC.DocTag("param {object} date A valid date.");
+	is(""+tag, "A valid date.", "stringifying a tag returns the desc.");
+ */
+
+/**
+	Find and shift off the title of a tag.
+	@param {string} src
+	@return src
+ */
+JSDOC.DocTag.prototype.nibbleTitle = function(src) {
+	if (typeof src != "string") throw "src must be a string not "+(typeof src);
+	
+	var parts = src.match(/^\s*(\S+)(?:\s([\s\S]*))?$/);
+
+	if (parts && parts[1]) this.title = parts[1];
+	if (parts && parts[2]) src = parts[2];
+	else src = "";
+	
+	return src;
+}
+
+/*t:
+	plan(8, "testing JSDOC.DocTag#nibbleTitle");
+	
+	var tag = new JSDOC.DocTag();
+	
+	tag.init().nibbleTitle("aTitleGoesHere");
+	is(tag.title, "aTitleGoesHere", "a title can be found in a single-word string.");
+	
+	var src = tag.init().nibbleTitle("aTitleGoesHere and the rest");
+	is(tag.title, "aTitleGoesHere", "a title can be found in a multi-word string.");
+	is(src, "and the rest", "the rest is returned when the title is nibbled off.");
+	
+	src = tag.init().nibbleTitle("");
+	is(tag.title, "", "given an empty string the title is empty.");
+	is(src, "", "the rest is empty when the tag is empty.");
+
+	var src = tag.init().nibbleTitle(" aTitleGoesHere\n  a description");
+	is(tag.title, "aTitleGoesHere", "leading and trailing spaces are not part of the title.");
+	is(src, "  a description", "leading spaces (less one) are part of the description.");
+
+	tag.init().nibbleTitle("a.Title::Goes_Here foo");
+	is(tag.title, "a.Title::Goes_Here", "titles with punctuation are allowed.");
+ */
+
+/**
+	Find and shift off the type of a tag.
+	@requires frame/String.js
+	@param {string} src
+	@return src
+ */
+JSDOC.DocTag.prototype.nibbleType = function(src) {
+	if (typeof src != "string") throw "src must be a string not "+(typeof src);
+	
+	if (src.match(/^\s*\{/)) {
+		var typeRange = src.balance("{", "}");
+		if (typeRange[1] == -1) {
+			throw "Malformed comment tag ignored. Tag type requires an opening { and a closing }: "+src;
+		}
+		this.type = src.substring(typeRange[0]+1, typeRange[1]).trim();
+		this.type = this.type.replace(/\s*,\s*/g, "|"); // multiples can be separated by , or |
+		src = src.substring(typeRange[1]+1);
+	}
+	
+	return src;
+}
+
+/*t:
+	plan(5, "testing JSDOC.DocTag.parser.nibbleType");
+	requires("../frame/String.js");
+	
+	var tag = new JSDOC.DocTag();
+	
+	tag.init().nibbleType("{String[]} aliases");
+	is(tag.type, "String[]", "type can have non-alpha characters.");
+	
+	tag.init().nibbleType("{ aTypeGoesHere  } etc etc");
+	is(tag.type, "aTypeGoesHere", "type is trimmed.");
+	
+	tag.init().nibbleType("{ oneType, twoType ,\n threeType  } etc etc");
+	is(tag.type, "oneType|twoType|threeType", "multiple types can be separated by commas.");
+	
+	var error;
+	try { tag.init().nibbleType("{widget foo"); }
+	catch(e) { error = e; }
+	is(typeof error, "string", "malformed tag type throws error.");
+	isnt(error.indexOf("Malformed"), -1, "error message tells tag is malformed.");
+ */
+
+/**
+	Find and shift off the name of a tag.
+	@requires frame/String.js
+	@param {string} src
+	@return src
+ */
+JSDOC.DocTag.prototype.nibbleName = function(src) {
+	if (typeof src != "string") throw "src must be a string not "+(typeof src);
+	
+	src = src.trim();
+	
+	// is optional?
+	if (src.charAt(0) == "[") {
+		var nameRange = src.balance("[", "]");
+		if (nameRange[1] == -1) {
+			throw "Malformed comment tag ignored. Tag optional name requires an opening [ and a closing ]: "+src;
+		}
+		this.name = src.substring(nameRange[0]+1, nameRange[1]).trim();
+		this.isOptional = true;
+		
+		src = src.substring(nameRange[1]+1);
+		
+		// has default value?
+		var nameAndValue = this.name.split("=");
+		if (nameAndValue.length) {
+			this.name = nameAndValue.shift().trim();
+			this.defaultValue = nameAndValue.join("=");
+		}
+	}
+	else {
+		var parts = src.match(/^(\S+)(?:\s([\s\S]*))?$/);
+		if (parts) {
+			if (parts[1]) this.name = parts[1];
+			if (parts[2]) src = parts[2].trim();
+			else src = "";
+		}
+	}	
+
+	return src;
+}
+
+/*t:
+	requires("../frame/String.js");
+	plan(9, "testing JSDOC.DocTag.parser.nibbleName");
+	
+	var tag = new JSDOC.DocTag();
+	
+	tag.init().nibbleName("[foo] This is a description.");
+	is(tag.isOptional, true, "isOptional syntax is detected.");
+	is(tag.name, "foo", "optional param name is found.");
+ 	
+	tag.init().nibbleName("[foo] This is a description.");
+	is(tag.isOptional, true, "isOptional syntax is detected when no type.");
+	is(tag.name, "foo", "optional param name is found when no type.");
+	
+	tag.init().nibbleName("[foo=7] This is a description.");
+ 	is(tag.name, "foo", "optional param name is found when default value.");
+ 	is(tag.defaultValue, 7, "optional param default value is found when default value.");
+ 	
+ 	//tag.init().nibbleName("[foo= a value] This is a description.");
+ 	//is(tag.defaultValue, " a value", "optional param default value is found when default value has spaces (issue #112).");
+ 	
+ 	tag.init().nibbleName("[foo=[]] This is a description.");
+ 	is(tag.defaultValue, "[]", "optional param default value is found when default value is [] (issue #95).");
+ 	
+ 	tag.init().nibbleName("[foo=a=b] This is a description.");
+ 	is(tag.name, "foo", "optional param name is found when default value is a=b.");
+ 	is(tag.defaultValue, "a=b", "optional param default value is found when default value is a=b.")
+ */
+
+/*t:
+	plan(32, "Testing JSDOC.DocTag.parser.");
+	requires("../frame/String.js");
+	
+ 	var tag = new JSDOC.DocTag();
+ 	
+ 	is(typeof tag, "object", "JSDOC.DocTag.parser with an empty string returns an object.");
+ 	is(typeof tag.title, "string", "returned object has a string property 'title'.");
+ 	is(typeof tag.type, "string", "returned object has a string property 'type'.");
+ 	is(typeof tag.name, "string", "returned object has a string property 'name'.");
+ 	is(typeof tag.defaultValue, "string", "returned object has a string property 'defaultValue'.");
+ 	is(typeof tag.isOptional, "boolean", "returned object has a boolean property 'isOptional'.");
+ 	is(typeof tag.desc, "string", "returned object has a string property 'desc'.");
+  
+  	tag = new JSDOC.DocTag("param {widget} foo");
+  	is(tag.title, "param", "param title is found.");
+  	is(tag.name, "foo", "param name is found when desc is missing.");
+ 	is(tag.desc, "", "param desc is empty when missing.");
+ 	
+ 	tag = new JSDOC.DocTag("param {object} date A valid date.");
+ 	is(tag.name, "date", "param name is found with a type.");
+ 	is(tag.type, "object", "param type is found.");
+ 	is(tag.desc, "A valid date.", "param desc is found with a type.");
+ 	
+  	tag = new JSDOC.DocTag("param aName a description goes\n    here.");
+	is(tag.name, "aName", "param name is found without a type.");
+ 	is(tag.desc, "a description goes\n    here.", "param desc is found without a type.");
+ 	
+ 	tag = new JSDOC.DocTag("param {widget}");
+ 	is(tag.name, "", "param name is empty when it is not given.");
+	
+	tag = new JSDOC.DocTag("param {widget} [foo] This is a description.");
+	is(tag.name, "foo", "optional param name is found.");
+	
+	tag = new JSDOC.DocTag("return {aType} This is a description.");
+	is(tag.type, "aType", "when return tag has no name, type is found.");
+	is(tag.desc, "This is a description.", "when return tag has no name, desc is found.");
+	
+	tag = new JSDOC.DocTag("author Joe Coder <jcoder@example.com>");
+	is(tag.title, "author", "author tag has a title.");
+	is(tag.type, "", "the author tag has no type.");
+	is(tag.name, "", "the author tag has no name.");
+	is(tag.desc, "Joe Coder <jcoder@example.com>", "author tag has desc.");
+	
+	tag = new JSDOC.DocTag("private \t\n  ");
+	is(tag.title, "private", "private tag has a title.");
+	is(tag.type, "", "the private tag has no type.");
+	is(tag.name, "", "the private tag has no name.");
+	is(tag.desc, "", "private tag has no desc.");
+
+	tag = new JSDOC.DocTag("example\n   example(code);\n   more();");
+	is(tag.desc, "   example(code);\n   more();", "leading whitespace (less one) in examples code is preserved.");
+	
+	tag = new JSDOC.DocTag("param theName  \n");
+	is(tag.name, "theName", "name only is found.");
+	
+	tag = new JSDOC.DocTag("type theDesc  \n");
+	is(tag.desc, "theDesc", "desc only is found.");
+	
+	tag = new JSDOC.DocTag("type {theType} \n");
+	is(tag.type, "theType", "type only is found.");
+	
+	tag = new JSDOC.DocTag("");
+	is(tag.title, "", "title is empty when tag is empty.");
+ */
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/JsDoc.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/JsDoc.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/JsDoc.js	(revision 2890)
@@ -0,0 +1,162 @@
+/**
+	@constructor
+	@param [opt] Used to override the commandline options. Useful for testing.
+	@version $Id: JsDoc.js 592 2008-05-09 07:43:33Z micmath $
+*/
+JSDOC.JsDoc = function(/**object*/ opt) {
+	if (opt) {
+		JSDOC.opt = opt;
+	}
+	
+	// the -c option: use a configuration file
+	if (JSDOC.opt.c) {
+		eval("JSDOC.conf = " + IO.readFile(JSDOC.opt.c));
+		
+		LOG.inform("Using configuration file at '"+JSDOC.opt.c+"'.");
+		
+		for (var c in JSDOC.conf) {
+			if (c !== "D" && !defined(JSDOC.opt[c])) { // commandline overrules config file
+				JSDOC.opt[c] = JSDOC.conf[c];
+			}
+		}
+		
+		if (typeof JSDOC.conf["_"] != "undefined") {
+			JSDOC.opt["_"] = JSDOC.opt["_"].concat(JSDOC.conf["_"]);
+		}
+		
+		LOG.inform("With configuration: ");
+		for (var o in JSDOC.opt) {
+			LOG.inform("    "+o+": "+JSDOC.opt[o]);
+		}
+	}
+	
+	if (JSDOC.opt.h) {
+		JSDOC.usage();
+		quit();
+	}
+	
+	// defend against options that are not sane 
+	if (JSDOC.opt._.length == 0) {
+		LOG.warn("No source files to work on. Nothing to do.");
+		quit();
+	}
+	if (JSDOC.opt.t === true || JSDOC.opt.d === true) {
+		JSDOC.usage();
+	}
+	
+	if (typeof JSDOC.opt.d == "string") {
+		if (!JSDOC.opt.d.charAt(JSDOC.opt.d.length-1).match(/[\\\/]/)) {
+			JSDOC.opt.d = JSDOC.opt.d+"/";
+		}
+		LOG.inform("Output directory set to '"+JSDOC.opt.d+"'.");
+		IO.mkPath(JSDOC.opt.d);
+	}
+	if (JSDOC.opt.e) IO.setEncoding(JSDOC.opt.e);
+	
+	// the -r option: scan source directories recursively
+	if (typeof JSDOC.opt.r == "boolean") JSDOC.opt.r = 10;
+	else if (!isNaN(parseInt(JSDOC.opt.r))) JSDOC.opt.r = parseInt(JSDOC.opt.r);
+	else JSDOC.opt.r = 1;
+	
+	// the -D option: define user variables
+	var D = {};
+	if (JSDOC.opt.D) {
+		for (var i = 0; i < JSDOC.opt.D.length; i++) {
+			var defineParts = JSDOC.opt.D[i].split(":", 2);
+			if (defineParts) D[defineParts[0]] = defineParts[1];
+		}
+	}
+	JSDOC.opt.D = D;
+	// combine any conf file D options with the commandline D options
+	if (defined(JSDOC.conf)) for (var c in JSDOC.conf.D) {
+ 		if (!defined(JSDOC.opt.D[c])) {
+ 			JSDOC.opt.D[c] = JSDOC.conf.D[c];
+ 		}
+ 	}
+
+	// Load additional file handlers
+	// the -H option: filetype handlers
+	JSDOC.handlers = {};
+/*	
+	if (JSDOC.opt.H) {
+		for (var i = 0; i < JSDOC.opt.H.length; i++) {
+			var handlerDef = JSDOC.opt.H[i].split(":");
+			LOG.inform("Adding '." + handlerDef[0] + "' content handler from handlers/" + handlerDef[1] + ".js");
+			IO.include("handlers/" + handlerDef[1] + ".js");
+			if (!eval("typeof "+handlerDef[1])) {
+				LOG.warn(handlerDef[1] + "is not defined in "+handlerDef[1] + ".js");
+			}
+			else {
+				JSDOC.handlers[handlerDef[0]] = eval(handlerDef[1]);
+			}
+		}
+	}
+*/	
+	// Give plugins a chance to initialize
+	if (defined(JSDOC.PluginManager)) {
+		JSDOC.PluginManager.run("onInit", this);
+	}
+
+	JSDOC.opt.srcFiles = this._getSrcFiles();
+	this._parseSrcFiles();
+	//var handler = symbols.handler;
+	this.symbolSet = JSDOC.Parser.symbols;
+	//this.symbolGroup.handler = handler;
+}
+
+/**
+	Retrieve source file list.
+	@returns {String[]} The pathnames of the files to be parsed.
+ */
+JSDOC.JsDoc.prototype._getSrcFiles = function() {
+	this.srcFiles = [];
+	
+	var ext = ["js"];
+	if (JSDOC.opt.x) {
+		ext = JSDOC.opt.x.split(",").map(function($) {return $.toLowerCase()});
+	}
+	
+	for (var i = 0; i < JSDOC.opt._.length; i++) {
+		this.srcFiles = this.srcFiles.concat(
+			IO.ls(JSDOC.opt._[i], JSDOC.opt.r).filter(
+				function($) {
+					var thisExt = $.split(".").pop().toLowerCase();
+					return (ext.indexOf(thisExt) > -1 || thisExt in JSDOC.handlers); // we're only interested in files with certain extensions
+				}
+			)
+		);
+	}
+	
+	return this.srcFiles;
+}
+
+JSDOC.JsDoc.prototype._parseSrcFiles = function() {
+	JSDOC.Parser.init();
+	for (var i = 0, l = this.srcFiles.length; i < l; i++) {
+		var srcFile = this.srcFiles[i];
+		
+		try {
+			var src = IO.readFile(srcFile);
+		}
+		catch(e) {
+			LOG.warn("Can't read source file '"+srcFile+"': "+e.message);
+		}
+
+		// Check to see if there is a handler for this file type
+//		var ext = FilePath.fileExtension(srcFile);
+// 		if (JSDOC.handlers[ext]) {
+// 			LOG.inform(" Using external handler for '" + ext + "'");
+// 
+// 			symbols = symbols.concat(JSDOC.handlers[ext].handle(srcFile, src));
+// 			symbols.handler = JSDOC.handlers[ext];
+// 		}
+// 		else {
+			// The default (JSDOC) handler
+			var tr = new JSDOC.TokenReader();
+			var ts = new JSDOC.TokenStream(tr.tokenize(new JSDOC.TextStream(src)));
+	
+			JSDOC.Parser.parse(ts, srcFile);
+//		}
+	}
+	JSDOC.Parser.finish();
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/JsPlate.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/JsPlate.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/JsPlate.js	(revision 2890)
@@ -0,0 +1,100 @@
+/**
+	@constructor
+*/
+JSDOC.JsPlate = function(templateFile) {
+	if (templateFile) this.template = IO.readFile(templateFile);
+	
+	this.templateFile = templateFile;
+	this.code = "";
+	this.parse();
+}
+
+JSDOC.JsPlate.prototype.parse = function() {
+	this.template = this.template.replace(/\{#[\s\S]+?#\}/gi, "");
+	this.code = "var output=``"+this.template;
+
+	this.code = this.code.replace(
+		/<for +each="(.+?)" +in="(.+?)" *>/gi, 
+		function (match, eachName, inName) {
+			return "``;\rvar $"+eachName+"_keys = keys("+inName+");\rfor(var $"+eachName+"_i = 0; $"+eachName+"_i < $"+eachName+"_keys.length; $"+eachName+"_i++) {\rvar $"+eachName+"_last = ($"+eachName+"_i == $"+eachName+"_keys.length-1);\rvar $"+eachName+"_key = $"+eachName+"_keys[$"+eachName+"_i];\rvar "+eachName+" = "+inName+"[$"+eachName+"_key];\routput+=``";
+		}
+	);	
+	this.code = this.code.replace(/<if test="(.+?)">/g, "``;\rif ($1) { output+=``");
+	this.code = this.code.replace(/<elseif test="(.+?)"\s*\/>/g, "``;}\relse if ($1) { output+=``");
+	this.code = this.code.replace(/<else\s*\/>/g, "``;}\relse { output+=``");
+	this.code = this.code.replace(/<\/(if|for)>/g, "``;\r};\routput+=``");
+	this.code = this.code.replace(
+		/\{\+\s*([\s\S]+?)\s*\+\}/gi,
+		function (match, code) {
+			code = code.replace(/"/g, "``"); // prevent qoute-escaping of inline code
+			code = code.replace(/(\r?\n)/g, " ");
+			return "``+ ("+code+") +``";
+		}
+	);
+	this.code = this.code.replace(
+		/\{!\s*([\s\S]+?)\s*!\}/gi,
+		function (match, code) {
+			code = code.replace(/"/g, "``"); // prevent qoute-escaping of inline code
+			code = code.replace(/(\n)/g, " ");
+			return "``; "+code+";\routput+=``";
+		}
+	);
+	this.code = this.code+"``;";
+
+	this.code = this.code.replace(/(\r?\n)/g, "\\n");
+	this.code = this.code.replace(/"/g, "\\\"");
+	this.code = this.code.replace(/``/g, "\"");
+}
+
+JSDOC.JsPlate.prototype.toCode = function() {
+	return this.code;
+}
+
+JSDOC.JsPlate.keys = function(obj) {
+	var keys = [];
+	if (obj.constructor.toString().indexOf("Array") > -1) {
+		for (var i = 0; i < obj.length; i++) {
+			keys.push(i);
+		}
+	}
+	else {
+		for (var i in obj) {
+			keys.push(i);
+		}
+	}
+	return keys;
+};
+
+JSDOC.JsPlate.values = function(obj) {
+	var values = [];
+	if (obj.constructor.toString().indexOf("Array") > -1) {
+		for (var i = 0; i < obj.length; i++) {
+			values.push(obj[i]);
+		}
+	}
+	else {
+		for (var i in obj) {
+			values.push(obj[i]);
+		}
+	}
+	return values;
+};
+
+JSDOC.JsPlate.prototype.process = function(data) {
+	var keys = JSDOC.JsPlate.keys;
+	var values = JSDOC.JsPlate.values;
+	
+	try {
+		eval(this.code);
+	}
+	catch (e) {
+		print(">> There was an error evaluating the compiled code from template: "+this.templateFile);
+		print("   The error was on line "+e.lineNumber+" "+e.name+": "+e.message);
+		var lines = this.code.split("\r");
+		if (e.lineNumber-2 >= 0) print("line "+(e.lineNumber-1)+": "+lines[e.lineNumber-2]);
+		print("line "+e.lineNumber+": "+lines[e.lineNumber-1]);
+		print("");
+	}
+	/*debug*///print(this.code);
+	return output;
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Lang.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Lang.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Lang.js	(revision 2890)
@@ -0,0 +1,144 @@
+/**
+	@namespace
+*/
+JSDOC.Lang = {
+}
+
+JSDOC.Lang.isBuiltin = function(name) {
+	return (JSDOC.Lang.isBuiltin.coreObjects.indexOf(name) > -1);
+}
+JSDOC.Lang.isBuiltin.coreObjects = ['_global_', 'Array', 'Boolean', 'Date', 'Function', 'Math', 'Number', 'Object', 'RegExp', 'String'];
+
+JSDOC.Lang.whitespace = function(ch) {
+	return JSDOC.Lang.whitespace.names[ch];
+}
+JSDOC.Lang.whitespace.names = {
+	" ":      "SPACE",
+	"\f":     "FORMFEED",
+	"\t":     "TAB",
+	"\u0009": "UNICODE_TAB",
+	"\u000A": "UNICODE_NBR",
+	"\u0008": "VERTICAL_TAB"
+};
+
+JSDOC.Lang.newline = function(ch) {
+	return JSDOC.Lang.newline.names[ch];
+}
+JSDOC.Lang.newline.names = {
+	"\n":     "NEWLINE",
+	"\r":     "RETURN",
+	"\u000A": "UNICODE_LF",
+	"\u000D": "UNICODE_CR",
+	"\u2029": "UNICODE_PS",
+	"\u2028": "UNICODE_LS"
+};
+
+JSDOC.Lang.keyword = function(word) {
+	return JSDOC.Lang.keyword.names["="+word];
+}
+JSDOC.Lang.keyword.names = {
+	"=break":      "BREAK",
+	"=case":       "CASE",
+	"=catch":      "CATCH",
+	"=const":      "VAR",
+	"=continue":   "CONTINUE",
+	"=default":    "DEFAULT",
+	"=delete":     "DELETE",
+	"=do":         "DO",
+	"=else":       "ELSE",
+	"=false":      "FALSE",
+	"=finally":    "FINALLY",
+	"=for":        "FOR",
+	"=function":   "FUNCTION",
+	"=if":         "IF",
+	"=in":         "IN",
+	"=instanceof": "INSTANCEOF",
+	"=new":        "NEW",
+	"=null":       "NULL",
+	"=return":     "RETURN",
+	"=switch":     "SWITCH",
+	"=this":       "THIS",
+	"=throw":      "THROW",
+	"=true":       "TRUE",
+	"=try":        "TRY",
+	"=typeof":     "TYPEOF",
+	"=void":       "VOID",
+	"=while":      "WHILE",
+	"=with":       "WITH",
+	"=var":        "VAR"
+};
+
+JSDOC.Lang.punc = function(ch) {
+	return JSDOC.Lang.punc.names[ch];
+}
+JSDOC.Lang.punc.names = {
+	";":   "SEMICOLON",
+	",":   "COMMA",
+	"?":   "HOOK",
+	":":   "COLON",
+	"||":  "OR", 
+	"&&":  "AND",
+	"|":   "BITWISE_OR",
+	"^":   "BITWISE_XOR",
+	"&":   "BITWISE_AND",
+	"===": "STRICT_EQ", 
+	"==":  "EQ",
+	"=":   "ASSIGN",
+	"!==": "STRICT_NE",
+	"!=":  "NE",
+	"<<":  "LSH",
+	"<=":  "LE", 
+	"<":   "LT",
+	">>>": "URSH",
+	">>":  "RSH",
+	">=":  "GE",
+	">":   "GT", 
+	"++":  "INCREMENT",
+	"--":  "DECREMENT",
+	"+":   "PLUS",
+	"-":   "MINUS",
+	"*":   "MUL",
+	"/":   "DIV", 
+	"%":   "MOD",
+	"!":   "NOT",
+	"~":   "BITWISE_NOT",
+	".":   "DOT",
+	"[":   "LEFT_BRACKET",
+	"]":   "RIGHT_BRACKET",
+	"{":   "LEFT_CURLY",
+	"}":   "RIGHT_CURLY",
+	"(":   "LEFT_PAREN",
+	")":   "RIGHT_PAREN"
+};
+
+JSDOC.Lang.matching = function(name) {
+	return JSDOC.Lang.matching.names[name];
+}
+JSDOC.Lang.matching.names = {
+	"LEFT_PAREN": "RIGHT_PAREN",
+	"RIGHT_PAREN": "LEFT_PAREN",
+	"LEFT_CURLY": "RIGHT_CURLY",
+	"RIGHT_CURLY": "LEFT_CURLY",
+	"LEFT_BRACE": "RIGHT_BRACE",
+	"RIGHT_BRACE": "LEFT_BRACE"
+}
+
+JSDOC.Lang.isNumber = function(str) {
+	return /^(\.[0-9]|[0-9]+\.|[0-9])[0-9]*([eE][+-][0-9]+)?$/i.test(str);
+}
+
+JSDOC.Lang.isHexDec = function(str) {
+	return /^0x[0-9A-F]+$/i.test(str);
+}
+
+JSDOC.Lang.isWordChar = function(str) {
+	return /^[a-zA-Z0-9$_.]+$/.test(str);
+}
+
+JSDOC.Lang.isSpace = function(str) {
+	return (typeof JSDOC.Lang.whitespace(str) != "undefined");
+}
+
+JSDOC.Lang.isNewline = function(str) {
+	return (typeof JSDOC.Lang.newline(str) != "undefined");
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Parser.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Parser.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Parser.js	(revision 2890)
@@ -0,0 +1,109 @@
+if (typeof JSDOC == "undefined") JSDOC = {};
+
+/**
+	@namespace
+	@requires JSDOC.Walker
+	@requires JSDOC.Symbol
+	@requires JSDOC.DocComment
+*/
+JSDOC.Parser = {
+	conf: {
+		ignoreCode:               JSDOC.opt.n,
+		ignoreAnonymous:           true, // factory: true
+		treatUnderscoredAsPrivate: true, // factory: true
+		explain:                   false // factory: false
+	},
+	
+	addSymbol: function(symbol) {
+		// if a symbol alias is documented more than once the last one with the user docs wins
+		if (JSDOC.Parser.symbols.hasSymbol(symbol.alias)) {
+			var oldSymbol = JSDOC.Parser.symbols.getSymbol(symbol.alias);
+
+			if (oldSymbol.comment.isUserComment) {
+				if (symbol.comment.isUserComment) { // old and new are both documented
+					LOG.warn("The symbol '"+symbol.alias+"' is documented more than once.");
+				}
+				else { // old is documented but new isn't
+					return;
+				}
+			}
+		}
+		
+		// we don't document anonymous things
+		if (JSDOC.Parser.conf.ignoreAnonymous && symbol.name.match(/\$anonymous\b/)) return;
+
+		// uderscored things may be treated as if they were marked private, this cascades
+		if (JSDOC.Parser.conf.treatUnderscoredAsPrivate && symbol.name.match(/[.#-]_[^.#-]+$/)) {
+			if (!symbol.comment.getTag("public")) symbol.isPrivate = true;
+		}
+		
+		// -p flag is required to document private things
+		if (!JSDOC.opt.p && symbol.isPrivate) return; // issue #161 fixed by mcbain.asm
+		
+		// ignored things are not documented, this doesn't cascade
+		if (symbol.isIgnored) return;
+		
+		JSDOC.Parser.symbols.addSymbol(symbol);
+	},
+	
+	addBuiltin: function(name) {
+		var builtin = new JSDOC.Symbol(name, [], "CONSTRUCTOR", new JSDOC.DocComment(""));
+		builtin.isNamespace = true;
+		builtin.srcFile = "";
+		builtin.isPrivate = false;
+		JSDOC.Parser.addSymbol(builtin);
+		return builtin;
+	},
+	
+	init: function() {
+		JSDOC.Parser.symbols = new JSDOC.SymbolSet();
+		JSDOC.Parser.walker = new JSDOC.Walker();
+	},
+	
+	finish: function() {
+		JSDOC.Parser.symbols.relate();		
+		
+		// make a litle report about what was found
+		if (JSDOC.Parser.conf.explain) {
+			var symbols = JSDOC.Parser.symbols.toArray();
+			var srcFile = "";
+			for (var i = 0, l = symbols.length; i < l; i++) {
+				var symbol = symbols[i];
+				if (srcFile != symbol.srcFile) {
+					srcFile = symbol.srcFile;
+					print("\n"+srcFile+"\n-------------------");
+				}
+				print(i+":\n  alias => "+symbol.alias + "\n  name => "+symbol.name+ "\n  isa => "+symbol.isa + "\n  memberOf => " + symbol.memberOf + "\n  isStatic => " + symbol.isStatic + ",  isInner => " + symbol.isInner);
+			}
+			print("-------------------\n");
+		}
+	}
+}
+
+JSDOC.Parser.parse = function(/**JSDOC.TokenStream*/ts, /**String*/srcFile) {
+	JSDOC.Symbol.srcFile = (srcFile || "");
+	JSDOC.DocComment.shared = ""; // shared comments don't cross file boundaries
+	
+	if (!JSDOC.Parser.walker) JSDOC.Parser.init();
+	JSDOC.Parser.walker.walk(ts); // adds to our symbols
+	
+	// filter symbols by option
+	for (p in JSDOC.Parser.symbols._index) {
+		var symbol = JSDOC.Parser.symbols.getSymbol(p);
+		
+		if (!symbol) continue;
+		
+		if (symbol.is("FILE") || symbol.is("GLOBAL")) {
+			continue;
+		}
+		else if (!JSDOC.opt.a && !symbol.comment.isUserComment) {
+			JSDOC.Parser.symbols.deleteSymbol(symbol.alias);
+		}
+		
+		if (/#$/.test(symbol.alias)) { // we don't document prototypes
+			JSDOC.Parser.symbols.deleteSymbol(symbol.alias);
+		}
+	}
+	
+	return JSDOC.Parser.symbols.toArray();
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/PluginManager.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/PluginManager.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/PluginManager.js	(revision 2890)
@@ -0,0 +1,33 @@
+/**
+	@namespace Holds functionality related to running plugins.
+*/
+JSDOC.PluginManager = {
+}
+
+/**
+	@param name A unique name that identifies that plugin.
+	@param handlers A collection of named functions. The names correspond to hooks in the core code.
+*/
+JSDOC.PluginManager.registerPlugin = function(/**String*/name, /**Object*/handlers) {
+	if (!defined(JSDOC.PluginManager.plugins))
+		/** The collection of all plugins. Requires a unique name for each.
+		*/
+		JSDOC.PluginManager.plugins = {};
+	
+	
+	JSDOC.PluginManager.plugins[name] = handlers;
+}
+
+/**
+	@param hook The name of the hook that is being caught.
+	@param target Any object. This will be passed as the only argument to the handler whose
+	name matches the hook name. Handlers cannot return a value, so must modify the target
+	object to have an effect.
+*/
+JSDOC.PluginManager.run = function(/**String*/hook, /**Mixed*/target) {
+	for (var name in JSDOC.PluginManager.plugins) {
+		if (defined(JSDOC.PluginManager.plugins[name][hook])) {
+			JSDOC.PluginManager.plugins[name][hook](target);
+		}
+	}
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Symbol.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Symbol.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Symbol.js	(revision 2890)
@@ -0,0 +1,600 @@
+if (typeof JSDOC == "undefined") JSDOC = {};
+
+/**
+	Create a new Symbol.
+	@class Represents a symbol in the source code.
+ */
+JSDOC.Symbol = function() {
+	this.init();
+	if (arguments.length) this.populate.apply(this, arguments);
+}
+
+JSDOC.Symbol.prototype.init = function() {
+	this.$args = [];
+	this.addOn = "";
+	this.alias = "";
+	this.augments = [];
+	this.author = "";
+	this.classDesc = "";
+	this.comment = {};
+	this.defaultValue = undefined;
+	this.deprecated = "";
+	this.desc = "";
+	this.events = [];
+	this.example = [];
+	this.exceptions = [];
+	this.inherits = [];
+	this.inheritsFrom = [];
+	this.isa = "OBJECT";
+	this.isEvent = false;
+	this.isConstant = false;
+	this.isIgnored = false;
+	this.isInner = false;
+	this.isNamespace = false;
+	this.isPrivate = false;
+	this.isStatic = false;
+	this.memberOf = "";
+	this.methods = [];
+	this._name = "";
+	this._params = [];
+	this.properties = [];
+	this.requires = [];
+	this.returns = [];
+	this.see = [];
+	this.since = "";
+	this.srcFile = {};
+	this.type = "";
+	this.version = "";
+}
+
+JSDOC.Symbol.prototype.serialize = function() {
+	var keys = [];
+	for (var p in this) {
+		keys.push (p);
+	}
+	keys = keys.sort();
+	
+	var out = "";
+	for (var i in keys) {
+		if (typeof this[keys[i]] == "function") continue;
+		out += keys[i]+" => "+Dumper.dump(this[keys[i]])+",\n";
+	}
+	return "\n{\n" + out + "}\n";
+}
+
+JSDOC.Symbol.prototype.clone = function() {
+	var clone = new JSDOC.Symbol();
+	clone.populate.apply(clone, this.$args); // repopulate using the original arguments
+	clone.srcFile = this.srcFile; // not the current srcFile, the one when the original was made
+	return clone;
+}
+
+JSDOC.Symbol.prototype.__defineSetter__("name",
+	function(n) { n = n.replace(/^_global_[.#-]/, ""); n = n.replace(/\.prototype\.?/g, '#'); this._name = n; }
+);
+JSDOC.Symbol.prototype.__defineGetter__("name",
+	function() { return this._name; }
+);
+JSDOC.Symbol.prototype.__defineSetter__("params", 
+	function(v) {
+		for (var i = 0, l = v.length; i < l; i++) {
+			if (v[i].constructor != JSDOC.DocTag) { // may be a generic object parsed from signature, like {type:..., name:...}
+				this._params[i] = new JSDOC.DocTag("param"+((v[i].type)?" {"+v[i].type+"}":"")+" "+v[i].name);
+			}
+			else {
+				this._params[i] = v[i];
+			}
+		}
+	}
+);
+JSDOC.Symbol.prototype.__defineGetter__("params",
+	function() { return this._params; }
+);
+
+JSDOC.Symbol.prototype.populate = function(
+		/** String */ name,
+		/** Object[] */ params,
+		/** String */ isa,
+		/** JSDOC.DocComment */ comment
+) {
+	this.$args = arguments;
+	
+	this.name = name;
+	this.alias = this.name;
+	this.params = params;
+	this.isa = (isa == "VIRTUAL")? "OBJECT":isa;
+	this.comment = comment || new JSDOC.DocComment("");
+	this.srcFile = JSDOC.Symbol.srcFile;
+	
+	if (this.is("FILE") && !this.alias) this.alias = this.srcFile;
+
+	this.setTags();
+	
+	if (typeof JSDOC.PluginManager != "undefined") {
+		JSDOC.PluginManager.run("onSymbol", this);
+	}
+}
+
+JSDOC.Symbol.prototype.setTags = function() {
+	// @author
+	var authors = this.comment.getTag("author");
+	if (authors.length) {
+		this.author = authors.map(function($){return $.desc;}).join(", ");
+	}
+	
+	/*t:
+		plan(34, "testing JSDOC.Symbol");
+		
+		requires("../lib/JSDOC/DocComment.js");
+		requires("../frame/String.js");
+		requires("../lib/JSDOC/DocTag.js");
+
+		var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@author Joe Smith*"+"/"));
+		is(sym.author, "Joe Smith", "@author tag, author is found.");
+	*/
+	
+	// @desc
+	var descs = this.comment.getTag("desc");
+	if (descs.length) {
+		this.desc = descs.map(function($){return $.desc;}).join("\n"); // multiple descriptions are concatenated into one
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@desc This is a description.*"+"/"));
+		is(sym.desc, "This is a description.", "@desc tag, description is found.");
+	*/
+	
+	// @overview
+	if (this.is("FILE")) {
+		if (!this.alias) this.alias = this.srcFile;
+		
+		var overviews = this.comment.getTag("overview");
+		if (overviews.length) {
+			this.desc = [this.desc].concat(overviews.map(function($){return $.desc;})).join("\n");
+		}
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@overview This is an overview.*"+"/"));
+		is(sym.desc, "\nThis is an overview.", "@overview tag, description is found.");
+	*/
+	
+	// @since
+	var sinces = this.comment.getTag("since");
+	if (sinces.length) {
+		this.since = sinces.map(function($){return $.desc;}).join(", ");
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@since 1.01*"+"/"));
+		is(sym.since, "1.01", "@since tag, description is found.");
+	*/
+	
+	// @constant
+	if (this.comment.getTag("constant").length) {
+		this.isConstant = true;
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@constant*"+"/"));
+		is(sym.isConstant, true, "@constant tag, isConstant set.");
+	*/
+	
+	// @version
+	var versions = this.comment.getTag("version");
+	if (versions.length) {
+		this.version = versions.map(function($){return $.desc;}).join(", ");
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@version 2.0x*"+"/"));
+		is(sym.version, "2.0x", "@version tag, version is found.");
+	*/
+	
+	// @deprecated
+	var deprecateds = this.comment.getTag("deprecated");
+	if (deprecateds.length) {
+		this.deprecated = deprecateds.map(function($){return $.desc;}).join("\n");
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@deprecated Use other method.*"+"/"));
+		is(sym.deprecated, "Use other method.", "@deprecated tag, desc is found.");
+	*/
+	
+	// @example
+	var examples = this.comment.getTag("example");
+	if (examples.length) {
+		this.example = examples.map(
+			// trim trailing whitespace
+			function($) {
+				$.desc = $.desc.replace(/\s+$/, "");
+				return $;
+			}
+		);
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@example This\n  is an example. \n*"+"/"));
+		isnt(typeof sym.example[0], "undefined", "@example tag, creates sym.example array.");
+		is(sym.example[0], "This\n  is an example.", "@example tag, desc is found.");
+	*/
+	
+	// @see
+	var sees = this.comment.getTag("see");
+	if (sees.length) {
+		var thisSee = this.see;
+		sees.map(function($){thisSee.push($.desc);});
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@see The other thing.*"+"/"));
+		is(sym.see, "The other thing.", "@see tag, desc is found.");
+	*/
+	
+	// @class
+	var classes = this.comment.getTag("class");
+	if (classes.length) {
+		this.isa = "CONSTRUCTOR";
+		this.classDesc = classes[0].desc; // desc can't apply to the constructor as there is none.
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@class This describes the class.*"+"/"));
+		is(sym.isa, "CONSTRUCTOR", "@class tag, makes symbol a constructor.");
+		is(sym.classDesc, "This describes the class.", "@class tag, class description is found.");
+	*/
+	
+	// @namespace
+	var namespaces = this.comment.getTag("namespace");
+	if (namespaces.length) {
+		this.classDesc = namespaces[0].desc+"\n"+this.desc; // desc can't apply to the constructor as there is none.
+		this.isNamespace = true;
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@namespace This describes the namespace.*"+"/"));
+		is(sym.classDesc, "This describes the namespace.\n", "@namespace tag, class description is found.");
+	*/
+	
+	// @param
+	var params = this.comment.getTag("param");
+	if (params.length) {
+		// user-defined params overwrite those with same name defined by the parser
+		var thisParams = this.params;
+
+		if (thisParams.length == 0) { // none exist yet, so just bung all these user-defined params straight in
+			this.params = params;
+		}
+		else { // need to overlay these user-defined params on to existing parser-defined params
+			for (var i = 0, l = params.length; i < l; i++) {
+				if (thisParams[i]) {
+					if (params[i].type) thisParams[i].type = params[i].type;
+					thisParams[i].name = params[i].name;
+					thisParams[i].desc = params[i].desc;
+					thisParams[i].isOptional = params[i].isOptional;
+					thisParams[i].defaultValue = params[i].defaultValue;
+				}
+				else thisParams[i] = params[i];
+			}
+		}
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [{type: "array", name: "pages"}], "FUNCTION", new JSDOC.DocComment("/**Description.*"+"/"));
+		is(sym.params.length, 1, "parser defined param is found.");
+		
+		sym = new JSDOC.Symbol("foo", [], "FUNCTION", new JSDOC.DocComment("/**Description.\n@param {array} pages*"+"/"));
+		is(sym.params.length, 1, "user defined param is found.");
+		is(sym.params[0].type, "array", "user defined param type is found.");
+		is(sym.params[0].name, "pages", "user defined param name is found.");
+		
+		sym = new JSDOC.Symbol("foo", [{type: "array", name: "pages"}], "FUNCTION", new JSDOC.DocComment("/**Description.\n@param {string} uid*"+"/"));
+		is(sym.params.length, 1, "user defined param overwrites parser defined param.");
+		is(sym.params[0].type, "string", "user defined param type overwrites parser defined param type.");
+		is(sym.params[0].name, "uid", "user defined param name overwrites parser defined param name.");
+	
+		sym = new JSDOC.Symbol("foo", [{type: "array", name: "pages"}, {type: "number", name: "count"}], "FUNCTION", new JSDOC.DocComment("/**Description.\n@param {string} uid*"+"/"));
+		is(sym.params.length, 2, "user defined params  overlay parser defined params.");
+		is(sym.params[1].type, "number", "user defined param type overlays parser defined param type.");
+		is(sym.params[1].name, "count", "user defined param name overlays parser defined param name.");
+
+		sym = new JSDOC.Symbol("foo", [], "FUNCTION", new JSDOC.DocComment("/**Description.\n@param {array} pages The pages description.*"+"/"));
+		is(sym.params.length, 1, "user defined param with description is found.");
+		is(sym.params[0].desc, "The pages description.", "user defined param description is found.");
+	*/
+	
+	// @constructor
+	if (this.comment.getTag("constructor").length) {
+		this.isa = "CONSTRUCTOR";
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@constructor*"+"/"));
+		is(sym.isa, "CONSTRUCTOR", "@constructor tag, makes symbol a constructor.");
+	*/
+	
+	// @static
+	if (this.comment.getTag("static").length) {
+		this.isStatic = true;
+		if (this.isa == "CONSTRUCTOR") {
+			this.isNamespace = true;
+		}
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@static\n@constructor*"+"/"));
+		is(sym.isStatic, true, "@static tag, makes isStatic true.");
+		is(sym.isNamespace, true, "@static and @constructor tag, makes isNamespace true.");
+	*/
+	
+	// @inner
+	if (this.comment.getTag("inner").length) {
+		this.isInner = true;
+		this.isStatic = false;
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@inner*"+"/"));
+		is(sym.isStatic, false, "@inner tag, makes isStatic false.");
+		is(sym.isInner, true, "@inner makes isInner true.");
+	*/
+	
+	// @field
+	if (this.comment.getTag("field").length) {
+		this.isa = "OBJECT";
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "FUNCTION", new JSDOC.DocComment("/**@field*"+"/"));
+		is(sym.isa, "OBJECT", "@field tag, makes symbol an object.");
+	*/
+	
+	// @function
+	if (this.comment.getTag("function").length) {
+		this.isa = "FUNCTION";
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@function*"+"/"));
+		is(sym.isa, "FUNCTION", "@function tag, makes symbol a function.");
+	*/
+	
+	// @event
+	var events = this.comment.getTag("event");
+	if (events.length) {
+		this.isa = "FUNCTION";
+		this.isEvent = true;
+	}
+	
+	/*t:
+		var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@event*"+"/"));
+		is(sym.isa, "FUNCTION", "@event tag, makes symbol a function.");
+		is(sym.isEvent, true, "@event makes isEvent true.");
+	*/
+	
+	// @name
+	var names = this.comment.getTag("name");
+	if (names.length) {
+		this.name = names[0].desc;
+	}
+	
+	/*t:
+		// todo
+	*/
+	
+	// @property
+	var properties = this.comment.getTag("property");
+	if (properties.length) {
+		thisProperties = this.properties;
+		for (var i = 0; i < properties.length; i++) {
+			var property = new JSDOC.Symbol(this.alias+"#"+properties[i].name, [], "OBJECT", new JSDOC.DocComment("/**"+properties[i].desc+"\n@name "+properties[i].name+"\n@memberOf "+this.alias+"#*/"));
+			// TODO: shouldn't the following happen in the addProperty method of Symbol?
+			property.name = properties[i].name;
+			property.memberOf = this.alias;
+			if (properties[i].type) property.type = properties[i].type;
+			if (properties[i].defaultValue) property.defaultValue = properties[i].defaultValue;
+			this.addProperty(property);
+			JSDOC.Parser.addSymbol(property);
+		}
+	}
+	
+	/*t:
+		// todo
+	*/
+
+	// @return
+	var returns = this.comment.getTag("return");
+	if (returns.length) { // there can be many return tags in a single doclet
+		this.returns = returns;
+		this.type = returns.map(function($){return $.type}).join(", ");
+	}
+	
+	/*t:
+		// todo
+	*/
+	
+	// @exception
+	this.exceptions = this.comment.getTag("throws");
+	
+	/*t:
+		// todo
+	*/
+	
+	// @requires
+	var requires = this.comment.getTag("requires");
+	if (requires.length) {
+		this.requires = requires.map(function($){return $.desc});
+	}
+	
+	/*t:
+		// todo
+	*/
+	
+	// @type
+	var types = this.comment.getTag("type");
+	if (types.length) {
+		this.type = types[0].desc; //multiple type tags are ignored
+	}
+	
+	/*t:
+		// todo
+	*/
+	
+	// @private
+	if (this.comment.getTag("private").length || this.isInner) {
+		this.isPrivate = true;
+	}
+	
+	// @ignore
+	if (this.comment.getTag("ignore").length) {
+		this.isIgnored = true;
+	}
+	
+	/*t:
+		// todo
+	*/
+	
+	// @inherits ... as ...
+	var inherits = this.comment.getTag("inherits");
+	if (inherits.length) {
+		for (var i = 0; i < inherits.length; i++) {
+			if (/^\s*([a-z$0-9_.#-]+)(?:\s+as\s+([a-z$0-9_.#]+))?/i.test(inherits[i].desc)) {
+				var inAlias = RegExp.$1;
+				var inAs = RegExp.$2 || inAlias;
+
+				if (inAlias) inAlias = inAlias.replace(/\.prototype\.?/g, "#");
+				
+				if (inAs) {
+					inAs = inAs.replace(/\.prototype\.?/g, "#");
+					inAs = inAs.replace(/^this\.?/, "#");
+				}
+
+				if (inAs.indexOf(inAlias) != 0) { //not a full namepath
+					var joiner = ".";
+					if (this.alias.charAt(this.alias.length-1) == "#" || inAs.charAt(0) == "#") {
+						joiner = "";
+					}
+					inAs = this.alias + joiner + inAs;
+				}
+			}
+			this.inherits.push({alias: inAlias, as: inAs});
+		}
+	}
+	
+	/*t:
+		// todo
+	*/
+
+	// @augments
+	this.augments = this.comment.getTag("augments");
+	
+	// @default
+	var defaults = this.comment.getTag("default");
+	if (defaults.length) {
+		if (this.is("OBJECT")) {
+			this.defaultValue = defaults[0].desc;
+		}
+	}
+	
+	/*t:
+		// todo
+	*/
+	
+	// @memberOf
+	var memberOfs = this.comment.getTag("memberOf");
+	if (memberOfs.length) {
+		this.memberOf = memberOfs[0].desc;
+		this.memberOf = this.memberOf.replace(/\.prototype\.?/g, "#");
+	}
+
+	/*t:
+		// todo
+	*/
+	
+	// @public
+	if (this.comment.getTag("public").length) {
+		this.isPrivate = false;
+	}
+	
+	/*t:
+		// todo
+	*/
+}
+
+JSDOC.Symbol.prototype.is = function(what) {
+	return this.isa === what;
+}
+
+JSDOC.Symbol.prototype.isBuiltin = function() {
+	return JSDOC.Lang.isBuiltin(this.alias);
+}
+
+JSDOC.Symbol.prototype.setType = function(/**String*/comment, /**Boolean*/overwrite) {
+	if (!overwrite && this.type) return;
+	var typeComment = JSDOC.DocComment.unwrapComment(comment);
+	this.type = typeComment;
+}
+
+JSDOC.Symbol.prototype.inherit = function(symbol) {
+	if (!this.hasMember(symbol.name) && !symbol.isInner) {
+		if (symbol.is("FUNCTION"))
+			this.methods.push(symbol);
+		else if (symbol.is("OBJECT"))
+			this.properties.push(symbol);
+	}
+}
+
+JSDOC.Symbol.prototype.hasMember = function(name) {
+	return (this.hasMethod(name) || this.hasProperty(name));
+}
+
+JSDOC.Symbol.prototype.addMember = function(symbol) {
+	if (symbol.is("FUNCTION")) { this.addMethod(symbol); }
+	else if (symbol.is("OBJECT")) { this.addProperty(symbol); }
+}
+
+JSDOC.Symbol.prototype.hasMethod = function(name) {
+	var thisMethods = this.methods;
+	for (var i = 0, l = thisMethods.length; i < l; i++) {
+		if (thisMethods[i].name == name) return true;
+		if (thisMethods[i].alias == name) return true;
+	}
+	return false;
+}
+
+JSDOC.Symbol.prototype.addMethod = function(symbol) {
+	var methodAlias = symbol.alias;
+	var thisMethods = this.methods;
+	for (var i = 0, l = thisMethods.length; i < l; i++) {
+		if (thisMethods[i].alias == methodAlias) {
+			thisMethods[i] = symbol; // overwriting previous method
+			return;
+		}
+	}
+	thisMethods.push(symbol); // new method with this alias
+}
+
+JSDOC.Symbol.prototype.hasProperty = function(name) {
+	var thisProperties = this.properties;
+	for (var i = 0, l = thisProperties.length; i < l; i++) {
+		if (thisProperties[i].name == name) return true;
+		if (thisProperties[i].alias == name) return true;
+	}
+	return false;
+}
+
+JSDOC.Symbol.prototype.addProperty = function(symbol) {
+	var propertyAlias = symbol.alias;
+	var thisProperties = this.properties;
+	for (var i = 0, l = thisProperties.length; i < l; i++) {
+		if (thisProperties[i].alias == propertyAlias) {
+			thisProperties[i] = symbol; // overwriting previous property
+			return;
+		}
+	}
+
+	thisProperties.push(symbol); // new property with this alias
+}
+
+JSDOC.Symbol.srcFile = ""; //running reference to the current file being parsed
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/SymbolSet.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/SymbolSet.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/SymbolSet.js	(revision 2890)
@@ -0,0 +1,220 @@
+if (typeof JSDOC == "undefined") JSDOC = {};
+
+/** @constructor */
+JSDOC.SymbolSet = function() {
+	this.init();
+}
+
+JSDOC.SymbolSet.prototype.init = function() {
+	this._index = {};
+}
+
+JSDOC.SymbolSet.prototype.keys = function() {
+	var found = [];
+	for (var p in this._index) {
+		found.push(p);
+	}
+	return found;
+}
+
+JSDOC.SymbolSet.prototype.hasSymbol = function(alias) {
+	return this._index.hasOwnProperty(alias);
+}
+
+JSDOC.SymbolSet.prototype.addSymbol = function(symbol) {
+	if (this.hasSymbol(symbol.alias)) {
+		LOG.warn("Overwriting symbol documentation for: "+symbol.alias + ".");
+	}
+	this._index[symbol.alias] = symbol;
+}
+
+JSDOC.SymbolSet.prototype.getSymbol = function(alias) {
+	if (this.hasSymbol(alias)) return this._index[alias];
+}
+
+JSDOC.SymbolSet.prototype.getSymbolByName = function(name) {
+	for (var p in this._index) {
+		var symbol = this.getSymbol(p);
+		if (symbol.name == name) return symbol;
+	}
+}
+
+JSDOC.SymbolSet.prototype.toArray = function() {
+	var found = [];
+	for (var p in this._index) {
+		found.push(this._index[p]);
+	}
+	return found;
+}
+
+JSDOC.SymbolSet.prototype.deleteSymbol = function(alias) {
+	if (!this.hasSymbol(alias)) return;
+	delete this._index[alias];
+}
+
+JSDOC.SymbolSet.prototype.renameSymbol = function(oldName, newName) {
+	// todo: should check if oldname or newname already exist
+	this._index[newName] = this._index[oldName];
+	this.deleteSymbol(oldName);
+	this._index[newName].alias = newName;
+	return newName;
+}
+
+JSDOC.SymbolSet.prototype.relate = function() {
+	this.resolveBorrows();
+	this.resolveMemberOf();
+	this.resolveAugments();
+}
+
+JSDOC.SymbolSet.prototype.resolveBorrows = function() {
+	for (p in this._index) {
+		var symbol = this._index[p];
+		if (symbol.is("FILE") || symbol.is("GLOBAL")) continue;
+		
+		var borrows = symbol.inherits;
+		for (var i = 0; i < borrows.length; i++) {
+			var borrowed = this.getSymbol(borrows[i].alias);
+			if (!borrowed) {
+				LOG.warn("Can't borrow undocumented "+borrows[i].alias+".");
+				continue;
+			}
+			
+			var borrowAsName = borrows[i].as;
+			var borrowAsAlias = borrowAsName;
+			if (!borrowAsName) {
+				LOG.warn("Malformed @borrow, 'as' is required.");
+				continue;
+			}
+			
+			if (borrowAsName.length > symbol.alias.length && borrowAsName.indexOf(symbol.alias) == 0) {
+				borrowAsName = borrowAsName.replace(borrowed.alias, "")
+			}
+			else {
+				var joiner = "";
+				if (borrowAsName.charAt(0) != "#") joiner = ".";
+				borrowAsAlias = borrowed.alias + joiner + borrowAsName;
+			}
+			
+			borrowAsName = borrowAsName.replace(/^[#.]/, "");
+					
+			if (this.hasSymbol(borrowAsAlias)) continue;
+
+			var clone = borrowed.clone();
+			clone.name = borrowAsName;
+			clone.alias = borrowAsAlias;
+			this.addSymbol(clone);
+		}
+	}
+}
+
+JSDOC.SymbolSet.prototype.resolveMemberOf = function() {
+	for (var p in this._index) {
+		var symbol = this.getSymbol(p);
+		
+		if (symbol.is("FILE") || symbol.is("GLOBAL")) continue;
+		
+		// the memberOf value was provided in the @memberOf tag
+		else if (symbol.memberOf) {
+			var parts = symbol.alias.match(new RegExp("^("+symbol.memberOf+"[.#-])(.+)$"));
+			
+			// like foo.bar is a memberOf foo
+			if (parts) {
+				symbol.memberOf = parts[1];
+				symbol.name = parts[2];
+			}
+			// like bar is a memberOf foo
+			else {
+				var joiner = symbol.memberOf.charAt(symbol.memberOf.length-1);
+				if (!/[.#-]/.test(joiner)) symbol.memberOf += ".";
+				
+				this.renameSymbol(p, symbol.memberOf + symbol.name);
+			}
+		}
+		// the memberOf must be calculated
+		else {
+			var parts = symbol.alias.match(/^(.*[.#-])([^.#-]+)$/);
+			if (parts) {
+				symbol.memberOf = parts[1];
+				symbol.name = parts[2];				
+			}
+		}
+
+		// set isStatic, isInner
+		if (symbol.memberOf) {
+			switch (symbol.memberOf.charAt(symbol.memberOf.length-1)) {
+				case '#' :
+					symbol.isStatic = false;
+					symbol.isInner = false;
+				break;
+				case '.' :
+					symbol.isStatic = true;
+					symbol.isInner = false;
+				break;
+				case '-' :
+					symbol.isStatic = false;
+					symbol.isInner = true;
+				break;
+			}
+		}
+		
+		// unowned methods and fields belong to the global object
+		if (!symbol.is("CONSTRUCTOR") && !symbol.isNamespace && symbol.memberOf == "") {
+			symbol.memberOf = "_global_";
+		}
+		
+		// clean up
+		if (symbol.memberOf.match(/[.#-]$/)) {
+			symbol.memberOf = symbol.memberOf.substr(0, symbol.memberOf.length-1);
+		}
+		
+		// add to parent's methods or properties list
+		if (symbol.memberOf) {
+			var container = this.getSymbol(symbol.memberOf);
+			if (!container) {
+				if (JSDOC.Lang.isBuiltin(symbol.memberOf)) container = JSDOC.Parser.addBuiltin(symbol.memberOf);
+				else {
+					LOG.warn("Can't document "+symbol.name +" as a member of undocumented symbol "+symbol.memberOf+".");
+				}
+			}
+			
+			if (container) container.addMember(symbol);
+		}
+	}
+}
+
+JSDOC.SymbolSet.prototype.resolveAugments = function() {
+	for (var p in this._index) {
+		var symbol = this.getSymbol(p);
+		
+		if (symbol.alias == "_global_" || symbol.is("FILE")) continue;
+		JSDOC.SymbolSet.prototype.walk.apply(this, [symbol]);
+	}
+}
+
+JSDOC.SymbolSet.prototype.walk = function(symbol) {
+	var augments = symbol.augments;
+	for(var i = 0; i < augments.length; i++) {
+		var contributer = this.getSymbol(augments[i]);
+		if (contributer) {
+			if (contributer.augments.length) {
+				JSDOC.SymbolSet.prototype.walk.apply(this, [contributer]);
+			}
+			
+			symbol.inheritsFrom.push(contributer.alias);
+			if (!isUnique(symbol.inheritsFrom)) {
+				//LOG.warn("Can't resolve augments: Circular reference: "+symbol.alias+" inherits from "+contributer.alias+" more than once.");
+			}
+			else {
+				var cmethods = contributer.methods;
+				var cproperties = contributer.properties;
+				
+				for (var ci = 0, cl = cmethods.length; ci < cl; ci++)
+					symbol.inherit(cmethods[ci]);
+				for (var ci = 0, cl = cproperties.length; ci < cl; ci++)
+					symbol.inherit(cproperties[ci]);
+			}
+		}
+		else LOG.warn("Can't augment contributer: "+augments[i]+", not found.");
+	}
+}
+
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/TextStream.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/TextStream.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/TextStream.js	(revision 2890)
@@ -0,0 +1,41 @@
+
+/**
+	@constructor
+*/
+JSDOC.TextStream = function(text) {
+	if (typeof(text) == "undefined") text = "";
+	text = ""+text;
+	this.text = text;
+	this.cursor = 0;
+}
+
+JSDOC.TextStream.prototype.look = function(n) {
+	if (typeof n == "undefined") n = 0;
+	
+	if (this.cursor+n < 0 || this.cursor+n >= this.text.length) {
+		var result = new String("");
+		result.eof = true;
+		return result;
+	}
+	return this.text.charAt(this.cursor+n);
+}
+
+JSDOC.TextStream.prototype.next = function(n) {
+	if (typeof n == "undefined") n = 1;
+	if (n < 1) return null;
+	
+	var pulled = "";
+	for (var i = 0; i < n; i++) {
+		if (this.cursor+i < this.text.length) {
+			pulled += this.text.charAt(this.cursor+i);
+		}
+		else {
+			var result = new String("");
+			result.eof = true;
+			return result;
+		}
+	}
+
+	this.cursor += n;
+	return pulled;
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Token.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Token.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Token.js	(revision 2890)
@@ -0,0 +1,18 @@
+if (typeof JSDOC == "undefined") JSDOC = {};
+
+/**
+	@constructor
+*/
+JSDOC.Token = function(data, type, name) {
+	this.data = data;
+	this.type = type;
+	this.name = name;
+}
+
+JSDOC.Token.prototype.toString = function() { 
+	return "<"+this.type+" name=\""+this.name+"\">"+this.data+"</"+this.type+">";
+}
+
+JSDOC.Token.prototype.is = function(what) {
+	return this.name === what || this.type === what;
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/TokenReader.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/TokenReader.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/TokenReader.js	(revision 2890)
@@ -0,0 +1,332 @@
+if (typeof JSDOC == "undefined") JSDOC = {};
+
+/**
+	@class Search a {@link JSDOC.TextStream} for language tokens.
+*/
+JSDOC.TokenReader = function() {
+	this.keepDocs = true;
+	this.keepWhite = false;
+	this.keepComments = false;
+}
+
+/**
+	@type {JSDOC.Token[]}
+ */
+JSDOC.TokenReader.prototype.tokenize = function(/**JSDOC.TextStream*/stream) {
+	var tokens = [];
+	/**@ignore*/ tokens.last    = function() { return tokens[tokens.length-1]; }
+	/**@ignore*/ tokens.lastSym = function() {
+		for (var i = tokens.length-1; i >= 0; i--) {
+			if (!(tokens[i].is("WHIT") || tokens[i].is("COMM"))) return tokens[i];
+		}
+	}
+
+	while (!stream.look().eof) {
+		if (this.read_mlcomment(stream, tokens)) continue;
+		if (this.read_slcomment(stream, tokens)) continue;
+		if (this.read_dbquote(stream, tokens))   continue;
+		if (this.read_snquote(stream, tokens))   continue;
+		if (this.read_regx(stream, tokens))      continue;
+		if (this.read_numb(stream, tokens))      continue;
+		if (this.read_punc(stream, tokens))      continue;
+		if (this.read_space(stream, tokens))     continue;
+		if (this.read_newline(stream, tokens))   continue;
+		if (this.read_word(stream, tokens))      continue;
+		
+		// if execution reaches here then an error has happened
+		tokens.push(new JSDOC.Token(stream.next(), "TOKN", "UNKNOWN_TOKEN"));
+	}
+	return tokens;
+}
+
+/**
+	@returns {Boolean} Was the token found?
+ */
+JSDOC.TokenReader.prototype.read_word = function(/**JSDOC.TokenStream*/stream, tokens) {
+	var found = "";
+	while (!stream.look().eof && JSDOC.Lang.isWordChar(stream.look())) {
+		found += stream.next();
+	}
+	
+	if (found === "") {
+		return false;
+	}
+	else {
+		var name;
+		if ((name = JSDOC.Lang.keyword(found))) tokens.push(new JSDOC.Token(found, "KEYW", name));
+		else tokens.push(new JSDOC.Token(found, "NAME", "NAME"));
+		return true;
+	}
+}
+
+/**
+	@returns {Boolean} Was the token found?
+ */
+JSDOC.TokenReader.prototype.read_punc = function(/**JSDOC.TokenStream*/stream, tokens) {
+	var found = "";
+	var name;
+	while (!stream.look().eof && JSDOC.Lang.punc(found+stream.look())) {
+		found += stream.next();
+	}
+	
+	if (found === "") {
+		return false;
+	}
+	else {
+		tokens.push(new JSDOC.Token(found, "PUNC", JSDOC.Lang.punc(found)));
+		return true;
+	}
+}
+
+/**
+	@returns {Boolean} Was the token found?
+ */
+JSDOC.TokenReader.prototype.read_space = function(/**JSDOC.TokenStream*/stream, tokens) {
+	var found = "";
+	
+	while (!stream.look().eof && JSDOC.Lang.isSpace(stream.look())) {
+		found += stream.next();
+	}
+	
+	if (found === "") {
+		return false;
+	}
+	else {
+		if (this.collapseWhite) found = " ";
+		if (this.keepWhite) tokens.push(new JSDOC.Token(found, "WHIT", "SPACE"));
+		return true;
+	}
+}
+
+/**
+	@returns {Boolean} Was the token found?
+ */
+JSDOC.TokenReader.prototype.read_newline = function(/**JSDOC.TokenStream*/stream, tokens) {
+	var found = "";
+	
+	while (!stream.look().eof && JSDOC.Lang.isNewline(stream.look())) {
+		found += stream.next();
+	}
+	
+	if (found === "") {
+		return false;
+	}
+	else {
+		if (this.collapseWhite) found = "\n";
+		if (this.keepWhite) tokens.push(new JSDOC.Token(found, "WHIT", "NEWLINE"));
+		return true;
+	}
+}
+
+/**
+	@returns {Boolean} Was the token found?
+ */
+JSDOC.TokenReader.prototype.read_mlcomment = function(/**JSDOC.TokenStream*/stream, tokens) {
+	if (stream.look() == "/" && stream.look(1) == "*") {
+		var found = stream.next(2);
+		
+		while (!stream.look().eof && !(stream.look(-1) == "/" && stream.look(-2) == "*")) {
+			found += stream.next();
+		}
+		
+		// to start doclet we allow /** or /*** but not /**/ or /****
+		if (/^\/\*\*([^\/]|\*[^*])/.test(found) && this.keepDocs) tokens.push(new JSDOC.Token(found, "COMM", "JSDOC"));
+		else if (this.keepComments) tokens.push(new JSDOC.Token(found, "COMM", "MULTI_LINE_COMM"));
+		return true;
+	}
+	return false;
+}
+
+/**
+	@returns {Boolean} Was the token found?
+ */
+JSDOC.TokenReader.prototype.read_slcomment = function(/**JSDOC.TokenStream*/stream, tokens) {
+	var found;
+	if (
+		(stream.look() == "/" && stream.look(1) == "/" && (found=stream.next(2)))
+		|| 
+		(stream.look() == "<" && stream.look(1) == "!" && stream.look(2) == "-" && stream.look(3) == "-" && (found=stream.next(4)))
+	) {
+		
+		while (!stream.look().eof && !JSDOC.Lang.isNewline(stream.look())) {
+			found += stream.next();
+		}
+		
+		if (this.keepComments) {
+			tokens.push(new JSDOC.Token(found, "COMM", "SINGLE_LINE_COMM"));
+		}
+		return true;
+	}
+	return false;
+}
+
+/**
+	@returns {Boolean} Was the token found?
+ */
+JSDOC.TokenReader.prototype.read_dbquote = function(/**JSDOC.TokenStream*/stream, tokens) {
+	if (stream.look() == "\"") {
+		// find terminator
+		var string = stream.next();
+		
+		while (!stream.look().eof) {
+			if (stream.look() == "\\") {
+				if (JSDOC.Lang.isNewline(stream.look(1))) {
+					do {
+						stream.next();
+					} while (!stream.look().eof && JSDOC.Lang.isNewline(stream.look()));
+					string += "\\\n";
+				}
+				else {
+					string += stream.next(2);
+				}
+			}
+			else if (stream.look() == "\"") {
+				string += stream.next();
+				tokens.push(new JSDOC.Token(string, "STRN", "DOUBLE_QUOTE"));
+				return true;
+			}
+			else {
+				string += stream.next();
+			}
+		}
+	}
+	return false; // error! unterminated string
+}
+
+/**
+	@returns {Boolean} Was the token found?
+ */
+JSDOC.TokenReader.prototype.read_snquote = function(/**JSDOC.TokenStream*/stream, tokens) {
+	if (stream.look() == "'") {
+		// find terminator
+		var string = stream.next();
+		
+		while (!stream.look().eof) {
+			if (stream.look() == "\\") { // escape sequence
+				string += stream.next(2);
+			}
+			else if (stream.look() == "'") {
+				string += stream.next();
+				tokens.push(new JSDOC.Token(string, "STRN", "SINGLE_QUOTE"));
+				return true;
+			}
+			else {
+				string += stream.next();
+			}
+		}
+	}
+	return false; // error! unterminated string
+}
+
+/**
+	@returns {Boolean} Was the token found?
+ */
+JSDOC.TokenReader.prototype.read_numb = function(/**JSDOC.TokenStream*/stream, tokens) {
+	if (stream.look() === "0" && stream.look(1) == "x") {
+		return this.read_hex(stream, tokens);
+	}
+	
+	var found = "";
+	
+	while (!stream.look().eof && JSDOC.Lang.isNumber(found+stream.look())){
+		found += stream.next();
+	}
+	
+	if (found === "") {
+		return false;
+	}
+	else {
+		if (/^0[0-7]/.test(found)) tokens.push(new JSDOC.Token(found, "NUMB", "OCTAL"));
+		else tokens.push(new JSDOC.Token(found, "NUMB", "DECIMAL"));
+		return true;
+	}
+}
+/*t:
+	requires("../lib/JSDOC/TextStream.js");
+	requires("../lib/JSDOC/Token.js");
+	requires("../lib/JSDOC/Lang.js");
+	
+	plan(3, "testing JSDOC.TokenReader.prototype.read_numb");
+	
+	//// setup
+	var src = "function foo(num){while (num+8.0 >= 0x20 && num < 0777){}}";
+	var tr = new JSDOC.TokenReader();
+	var tokens = tr.tokenize(new JSDOC.TextStream(src));
+	
+	var hexToken, octToken, decToken;
+	for (var i = 0; i < tokens.length; i++) {
+		if (tokens[i].name == "HEX_DEC") hexToken = tokens[i];
+		if (tokens[i].name == "OCTAL") octToken = tokens[i];
+		if (tokens[i].name == "DECIMAL") decToken = tokens[i];
+	}
+	////
+	
+	is(decToken.data, "8.0", "decimal number is found in source.");
+	is(hexToken.data, "0x20", "hexdec number is found in source (issue #99).");
+	is(octToken.data, "0777", "octal number is found in source.");
+*/
+
+/**
+	@returns {Boolean} Was the token found?
+ */
+JSDOC.TokenReader.prototype.read_hex = function(/**JSDOC.TokenStream*/stream, tokens) {
+	var found = stream.next(2);
+	
+	while (!stream.look().eof) {
+		if (JSDOC.Lang.isHexDec(found) && !JSDOC.Lang.isHexDec(found+stream.look())) { // done
+			tokens.push(new JSDOC.Token(found, "NUMB", "HEX_DEC"));
+			return true;
+		}
+		else {
+			found += stream.next();
+		}
+	}
+	return false;
+}
+
+/**
+	@returns {Boolean} Was the token found?
+ */
+JSDOC.TokenReader.prototype.read_regx = function(/**JSDOC.TokenStream*/stream, tokens) {
+	var last;
+	if (
+		stream.look() == "/"
+		&& 
+		(
+			
+			(
+				!(last = tokens.lastSym()) // there is no last, the regex is the first symbol
+				|| 
+				(
+					   !last.is("NUMB")
+					&& !last.is("NAME")
+					&& !last.is("RIGHT_PAREN")
+					&& !last.is("RIGHT_BRACKET")
+				)
+			)
+		)
+	) {
+		var regex = stream.next();
+		
+		while (!stream.look().eof) {
+			if (stream.look() == "\\") { // escape sequence
+				regex += stream.next(2);
+			}
+			else if (stream.look() == "/") {
+				regex += stream.next();
+				
+				while (/[gmi]/.test(stream.look())) {
+					regex += stream.next();
+				}
+				
+				tokens.push(new JSDOC.Token(regex, "REGX", "REGX"));
+				return true;
+			}
+			else {
+				regex += stream.next();
+			}
+		}
+		// error: unterminated regex
+	}
+	return false;
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/TokenStream.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/TokenStream.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/TokenStream.js	(revision 2890)
@@ -0,0 +1,133 @@
+if (typeof JSDOC == "undefined") JSDOC = {};
+
+/**
+	@constructor
+*/
+JSDOC.TokenStream = function(tokens) {
+	this.tokens = (tokens || []);
+	this.rewind();
+}
+
+/**
+	@constructor
+	@private
+*/
+function VoidToken(/**String*/type) {
+	this.toString = function() {return "<VOID type=\""+type+"\">"};
+	this.is = function(){return false;}
+}
+
+JSDOC.TokenStream.prototype.rewind = function() {
+	this.cursor = -1;
+}
+
+/**
+	@type JSDOC.Token
+*/
+JSDOC.TokenStream.prototype.look = function(/**Number*/n, /**Boolean*/considerWhitespace) {
+	if (typeof n == "undefined") n = 0;
+
+	if (considerWhitespace == true) {
+		if (this.cursor+n < 0 || this.cursor+n > this.tokens.length) return {};
+		return this.tokens[this.cursor+n];
+	}
+	else {
+		var count = 0;
+		var i = this.cursor;
+
+		while (true) {
+			if (i < 0) return new JSDOC.Token("", "VOID", "START_OF_STREAM");
+			else if (i > this.tokens.length) return new JSDOC.Token("", "VOID", "END_OF_STREAM");
+
+			if (i != this.cursor && (this.tokens[i] === undefined || this.tokens[i].is("WHIT"))) {
+				if (n < 0) i--; else i++;
+				continue;
+			}
+			
+			if (count == Math.abs(n)) {
+				return this.tokens[i];
+			}
+			count++;
+			(n < 0)? i-- : i++;
+		}
+
+		return new JSDOC.Token("", "VOID", "STREAM_ERROR"); // because null isn't an object and caller always expects an object
+	}
+}
+
+/**
+	@type JSDOC.Token|JSDOC.Token[]
+*/
+JSDOC.TokenStream.prototype.next = function(/**Number*/howMany) {
+	if (typeof howMany == "undefined") howMany = 1;
+	if (howMany < 1) return null;
+	var got = [];
+
+	for (var i = 1; i <= howMany; i++) {
+		if (this.cursor+i >= this.tokens.length) {
+			return null;
+		}
+		got.push(this.tokens[this.cursor+i]);
+	}
+	this.cursor += howMany;
+
+	if (howMany == 1) {
+		return got[0];
+	}
+	else return got;
+}
+
+/**
+	@type JSDOC.Token[]
+*/
+JSDOC.TokenStream.prototype.balance = function(/**String*/start, /**String*/stop) {
+	if (!stop) stop = JSDOC.Lang.matching(start);
+	
+	var depth = 0;
+	var got = [];
+	var started = false;
+	
+	while ((token = this.look())) {
+		if (token.is(start)) {
+			depth++;
+			started = true;
+		}
+		
+		if (started) {
+			got.push(token);
+		}
+		
+		if (token.is(stop)) {
+			depth--;
+			if (depth == 0) return got;
+		}
+		if (!this.next()) break;
+	}
+}
+
+JSDOC.TokenStream.prototype.getMatchingToken = function(/**String*/start, /**String*/stop) {
+	var depth = 0;
+	var cursor = this.cursor;
+	
+	if (!start) {
+		start = JSDOC.Lang.matching(stop);
+		depth = 1;
+	}
+	if (!stop) stop = JSDOC.Lang.matching(start);
+	
+	while ((token = this.tokens[cursor])) {
+		if (token.is(start)) {
+			depth++;
+		}
+		
+		if (token.is(stop) && cursor) {
+			depth--;
+			if (depth == 0) return this.tokens[cursor];
+		}
+		cursor++;
+	}
+}
+
+JSDOC.TokenStream.prototype.insertAhead = function(/**JSDOC.Token*/token) {
+	this.tokens.splice(this.cursor+1, 0, token);
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Util.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Util.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Util.js	(revision 2890)
@@ -0,0 +1,32 @@
+/**
+ * @namespace
+ * @deprecated Use {@link FilePath} instead.
+ */
+JSDOC.Util = {
+}
+
+/**
+ * @deprecated Use {@link FilePath.fileName} instead.
+ */
+JSDOC.Util.fileName = function(path) {
+	LOG.warn("JSDOC.Util.fileName is deprecated. Use FilePath.fileName instead.");
+	var nameStart = Math.max(path.lastIndexOf("/")+1, path.lastIndexOf("\\")+1, 0);
+	return path.substring(nameStart);
+}
+
+/**
+ * @deprecated Use {@link FilePath.fileExtension} instead.
+ */
+JSDOC.Util.fileExtension = function(filename) {
+	LOG.warn("JSDOC.Util.fileExtension is deprecated. Use FilePath.fileExtension instead.");
+	return filename.split(".").pop().toLowerCase();
+};
+
+/**
+ * @deprecated Use {@link FilePath.dir} instead.
+ */
+JSDOC.Util.dir = function(path) {
+	LOG.warn("JSDOC.Util.dir is deprecated. Use FilePath.dir instead.");
+	var nameStart = Math.max(path.lastIndexOf("/")+1, path.lastIndexOf("\\")+1, 0);
+	return path.substring(0, nameStart-1);
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Walker.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Walker.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/lib/JSDOC/Walker.js	(revision 2890)
@@ -0,0 +1,416 @@
+if (typeof JSDOC == "undefined") JSDOC = {};
+
+/** @constructor */
+JSDOC.Walker = function(/**JSDOC.TokenStream*/ts) {
+	this.init();
+	if (typeof ts != "undefined") {
+		this.walk(ts);
+	}
+}
+
+JSDOC.Walker.prototype.init = function() {
+	this.ts = null;
+
+	var globalSymbol = new JSDOC.Symbol("_global_", [], "GLOBAL", new JSDOC.DocComment(""));
+	globalSymbol.isNamespace = true;
+	globalSymbol.srcFile = "";
+	globalSymbol.isPrivate = false;
+	JSDOC.Parser.addSymbol(globalSymbol);
+	this.lastDoc = null;
+	this.token = null;
+	
+	/**
+		The chain of symbols under which we are currently nested.
+		@type Array
+	*/
+	this.namescope = [globalSymbol];
+	this.namescope.last = function(n){ if (!n) n = 0; return this[this.length-(1+n)] || "" };
+}
+
+JSDOC.Walker.prototype.walk = function(/**JSDOC.TokenStream*/ts) {
+	this.ts = ts;
+	while (this.token = this.ts.look()) {
+		if (this.token.popNamescope) {
+			
+			var symbol = this.namescope.pop();
+			if (symbol.is("FUNCTION")) {
+				if (this.ts.look(1).is("LEFT_PAREN") && symbol.comment.getTag("function").length == 0) {
+					symbol.isa = "OBJECT";
+				}
+			}
+		}
+		this.step();
+		if (!this.ts.next()) break;
+	}
+}
+
+JSDOC.Walker.prototype.step = function() {
+	if (this.token.is("JSDOC")) { // it's a doc comment
+	
+		var doc = new JSDOC.DocComment(this.token.data);
+		
+		if (doc.getTag("lends").length > 0) { // it's a new namescope
+			var lends = doc.getTag("lends")[0];
+
+			var name = lends.desc
+			if (!name) throw "@lends tag requires a value.";
+			
+			var symbol = new JSDOC.Symbol(name, [], "OBJECT", doc);
+			
+			this.namescope.push(symbol);
+			
+			var matching = this.ts.getMatchingToken("LEFT_CURLY");
+			if (matching) matching.popNamescope = name;
+			else LOG.warn("Mismatched } character. Can't parse code.");
+			
+			this.lastDoc = null;
+			return true;
+		}
+		else if (doc.getTag("name").length > 0 && doc.getTag("overview").length == 0) { // it's a virtual symbol
+			var virtualName = doc.getTag("name")[0].desc;
+			if (!virtualName) throw "@name tag requires a value.";
+			
+			var symbol = new JSDOC.Symbol(virtualName, [], "VIRTUAL", doc);
+			
+			JSDOC.Parser.addSymbol(symbol);
+			
+			this.lastDoc = null;
+			return true;
+		}
+		else if (doc.meta) { // it's a meta doclet
+			if (doc.meta == "@+") JSDOC.DocComment.shared = doc.src;
+			else if (doc.meta == "@-") JSDOC.DocComment.shared = "";
+			else if (doc.meta == "nocode+") JSDOC.Parser.conf.ignoreCode = true;
+			else if (doc.meta == "nocode-") JSDOC.Parser.conf.ignoreCode = JSDOC.opt.n;
+			else throw "Unrecognized meta comment: "+doc.meta;
+			
+			this.lastDoc = null;
+			return true;
+		}
+		else if (doc.getTag("overview").length > 0) { // it's a file overview
+			symbol = new JSDOC.Symbol("", [], "FILE", doc);
+			
+			JSDOC.Parser.addSymbol(symbol);
+			
+			this.lastDoc = null;
+			return true;
+		}
+		else {
+			this.lastDoc = doc;
+			return false;
+		}
+	}
+	else if (!JSDOC.Parser.conf.ignoreCode) { // it's code
+		if (this.token.is("NAME")) {
+			var symbol;
+			var name = this.token.data;
+			var doc = null; if (this.lastDoc) doc = this.lastDoc;
+			var params = [];
+			
+			// it's inside an anonymous object
+			if (this.ts.look(1).is("COLON") && this.ts.look(-1).is("LEFT_CURLY") && !(this.ts.look(-2).is("JSDOC") || this.namescope.last().comment.getTag("lends").length || this.ts.look(-2).is("ASSIGN") || this.ts.look(-2).is("COLON"))) {
+				name = "$anonymous";
+				name = this.namescope.last().alias+"-"+name
+				
+				params = [];
+				
+				symbol = new JSDOC.Symbol(name, params, "OBJECT", doc);
+
+				JSDOC.Parser.addSymbol(symbol);
+				
+				this.namescope.push(symbol);
+				
+				var matching = this.ts.getMatchingToken(null, "RIGHT_CURLY");
+				if (matching) matching.popNamescope = name;
+				else LOG.warn("Mismatched } character. Can't parse code.");
+			}
+			// function foo() {}
+			else if (this.ts.look(-1).is("FUNCTION") && this.ts.look(1).is("LEFT_PAREN")) {
+				var isInner;
+				
+				if (this.lastDoc) doc = this.lastDoc;
+				name = this.namescope.last().alias+"-"+name;
+				if (!this.namescope.last().is("GLOBAL")) isInner = true;
+				
+				params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN"));
+				
+				symbol = new JSDOC.Symbol(name, params, "FUNCTION", doc);
+				if (isInner) symbol.isInner = true;
+				
+			
+				JSDOC.Parser.addSymbol(symbol);
+				
+				this.namescope.push(symbol);
+				
+				var matching = this.ts.getMatchingToken("LEFT_CURLY");
+				if (matching) matching.popNamescope = name;
+				else LOG.warn("Mismatched } character. Can't parse code.");
+			}
+			// foo = function() {}
+			else if (this.ts.look(1).is("ASSIGN") && this.ts.look(2).is("FUNCTION")) {
+				var isInner;
+				if (this.ts.look(-1).is("VAR") || this.isInner) {
+					name = this.namescope.last().alias+"-"+name
+					if (!this.namescope.last().is("GLOBAL")) isInner = true;
+				}
+				else if (name.indexOf("this.") == 0) {
+					name = this.resolveThis(name);
+				}
+				
+				if (this.lastDoc) doc = this.lastDoc;
+				params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN"));
+				
+				symbol = new JSDOC.Symbol(name, params, "FUNCTION", doc);
+				if (isInner) symbol.isInner = true;
+				
+				JSDOC.Parser.addSymbol(symbol);
+				
+				this.namescope.push(symbol);
+				
+				var matching = this.ts.getMatchingToken("LEFT_CURLY");
+				if (matching) matching.popNamescope = name;
+				else LOG.warn("Mismatched } character. Can't parse code.");
+			}
+			// foo = new function() {}
+			else if (this.ts.look(1).is("ASSIGN") && this.ts.look(2).is("NEW") && this.ts.look(3).is("FUNCTION")) {
+				var isInner;
+				if (this.ts.look(-1).is("VAR") || this.isInner) {
+					name = this.namescope.last().alias+"-"+name
+					if (!this.namescope.last().is("GLOBAL")) isInner = true;
+				}
+				else if (name.indexOf("this.") == 0) {
+					name = this.resolveThis(name);
+				}
+				
+				if (this.lastDoc) doc = this.lastDoc;
+				params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN"));
+				
+				symbol = new JSDOC.Symbol(name, params, "OBJECT", doc);
+				if (isInner) symbol.isInner = true;
+				
+			
+				JSDOC.Parser.addSymbol(symbol);
+				
+				symbol.scopeType = "INSTANCE";
+				this.namescope.push(symbol);
+				
+				var matching = this.ts.getMatchingToken("LEFT_CURLY");
+				if (matching) matching.popNamescope = name;
+				else LOG.warn("Mismatched } character. Can't parse code.");
+			}
+			// foo: function() {}
+			else if (this.ts.look(1).is("COLON") && this.ts.look(2).is("FUNCTION")) {
+				name = (this.namescope.last().alias+"."+name).replace("#.", "#");
+				
+				if (this.lastDoc) doc = this.lastDoc;
+				params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN"));
+				
+				if (doc && doc.getTag("constructs").length) {
+					name = name.replace(/\.prototype(\.|$)/, "#");
+					
+					if (name.indexOf("#") > -1) name = name.match(/(^[^#]+)/)[0];
+					else name = this.namescope.last().alias;
+
+					symbol = new JSDOC.Symbol(name, params, "CONSTRUCTOR", doc);
+				}
+				else {
+					symbol = new JSDOC.Symbol(name, params, "FUNCTION", doc);
+				}
+				
+				
+				JSDOC.Parser.addSymbol(symbol);
+				
+				this.namescope.push(symbol);
+				
+				var matching = this.ts.getMatchingToken("LEFT_CURLY");
+				if (matching) matching.popNamescope = name;
+				else LOG.warn("Mismatched } character. Can't parse code.");
+			}
+			// foo = {}
+			else if (this.ts.look(1).is("ASSIGN") && this.ts.look(2).is("LEFT_CURLY")) {
+				var isInner;
+				if (this.ts.look(-1).is("VAR") || this.isInner) {
+					name = this.namescope.last().alias+"-"+name
+					if (!this.namescope.last().is("GLOBAL")) isInner = true;
+				}
+				else if (name.indexOf("this.") == 0) {
+					name = this.resolveThis(name);
+				}
+				
+				if (this.lastDoc) doc = this.lastDoc;
+				
+				symbol = new JSDOC.Symbol(name, params, "OBJECT", doc);
+				if (isInner) symbol.isInner = true;
+				
+			
+				if (doc) JSDOC.Parser.addSymbol(symbol);
+
+				this.namescope.push(symbol);
+				
+				var matching = this.ts.getMatchingToken("LEFT_CURLY");
+				if (matching) matching.popNamescope = name;
+				else LOG.warn("Mismatched } character. Can't parse code.");
+			}
+			// foo = x
+			else if (this.ts.look(1).is("ASSIGN")) {
+				
+				var isInner;
+				if (this.ts.look(-1).is("VAR") || this.isInner) {
+					name = this.namescope.last().alias+"-"+name
+					if (!this.namescope.last().is("GLOBAL")) isInner = true;
+				}
+				else if (name.indexOf("this.") == 0) {
+					name = this.resolveThis(name);
+				}
+				
+				if (this.lastDoc) doc = this.lastDoc;
+				
+				symbol = new JSDOC.Symbol(name, params, "OBJECT", doc);
+				if (isInner) symbol.isInner = true;
+				
+			
+				if (doc) JSDOC.Parser.addSymbol(symbol);
+			}
+			// foo: {}
+			else if (this.ts.look(1).is("COLON") && this.ts.look(2).is("LEFT_CURLY")) {
+				name = (this.namescope.last().alias+"."+name).replace("#.", "#");
+				
+				if (this.lastDoc) doc = this.lastDoc;
+				
+				symbol = new JSDOC.Symbol(name, params, "OBJECT", doc);
+				
+			
+				if (doc) JSDOC.Parser.addSymbol(symbol);
+				
+				this.namescope.push(symbol);
+				
+				var matching = this.ts.getMatchingToken("LEFT_CURLY");
+				if (matching) matching.popNamescope = name;
+				else LOG.warn("Mismatched } character. Can't parse code.");
+			}
+			// foo: x
+			else if (this.ts.look(1).is("COLON")) {
+				name = (this.namescope.last().alias+"."+name).replace("#.", "#");;
+				
+				if (this.lastDoc) doc = this.lastDoc;
+				
+				symbol = new JSDOC.Symbol(name, params, "OBJECT", doc);
+				
+			
+				if (doc) JSDOC.Parser.addSymbol(symbol);
+			}
+			// foo(...)
+			else if (this.ts.look(1).is("LEFT_PAREN")) {
+				var functionCall = {name: name};
+				if (!this.ts.look(2).is("RIGHT_PAREN")) functionCall.arg1 = this.ts.look(2).data;
+				
+				if (typeof JSDOC.PluginManager != "undefined") {
+					JSDOC.PluginManager.run("onFunctionCall", functionCall);
+					if (functionCall.doc) {
+						this.ts.insertAhead(new JSDOC.Token(functionCall.doc, "COMM", "JSDOC"));
+					}
+				}
+			}
+			this.lastDoc = null;
+		}
+		else if (this.token.is("FUNCTION")) { // it's an anonymous function
+			if (
+				(!this.ts.look(-1).is("COLON") || !this.ts.look(-1).is("ASSIGN"))
+				&& !this.ts.look(1).is("NAME")
+			) {
+				if (this.lastDoc) doc = this.lastDoc;
+				
+				name = "$anonymous";
+				name = this.namescope.last().alias+"-"+name
+				
+				params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN"));
+				
+				symbol = new JSDOC.Symbol(name, params, "FUNCTION", doc);
+				
+			
+				JSDOC.Parser.addSymbol(symbol);
+				
+				this.namescope.push(symbol);
+				
+				var matching = this.ts.getMatchingToken("LEFT_CURLY");
+				if (matching) matching.popNamescope = name;
+				else LOG.warn("Mismatched } character. Can't parse code.");
+			}
+		}
+	}
+	return true;
+}
+
+/**
+	Resolves what "this." means when it appears in a name.
+	@param name The name that starts with "this.".
+	@returns The name with "this." resolved.
+ */
+JSDOC.Walker.prototype.resolveThis = function(name) {
+	name.match(/^this\.(.+)$/)
+	var nameFragment = RegExp.$1;
+	if (!nameFragment) return name;
+	
+	var symbol = this.namescope.last();
+	var scopeType = symbol.scopeType || symbol.isa;
+
+	// if we are in a constructor function, `this` means the instance
+	if (scopeType == "CONSTRUCTOR") {
+		name = symbol.alias+"#"+nameFragment;
+	}
+	
+	// if we are in an anonymous constructor function, `this` means the instance
+	else if (scopeType == "INSTANCE") {
+		name = symbol.alias+"."+nameFragment;
+	}
+	
+	// if we are in a function, `this` means the container (possibly the global)
+	else if (scopeType == "FUNCTION") {
+		// in a method of a prototype, so `this` means the constructor
+		if (symbol.alias.match(/(^.*)[#.-][^#.-]+/)) {
+			var parentName = RegExp.$1;
+			var parent = JSDOC.Parser.symbols.getSymbol(parentName);
+
+			if (!parent) {
+				if (JSDOC.Lang.isBuiltin(parentName)) parent = JSDOC.Parser.addBuiltin(parentName);
+				else {
+					if (symbol.alias.indexOf("$anonymous") < 0) // these will be ignored eventually
+						LOG.warn("Can't document "+symbol.alias+" without first documenting "+parentName+".");
+				}
+			}
+			if (parent) name = parentName+(parent.is("CONSTRUCTOR")?"#":".")+nameFragment;
+		}
+		else {
+			parent = this.namescope.last(1);
+			name = parent.alias+(parent.is("CONSTRUCTOR")?"#":".")+nameFragment;
+		}
+	}
+	// otherwise it means the global
+	else {
+		name = nameFragment;
+	}
+	
+	return name;
+}
+
+JSDOC.Walker.onParamList = function(/**Array*/paramTokens) {
+	if (!paramTokens) {
+		LOG.warn("Malformed parameter list. Can't parse code.");
+		return [];
+	}
+	var params = [];
+	for (var i = 0, l = paramTokens.length; i < l; i++) {
+		if (paramTokens[i].is("JSDOC")) {
+			var paramType = paramTokens[i].data.replace(/(^\/\*\* *| *\*\/$)/g, "");
+			
+			if (paramTokens[i+1] && paramTokens[i+1].is("NAME")) {
+				i++;
+				params.push({type: paramType, name: paramTokens[i].data});
+			}
+		}
+		else if (paramTokens[i].is("NAME")) {
+			params.push({name: paramTokens[i].data});
+		}
+	}
+	return params;
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/main.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/main.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/main.js	(revision 2890)
@@ -0,0 +1,74 @@
+/**
+ * @version $Id: main.js 570 2008-04-07 23:54:50Z micmath $
+ */
+
+function main() {
+	IO.include("lib/JSDOC.js");
+	IO.includeDir("plugins/");
+	
+	if (JSDOC.opt.v) LOG.verbose = true;
+	if (JSDOC.opt.o) LOG.out = IO.open(JSDOC.opt.o);
+	
+	if (JSDOC.opt.T) {
+		LOG.inform("JsDoc Toolkit running in test mode at "+new Date()+".");
+		IO.include("frame/Testrun.js");
+		IO.include("test.js");
+	}
+	else {
+		LOG.inform("JsDoc Toolkit main() running at "+new Date()+".");
+		LOG.inform("With options: ");
+		for (var o in JSDOC.opt) {
+			LOG.inform("    "+o+": "+JSDOC.opt[o]);
+		}
+		
+		var jsdoc = new JSDOC.JsDoc();
+		
+		if (JSDOC.opt.Z) { // secret debugging option
+			LOG.warn("So you want to see the data structure, eh? This might hang if you have circular refs...");
+			IO.include("frame/Dumper.js");
+			var symbols = jsdoc.symbolSet.toArray();
+			for (var i = 0, l = symbols.length; i < l; i++) {
+				var symbol = symbols[i];
+				print("// symbol: " + symbol.alias);
+				print(symbol.serialize());
+			}
+		}
+		else {
+			var template = JSDOC.opt.t || System.getProperty("jsdoc.template.dir");
+
+			var handler = jsdoc.symbolSet.handler;
+			if (handler && handler.publish) {
+				handler.publish(jsdoc.symbolSet);
+			}
+			else {
+				if (typeof template != "undefined") {
+					try {
+						load(template+"/publish.js");
+						if (!publish) {
+							LOG.warn("No publish() function is defined in that template so nothing to do.");
+						}
+						else {
+							publish(jsdoc.symbolSet);
+						}
+					}
+					catch(e) {
+						LOG.warn("Sorry, that doesn't seem to be a valid template: "+template+"/publish.js : "+e);
+					}
+				}
+				else {
+					LOG.warn("No template or handlers given. Might as well read the usage notes.");
+					JSDOC.usage();
+				}
+			}
+		}
+	}
+	
+	if (LOG.warnings.length) {
+		print(LOG.warnings.length+" warning"+(LOG.warnings.length != 1? "s":"")+".");
+	}
+	
+	if (LOG.out) {
+		LOG.out.flush();
+		LOG.out.close();
+	}
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/plugins/commentSrcJson.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/plugins/commentSrcJson.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/plugins/commentSrcJson.js	(revision 2890)
@@ -0,0 +1,19 @@
+JSDOC.PluginManager.registerPlugin(
+	"JSDOC.commentSrcJson",
+	{
+		onDocCommentSrc: function(commentSrc) {
+			var json;
+			if (/^\s*@json\b/.test(commentSrc)) {
+				commentSrc = commentSrc.replace("@json", "");
+				eval("json = "+commentSrc);
+				var tagged = "";
+				for (var i in json) {
+					var tag = json[i];
+					// todo handle cases where tag is an object
+					tagged += "@"+i+" "+tag+"\n";
+				}
+				return tagged;
+			}
+		}
+	}
+);
Index: /CKReleaser/trunk/tools/jsdoc/app/plugins/frameworkPrototype.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/plugins/frameworkPrototype.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/plugins/frameworkPrototype.js	(revision 2890)
@@ -0,0 +1,16 @@
+JSDOC.PluginManager.registerPlugin(
+	"JSDOC.frameworkPrototype",
+	{
+		onPrototypeClassCreate: function(classCreator) {
+			var desc = "";
+			if (classCreator.comment) {
+				desc = classCreator.comment;
+			}
+			var insert = desc+"/** @name "+classCreator.name+"\n@constructor\n@scope "+classCreator.name+".prototype */"
+			
+			insert = insert.replace(/\*\/\/\*\*/g, "\n");
+			/*DEBUG*///print("insert is "+insert);
+			classCreator.addComment.data = insert;
+		}
+	}
+);
Index: /CKReleaser/trunk/tools/jsdoc/app/plugins/functionCall.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/plugins/functionCall.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/plugins/functionCall.js	(revision 2890)
@@ -0,0 +1,10 @@
+JSDOC.PluginManager.registerPlugin(
+	"JSDOC.functionCall",
+	{
+		onFunctionCall: function(functionCall) {
+			if (functionCall.name == "dojo.define" && functionCall.arg1) {
+				functionCall.doc = "/** @lends "+eval(functionCall.arg1)+".prototype */";
+			}
+		}
+	}
+);
Index: /CKReleaser/trunk/tools/jsdoc/app/plugins/publishSrcHilite.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/plugins/publishSrcHilite.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/plugins/publishSrcHilite.js	(revision 2890)
@@ -0,0 +1,62 @@
+JSDOC.PluginManager.registerPlugin(
+	"JSDOC.publishSrcHilite",
+	{
+		onPublishSrc: function(src) {
+			if (src.path in JsHilite.cache) {
+				return; // already generated src code
+			}
+			else JsHilite.cache[src.path] = true;
+		
+			try {
+				var sourceCode = IO.readFile(src.path);
+			}
+			catch(e) {
+				print(e.message);
+				quit();
+			}
+
+			var hiliter = new JsHilite(sourceCode, src.charset);
+			src.hilited = hiliter.hilite();
+		}
+	}
+);
+
+function JsHilite(src, charset) {
+
+	var tr = new JSDOC.TokenReader();
+	
+	tr.keepComments = true;
+	tr.keepDocs = true;
+	tr.keepWhite = true;
+	
+	this.tokens = tr.tokenize(new JSDOC.TextStream(src));
+	
+	// TODO is redefining toString() the best way?
+	JSDOC.Token.prototype.toString = function() { 
+		return "<span class=\""+this.type+"\">"+this.data.replace(/</g, "&lt;")+"</span>";
+	}
+	
+	if (!charset) charset = "utf-8";
+	
+	this.header = '<html><head><meta http-equiv="content-type" content="text/html; charset='+charset+'"> '+
+	"<style>\n\
+	.KEYW {color: #933;}\n\
+	.COMM {color: #bbb; font-style: italic;}\n\
+	.NUMB {color: #393;}\n\
+	.STRN {color: #393;}\n\
+	.REGX {color: #339;}\n\
+	.line {border-right: 1px dotted #666; color: #666; font-style: normal;}\n\
+	</style></head><body><pre>";
+	this.footer = "</pre></body></html>";
+	this.showLinenumbers = true;
+}
+
+JsHilite.cache = {};
+
+JsHilite.prototype.hilite = function() {
+	var hilited = this.tokens.join("");
+	var line = 1;
+	if (this.showLinenumbers) hilited = hilited.replace(/(^|\n)/g, function(m){return m+"<span class='line'>"+((line<10)? " ":"")+((line<100)? " ":"")+(line++)+"</span> "});
+	
+	return this.header+hilited+this.footer;
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/plugins/symbolLink.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/plugins/symbolLink.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/plugins/symbolLink.js	(revision 2890)
@@ -0,0 +1,9 @@
+JSDOC.PluginManager.registerPlugin(
+	"JSDOC.symbolLink",
+	{
+		onSymbolLink: function(link) {
+			// modify link.linkPath
+			// or link.linkText here
+		}
+	}
+);
Index: /CKReleaser/trunk/tools/jsdoc/app/plugins/tagParamConfig.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/plugins/tagParamConfig.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/plugins/tagParamConfig.js	(revision 2890)
@@ -0,0 +1,31 @@
+JSDOC.PluginManager.registerPlugin(
+	"JSDOC.tagParamConfig",
+	{
+		onDocCommentTags: function(comment) {
+			var currentParam = null;
+			var tags = comment.tags;
+			for (var i = 0, l = tags.length; i < l; i++) {
+				
+				if (tags[i].title == "param") {
+					if (tags[i].name.indexOf(".") == -1) {
+						currentParam = i;
+					}
+				}
+				else if (tags[i].title == "config") {
+					tags[i].title = "param";
+					if (currentParam == null) {
+						tags[i].name = "arguments"+"."+tags[i].name;
+					}
+					else if (tags[i].name.indexOf(tags[currentParam].name+".") != 0) {
+						tags[i].name = tags[currentParam].name+"."+tags[i].name;
+					}
+					currentParam != null
+					//tags[currentParam].properties.push(tags[i]);
+				}
+				else {
+					currentParam = null;
+				}
+			}
+		}
+	}
+);
Index: /CKReleaser/trunk/tools/jsdoc/app/plugins/tagSynonyms.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/plugins/tagSynonyms.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/plugins/tagSynonyms.js	(revision 2890)
@@ -0,0 +1,43 @@
+JSDOC.PluginManager.registerPlugin(
+	"JSDOC.tagSynonyms",
+	{
+		onDocCommentSrc: function(comment) {
+			comment.src = comment.src.replace(/@methodOf\b/i, "@function\n@memberOf");
+			comment.src = comment.src.replace(/@fieldOf\b/i, "@field\n@memberOf");
+		},
+		
+		onDocCommentTags: function(comment) {
+			for (var i = 0, l = comment.tags.length; i < l; i++) {
+				var title = comment.tags[i].title.toLowerCase();
+				var syn;
+				if ((syn = JSDOC.tagSynonyms.synonyms["="+title])) {
+					comment.tags[i].title = syn;
+				}
+			}
+		}
+	}
+);
+
+new Namespace(
+	"JSDOC.tagSynonyms",
+	function() {
+		JSDOC.tagSynonyms.synonyms = {
+			"=member":             "memberOf",
+			"=memberof":           "memberOf",
+			"=description":        "desc",
+			"=exception":          "throws",
+			"=argument":           "param",
+			"=returns":            "return",
+			"=classdescription":   "class",
+			"=fileoverview":       "overview",
+			"=extends":            "augments",
+			"=base":               "augments",
+			"=projectdescription": "overview",
+			"=classdescription":   "class",
+			"=link":               "see",
+			"=borrows":            "inherits",
+			"=scope":              "lends",
+			"=construct":          "constructor"
+		}
+	}
+);
Index: /CKReleaser/trunk/tools/jsdoc/app/run.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/run.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/run.js	(revision 2890)
@@ -0,0 +1,346 @@
+/**
+ * @fileOverview
+ * A bootstrap script that creates some basic required objects
+ * for loading other scripts.
+ * @author Michael Mathews, micmath@gmail.com
+ * @version $Id: run.js 554 2008-04-01 00:12:25Z micmath $
+ */
+
+/**
+ * @namespace Keep track of any messages from the running script.
+ */
+LOG = {
+	warn: function(msg, e) {
+		if (e) msg = e.fileName+", line "+e.lineNumber+": "+msg;
+		
+		msg = ">> WARNING: "+msg;
+		LOG.warnings.push(msg);
+		if (LOG.out) LOG.out.write(msg+"\n");
+		else print(msg);
+	},
+
+	inform: function(msg) {
+		msg = " > "+msg;
+		if (LOG.out) LOG.out.write(msg+"\n");
+		else if (typeof LOG.verbose != "undefined" && LOG.verbose) print(msg);
+	}
+};
+LOG.warnings = [];
+LOG.verbose = false
+LOG.out = undefined;
+
+/**
+ *	@class Manipulate a filepath.
+ */
+function FilePath(absPath, separator) {
+	this.slash =  separator || "/"; 
+	this.root = this.slash;
+	this.path = [];
+	this.file = "";
+	
+	var parts = absPath.split(/[\\\/]/);
+	if (parts) {
+		if (parts.length) this.root = parts.shift() + this.slash;
+		if (parts.length) this.file =  parts.pop()
+		if (parts.length) this.path = parts;
+	}
+	
+	this.path = this.resolvePath();
+}
+
+/** Collapse any dot-dot or dot items in a filepath. */
+FilePath.prototype.resolvePath = function() {
+	var resolvedPath = [];
+	for (var i = 0; i < this.path.length; i++) {
+		if (this.path[i] == "..") resolvedPath.pop();
+		else if (this.path[i] != ".") resolvedPath.push(this.path[i]);
+	}
+	return resolvedPath;
+}
+
+/** Trim off the filename. */
+FilePath.prototype.toDir = function() {
+	if (this.file) this.file = "";
+	return this;
+}
+
+/** Go up a directory. */
+FilePath.prototype.upDir = function() {
+	this.toDir();
+	if (this.path.length) this.path.pop();
+	return this;
+}
+
+FilePath.prototype.toString = function() {
+	return this.root
+		+ this.path.join(this.slash)
+		+ ((this.path.length > 0)? this.slash : "")
+		+ this.file;
+}
+
+/**
+ * Turn a path into just the name of the file.
+ */
+FilePath.fileName = function(path) {
+	var nameStart = Math.max(path.lastIndexOf("/")+1, path.lastIndexOf("\\")+1, 0);
+	return path.substring(nameStart);
+}
+
+/**
+ * Get the extension of a filename
+ */
+FilePath.fileExtension = function(filename) {
+   return filename.split(".").pop().toLowerCase();
+};
+
+/**
+ * Turn a path into just the directory part.
+ */
+FilePath.dir = function(path) {
+	var nameStart = Math.max(path.lastIndexOf("/")+1, path.lastIndexOf("\\")+1, 0);
+	return path.substring(0, nameStart-1);
+}
+
+
+importClass(java.lang.System);
+
+/**
+ * @namespace A collection of information about your system.
+ */
+SYS = {
+	/**
+	 * Information about your operating system: arch, name, version.
+	 * @type string
+	 */
+	os: [
+		new String(System.getProperty("os.arch")),
+		new String(System.getProperty("os.name")),
+		new String(System.getProperty("os.version"))
+	].join(", "),
+	
+	/**
+	 * Which way does your slash lean.
+	 * @type string
+	 */
+	slash: System.getProperty("file.separator")||"/",
+	
+	/**
+	 * The path to the working directory where you ran java.
+	 * @type string
+	 */
+	userDir: new String(System.getProperty("user.dir")),
+	
+	/**
+	 * Where is Java's home folder.
+	 * @type string
+	 */
+	javaHome: new String(System.getProperty("java.home")),
+	
+	/**
+	 * The absolute path to the directory containing this script.
+	 * @type string
+	 */
+	pwd: undefined
+};
+
+// jsrun appends an argument, with the path to here.
+if (arguments[arguments.length-1].match(/^-j=(.+)/)) {
+	if (RegExp.$1.charAt(0) == SYS.slash || RegExp.$1.charAt(1) == ":") { // absolute path to here
+		SYS.pwd = new FilePath(RegExp.$1).toDir().toString();
+	}
+	else { // relative path to here
+		SYS.pwd = new FilePath(SYS.userDir + SYS.slash + RegExp.$1).toDir().toString();
+	}
+	arguments.pop();
+}
+else {
+	print("The run.js script requires you use jsrun.jar.");
+	quit();
+}
+
+// shortcut
+var File = Packages.java.io.File;
+
+/**
+ * @namespace A collection of functions that deal with reading a writing to disk.
+ */
+IO = {
+
+	/**
+	 * Create a new file in the given directory, with the given name and contents.
+	 */
+	saveFile: function(/**string*/ outDir, /**string*/ fileName, /**string*/ content) {
+		var out = new Packages.java.io.PrintWriter(
+			new Packages.java.io.OutputStreamWriter(
+				new Packages.java.io.FileOutputStream(outDir+SYS.slash+fileName),
+				IO.encoding
+			)
+		);
+		out.write(content);
+		out.flush();
+		out.close();
+	},
+	
+	/**
+	 * @type string
+	 */
+	readFile: function(/**string*/ path) {
+		if (!IO.exists(path)) {
+			throw "File doesn't exist there: "+path;
+		}
+		return readFile(path, IO.encoding);
+	},
+
+	/**
+	 * @param inFile 
+	 * @param outDir
+	 * @param [fileName=The original filename]
+	 */
+	copyFile: function(/**string*/ inFile, /**string*/ outDir, /**string*/ fileName) {
+		if (fileName == null) fileName = FilePath.fileName(inFile);
+	
+		var inFile = new File(inFile);
+		var outFile = new File(outDir+SYS.slash+fileName);
+		
+		var bis = new Packages.java.io.BufferedInputStream(new Packages.java.io.FileInputStream(inFile), 4096);
+		var bos = new Packages.java.io.BufferedOutputStream(new Packages.java.io.FileOutputStream(outFile), 4096);
+		var theChar;
+		while ((theChar = bis.read()) != -1) {
+			bos.write(theChar);
+		}
+		bos.close();
+		bis.close();
+	},
+
+	/**
+	 * Creates a series of nested directories.
+	 */
+	mkPath: function(/**Array*/ path) {
+		if (path.constructor != Array) path = path.split(/[\\\/]/);
+		var make = "";
+		for (var i = 0, l = path.length; i < l; i++) {
+			make += path[i] + SYS.slash;
+			if (! IO.exists(make)) {
+				IO.makeDir(make);
+			}
+		}
+	},
+	
+	/**
+	 * Creates a directory at the given path.
+	 */
+	makeDir: function(/**string*/ path) {
+		(new File(path)).mkdir();
+	},
+
+	/**
+	 * @type string[]
+	 * @param dir The starting directory to look in.
+	 * @param [recurse=1] How many levels deep to scan.
+	 * @returns An array of all the paths to files in the given dir.
+	 */
+	ls: function(/**string*/ dir, /**number*/ recurse, _allFiles, _path) {
+		if (_path === undefined) { // initially
+			var _allFiles = [];
+			var _path = [dir];
+		}
+		if (_path.length == 0) return _allFiles;
+		if (recurse === undefined) recurse = 1;
+		
+		dir = new File(dir);
+		if (!dir.directory) return [String(dir)];
+		var files = dir.list();
+		
+		for (var f = 0; f < files.length; f++) {
+			var file = String(files[f]);
+			if (file.match(/^\.[^\.\/\\]/)) continue; // skip dot files
+	
+			if ((new File(_path.join(SYS.slash)+SYS.slash+file)).list()) { // it's a directory
+				_path.push(file);
+				if (_path.length-1 < recurse) IO.ls(_path.join(SYS.slash), recurse, _allFiles, _path);
+				_path.pop();
+			}
+			else {
+				_allFiles.push((_path.join(SYS.slash)+SYS.slash+file).replace(SYS.slash+SYS.slash, SYS.slash));
+			}
+		}
+	
+		return _allFiles;
+	},
+
+	/**
+	 * @type boolean
+	 */
+	exists: function(/**string*/ path) {
+		file = new File(path);
+	
+		if (file.isDirectory()){
+			return true;
+		}
+		if (!file.exists()){
+			return false;
+		}
+		if (!file.canRead()){
+			return false;
+		}
+		return true;
+	},
+
+	/**
+	 * 
+	 */
+	open: function(/**string*/ path, /**string*/ append) {
+		var append = true;
+		var outFile = new File(path);
+		var out = new Packages.java.io.PrintWriter(
+			new Packages.java.io.OutputStreamWriter(
+				new Packages.java.io.FileOutputStream(outFile, append),
+				IO.encoding
+			)
+		);
+		return out;
+	},
+
+	/**
+	 * Sets {@link IO.encoding}.
+	 * Encoding is used when reading and writing text to files,
+	 * and in the meta tags of HTML output.
+	 */
+	setEncoding: function(/**string*/ encoding) {
+		if (/ISO-8859-([0-9]+)/i.test(encoding)) {
+			IO.encoding = "ISO8859_"+RegExp.$1;
+		}
+		else {
+			IO.encoding = encoding;
+		}
+	},
+
+	/**
+	 * @default "utf-8"
+	 * @private
+	 */
+	encoding: "utf-8",
+	
+	/**
+	 * Load the given script.
+	 */
+	include: function(relativePath) {
+		load(SYS.pwd+relativePath);
+	},
+	
+	/**
+	 * Loads all scripts from the given directory path.
+	 */
+	includeDir: function(path) {
+		if (!path) return;
+		
+		for (var lib = IO.ls(SYS.pwd+path), i = 0; i < lib.length; i++) 
+			load(lib[i]);
+	}
+}
+
+// now run the application
+IO.include("frame.js");
+IO.include("main.js");
+
+main();
Index: /CKReleaser/trunk/tools/jsdoc/app/t/TestDoc.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/t/TestDoc.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/t/TestDoc.js	(revision 2890)
@@ -0,0 +1,144 @@
+var TestDoc = {
+	fails: 0,
+	plans: 0,
+	passes: 0,
+	results: []
+};
+
+TestDoc.record = function(result) {
+	TestDoc.results.push(result);
+	if (typeof result.verdict == "boolean") {
+		if (result.verdict === false) TestDoc.fails++;
+		if (result.verdict === true) TestDoc.passes++;
+	}
+}
+
+TestDoc.prove = function(filePath) {
+	if (typeof document != "undefined" && typeof document.write != "undefined") {
+		if (TestDoc.console) print = function(s) { TestDoc.console.appendChild(document.createTextNode(s+"\n")); }
+		else print = function(s) { document.write(s+"<br />"); }
+	}
+	TestDoc.run(TestDoc.readFile(filePath));
+}
+
+TestDoc.run = function(src) {
+	try { eval(src); } catch(e) { print("# ERROR! "+e); }
+	
+	var chunks = src.split(/\/\*t:/);
+	
+	var run = function(chunk) {
+		// local shortcuts
+		var is = TestDoc.assertEquals;
+		var isnt = TestDoc.assertNotEquals;
+		var plan = TestDoc.plan;
+		var requires = TestDoc.requires;
+		
+		try { eval(chunk); } catch(e) { print("# ERROR! "+e); }
+	}
+	for (var start = -1, end = 0; (start = src.indexOf("/*t:", end)) > end; start = end) {
+		run(
+			src.substring(
+				start+4,
+				(end = src.indexOf("*/", start))
+			)
+		);
+	}
+}
+
+TestDoc.Result = function(verdict, message) {
+	this.verdict = verdict;
+	this.message = message;
+}
+
+TestDoc.Result.prototype.toString = function() {
+	if (typeof this.verdict == "boolean") {
+		return (this.verdict? "ok" : "not ok") + " " + (++TestDoc.report.counter) + " - " + this.message;
+	}
+	
+	return "# " + this.message;
+}
+
+TestDoc.requires = function(file) {
+	if (!TestDoc.requires.loaded[file]) {
+		load(file);
+		TestDoc.requires.loaded[file] = true;
+	}
+}
+TestDoc.requires.loaded = {};
+
+TestDoc.report = function() {
+	TestDoc.report.counter = 0;
+	print("1.."+TestDoc.plans);
+	for (var i = 0; i < TestDoc.results.length; i++) {
+		print(TestDoc.results[i]);
+	}
+	print("----------------------------------------");
+	if (TestDoc.fails == 0 && TestDoc.passes == TestDoc.plans) {
+		print("All tests successful.");
+	}
+	else {
+		print("Failed " + TestDoc.fails + "/" + TestDoc.plans + " tests, "+((TestDoc.plans == 0)? 0 : Math.round(TestDoc.passes/(TestDoc.passes+TestDoc.fails)*10000)/100)+"% okay. Planned to run "+TestDoc.plans+", did run "+(TestDoc.passes+TestDoc.fails)+".")
+	}
+}
+
+TestDoc.plan = function(n, message) {
+	TestDoc.plans += n;
+	TestDoc.record(new TestDoc.Result(null, message+" ("+n+" tests)"));
+}
+
+TestDoc.assertEquals = function(a, b, message) {
+	var result = (a == b);
+	if (!result) message += "\n#\n# " + a + " does not equal " + b + "\n#";
+	TestDoc.record(new TestDoc.Result(result, message));
+}
+
+TestDoc.assertNotEquals = function(a, b, message) {
+	var result = (a != b);
+	if (!result) message += "\n#\n# " + a + " equals " + b + "\n#";
+	TestDoc.record(new TestDoc.Result(result, message));
+}
+
+TestDoc.readFile = (function(){
+	// rhino
+	if (typeof readFile == "function") {
+		return function(url) {
+			var text = readFile(url);
+			return text || "";
+		}
+	}
+
+	// a web browser
+	else {
+		return function(url) {
+			var httpRequest;
+		
+			if (window.XMLHttpRequest) { // Mozilla, Safari, etc
+				httpRequest = new XMLHttpRequest();
+			} 
+			else if (window.ActiveXObject) { // IE
+				try {
+					httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
+				} 
+				catch (e) {
+				   try {
+						httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
+					} 
+					catch (e) {
+					}
+				}
+			}
+		
+			if (!httpRequest) { throw "Cannot create HTTP Request."; }
+			
+			httpRequest.open('GET', url, false);
+			httpRequest.send('');
+			if (httpRequest.readyState == 4) {
+				if (httpRequest.status >= 400) {
+					throw "The HTTP Request returned an error code: "+httpRequest.status;
+				}
+			}
+			
+			return httpRequest.responseText || "";
+		}
+	}
+})();
Index: /CKReleaser/trunk/tools/jsdoc/app/t/runner.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/t/runner.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/t/runner.js	(revision 2890)
@@ -0,0 +1,13 @@
+// try: java -jar ../../jsrun.jar runner.js
+
+load("TestDoc.js");
+
+TestDoc.prove("../frame/Opt.js");
+TestDoc.prove("../lib/JSDOC.js");
+TestDoc.prove("../frame/String.js");
+TestDoc.prove("../lib/JSDOC/DocTag.js");
+TestDoc.prove("../lib/JSDOC/DocComment.js");
+TestDoc.prove("../lib/JSDOC/TokenReader.js");
+TestDoc.prove("../lib/JSDOC/Symbol.js");
+
+TestDoc.report();
Index: /CKReleaser/trunk/tools/jsdoc/app/test.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test.js	(revision 2890)
@@ -0,0 +1,304 @@
+load("app/frame/Dumper.js");
+function symbolize(opt) {
+	symbols = null;
+	jsdoc = new JSDOC.JsDoc(opt);
+	symbols = jsdoc.symbolSet;
+}
+
+
+var testCases = [
+	function() {
+		symbolize({a:true, p:true, _: [SYS.pwd+"test/overview.js"]});
+		//print(Dumper.dump(symbols));	
+		is('symbols.getSymbolByName("My Cool Library").name', 'My Cool Library', 'File overview can be found by alias.');		
+	}
+	,
+	function() {
+		symbolize({_: [SYS.pwd+"test/name.js"]});
+
+		is('symbols.getSymbol("Response").name', "Response", 'Virtual class name is found.');
+		is('symbols.getSymbol("Response#text").alias', "Response#text", 'Virtual method name is found.');
+		is('symbols.getSymbol("Response#text").memberOf', "Response", 'Virtual method parent name is found.');
+	}
+	,
+	function() {
+		symbolize({a:true, p:true, _: [SYS.pwd+"test/prototype.js"]});
+
+		is('symbols.getSymbol("Article").name', "Article", 'Function set to constructor prototype with inner constructor name is found.');
+		is('symbols.getSymbol("Article").hasMethod("init")', true, 'The initializer method name of prototype function is correct.');
+		is('symbols.getSymbol("Article").hasMember("counter")', true, 'A static property set in the prototype definition is found.');
+		is('symbols.getSymbol("Article").hasMember("title")', true, 'An instance property set in the prototype is found.');
+		is('symbols.getSymbol("Article#title").isStatic', false, 'An instance property has isStatic set to false.');
+		is('symbols.getSymbol("Article.counter").name', "counter", 'A static property set in the initializer has the name set correctly.');
+		is('symbols.getSymbol("Article.counter").memberOf', "Article", 'A static property set in the initializer has the memberOf set correctly.');
+		is('symbols.getSymbol("Article.counter").isStatic', true, 'A static property set in the initializer has isStatic set to true.');
+	}
+	,
+	function() {
+		symbolize({a:true, _: [SYS.pwd+"test/prototype_oblit.js"]});
+		
+		is('symbols.getSymbol("Article").name', "Article", 'Oblit set to constructor prototype name is found.');
+		is('typeof symbols.getSymbol("Article.prototype")', "undefined", 'The prototype oblit is not a symbol.');
+		is('symbols.getSymbol("Article#getTitle").name', "getTitle", 'The nonstatic method name of prototype oblit is correct.');
+		is('symbols.getSymbol("Article#getTitle").alias', "Article#getTitle", 'The alias of non-static method of prototype oblit is correct.');
+		is('symbols.getSymbol("Article#getTitle").isStatic', false, 'The isStatic of a nonstatic method of prototype oblit is correct.');
+		is('symbols.getSymbol("Article.getTitle").name', "getTitle", 'The static method name of prototype oblit is correct.');
+		is('symbols.getSymbol("Article.getTitle").isStatic', true, 'The isStatic of a static method of prototype oblit is correct.');
+		is('symbols.getSymbol("Article#getTitle").isa', "FUNCTION", 'The isa of non-static method of prototype oblit is correct.');
+		is('symbols.getSymbol("Article.getTitle").alias', "Article.getTitle", 'The alias of a static method of prototype oblit is correct.');
+		is('symbols.getSymbol("Article.getTitle").isa', "FUNCTION", 'The isa of static method of prototype oblit is correct.');
+	}
+	,
+	function() {
+		symbolize({a:true, p:true, _: [SYS.pwd+"test/prototype_oblit_constructor.js"]});
+		
+		is('symbols.getSymbol("Article").name', "Article", 'Oblit set to constructor prototype with inner constructor name is found.');
+		is('symbols.getSymbol("Article#init").name', "init", 'The initializer method name of prototype oblit is correct.');
+		is('symbols.getSymbol("Article").hasMember("pages")', true, 'Property set by initializer method "this" is on the outer constructor.');
+		is('symbols.getSymbol("Article#Title").name', "Title", 'Name of the inner constructor name is found.');
+		is('symbols.getSymbol("Article#Title").memberOf', "Article", 'The memberOf of the inner constructor name is found.');
+		is('symbols.getSymbol("Article#Title").isa', "CONSTRUCTOR", 'The isa of the inner constructor name is constructor.');
+		is('symbols.getSymbol("Article#Title").hasMember("title")', true, 'A property set on the inner constructor "this"  is on the inner constructor.');
+	}
+	,
+	function() {
+		symbolize({a:true, p:true, _: [SYS.pwd+"test/inner.js"]});
+		
+		is('symbols.getSymbol("Outer").name', "Outer", 'Outer constructor prototype name is found.');
+		is('symbols.getSymbol("Outer").methods.length', 1, 'Inner function doesnt appear as a method of the outer.');
+		is('symbols.getSymbol("Outer").hasMethod("open")', true, 'Outer constructors methods arent affected by inner function.');
+		is('symbols.getSymbol("Outer-Inner").alias', "Outer-Inner", 'Alias of inner function is found.');
+		is('symbols.getSymbol("Outer-Inner").isa', "CONSTRUCTOR", 'isa of inner function constructor is found.');
+		is('symbols.getSymbol("Outer-Inner").memberOf', "Outer", 'The memberOf of inner function is found.');
+		is('symbols.getSymbol("Outer-Inner").name', "Inner", 'The name of inner function is found.');
+		is('symbols.getSymbol("Outer-Inner#name").name', "name", 'A member of the inner function constructor, attached to "this" is found on inner.');
+		is('symbols.getSymbol("Outer-Inner#name").memberOf', "Outer-Inner", 'The memberOf of an inner function member is found.');		
+	}
+	,
+	function() {
+		symbolize({a:true, _: [SYS.pwd+"test/prototype_nested.js"]});
+		
+		is('symbols.getSymbol("Word").name', "Word", 'Base constructor name is found.');
+		is('symbols.getSymbol("Word").hasMethod("reverse")', true, 'Base constructor method is found.');
+		is('symbols.getSymbol("Word").methods.length', 1, 'Base constructor has only one method.');
+		is('symbols.getSymbol("Word").memberOf', "", 'Base constructor memberOf is empty.');
+		is('symbols.getSymbol("Word#reverse").name', "reverse", 'Member of constructor prototype name is found.');
+		is('symbols.getSymbol("Word#reverse").memberOf', "Word", 'Member of constructor prototype memberOf is found.');
+		is('symbols.getSymbol("Word#reverse.utf8").name', "utf8", 'Member of constructor prototype method name is found.');
+		is('symbols.getSymbol("Word#reverse.utf8").memberOf', "Word#reverse", 'Static nested member memberOf is found.');
+	}
+	,
+	function() {
+		symbolize({a:true, _: [SYS.pwd+"test/namespace_nested.js"]});
+		
+		is('symbols.getSymbol("ns1").name', "ns1", 'Base namespace name is found.');
+		is('symbols.getSymbol("ns1").memberOf', "", 'Base namespace memberOf is empty (its a constructor).');
+		is('symbols.getSymbol("ns1.ns2").name', "ns2", 'Nested namespace name is found.');
+ 		is('symbols.getSymbol("ns1.ns2").alias', "ns1.ns2", 'Nested namespace alias is found.');
+ 		is('symbols.getSymbol("ns1.ns2").memberOf', "ns1", 'Nested namespace memberOf is found.');
+ 		is('symbols.getSymbol("ns1.ns2.Function1").name', "Function1", 'Method of nested namespace name is found.');
+ 		is('symbols.getSymbol("ns1.ns2.Function1").memberOf', "ns1.ns2", 'Constructor of nested namespace memberOf is found.');			
+	}
+	,
+	function() {
+		symbolize({a:true, p:true, _: [SYS.pwd+"test/functions_nested.js"]});
+		
+		is('symbols.getSymbol("Zop").name', "Zop", 'Any constructor name is found.');
+		is('symbols.getSymbol("Zop").isa', "CONSTRUCTOR", 'It isa constructor.');
+		is('symbols.getSymbol("Zop").hasMethod("zap")', true, 'Its method name, set later, is in methods array.');
+		is('symbols.getSymbol("Foo").name', "Foo", 'The containing constructor name is found.');
+		is('symbols.getSymbol("Foo").hasMethod("methodOne")', true, 'Its method name is found.');
+		is('symbols.getSymbol("Foo").hasMethod("methodTwo")', true, 'Its second method name is found.');
+		is('symbols.getSymbol("Foo#methodOne").alias', "Foo#methodOne", 'A methods alias is found.');
+		is('symbols.getSymbol("Foo#methodOne").isStatic', false, 'A methods is not static.');
+		is('symbols.getSymbol("Bar").name', "Bar", 'A global function declared inside another function is found.');
+		is('symbols.getSymbol("Bar").isa', "FUNCTION", 'It isa function.');
+		is('symbols.getSymbol("Bar").memberOf', "_global_", 'It is global.');
+		is('symbols.getSymbol("Foo-inner").name', "inner", 'An inner functions name is found.');
+		is('symbols.getSymbol("Foo-inner").memberOf', "Foo", 'It is member of the outer function.');
+		is('symbols.getSymbol("Foo-inner").isInner', true, 'It is an inner function.');
+	}
+	,
+	function() {
+		symbolize({a:true, _: [SYS.pwd+"test/memberof_constructor.js"]});
+		
+		is('symbols.getSymbol("Circle#Tangent").name', "Tangent", 'Constructor set on prototype using @member has correct name.');
+ 		is('symbols.getSymbol("Circle#Tangent").memberOf', "Circle", 'Constructor set on prototype using @member has correct memberOf.');
+ 		is('symbols.getSymbol("Circle#Tangent").alias', "Circle#Tangent", 'Constructor set on prototype using @member has correct alias.');
+ 		is('symbols.getSymbol("Circle#Tangent").isa', "CONSTRUCTOR", 'Constructor set on prototype using @member has correct isa.');
+		is('symbols.getSymbol("Circle#Tangent").isStatic', false, 'Constructor set on prototype using @member is not static.');
+		is('symbols.getSymbol("Circle#Tangent#getDiameter").name', "getDiameter", 'Method set on prototype using @member has correct name.');
+		is('symbols.getSymbol("Circle#Tangent#getDiameter").memberOf', "Circle#Tangent", 'Method set on prototype using @member has correct memberOf.');
+		is('symbols.getSymbol("Circle#Tangent#getDiameter").alias', "Circle#Tangent#getDiameter", 'Method set on prototype using @member has correct alias.');
+		is('symbols.getSymbol("Circle#Tangent#getDiameter").isa', "FUNCTION", 'Method set on prototype using @member has correct isa.');
+		is('symbols.getSymbol("Circle#Tangent#getDiameter").isStatic', false, 'Method set on prototype using @member is not static.');
+	}
+	,
+	function() {
+		symbolize({a:true, p: true,  _: [SYS.pwd+"test/memberof.js"]});
+		
+		is('symbols.getSymbol("pack.install").alias', "pack.install", 'Using @memberOf sets alias, when parent name is in memberOf tag.');
+		is('symbols.getSymbol("pack.install.overwrite").name', "install.overwrite", 'Using @memberOf sets name, even if the name is dotted.');
+		is('symbols.getSymbol("pack.install.overwrite").memberOf', "pack", 'Using @memberOf sets memberOf.');
+ 		is('symbols.getSymbol("pack.install.overwrite").isStatic', true, 'Using @memberOf with value not ending in octothorp sets isStatic to true.');
+	}
+	,
+	function() {
+		symbolize({a:true, p:true, _: [SYS.pwd+"test/borrows.js"]});
+
+		is('symbols.getSymbol("Layout").name', "Layout", 'Constructor can be found.');
+		is('symbols.getSymbol("Layout").hasMethod("init")', true, 'Constructor method name can be found.');
+		is('symbols.getSymbol("Layout").hasMember("orientation")', true, 'Constructor property name can be found.');
+		
+		is('symbols.getSymbol("Page").hasMethod("reset")', true, 'Second constructor method name can be found.');
+		is('symbols.getSymbol("Page").hasMember("orientation")', true, 'Second constructor borrowed property name can be found in properties.');
+		is('symbols.getSymbol("Page#orientation").memberOf', "Page", 'Second constructor borrowed property memberOf can be found.');
+		is('symbols.getSymbol("Page").hasMethod("myGetInnerElements")', true, 'Can borrow an inner function, add it as a static function.');
+
+		is('symbols.getSymbol("ThreeColumnPage#init").alias', "ThreeColumnPage#init", 'Third constructor method can be found even though method with same name is borrowed.');
+		is('symbols.getSymbol("ThreeColumnPage#reset").alias', "ThreeColumnPage#reset", 'Borrowed method can be found.');
+		is('symbols.getSymbol("ThreeColumnPage#orientation").alias', "ThreeColumnPage#orientation", 'Twice borrowed method can be found.');
+	
+	}
+	,
+	function() {
+		symbolize({a:true, p:true, _: [SYS.pwd+"test/borrows2.js"]});
+
+		is('symbols.getSymbol("Foo").hasMethod("my_zop")', true, 'Borrowed method can be found.');		
+		is('symbols.getSymbol("Bar").hasMethod("my_zip")', true, 'Second borrowed method can be found.');
+	}
+	,
+	function() {
+		symbolize({a:true, p:true, _: [SYS.pwd+"test/constructs.js"]});
+
+		is('symbols.getSymbol("Person").hasMethod("say")', true, 'The constructs tag creates a class that lends can add a method to.');		
+	}
+	,
+	function() {
+		symbolize({a: true, _: [SYS.pwd+"test/augments.js", SYS.pwd+"test/augments2.js"]});
+		
+		is('symbols.getSymbol("Page").augments[0]', "Layout", 'An augmented class can be found.');
+		is('symbols.getSymbol("Page#reset").alias', "Page#reset", 'Method of augmenter can be found.');
+		is('symbols.getSymbol("Page").hasMethod("Layout#init")', true, 'Method from augmented can be found.');
+		is('symbols.getSymbol("Page").hasMember("Layout#orientation")', true, 'Property from augmented can be found.');
+		is('symbols.getSymbol("Page").methods.length', 3, 'Methods of augmented class are included in methods array.');
+	
+		is('symbols.getSymbol("ThreeColumnPage").augments[0]', "Page", 'The extends tag is a synonym for augments.');
+		is('symbols.getSymbol("ThreeColumnPage").hasMethod("ThreeColumnPage#init")', true, 'Local method overrides augmented method of same name.');
+		is('symbols.getSymbol("ThreeColumnPage").methods.length', 3, 'Local method count is right.');
+		
+		is('symbols.getSymbol("NewsletterPage").augments[0]', "ThreeColumnPage", 'Can augment across file boundaries.');
+		is('symbols.getSymbol("NewsletterPage").augments.length', 2, 'Multiple augments are supported.');
+		is('symbols.getSymbol("NewsletterPage").inherits[0].alias', "Junkmail#annoy", 'Inherited method with augments.');
+		is('symbols.getSymbol("NewsletterPage").methods.length', 6, 'Methods of augmented class are included in methods array across files.');
+		is('symbols.getSymbol("NewsletterPage").properties.length', 1, 'Properties of augmented class are included in properties array across files.');
+	}
+	,
+	function() {
+		symbolize({a:true, _: [SYS.pwd+"test/static_this.js"]});
+		
+		is('symbols.getSymbol("box.holder").name', "holder", 'Static namespace name can be found.');
+		is('symbols.getSymbol("box.holder.foo").name', "foo", 'Static namespace method name can be found.');
+		is('symbols.getSymbol("box.holder").isStatic', true, 'Static namespace method is static.');
+		
+		is('symbols.getSymbol("box.holder.counter").name', "counter", 'Instance namespace property name set on "this" can be found.');
+		is('symbols.getSymbol("box.holder.counter").alias', "box.holder.counter", 'Instance namespace property alias set on "this" can be found.');
+		is('symbols.getSymbol("box.holder.counter").memberOf', "box.holder", 'Static namespace property memberOf set on "this" can be found.');
+	}
+	,
+	function() {
+		symbolize({a:true, p: true, _: [SYS.pwd+"test/lend.js"]});
+
+		is('symbols.getSymbol("Person").name', "Person", 'Class defined in lend comment is found.');
+		is('symbols.getSymbol("Person").hasMethod("initialize")', true, 'Lent instance method name can be found.');
+		is('symbols.getSymbol("Person").hasMethod("say")', true, 'Second instance method can be found.');
+		is('symbols.getSymbol("Person#sing").isStatic', false, 'Instance method is known to be not static.');
+		
+		is('symbols.getSymbol("Person.getCount").name', "getCount", 'Static method name from second lend comment can be found.');
+		is('symbols.getSymbol("Person.getCount").isStatic', true, 'Static method from second lend comment is known to be static.');
+	
+		is('LOG.warnings.filter(function($){if($.indexOf("notok") > -1) return $}).length', 1, 'A warning is emitted when lending to an undocumented parent.');
+	}
+	,
+	function() {
+		symbolize({a:true, _: [SYS.pwd+"test/param_inline.js"]});
+	
+		is('symbols.getSymbol("Layout").params[0].type', "int", 'Inline param name is set.');
+		is('symbols.getSymbol("Layout").params[0].desc', "The number of columns.", 'Inline param desc is set from comment.');
+		is('symbols.getSymbol("Layout#getElement").params[0].name', "id", 'User defined param documentation takes precedence over parser defined.');
+		is('symbols.getSymbol("Layout#getElement").params[0].isOptional', true, 'Default for param is to not be optional.');
+		is('symbols.getSymbol("Layout#getElement").params[1].isOptional', false, 'Can mark a param as being optional.');
+		is('symbols.getSymbol("Layout#getElement").params[1].type', "number|string", 'Type of inline param doc can have multiple values.');
+		is('symbols.getSymbol("Layout#Canvas").params[0].type', "", 'Type can be not defined for some params.');
+		is('symbols.getSymbol("Layout#Canvas").params[2].type', "int", 'Type can be defined inline for only some params.');
+		is('symbols.getSymbol("Layout#rotate").params.length', 0, 'Docomments inside function sig is ignored without a param.');
+		is('symbols.getSymbol("Layout#init").params[2].type', "zoppler", 'Doc comment type overrides inline type for param with same name.');
+	}
+	,
+	function() {
+		symbolize({a: true, _: [SYS.pwd+"test/shared.js", SYS.pwd+"test/shared2.js"]});
+
+		is('symbols.getSymbol("Array#some").name', 'some', 'The name of a symbol in a shared section is found.');
+		is('symbols.getSymbol("Array#some").alias', 'Array#some', 'The alias of a symbol in a shared section is found.');
+		is('symbols.getSymbol("Array#some").desc', "Extension to builtin array.", 'A description can be shared.');
+		is('symbols.getSymbol("Array#filter").desc', "Extension to builtin array.\nChange every element of an array.", 'A shared description is appended.');
+		is('symbols.getSymbol("Queue").desc', "A first in, first out data structure.", 'A description is not shared when outside a shared section.');
+		is('symbols.getSymbol("Queue.rewind").alias', "Queue.rewind", 'Second shared tag can be started.');
+		is('symbols.getSymbol("startOver").alias', "startOver", 'Shared tag doesnt cross over files.');
+	}
+	,
+	function() {
+		symbolize({a: true, _: [SYS.pwd+"test/config.js"]});
+		is('symbols.getSymbol("Contact").params[0].name', 'person', 'The name of a param is found.');
+		is('symbols.getSymbol("Contact").params[1].name', 'person.name', 'The name of a param set with a dot name is found.');
+		is('symbols.getSymbol("Contact").params[2].name', 'person.age', 'The name of a param set with a dot name is found.');
+		is('symbols.getSymbol("Contact").params[4].name', 'connection', 'The name of a param after config is found.');
+		
+		is('symbols.getSymbol("Family").params[0].name', 'persons', 'Another name of a param is found.');
+		is('symbols.getSymbol("Family").params[1].name', 'persons.Father', 'The name of a param+config is found.');
+		is('symbols.getSymbol("Family").params[2].name', 'persons.Mother', 'The name of a second param+config is found.');
+		is('symbols.getSymbol("Family").params[3].name', 'persons.Children', 'The name of a third param+config is found.');
+			
+	}
+	,
+	function() {
+		symbolize({a:true, p:true, _: [SYS.pwd+"test/ignore.js"]});
+		is('LOG.warnings.filter(function($){if($.indexOf("undocumented symbol Ignored") > -1) return $}).length', 1, 'A warning is emitted when documenting members of an ignored parent.');
+
+	}
+	,
+	function() {
+		symbolize({a:true, p:true, _: [SYS.pwd+"test/functions_anon.js"]});
+		is('symbols.getSymbol("a.b").alias', 'a.b', 'In anonymous constructor this is found to be the container object.');
+		is('symbols.getSymbol("a.f").alias', 'a.f', 'In anonymous constructor this can have a method.');
+		is('symbols.getSymbol("a.c").alias', 'a.c', 'In anonymous constructor method this is found to be the container object.');
+		is('symbols.getSymbol("g").alias', 'g', 'In anonymous function executed inline this is the global.');
+		is('symbols.getSymbol("bar2.p").alias', 'bar2.p', 'In named constructor executed inline this is the container object.');
+		is('symbols.getSymbol("module.pub").alias', 'module.pub', 'In parenthesized anonymous function executed inline function scoped variables arent documented.');
+
+	}
+	,
+	function() {
+		symbolize({a:true, p:true, _: [SYS.pwd+"test/oblit_anon.js"]});
+		is('symbols.getSymbol("opt").name', 'opt', 'Anonymous object properties are assigned to $anonymous.');
+		is('symbols.getSymbol("opt.conf.keep").alias', 'opt.conf.keep', 'Anonymous object properties are assigned to $anonymous.');
+		is('symbols.getSymbol("opt.conf.base").alias', 'opt.conf.base', 'Anonymous object properties are assigned to $anonymous.');
+		
+	}
+	,
+	function() {
+		symbolize({a:true, p:true, _: [SYS.pwd+"test/params_optional.js"]});
+		is('symbols.getSymbol("Document").params.length', 3, 'Correct number of params are found when optional param syntax is used.');
+		is('symbols.getSymbol("Document").params[1].name', "id", 'Name of optional param is found.');
+		is('symbols.getSymbol("Document").params[1].isOptional', true, 'Optional param is marked isOptional.');
+		is('symbols.getSymbol("Document").params[2].name', "title", 'Name of optional param with default value is found.');
+		is('symbols.getSymbol("Document").params[2].isOptional', true, 'Optional param with default value is marked isOptional.');
+		is('symbols.getSymbol("Document").params[2].defaultValue', " This is untitled.", 'Optional param default value is found.');
+
+
+	}
+];
+
+
+//// run and print results
+print(testrun(testCases));
Index: /CKReleaser/trunk/tools/jsdoc/app/test/addon.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/addon.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/addon.js	(revision 2890)
@@ -0,0 +1,24 @@
+String.prototype.reverse = function() {
+}
+
+String.prototype.reverse.utf8 = function() {
+}
+
+Function.count = function() {
+}
+
+/** @memberOf Function */
+Function.count.reset = function() {
+}
+
+/** @memberOf Function */
+count.getValue = function() {
+}
+
+/** @memberOf Function.prototype */
+getSig = function() {
+}
+
+/** @memberOf Function.prototype */
+Function.prototype.getProps = function() {
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/anon_inner.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/anon_inner.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/anon_inner.js	(revision 2890)
@@ -0,0 +1,14 @@
+/**
+ * @name bar
+ * @namespace
+ */
+ 
+new function() {
+    /**
+     * @name bar-foo
+     * @function
+     * @param {number} x
+     */
+    function foo(x) {
+    }
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/augments.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/augments.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/augments.js	(revision 2890)
@@ -0,0 +1,31 @@
+/**
+@constructor
+*/
+function Layout(p) {
+	this.init = function(p) {
+	}
+	
+	this.getId = function() {
+	}
+	
+	/** @type Page */
+	this.orientation = "landscape";
+}
+
+/**
+@constructor
+@augments Layout
+*/
+function Page() {
+	this.reset = function(b) {
+	}
+}
+
+/**
+@extends Page
+@constructor
+*/
+function ThreeColumnPage() {
+	this.init = function(resetCode) {
+	}
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/augments2.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/augments2.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/augments2.js	(revision 2890)
@@ -0,0 +1,26 @@
+/**
+@constructor
+*/
+function LibraryItem() {
+	this.reserve = function() {
+	}
+}
+
+/**
+@constructor
+*/
+function Junkmail() {
+	this.annoy = function() {
+	}
+}
+
+/**
+@inherits Junkmail.prototype.annoy as pester
+@augments ThreeColumnPage
+@augments LibraryItem
+@constructor
+*/
+function NewsletterPage() {
+	this.getHeadline = function() {
+	}
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/borrows.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/borrows.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/borrows.js	(revision 2890)
@@ -0,0 +1,41 @@
+/**
+@constructor
+*/
+function Layout(p) {
+	/** initilize 1 */
+	this.init = function(p) {
+	}
+	
+	/** get the id */
+	this.getId = function() {
+	}
+	
+	/** @type string */
+	this.orientation = "landscape";
+	
+	function getInnerElements(elementSecretId){
+	}
+}
+
+/**
+@constructor
+@borrows Layout#orientation as #orientation
+@borrows Layout-getInnerElements as myGetInnerElements
+*/
+function Page() {
+	/** reset the page */
+	this.reset = function(b) {
+	}
+}
+
+/**
+@constructor
+@borrows Layout.prototype.orientation as this.orientation
+@borrows Layout.prototype.init as #init
+@inherits Page.prototype.reset as #reset
+*/
+function ThreeColumnPage() {
+	/** initilize 2 */
+	this.init = function(p) {
+	}
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/borrows2.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/borrows2.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/borrows2.js	(revision 2890)
@@ -0,0 +1,23 @@
+// testing circular borrows
+
+/**
+	@class
+	@borrows Bar#zop as this.my_zop
+*/
+function Foo() {
+	/** this is a zip. */
+	this.zip = function() {}
+	
+	this.my_zop = new Bar().zop;
+}
+
+/**
+	@class
+	@borrows Foo#zip as this.my_zip
+*/
+function Bar() {
+	/** this is a zop. */
+	this.zop = function() {}
+	
+	this.my_zip = new Foo().zip;
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/config.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/config.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/config.js	(revision 2890)
@@ -0,0 +1,22 @@
+/**
+ * @constructor
+ * @param person The person.
+ * @param {string} person.name The person's name.
+ * @config {integer} age The person's age.
+ * @config [id=1] Optional id number to use.
+ * @param connection
+ */
+function Contact(person, connection) {
+
+}
+
+/**
+ * @constructor
+ * @param persons
+ * @config {string} Father The paternal person.
+ * @config {string} Mother The maternal person.
+ * @config {string[]} Children And the rest.
+ */
+function Family(/**Object*/persons) {
+
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/constructs.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/constructs.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/constructs.js	(revision 2890)
@@ -0,0 +1,18 @@
+var Person = makeClass(
+    /**
+      @scope Person
+    */
+    {
+        /**
+        	This is just another way to define a constructor.
+        	@constructs
+        	@param {string} name The name of the person.
+         */
+        initialize: function(name) {
+            this.name = name;
+        },
+        say: function(message) {
+            return this.name + " says: " + message;
+        }
+    }
+);
Index: /CKReleaser/trunk/tools/jsdoc/app/test/encoding.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/encoding.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/encoding.js	(revision 2890)
@@ -0,0 +1,10 @@
+
+/**
+ * @Constructor
+ * @desc 配置文件
+ * @class 什么也不返回
+ */
+function Test(conf) {
+    // do something;
+}
+
Index: /CKReleaser/trunk/tools/jsdoc/app/test/encoding_other.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/encoding_other.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/encoding_other.js	(revision 2890)
@@ -0,0 +1,12 @@
+
+/**
+ * @Constructor
+ * @desc ðïîÛ
+ * @class ßàáâãäåæçèçìëêíîï °±²³´µ¡¶·¸¹
+ */
+function Test(conf) {
+    // do something;
+}
+
+// run with commanline option -e=iso-8859-5
+
Index: /CKReleaser/trunk/tools/jsdoc/app/test/functions_anon.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/functions_anon.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/functions_anon.js	(revision 2890)
@@ -0,0 +1,39 @@
+/** an anonymous constructor executed inline */
+a = new function() {
+	/** a.b*/
+    this.b = 1;
+    /** a.f */
+    this.f = function() {
+    	/** a.c */
+    	this.c = 2;
+    }
+}
+
+
+/**
+	named function executed inline
+*/
+bar1 = function Zoola1() {
+	/** property of global */
+	this.g = 1;
+}();
+
+/**
+	named constructor executed inline
+*/
+bar2 = new function Zoola2() {
+	/** property of bar */
+	this.p = 1;
+};
+
+/** module pattern */
+module = (function () {
+	/** won't appear in documentation */
+	var priv = 1;
+	
+	/** @scope module */
+	return {
+		/** will appear as a property of module */
+		pub: 1
+	}
+})();
Index: /CKReleaser/trunk/tools/jsdoc/app/test/functions_nested.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/functions_nested.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/functions_nested.js	(revision 2890)
@@ -0,0 +1,33 @@
+/** @constructor */
+function Zop() {
+}
+
+/**
+ @class
+*/
+Foo = function(id) {
+	// this is a bit twisted, but if you call Foo() you will then
+	// modify Foo(). This is kinda, sorta non-insane, because you
+	// would have to call Foo() 100% of the time to use Foo's methods
+	Foo.prototype.methodOne = function(bar) {
+	  alert(bar);
+	};
+	
+	// same again
+	Foo.prototype.methodTwo = function(bar2) {
+	  alert(bar2);
+	};
+	
+	// and these are only executed if the enclosing function is actually called
+	// and who knows if that will ever happen?
+	Bar = function(pez) {
+	  alert(pez);
+	};
+	Zop.prototype.zap = function(p){
+		alert(p);
+	};
+	
+	// but this is only visible inside Foo
+	function inner() {
+	}
+};
Index: /CKReleaser/trunk/tools/jsdoc/app/test/global.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/global.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/global.js	(revision 2890)
@@ -0,0 +1,13 @@
+/** ecks */
+var x = [1, 2, 4];
+
+var y = {
+	foo: function(){
+	}
+}
+
+bar = function() {
+}
+
+function zop() {
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/globals.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/globals.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/globals.js	(revision 2890)
@@ -0,0 +1,25 @@
+function example(/**Circle*/a, b) {
+	/** a global defined in function  */
+	var number = a;
+	
+	var hideNumber = function(){
+	}
+	
+	setNumber = function(){
+	}
+	alert('You have chosen: ' + b);
+}
+
+function initPage() {
+	var supported = document.createElement && document.getElementsByTagName;
+	if (!supported) return;
+	// start of DOM script
+	var x = document.getElementById('writeroot');
+	// etc.
+}
+
+/** an example var */
+var document = new Document(x, y);
+
+var getNumber = function(){
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/ignore.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/ignore.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/ignore.js	(revision 2890)
@@ -0,0 +1,10 @@
+/**
+ * A test constructor.
+ * @constructor
+ * @ignore
+ */
+function Ignored() {
+	/** a method */
+    this.bar = function() {
+    }
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/inner.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/inner.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/inner.js	(revision 2890)
@@ -0,0 +1,16 @@
+/**
+ * @constructor
+ */
+function Outer() {
+  /**
+   * @constructor
+   */
+  function Inner(name) {
+    /** The name of this. */
+    this.name = name;
+  }
+
+  this.open = function(name) {
+    return (new Inner(name));
+  }
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/jsdoc_test.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/jsdoc_test.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/jsdoc_test.js	(revision 2890)
@@ -0,0 +1,477 @@
+/** 
+ * @fileoverview This file is to be used for testing the JSDoc parser
+ * It is not intended to be an example of good JavaScript OO-programming,
+ * nor is it intended to fulfill any specific purpose apart from 
+ * demonstrating the functionality of the 
+ * <a href='http://sourceforge.net/projects/jsdoc'>JSDoc</a> parser
+ *
+ * @author Gabriel Reid gab_reid@users.sourceforge.net
+ * @version 0.1 
+ */
+
+
+/**
+ * Construct a new Shape object.
+ * @class This is the basic Shape class.  
+ * It can be considered an abstract class, even though no such thing
+ * really existing in JavaScript
+ * @constructor
+ * @throws MemoryException if there is no more memory 
+ * @throws GeneralShapeException rarely (if ever)
+ * @return {Shape|Coordinate} A new shape.
+ */
+function Shape(){
+  
+   /**
+    * This is an example of a function that is not given as a property
+    * of a prototype, but instead it is assigned within a constructor.
+    * For inner functions like this to be picked up by the parser, the
+    * function that acts as a constructor <b>must</b> be denoted with
+    * the <b>&#64;constructor</b> tag in its comment.
+    * @type String
+    */
+   this.getClassName = function(){
+      return "Shape";
+   }
+
+   /** 
+    * This is an inner method, just used here as an example
+    * @since version 0.5
+    * @author Sue Smart
+    */
+   function addReference(){
+       // Do nothing...
+   }
+   
+}
+
+/**
+ * Create a new Hexagon instance.
+ * @extends Shape
+ * @class Hexagon is a class that is a <i>logical</i> sublcass of 
+ * {@link Shape} (thanks to the <code>&#64;extends</code> tag), but in 
+ * reality it is completely unrelated to Shape.
+ * @param {int} sideLength The length of one side for the new Hexagon
+ * @example
+ * var h = new Hexagon(2);
+ * @example
+ * if (hasHex) {
+ *     hex   = new Hexagon(5);
+ *     color = hex.getColor();
+ * }
+ */
+function Hexagon(sideLength) {
+}
+
+
+/**
+ * This is an unattached (static) function that adds two integers together.
+ * @param {int} One The first number to add 
+ * @param {int} Two The second number to add 
+ * @author Gabriel Reid
+ * @deprecated So you shouldn't use it anymore!
+ */
+function Add(One, Two){
+    return One + Two;
+}
+
+
+/**
+ * The color of this shape
+ * @type Color
+ */
+Shape.prototype.color = null;
+
+/**
+ * The border of this shape. 
+ * @field
+ * @type int
+ */
+Shape.prototype.border = function(){return border;};
+
+/*
+ * These are all the instance method implementations for Shape
+ */
+
+/**
+ * Get the coordinates of this shape. It is assumed that we're always talking
+ * about shapes in a 2D location here.
+ * @requires The {@link Shape} class
+ * @returns A Coordinate object representing the location of this Shape
+ * @type Coordinate[]
+ */
+Shape.prototype.getCoords = function(){
+   return this.coords;
+}
+
+/**
+ * Get the color of this shape.
+ * @see #setColor
+ * @see The <a href="http://example.com">Color</a> library.
+ * @link Shape
+ * @type Color
+ */
+Shape.prototype.getColor = function(){
+   return this.color;
+}
+
+/**
+ * Set the coordinates for this Shape
+ * @param {Coordinate} coordinates The coordinates to set for this Shape
+ */
+Shape.prototype.setCoords = function(coordinates){
+   this.coords = coordinates;
+}
+
+/**
+ * Set the color for this Shape
+ * @param {Color} color The color to set for this Shape
+ * @param other There is no other param, but it can still be documented if
+ *              optional parameters are used
+ * @throws NonExistantColorException (no, not really!)
+ * @see #getColor
+ */
+Shape.prototype.setColor = function(color){
+   this.color = color;
+}
+
+/**
+ * Clone this shape
+ * @returns A copy of this shape
+ * @type Shape
+ * @author Gabriel Reid
+ */
+Shape.prototype.clone = function(){
+   return new Shape();
+}
+
+/**
+ * Create a new Rectangle instance. 
+ * @class A basic rectangle class, inherits from Shape.
+ * This class could be considered a concrete implementation class
+ * @constructor
+ * @param {int} width The optional width for this Rectangle
+ * @param {int} height Thie optional height for this Rectangle
+ * @author Gabriel Reid
+ * @see Shape is the base class for this
+ * @augments Shape
+ * @hilited
+ */
+function Rectangle(width, // This is the width 
+                  height // This is the height
+                  ){
+   if (width){
+      this.width = width;
+      if (height){
+	 this.height = height;
+      }
+   }
+}
+
+
+/* Inherit from Shape */
+Rectangle.prototype = new Shape();
+
+/**
+ * Value to represent the width of the Rectangle.
+ * <br>Text in <b>bold</b> and <i>italic</i> and a 
+ * link to <a href="http://sf.net">SourceForge</a>
+ * @private
+ * @type int
+ */
+Rectangle.prototype.width = 0;
+
+/**
+ * Value to represent the height of the Rectangle
+ * @private
+ * @type int
+ */
+Rectangle.prototype.height = 0;
+
+/**
+ * Get the type of this object. 
+ * @type String
+ */
+Rectangle.prototype.getClassName= function(){
+    return "Rectangle";
+}
+
+/**
+ * Get the value of the width for the Rectangle
+ * @type int
+ * @see Rectangle#setWidth
+ */
+Rectangle.prototype.getWidth = function(){
+   return this.width;
+}
+
+/**
+ * Get the value of the height for the Rectangle.
+ * Another getter is the {@link Shape#getColor} method in the 
+ * {@link Shape} base class.  
+ * @return The height of this Rectangle
+ * @type int
+ * @see Rectangle#setHeight
+ */
+Rectangle.prototype.getHeight = function(){
+    return this.height;
+}
+
+/**
+ * Set the width value for this Rectangle.
+ * @param {int} width The width value to be set
+ * @see #setWidth
+ */
+Rectangle.prototype.setWidth = function(width){
+   this.width = width;
+}
+
+/**
+ * Set the height value for this Rectangle.
+ * @param {int} height The height value to be set
+ * @see #getHeight
+ */
+Rectangle.prototype.setHeight = function(height){
+   this.height = height;
+}
+
+/**
+ * Get the value for the total area of this Rectangle
+ * @return total area of this Rectangle
+ * @type int
+ */
+Rectangle.prototype.getArea = function(){
+   return width * height;
+}
+
+
+/**
+ * Create a new Square instance.
+ * @class A Square is a subclass of {@link Rectangle}
+ * @param {int} width The optional width for this Rectangle
+ * @param {int} height The optional height for this Rectangle
+ * @augments Rectangle
+ */
+function Square(width, height){
+   if (width){
+      this.width = width;
+      if (height){
+	 this.height = height;
+      }
+   } 
+   
+}
+
+/* Square is a subclass of Rectangle */
+Square.prototype = new Rectangle();
+
+/**
+ * Set the width value for this Shape.
+ * @param {int} width The width value to be set
+ * @see #getWidth
+ */
+Square.prototype.setWidth = function(width){
+   this.width = this.height = width;
+}
+
+/**
+ * Set the height value for this Shape 
+ * Sets the {@link Rectangle#height} attribute in the Rectangle.
+ * @param {int} height The height value to be set
+ */
+Square.prototype.setHeight = function(height){
+   this.height = this.width = height;
+}
+
+
+/**
+ * Create a new Circle instance based on a radius.
+ * @class Circle class is another subclass of Shape
+ * @extends Shape
+ * @param {int} radius The optional radius of this {@link Circle }
+ * @mixin Square.prototype.setWidth as this.setDiameter
+ */
+function Circle(radius){
+   if (radius) {
+      /** The radius of the this Circle. */
+      this.radius = radius;
+   }
+}
+
+/* Circle inherits from {@link Shape} */
+Circle.prototype = new Shape();
+
+/** 
+ * The radius value for this Circle 
+ * @private
+ * @type int
+ */
+Circle.prototype.radius = 0;
+
+/** 
+ * A very simple class (static) field that is also a constant
+ * @final
+ * @type float
+ */
+Circle.PI = 3.14;
+
+/**
+ * Get the radius value for this Circle
+ * @type int
+ * @see #setRadius
+ */
+Circle.prototype.getRadius = function(){
+   return this.radius;
+}
+
+/** 
+ * Set the radius value for this Circle
+ * @param {int} radius The {@link Circle#radius} value to set
+ * @see #getRadius
+ */
+Circle.prototype.setRadius = function(radius){
+   this.radius = radius;
+}
+
+/** 
+ * An example of a  class (static) method that acts as a factory for Circle
+ * objects. Given a radius value, this method creates a new Circle.
+ * @param {int} radius The radius value to use for the new Circle.
+ * @type Circle
+ */
+Circle.createCircle = function(radius){
+    return new Circle(radius);
+}
+
+
+/**
+ * Create a new Coordinate instance based on x and y grid data.
+ * @class Coordinate is a class that can encapsulate location information.
+ * @param {int} [x=0] The optional x portion of the Coordinate
+ * @param {int} [y=0] The optinal y portion of the Coordinate
+ */
+function Coordinate(x, y){
+   if (x){
+      this.x = x;
+      if (y){
+	 this.y = y;
+      }
+   }
+}
+
+/** 
+ * The x portion of the Coordinate 
+ * @type int
+ * @see #getX
+ * @see #setX
+ */
+Coordinate.prototype.x = 0;
+
+/** 
+ * The y portion of the Coordinate 
+ * @type int
+ * @see #getY
+ * @see #setY
+ */
+Coordinate.prototype.y = 0;
+
+/**
+ * Gets the x portion of the Coordinate.
+ * @type int
+ * @see #setX
+ */
+Coordinate.prototype.getX = function(){
+   return this.x;
+}
+
+/** 
+ * Get the y portion of the Coordinate.
+ * @type int
+ * @see #setY
+ */
+Coordinate.prototype.getY = function(){
+   return this.y;
+}
+
+/**
+ * Sets the x portion of the Coordinate.
+ * @param {int} x The x value to set
+ * @see #getX
+ */
+Coordinate.prototype.setX = function(x){
+   this.x = x;
+}
+
+/** 
+ * Sets the y portion of the Coordinate.
+ * @param {int} y The y value to set
+ * @see #getY
+ */
+Coordinate.prototype.setY = function(y){
+   this.y = y;
+}
+
+/**
+ * @class This class exists to demonstrate the assignment of a class prototype
+ * as an anonymous block.
+ */
+function ShapeFactory(){
+}
+
+ShapeFactory.prototype = {
+   /** 
+    * Creates a new {@link Shape} instance.
+    * @return A new {@link Shape}
+    * @type Shape
+    */
+   createShape: function(){
+      return new Shape();
+   }
+}
+
+/**
+ * An example of a singleton class
+ * @param ... Arguments represent {@link coordinate}s in the shape.
+ * @constructor
+ */
+MySingletonShapeFactory = function(){
+
+   /**
+    * Get the next {@link Shape}
+    * @type Shape
+    * @return A new {@link Shape}
+    */
+   this.getShape = function(){ 
+      return null; 
+   }
+
+}
+
+
+/** 
+ * Create a new Foo instance.
+ * @class This is the Foo class. It exists to demonstrate 'nested' classes.
+ * @constructor 
+ * @see Foo.Bar
+ */
+function Foo(){}
+
+/** 
+ * Creates a new instance of Bar.
+ * @class This class exists to demonstrate 'nested' classes.
+ * @constructor 
+ * @see Foo.Bar
+ */
+function Bar(){}
+
+/** 
+ * Nested class
+ * @constructor 
+ */
+Foo.Bar = function(){
+	/** The x. */ this.x = 2;
+}
+
+Foo.Bar.prototype = new Bar();
+/** The y. */ 
+Foo.Bar.prototype.y = '3';
Index: /CKReleaser/trunk/tools/jsdoc/app/test/lend.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/lend.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/lend.js	(revision 2890)
@@ -0,0 +1,33 @@
+ /** @class  */
+var Person = Class.create(
+    /**
+      @lends Person.prototype
+    */
+    {
+      initialize: function(name) {
+            this.name = name;
+        },
+        say: function(message) {
+            return this.name + ': ' + message;
+        }
+    }
+ );
+
+/** @lends Person.prototype */
+{
+	/** like say but more musical */
+	sing: function(song) {
+	}
+}
+
+/** @lends Person */
+{
+	getCount: function() {
+	}
+}
+
+/** @lends Unknown.prototype */
+{
+	notok: function() {
+	}
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/memberof.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/memberof.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/memberof.js	(revision 2890)
@@ -0,0 +1,20 @@
+/** @constructor */
+pack = function() {
+	this.init = function(){}
+	function config(){}
+}
+ 
+ pack.build = function(task) {};
+
+/** @memberOf pack */
+pack.install = function() {}
+
+/** @memberOf pack */
+pack.install.overwrite = function() {}
+
+/** @memberOf pack */
+clean = function() {}
+
+
+/** @memberOf pack-config */
+install = function() {};
Index: /CKReleaser/trunk/tools/jsdoc/app/test/memberof_constructor.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/memberof_constructor.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/memberof_constructor.js	(revision 2890)
@@ -0,0 +1,15 @@
+/** @constructor */
+function Circle(){}
+
+/**
+ @constructor
+ @memberOf Circle.prototype
+ */
+Tangent = function(){};
+
+/**
+	@member Circle.prototype.Tangent.prototype
+*/
+getDiameter = function(){};
+
+
Index: /CKReleaser/trunk/tools/jsdoc/app/test/name.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/name.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/name.js	(revision 2890)
@@ -0,0 +1,19 @@
+/**
+ @name Response
+ @class
+*/
+
+Response.prototype = {
+	/**
+	 @name Response#text
+	 @function
+	 @description
+		Gets the body of the response as plain text
+	 @returns {String}
+		Response as text
+	*/
+
+	text: function() {
+		return this.nativeResponse.responseText;
+	}
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/namespace_nested.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/namespace_nested.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/namespace_nested.js	(revision 2890)
@@ -0,0 +1,23 @@
+/** 
+	@namespace This is the first namespace.
+*/
+ns1 = {};
+
+/** 
+	This is the second namespace.
+	@namespace
+*/
+ns1.ns2 = {};
+
+/**
+	This part of ns1.ns2
+	@constructor
+*/
+ns1.ns2.Function1 = function() {
+};
+
+ns1.staticFunction = function() {
+};
+
+/** A static field in a namespace. */
+ns1.ns2.staticField = 1;
Index: /CKReleaser/trunk/tools/jsdoc/app/test/nocode.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/nocode.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/nocode.js	(revision 2890)
@@ -0,0 +1,13 @@
+/**#nocode+*/
+    /**
+      @name star
+      @function
+    */
+    function blahblah() {
+    
+    }
+/**#nocode-*/
+
+function yaddayadda() {
+
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/oblit_anon.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/oblit_anon.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/oblit_anon.js	(revision 2890)
@@ -0,0 +1,20 @@
+/** the options */
+opt = Opt.get(
+	arguments, 
+	{
+	 d: "directory",
+	 c: "conf",
+	 "D[]": "define"
+	}
+);
+
+/** configuration */
+opt.conf = {
+	/** keep */
+	keep: true,
+	/** base */
+	base: getBase(this, {p: properties})
+}
+
+
+
Index: /CKReleaser/trunk/tools/jsdoc/app/test/overview.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/overview.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/overview.js	(revision 2890)
@@ -0,0 +1,20 @@
+/**
+ * @overview This "library" contains a 
+ *               lot of classes and functions.
+ * @example
+ <pre>
+	var x (x < 1);
+	alert("This 'is' \"code\"");
+ </pre>
+ * @name My Cool Library
+ * @author 	Joe Smith jsmith@company.com
+ * @version 	0.1 
+ */
+ 
+/** 
+ * Gets the current foo 
+ * @param {String} fooId	The unique identifier for the foo.
+ * @return {Object}	Returns the current foo.
+ */
+function getFoo(fooID){
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/param_inline.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/param_inline.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/param_inline.js	(revision 2890)
@@ -0,0 +1,37 @@
+/**
+	@constructor
+	@param columns The number of columns.
+*/
+function Layout(/**int*/columns){
+	/**
+		@param [id] The id of the element.
+		@param elName The name of the element.
+	*/
+	this.getElement = function(
+		/** string */ elName,
+		/** number|string */ id
+	) {
+	};
+	
+	/** 
+		@constructor
+	 */
+	this.Canvas = function(top, left, /**int*/width, height) {
+		/** Is it initiated yet? */
+		this.initiated = true;
+	}
+	
+	this.rotate = function(/**nothing*/) {
+	}
+	
+	/** 
+	@param x
+	@param y
+	@param {zoppler} z*/
+	this.init = function(x, y, /**abbler*/z) {
+		/** The xyz. */
+		this.xyz = x+y+z;
+		this.getXyz = function() {
+		}
+	}
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/params_optional.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/params_optional.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/params_optional.js	(revision 2890)
@@ -0,0 +1,8 @@
+
+/**
+ * @param {Page[]} pages
+ * @param {number} [id] Specifies the id, if applicable.
+ * @param {String} [title = This is untitled.] Specifies the title.
+ */
+function Document(pages, id, title){
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/prototype.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/prototype.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/prototype.js	(revision 2890)
@@ -0,0 +1,17 @@
+/** @constructor */
+function Article() {
+}
+
+Article.prototype.init = function(title) {
+	/** the instance title */
+	this.title = title;
+	
+	/** the static counter */
+	Article.counter = 1;
+}
+
+a = new Article();
+a.Init("my title");
+
+print(a.title);
+print(Article.counter);
Index: /CKReleaser/trunk/tools/jsdoc/app/test/prototype_nested.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/prototype_nested.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/prototype_nested.js	(revision 2890)
@@ -0,0 +1,9 @@
+/** @constructor */
+function Word() {
+}
+
+Word.prototype.reverse = function() {
+}
+
+Word.prototype.reverse.utf8 = function() {
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/prototype_oblit.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/prototype_oblit.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/prototype_oblit.js	(revision 2890)
@@ -0,0 +1,13 @@
+/** @constructor */
+function Article() {
+}
+
+Article.prototype = {
+	/** instance get title */
+	getTitle: function(){
+	}
+}
+
+/** static get title */
+Article.getTitle = function(){
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/prototype_oblit_constructor.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/prototype_oblit_constructor.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/prototype_oblit_constructor.js	(revision 2890)
@@ -0,0 +1,24 @@
+/** @constructor */
+function Article() {
+}
+
+Article.prototype = {
+	/** @constructor */
+	Title: function(title) {
+		/** the value of the Title instance */
+		this.title = title;
+	},
+	
+	init: function(pages) {
+		/** the value of the pages of the Article instance */
+		this.pages = pages;
+	}
+}
+
+f = new Article();
+f.init("one two three");
+
+t = new f.Title("my title");
+
+print(f.pages);
+print(t.title);
Index: /CKReleaser/trunk/tools/jsdoc/app/test/public.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/public.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/public.js	(revision 2890)
@@ -0,0 +1,10 @@
+/**@constructor*/
+function Foo() {
+	/**
+		@public
+		@static
+		@field
+	*/
+	var bar = function(x) {
+	}
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/shared.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/shared.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/shared.js	(revision 2890)
@@ -0,0 +1,42 @@
+
+/**
+ * Builtin object.
+ * @class
+ * @name Array
+ */
+ 
+/**#@+
+ * Extension to builtin array.
+ * @memberOf Array
+ * @method
+ */
+ 
+/**
+ * @returns Boolen if some array members...
+ */
+Array.prototype.some = function(){};
+
+/**
+ * Change every element of an array.
+ * @returns Filtered array copy.
+ */
+Array.prototype.filter = function(){};
+
+/**#@-*/
+
+
+/**
+ * A first in, first out data structure.
+ * @constructor
+ */
+Queue = function(){};
+
+/**#@+
+ * Extension to Queue.
+ * @memberOf Queue
+ */
+
+rewind = function(){
+}
+
+// should close automatically here.
Index: /CKReleaser/trunk/tools/jsdoc/app/test/shared2.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/shared2.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/shared2.js	(revision 2890)
@@ -0,0 +1,2 @@
+startOver = function(){
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/shortcuts.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/shortcuts.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/shortcuts.js	(revision 2890)
@@ -0,0 +1,22 @@
+// /**#=+
+//  * {
+//  *	'D': 'Date.prototype',
+//  *	'$N': 'Number'
+//  * }
+//  */
+// var D = Date.prototype,
+// $N = Number;
+// 
+// D.locale = function(){
+// };
+// 
+// /**
+// 	@return {string} The cardinal number string.
+// */
+// $N.nth = function(n){
+// };
+// 
+// LOAD.file = function(){
+// }
+// 
+// /**#=-*/
Index: /CKReleaser/trunk/tools/jsdoc/app/test/static_this.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/static_this.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/static_this.js	(revision 2890)
@@ -0,0 +1,13 @@
+/** the parent */
+var box = {};
+
+/** @namespace */
+box.holder = {}
+
+box.holder.foo = function() {
+	/** the counter */
+	this.counter = 1;
+}
+
+box.holder.foo();
+print(box.holder.counter);
Index: /CKReleaser/trunk/tools/jsdoc/app/test/synonyms.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/synonyms.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/synonyms.js	(revision 2890)
@@ -0,0 +1,23 @@
+/**
+	@class
+	@inherits Bar#zop as #my_zop
+*/
+function Foo() {
+	/** this is a zip. */
+	this.zip = function() {}
+	
+	/** from Bar */
+	this.my_zop = new Bar().zop;
+}
+
+/**
+	@class
+	@borrows Foo#zip as this.my_zip
+*/
+function Bar() {
+	/** this is a zop. */
+	this.zop = function() {}
+	
+	/** from Foo */
+	this.my_zip = new Foo().zip;
+}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/tosource.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/tosource.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/tosource.js	(revision 2890)
@@ -0,0 +1,23 @@
+/**
+ * @param {Object} object
+ * @return {string}
+ */
+function valueOf(object) {}
+
+/**
+ * @param {Object} object
+ * @return {string}
+ */
+function toString(object) {}
+
+/**
+ * @param {Object} object
+ * @return {string}
+ */
+function toSource(object) {}
+
+/**
+ * @param {Object} object
+ * @return {string}
+ */
+function constructor(object) {}
Index: /CKReleaser/trunk/tools/jsdoc/app/test/variable_redefine.js
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/app/test/variable_redefine.js	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/app/test/variable_redefine.js	(revision 2890)
@@ -0,0 +1,14 @@
+/** @constructor */
+function Foo() {
+	var bar = 1;
+	bar = 2; // redefining a private
+	
+	this.baz = 1;
+	baz = 2; // global
+	
+	/** a private */
+	var blap = {
+		/** in here */
+		tada: 1
+	}
+}
Index: /CKReleaser/trunk/tools/jsdoc/changes.txt
===================================================================
--- /CKReleaser/trunk/tools/jsdoc/changes.txt	(revision 2890)
+++ /CKReleaser/trunk/tools/jsdoc/changes.txt	(revision 2890)
@@ -0,0 +1,53 @@
+== 2.0.2 ==
+
+  * Fixed bug that sometimes caused an example of division in the source code to be interpretted as a regex by the JsDoc Toolkit analyzer. ( issue #158 )
+  * Fixed a bug that prevented private variables marked as @public from appearing in the documentation. ( issue #161 )
+  * Fixed bug that prevented variable names with underscored properties from appearing in summaries. ( issue #173 )
+
+== 2.0.1 ==
+
+  * Fixed bug that prevented @fileOverview tag from being recognized.
+  * Added support for @fieldOf as a synonym for @field plus @memberOf.
+  * Added support for @name tag in a @fileOverview comment to control the displayed name of the file.
+  * Added support for multiple @example tags. ( issue #152 )
+  * Modified style sheet of jsdoc template to make more readable. ( issue #151 )
+  * Fixed bug that prevented @since documentation from displaying correctly when it appeared in a class. ( issue #150 )
+  * Fixed bug that caused inhertited properties to sometimes not resolve correctly. ( issue #144 )
+  * Modified so that trailing whitespace in @example is always trimmed. ( issue #153 )
+  * Added support for elseif to JsPlate. (hat tip to fredck)
+  * Added support for @location urls in the @overview comment to the jsdoc template.
+
+== Changes From Versions 1.4.0 to 2.0.0 ==
+
+  * Upgraded included version of Rhino from 1.6 to 1.7R1.
+  * Removed circular references in parsed documentation objects.
+  * Improved inheritance handling, now properties and events can be inherited same as methods.
+  * Improved handling of cross-file relationships, now having two related objects in separate files is not a problem.
+  * Improved ability to recognize membership of previously defined objects.
+  * Added ability to redefine parsing behavior with plugins.
+  * @methodOf is a synonym for @function and @memberOf.
+  * Added @default to document default values of members that are objects.
+  * Added ability to parse and refer to inner functions.
+  * Fixed bug that appeared when calling a method to set properties of the instance referred to by "this".
+  * Added ability to automatically create links to other symbols.
+  * New "jsdoc" template now produces fully W3C valid XHTML.
+  * Inline parameter type hint comments are now documented.
+  * Fixed error: Locally scoped variables (declared with var) no longer appear as global.
+  * It is now possible to run JsDoc Toolkit from any directory.
+  * Added support for inline {@link ...} tags.
+  * Added support for the -H command-line option to allow for custom content handlers.
+  * Tag names @inherits and @scope changed to @borrows and @lends.
+  ? Combining @constructor in a doclet with @lends now supported.
+  * Multiple @lend tags now supported.
+  * Added support for the @constructs tag, used inside a @lends block.
+  * Added support for the @constant tag.
+  * Fixed bug that prevented the use of [] as a default value.
+  * Added support for the @field tag.
+  * Added support for the @public tag (applied to inner functions).
+  * @namespace tag can now be applied to functions, not just object literals.
+  * Added support for the -s command line option to suppress source code output.
+  * Added new unit test framework.
+  * Underscored symbols are now treated as if they have a @private tag by default.
+  * Improved support for anonymous constructors.
+  * Added support for the nocode meta tag.
+  
Index: /CKReleaser/trunk/tools/json/License.txt
===================================================================
--- /CKReleaser/trunk/tools/json/License.txt	(revision 2890)
+++ /CKReleaser/trunk/tools/json/License.txt	(revision 2890)
@@ -0,0 +1,9 @@
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Index: /CKReleaser/trunk/tools/json/json2.js
===================================================================
--- /CKReleaser/trunk/tools/json/json2.js	(revision 2890)
+++ /CKReleaser/trunk/tools/json/json2.js	(revision 2890)
@@ -0,0 +1,478 @@
+/*
+    http://www.JSON.org/json2.js
+    2008-11-19
+
+    Public Domain.
+
+    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+    See http://www.JSON.org/js.html
+
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
+
+        JSON.stringify(value, replacer, space)
+            value       any JavaScript value, usually an object or array.
+
+            replacer    an optional parameter that determines how object
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
+
+            space       an optional parameter that specifies the indentation
+                        of nested structures. If it is omitted, the text will
+                        be packed without extra whitespace. If it is a number,
+                        it will specify the number of spaces to indent at each
+                        level. If it is a string (such as '\t' or '&nbsp;'),
+                        it contains the characters used to indent at each level.
+
+            This method produces a JSON text from a JavaScript value.
+
+            When an object value is found, if the object contains a toJSON
+            method, its toJSON method will be called and the result will be
+            stringified. A toJSON method does not serialize: it returns the
+            value represented by the name/value pair that should be serialized,
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the object holding the key.
+
+            For example, this would serialize Dates as ISO strings.
+
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
+
+                    return this.getUTCFullYear()   + '-' +
+                         f(this.getUTCMonth() + 1) + '-' +
+                         f(this.getUTCDate())      + 'T' +
+                         f(this.getUTCHours())     + ':' +
+                         f(this.getUTCMinutes())   + ':' +
+                         f(this.getUTCSeconds())   + 'Z';
+                };
+
+            You can provide an optional replacer method. It will be passed the
+            key and value of each member, with this bound to the containing
+            object. The value that is returned from your method will be
+            serialized. If your method returns undefined, then the member will
+            be excluded from the serialization.
+
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
+            stringified.
+
+            Values that do not have JSON representations, such as undefined or
+            functions, will not be serialized. Such values in objects will be
+            dropped; in arrays they will be replaced with null. You can use
+            a replacer function to replace those with JSON values.
+            JSON.stringify(undefined) returns undefined.
+
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
+
+            If the space parameter is a non-empty string, then that string will
+            be used for indentation. If the space parameter is a number, then
+            the indentation will be that many spaces.
+
+            Example:
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}]);
+            // text is '["e",{"pluribus":"unum"}]'
+
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
+
+        JSON.parse(text, reviver)
+            This method parses a JSON text to produce an object or array.
+            It can throw a SyntaxError exception.
+
+            The optional reviver parameter is a function that can filter and
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
+
+            Example:
+
+            // Parse the text. Values that look like ISO date strings will
+            // be converted to Date objects.
+
+            myData = JSON.parse(text, function (key, value) {
+                var a;
+                if (typeof value === 'string') {
+                    a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+                    if (a) {
+                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+                            +a[5], +a[6]));
+                    }
+                }
+                return value;
+            });
+
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
+
+
+    This is a reference implementation. You are free to copy, modify, or
+    redistribute.
+
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
+*/
+
+/*jslint evil: true */
+
+/*global JSON */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
+*/
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (!this.JSON) {
+    JSON = {};
+}
+(function () {
+
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
+
+    if (typeof Date.prototype.toJSON !== 'function') {
+
+        Date.prototype.toJSON = function (key) {
+
+            return this.getUTCFullYear()   + '-' +
+                 f(this.getUTCMonth() + 1) + '-' +
+                 f(this.getUTCDate())      + 'T' +
+                 f(this.getUTCHours())     + ':' +
+                 f(this.getUTCMinutes())   + ':' +
+                 f(this.getUTCSeconds())   + 'Z';
+        };
+
+        String.prototype.toJSON =
+        Number.prototype.toJSON =
+        Boolean.prototype.toJSON = function (key) {
+            return this.valueOf();
+        };
+    }
+
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
+
+
+    function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
+
+
+    function str(key, holder) {
+
+// Produce a string from holder[key].
+
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
+
+// What happens next depends on the value's type.
+
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
+
+        case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+            return isFinite(value) ? String(value) : 'null';
+
+        case 'boolean':
+        case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+            return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+        case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+            if (!value) {
+                return 'null';
+            }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+            gap += indent;
+            partial = [];
+
+// Is the value an array?
+
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
+        }
+    }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+            var i;
+            gap = '';
+            indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
+                }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+            return str('', {'': value});
+        };
+    }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+            var j;
+
+            function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
+                            }
+                        }
+                    }
+                }
+                return reviver.call(holder, key, value);
+            }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+            if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+            throw new SyntaxError('JSON.parse');
+        };
+    }
+})();
Index: /CKReleaser/trunk/tools/tartool/License.txt
===================================================================
--- /CKReleaser/trunk/tools/tartool/License.txt	(revision 2890)
+++ /CKReleaser/trunk/tools/tartool/License.txt	(revision 2890)
@@ -0,0 +1,15 @@
+
+    ----  Public Domain  ----
+
+This work was autored by Timothy Gerard Endres, time@ice.com.
+
+This work has been placed into the public domain.
+
+You are free to use this work in any way you wish.
+
+DISCLAIMER
+
+THIS SOFTWARE IS PROVIDED AS-IS, WITH ABSOLUTELY NO WARRANTY.
+YOU ASSUME ALL RESPONSIBILITY FOR ANY AND ALL CONSEQUENCES
+THAT MAY RESULT FROM THE USE OF THIS SOFTWARE!
+
Index: /CKReleaser/trunk/tools/yui/License.txt
===================================================================
--- /CKReleaser/trunk/tools/yui/License.txt	(revision 2890)
+++ /CKReleaser/trunk/tools/yui/License.txt	(revision 2890)
@@ -0,0 +1,36 @@
+Copyright (c) 2008, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+
+version: 2.4.2
+
+Software License Agreement (BSD License)
+
+Copyright (c) 2008, Yahoo! Inc.
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of Yahoo! Inc. nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of Yahoo! Inc.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
