diff --git a/build/jsdoc-toolkit/app/frame/Link.js b/build/jsdoc-toolkit/app/frame/Link.js index 1e6241bf..0af037f2 100644 --- a/build/jsdoc-toolkit/app/frame/Link.js +++ b/build/jsdoc-toolkit/app/frame/Link.js @@ -27,6 +27,10 @@ function Link() { return this; } this.toSymbol = function(alias) { + if(/\[\]$/.test(alias)) { + alias = alias.replace(/\[\]$/, ''); + this.text = 'array of ' + alias + ' objects'; + } if (defined(alias)) this.alias = new String(alias); return this; } @@ -141,11 +145,15 @@ Link.prototype._makeSymbolLink = function(alias) { var linkText= this.text || alias; - var link = {linkPath: linkPath, linkText: linkText, linkInner: (this.innerName? "#"+this.innerName : "")}; + var link = {linkPath: linkPath, linkText: linkText.replace(/^#/, ''), linkInner: (this.innerName? "#" + this.innerName : "")}; if (typeof JSDOC.PluginManager != "undefined") { JSDOC.PluginManager.run("onSymbolLink", link); } + if (/^[A-Z].+\#/.test(link.linkText)) { + link.linkText = link.linkText.charAt(0).toLowerCase() + link.linkText.slice(1); + link.linkText = link.linkText.replace(/#/g, '.'); + } return ""+link.linkText+""; } diff --git a/build/jsdoc-toolkit/app/lib/JSDOC/DocComment.js b/build/jsdoc-toolkit/app/lib/JSDOC/DocComment.js index 4b21cd71..13322e47 100644 --- a/build/jsdoc-toolkit/app/lib/JSDOC/DocComment.js +++ b/build/jsdoc-toolkit/app/lib/JSDOC/DocComment.js @@ -37,7 +37,6 @@ JSDOC.DocComment.prototype.parse = function(/**String*/comment) { if (RegExp.$1) this.meta = RegExp.$1; if (RegExp.$2) this.src = RegExp.$2; } - if (typeof JSDOC.PluginManager != "undefined") { JSDOC.PluginManager.run("onDocCommentSrc", this); } @@ -50,7 +49,6 @@ JSDOC.DocComment.prototype.parse = function(/**String*/comment) { this.src .split(/(^|[\r\n])\s*@/) .filter(function($){return $.match(/\S/)}); - /** The tags found in the comment. @type JSDOC.DocTag[] diff --git a/build/jsdoc-toolkit/app/lib/JSDOC/DocTag.js b/build/jsdoc-toolkit/app/lib/JSDOC/DocTag.js index 77ec07ca..5d5447d5 100644 --- a/build/jsdoc-toolkit/app/lib/JSDOC/DocTag.js +++ b/build/jsdoc-toolkit/app/lib/JSDOC/DocTag.js @@ -36,7 +36,6 @@ JSDOC.DocTag.prototype.parse = function(src) { if (JSDOC.PluginManager) { JSDOC.PluginManager.run("onDocTagSynonym", this); } - src = this.nibbleType(src); // only some tags are allowed to have names. @@ -122,7 +121,7 @@ JSDOC.DocTag.prototype.nibbleTitle = function(src) { JSDOC.DocTag.prototype.nibbleType = function(src) { if (typeof src != "string") throw "src must be a string not "+(typeof src); - if (src.match(/^\s*\{/)) { + 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; diff --git a/build/jsdoc-toolkit/app/plugins/operator.js b/build/jsdoc-toolkit/app/plugins/operator.js new file mode 100644 index 00000000..b28ce4de --- /dev/null +++ b/build/jsdoc-toolkit/app/plugins/operator.js @@ -0,0 +1,11 @@ +JSDOC.PluginManager.registerPlugin( + "JSDOC.operator", + { + onSymbol: function(symbol) { + var operators = symbol.comment.getTag('operator'); + if (operators.length) { + symbol.operator = operators[0].desc; + } + } + } +); \ No newline at end of file diff --git a/build/jsdoc-toolkit/templates/jsdoc/allfiles.tmpl b/build/jsdoc-toolkit/templates/jsdoc/allfiles.tmpl index 4c7de1cd..faa182c9 100644 --- a/build/jsdoc-toolkit/templates/jsdoc/allfiles.tmpl +++ b/build/jsdoc-toolkit/templates/jsdoc/allfiles.tmpl @@ -25,7 +25,7 @@

{+new Link().toSrc(item.alias).withText(item.name)+}

- {+resolveLinks(item.desc)+} + {+processInlineTags(item.desc)+}
Author:
diff --git a/build/jsdoc-toolkit/templates/jsdoc/class.tmpl b/build/jsdoc-toolkit/templates/jsdoc/class.tmpl index 6fa500b0..a315e8cd 100644 --- a/build/jsdoc-toolkit/templates/jsdoc/class.tmpl +++ b/build/jsdoc-toolkit/templates/jsdoc/class.tmpl @@ -1,521 +1,110 @@ - - - - - - {! Link.base = "../"; /* all generated links will be relative to this */ !} - JsDoc Reference - {+data.alias+} +{! data.classId = data.alias.toLowerCase() !} +{! + var ownProperties = data.properties.filter(function($){return $.memberOf == data.alias && !$.isNamespace && !$.isStatic}); + var staticProperties = data.properties.filter(function($){return $.memberOf == data.alias && !$.isNamespace && $.isStatic}); + var ownMethods = data.methods.filter(function($){return $.memberOf == data.alias && !$.isNamespace && !$.isStatic && !$.isOperator}); + var staticMethods = data.methods.filter(function($){return $.memberOf == data.alias && !$.isNamespace && $.isStatic && !$.isOperator}); + var operatorMethods = data.methods.filter(function($){return $.memberOf == data.alias && !$.isNamespace && !$.isStatic && $.isOperator}); + if (operatorMethods.length) { + var operators = {}; + for (var i = 0, l = operatorMethods.length; i < l; i++) { + var operator = operatorMethods[i]; + var name = operator.name.replace(/\^[0-9]$/, ''); + if (!operators[name]) + operators[name] = []; + operators[name].push(operator); + } + } +!} + + + +{+data.alias+} + + + + + + + + + +
+

{+data.alias+}

+

{+processInlineTags(data.classDesc)+}

+
+ +

Constructors

+
+ + +
+
- - + + +

Operators

+ + {+ templates.operators.process(member) +} + +
+
- - - - {+include("static/header.html")+} - - - -
- - {+publish.classesIndex+} - -
- -
- -

- {! - var classType = ""; - - if (data.isBuiltin()) { - classType += "Built-In "; - } - - if (data.isNamespace) { - if (data.is('FUNCTION')) { - classType += "Function "; - } - classType += "Namespace "; - } - !} - {+classType+}{+data.alias+} -

- - -

-
Version - {+ data.version +}.
-
-
Extends - {+ - data.augments - .map( - function($) { return new Link().toSymbol($); } - ) - .join(", ") - +}.
-
- - {+resolveLinks(data.classDesc)+} -

- - - - {! var ownProperties = data.properties.filter(function($){return $.memberOf == data.alias && !$.isNamespace}); !} - -
- {! - var borrowedMembers = data.properties.filter(function($) {return $.memberOf != data.alias}); - - var contributers = []; - borrowedMembers.map(function($) {if (contributers.indexOf($.memberOf) < 0) contributers.push($.memberOf)}); - for (var i = 0, l = contributers.length; i < l; i++) { - output += - "
Functions inherited from "+new Link().toSymbol(contributers[i])+":
" - + - "
" + - borrowedMembers - .filter( - function($) { return $.memberOf == contributers[i] } - ) - .map( - function($) { return new Link().toSymbol($.alias).withText($.name) } - ) - .join(", ") - + - "
"; - } - !} -
-
-
- - - - {! var ownMethods = data.methods.filter(function($){return $.memberOf == data.alias && !$.isNamespace}); !} - - -
- {! - var borrowedMembers = data.methods.filter(function($) {return $.memberOf != data.alias}); - var contributers = []; - borrowedMembers.map(function($) {if (contributers.indexOf($.memberOf) < 0) contributers.push($.memberOf)}); - for (var i = 0, l = contributers.length; i < l; i++) { - output += - "
Methods inherited from "+new Link().toSymbol(contributers[i])+":
" - + - "
" + - borrowedMembers - .filter( - function($) { return $.memberOf == contributers[i] } - ) - .map( - function($) { return new Link().toSymbol($.alias).withText($.name) } - ) - .join(", ") - + - "
"; - } - - !} -
-
-
- - - {! var ownEvents = data.events.filter(function($){return $.memberOf == data.alias && !$.isNamespace}); !} - - -
- {! - var borrowedMembers = data.events.filter(function($) {return $.memberOf != data.alias}); - var contributers = []; - borrowedMembers.map(function($) {if (contributers.indexOf($.memberOf) < 0) contributers.push($.memberOf)}); - for (var i = 0, l = contributers.length; i < l; i++) { - output += - "
Events inherited from "+new Link().toSymbol(contributers[i])+":
" - + - "
" + - borrowedMembers - .filter( - function($) { return $.memberOf == contributers[i] } - ) - .map( - function($) { return new Link().toSymbol($.alias).withText($.name) } - ) - .join(", ") - + - "
"; - } - - !} -
-
-
- - - -
-
- Constructor -
- -
{! - if (data.isPrivate) output += "<private> "; - if (data.isInner) output += "<inner> "; - !} - {+ data.alias +}{+ makeSignature(data.params) +} -
- -
- {+resolveLinks(data.desc)+} -
Author: {+data.author+}.
-
- - - -
{+example+}
-
-
- - - -
-
Parameters:
- -
- {+item.name+} {+((item.type)?""+("{"+(new Link().toSymbol(item.type)+"} ")) : "")+} - Optional, Default: {+item.defaultValue+} -
-
{+resolveLinks(item.desc)+}
-
-
-
- -
-
Deprecated:
-
- {+resolveLinks(data.deprecated)+} -
-
-
- -
-
Since:
-
{+ data.since +}
-
-
- -
-
Throws:
- -
- {+((item.type)?"{"+(new Link().toSymbol(item.type))+"} " : "")+} {+item.name+} -
-
{+resolveLinks(item.desc)+}
-
-
-
- -
-
Returns:
- -
{+((item.type)?"{"+(new Link().toSymbol(item.type))+"} " : "")+}{+resolveLinks(item.desc)+}
-
-
-
- -
-
Requires:
- -
{+ new Link().toSymbol(item) +}
-
-
-
- -
-
See:
- -
{+ new Link().toSymbol(item) +}
-
-
-
- -
-
- - - -
- Properties -
- - -
{! - if (member.isPrivate) output += "<private> "; - if (member.isInner) output += "<inner> "; - // if (member.isStatic) output += "<static> "; - if (member.isConstant) output += "<constant> "; - !} - - {+member.memberOf+}.{+member.name+} - {{+new Link().toSymbol(member.type)+}} - -
-
- {+resolveLinks(member.desc)+} - -
- Defined in: {+new Link().toSrc(member.srcFile)+}. -
-
Author: {+member.author+}.
-
- - - -
{+example+}
-
-
- - -
-
Deprecated:
-
- {+ resolveLinks(member.deprecated) +} -
-
-
- -
-
Since:
-
{+ member.since +}
-
-
- -
-
See:
- -
{+ new Link().toSymbol(item) +}
-
-
-
- -
-
Default Value:
-
- {+resolveLinks(member.defaultValue)+} -
-
-
- -
-
-
+ +

Properties

+ + {+ templates.property.process(member) +} + +
+
- -
- Functions -
- - -
- {! - if (member.isPrivate) output += "<private> "; - if (member.isInner) output += "<inner> "; - // if (member.isStatic) output += "<static> "; - !} - {+member.memberOf+}.{+member.name.replace(/\^\d+$/, '')+}{+makeSignature(member.params)+} -
-
- {+resolveLinks(member.desc)+} -
Author: {+member.author+}.
-
- - - -
{+example+}
-
-
- - -
-
Parameters:
- -
- {+item.name+}{+((item.type)?"{"+(new Link().toSymbol(item.type))+"} " : "")+} - Optional, Default: {+item.defaultValue+} -
-
{+resolveLinks(item.desc)+}
-
-
-
- -
-
Deprecated:
-
- {+ resolveLinks(member.deprecated) +} -
-
-
- -
-
Since:
-
{+ member.since +}
-
-
- - -
-
Throws:
- -
- {+((item.type)?"{"+(new Link().toSymbol(item.type))+"} " : "")+} {+item.name+} -
-
{+resolveLinks(item.desc)+}
-
-
-
- -
-
Returns:
- -
{+((item.type)?"{"+(new Link().toSymbol(item.type))+"} " : "")+}{+resolveLinks(item.desc)+}
-
-
-
- -
-
Requires:
- -
{+ resolveLinks(item) +}
-
-
-
- -
-
See:
- -
{+ new Link().toSymbol(item) +}
-
-
-
- -
- + +

Functions

+ + {! member.desc = processGroupTitle(member.desc, member) !} + +

{+member.groupTitle+}

- - - -
- Event Detail -
- - -
{! - if (member.isPrivate) output += "<private> "; - if (member.isInner) output += "<inner> "; - // if (member.isStatic) output += "<static> "; - !} - - {{+new Link().toSymbol(member.type)+}} - {+member.memberOf+}.{+member.name+}{+makeSignature(member.params)+} - -
-
- {+resolveLinks(member.desc)+} - -
- Defined in: {+new Link().toSrc(member.srcFile)+}. -
-
Author: {+member.author+}.
-
- - - -
{+example+}
-
-
- - -
-
Parameters:
- -
- {+item.name+}{+((item.type)?"{"+(new Link().toSymbol(item.type))+"} " : "")+} - Optional, Default: {+item.defaultValue+} -
-
{+ resolveLinks(item.desc) +}
-
-
-
- -
-
Deprecated:
-
- {+ resolveLinks(member.deprecated) +} -
-
-
- -
-
Since:
-
{+ member.since +}
-
- -
- -
-
Throws:
- -
- {+((item.type)?"{"+(new Link().toSymbol(item.type))+"} " : "")+} {+item.name+} -
-
{+ resolveLinks(item.desc) +}
-
-
-
- -
-
Returns:
- -
{+((item.type)?"{"+(new Link().toSymbol(item.type))+"} " : "")+}{+resolveLinks(item.desc)+}
-
-
-
- -
-
Requires:
- -
{+ resolveLinks(item) +}
-
-
-
- -
-
See:
- -
{+ new Link().toSymbol(item) +}
-
-
-
+ {+ templates.method.process(member) +} +
+
+
+ +

Static Functions

+ + {+ templates.method.process(member) +} + +
+
-
- - - -
-
- - - -
- ©{+JSDOC.opt.D.copyright+}
- Documentation generated by JsDoc Toolkit {+JSDOC.VERSION+} on {+new Date()+} -
- - + \ No newline at end of file diff --git a/build/jsdoc-toolkit/templates/jsdoc/classorder.txt b/build/jsdoc-toolkit/templates/jsdoc/classorder.txt new file mode 100644 index 00000000..876b415e --- /dev/null +++ b/build/jsdoc-toolkit/templates/jsdoc/classorder.txt @@ -0,0 +1,29 @@ +Point +Rectangle +Size +Matrix +Color + GrayColor + RGBColor + GradientColor +Item + PathItem + Path + CompoundPath + TextItem + PointText + Raster + Group + Layer + PlacedItem + Symbol +Project +ProjectView +Segment +Curve +PathStyle + CharacterStyle +ParagraphStyle +Gradient +GradientStop +Symbol diff --git a/build/jsdoc-toolkit/templates/jsdoc/examples.tmpl b/build/jsdoc-toolkit/templates/jsdoc/examples.tmpl new file mode 100644 index 00000000..2d7284df --- /dev/null +++ b/build/jsdoc-toolkit/templates/jsdoc/examples.tmpl @@ -0,0 +1,6 @@ + + +

Sample code:

+
{+example+}
+
+
\ No newline at end of file diff --git a/build/jsdoc-toolkit/templates/jsdoc/index.tmpl b/build/jsdoc-toolkit/templates/jsdoc/index.tmpl index a5dc1158..80acc67a 100644 --- a/build/jsdoc-toolkit/templates/jsdoc/index.tmpl +++ b/build/jsdoc-toolkit/templates/jsdoc/index.tmpl @@ -25,7 +25,7 @@

{+(new Link().toSymbol(thisClass.alias))+}

- {+resolveLinks(summarize(thisClass.classDesc))+} + {+processInlineTags(summarize(thisClass.classDesc))+}

diff --git a/build/jsdoc-toolkit/templates/jsdoc/method.tmpl b/build/jsdoc-toolkit/templates/jsdoc/method.tmpl new file mode 100644 index 00000000..b1417076 --- /dev/null +++ b/build/jsdoc-toolkit/templates/jsdoc/method.tmpl @@ -0,0 +1,43 @@ +{! + var memberId = Helpers.getSymbolId(data); + var functionTitle = '' + data.name.replace(/\^\d+$/, '') + '' + makeSignature(data.params); + if (data.isStatic) + functionTitle = '' + data.memberOf + '.' + functionTitle; +!} + +
+ + +
\ No newline at end of file diff --git a/build/jsdoc-toolkit/templates/jsdoc/operators.tmpl b/build/jsdoc-toolkit/templates/jsdoc/operators.tmpl new file mode 100644 index 00000000..2b3b136c --- /dev/null +++ b/build/jsdoc-toolkit/templates/jsdoc/operators.tmpl @@ -0,0 +1,52 @@ +{! + var operatorCount = 0; + var operatorTitle = []; + for (var i = 0, l = data.length; i < l; i++) { + var type = data[i].params[0].type; + type = type.charAt(0).toUpperCase() + type.slice(1); + operatorTitle.push('' + Operator.getOperator(data[i]) + ' ' + type + ''); + } + operatorTitle = operatorTitle.join(', '); +!} + +{! + var type = operator.params[0].type; + type = type.charAt(0).toUpperCase() + type.slice(1); + var functionTitle = '' + Operator.getOperator(operator) + ' ' + type; +!} + + {! + var memberId = operator.name.toLowerCase().replace(/\^[0-9]$/,''); + !} +
+ + +
+
+{! operatorCount++; !} +
\ No newline at end of file diff --git a/build/jsdoc-toolkit/templates/jsdoc/parameters.tmpl b/build/jsdoc-toolkit/templates/jsdoc/parameters.tmpl new file mode 100644 index 00000000..5e68f38f --- /dev/null +++ b/build/jsdoc-toolkit/templates/jsdoc/parameters.tmpl @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/build/jsdoc-toolkit/templates/jsdoc/property.tmpl b/build/jsdoc-toolkit/templates/jsdoc/property.tmpl new file mode 100644 index 00000000..29b684ed --- /dev/null +++ b/build/jsdoc-toolkit/templates/jsdoc/property.tmpl @@ -0,0 +1,45 @@ +{! + data.desc = processGroupTitle(data.desc, data); + + var memberId = Helpers.getSymbolId(data); + var title = '' + data.name.replace(/\^\d+$/, '') + ''; + if (data.isStatic) + title = '' + data.memberOf + '.' + title; + +!} + +

{+data.groupTitle+}

+
+
+ + +
\ No newline at end of file diff --git a/build/jsdoc-toolkit/templates/jsdoc/publish.js b/build/jsdoc-toolkit/templates/jsdoc/publish.js index 42300f4b..726b7a3f 100644 --- a/build/jsdoc-toolkit/templates/jsdoc/publish.js +++ b/build/jsdoc-toolkit/templates/jsdoc/publish.js @@ -1,11 +1,73 @@ +var templates; + +var Operator = new function() { + var operators = { + add: '+', subtract: '-', multiply: '*', divide: '/', equals: '==', + modulo: '%' + }; + var operatorNames = { + add: 'Addition', subtract: 'Subtraction', multiply: 'Multiplication', + divide: 'Division', equals: 'Comparison', modulo: 'Modulo' + }; + + var operatorClasses = { + Point: true, + Size: true + }; + + return { + isOperator: function(symbol) { + // As a convention, only add non static bean properties to + // the documentation. static properties are all supposed to + // be uppercae and constants. + if (symbol.operator == 'none') + print(!(symbol.operator && symbol.operator != 'none')); + return symbol.params.length == 1 && !symbol.isStatic && ( + /^(add|subtract|multiply|divide|modulo)(\^[0-9])*$/.test(symbol.name) + && (symbol.operator != 'none') + ) || ( // equals + symbol.name == 'equals' + && symbol.returns.length && symbol.returns[0].type == 'boolean' + ); + }, + + getOperator: function(symbol) { + return operators[symbol.name.replace(/\^[0-9]$/,'')]; + } + } +} + +var Helpers = { + getSymbolId: function(symbol) { + var id = [symbol.name.toLowerCase().replace(/[\^][0-9]/g, '')]; + if (symbol.params) { + for (var i = 0, l = symbol.params.length; i < l; i++) { + var param = symbol.params[i]; + if (!param.isOptional) + id.push(param.name); + } + } + return id.join('-'); + } +}; + /** Called automatically by JsDoc Toolkit. */ function publish(symbolSet) { publish.conf = { // trailing slash expected for dirs - ext: ".html", - outDir: JSDOC.opt.d || SYS.pwd+"../out/jsdoc/", + ext: ".html", + outDir: JSDOC.opt.d || SYS.pwd+"../out/jsdoc/", templatesDir: JSDOC.opt.t || SYS.pwd+"../templates/jsdoc/", symbolsDir: "symbols/", - srcDir: "symbols/src/" + srcDir: "symbols/src/" + }; + + templates = { + _class: new JSDOC.JsPlate(publish.conf.templatesDir + "class.tmpl"), + method: new JSDOC.JsPlate(publish.conf.templatesDir + "method.tmpl"), + property: new JSDOC.JsPlate(publish.conf.templatesDir + "property.tmpl"), + parameters: new JSDOC.JsPlate(publish.conf.templatesDir + "parameters.tmpl"), + operators: new JSDOC.JsPlate(publish.conf.templatesDir + "operators.tmpl"), + examples: new JSDOC.JsPlate(publish.conf.templatesDir + "examples.tmpl") }; // is source output is suppressed, just display the links to the source file @@ -14,10 +76,13 @@ function publish(symbolSet) { return "<"+srcFilePath+">"; } } + + // Copy over the static files + copyDirectory( + new java.io.File(publish.conf.templatesDir + 'resources/'), + new java.io.File(publish.conf.outDir + 'resources/') + ); - // create the folders and subfolders to hold the output - IO.mkPath((publish.conf.outDir+"symbols/src").split("/")); - // used to allow Link to check the details of things being linked to Link.symbolSet = symbolSet; @@ -41,11 +106,11 @@ function publish(symbolSet) { // create the hilited source code files var files = JSDOC.opt.srcFiles; - for (var i = 0, l = files.length; i < l; i++) { - var file = files[i]; - var srcDir = publish.conf.outDir + "symbols/src/"; + for (var i = 0, l = files.length; i < l; i++) { + var file = files[i]; + var srcDir = publish.conf.outDir + "symbols/src/"; makeSrcFile(file, srcDir); - } + } // get a list of all the classes in the symbolset var classes = symbols.filter(isaClass).sort(makeSortby("alias")); @@ -76,6 +141,10 @@ function publish(symbolSet) { symbol.events = symbol.getEvents(); // 1 order matters symbol.methods = symbol.getMethods(); // 2 + for (var j = 0; j < symbol.methods.length; j++) { + var method = symbol.methods[j]; + method.isOperator = Operator.isOperator(method); + } Link.currentSymbol= symbol; var output = ""; @@ -173,29 +242,80 @@ function makeSrcFile(path, srcDir, name) { /** Build output for displaying function parameters. */ function makeSignature(params) { if (!params) return "()"; - var signature = "(" - + - params.filter( + var postString = ''; + var first = true; + params = params.filter( function($) { return $.name.indexOf(".") == -1; // don't show config params in signature } - ).map( - function($) { - return $.name; + ); + var signature = ''; + var postSignature = ''; + for (var i = 0, l = params.length; i < l; i++) { + var param = params[i]; + if (param.isOptional) { + signature += '['; + postSignature += ']'; } - ).join(", ") - + - ")"; - return signature; + if (i > 0) + signature += ', '; + signature += param.name; + } + return '(' + signature + postSignature + ')'; } -/** Find symbol {@link ...} strings in text and turn into html links */ -function resolveLinks(str, from) { - str = str.replace(/\{@link ([^} ]+) ?\}/gi, - function(match, symbolName) { - return new Link().toSymbol(symbolName); - } - ); - +function processGroupTitle(str, symbol) { + // if (/grouptitle/.test(str)) + // print('yeah'); + // print(str); + var groupTitle = str.match(/\{@grouptitle ([^}]+)\}/); + if (groupTitle) { + symbol.groupTitle = groupTitle[1]; + str = str.replace(/\{@grouptitle ([^}]+)\}/, ''); + } return str; } + +function processInlineTags(str) { + + // {@link ...} -> html links + str = str.replace(/\{@link ([^} ]+) ?\}/gi, + function(match, symbolName) { + return new Link().toSymbol(symbolName.replace(/[\^]/g, '-')); + } + ); + // {@code ...} -> code blocks + str = str.replace(/\{@code[\s]([^}]+)\}/gi, + function(match, code) { + return '' + code + ''; + } + ); + return str; +} + +function copyStatic(dir) { + var dir = publish.conf.templatesDir + 'resources/'; + +} + +function copyDirectory(sourceLocation, targetLocation) { + if (sourceLocation.isDirectory()) { + if (!targetLocation.exists()) { + targetLocation.mkdir(); + } + + var children = sourceLocation.list(); + for (var i = 0; i < children.length; i++) { + copyDirectory(new File(sourceLocation, children[i]), + new File(targetLocation, children[i])); + } + } else { + // Copy the file with FileChannels: + targetLocation.createNewFile(); + var src = new java.io.FileInputStream(sourceLocation).getChannel(); + var dst = new java.io.FileOutputStream(targetLocation).getChannel(); + var amount = dst.transferFrom(src, 0, src.size()); + src.close(); + dst.close(); + } +} \ No newline at end of file diff --git a/build/jsdoc-toolkit/templates/jsdoc/resources/assets/arrow-close.gif b/build/jsdoc-toolkit/templates/jsdoc/resources/assets/arrow-close.gif new file mode 100644 index 00000000..f5873063 Binary files /dev/null and b/build/jsdoc-toolkit/templates/jsdoc/resources/assets/arrow-close.gif differ diff --git a/build/jsdoc-toolkit/templates/jsdoc/resources/assets/arrow-open.gif b/build/jsdoc-toolkit/templates/jsdoc/resources/assets/arrow-open.gif new file mode 100644 index 00000000..09128340 Binary files /dev/null and b/build/jsdoc-toolkit/templates/jsdoc/resources/assets/arrow-open.gif differ diff --git a/build/jsdoc-toolkit/templates/jsdoc/resources/assets/dotted.gif b/build/jsdoc-toolkit/templates/jsdoc/resources/assets/dotted.gif new file mode 100644 index 00000000..feb54a0a Binary files /dev/null and b/build/jsdoc-toolkit/templates/jsdoc/resources/assets/dotted.gif differ diff --git a/build/jsdoc-toolkit/templates/jsdoc/resources/assets/paper-gray.gif b/build/jsdoc-toolkit/templates/jsdoc/resources/assets/paper-gray.gif new file mode 100644 index 00000000..f554edab Binary files /dev/null and b/build/jsdoc-toolkit/templates/jsdoc/resources/assets/paper-gray.gif differ diff --git a/build/jsdoc-toolkit/templates/jsdoc/resources/assets/spacer.gif b/build/jsdoc-toolkit/templates/jsdoc/resources/assets/spacer.gif new file mode 100644 index 00000000..75b945d2 Binary files /dev/null and b/build/jsdoc-toolkit/templates/jsdoc/resources/assets/spacer.gif differ diff --git a/build/jsdoc-toolkit/templates/jsdoc/resources/css/lighter.css b/build/jsdoc-toolkit/templates/jsdoc/resources/css/lighter.css new file mode 100644 index 00000000..39fcdc84 --- /dev/null +++ b/build/jsdoc-toolkit/templates/jsdoc/resources/css/lighter.css @@ -0,0 +1,115 @@ +ol.ltLighter { + font-family: Menlo, Consolas, "Vera Mono", monospace; + font-size: 10px; + overflow: auto; + white-space: pre-wrap; + word-wrap: break-word; +} + +ol.ltLighter { + white-space: -moz-pre-wrap; +} + +ol.ltLighter { + white-space: -pre-wrap; +} + +ol.ltLighter { + white-space: -o-pre-wrap; +} + +ol.ltLighter { + color: #939393; + list-style: decimal-leading-zero; + background-color: #f4f4f4; + margin-left: 0; + padding-left: 0; +} + +ol.ltLighter li { + border-left: 1px solid #939393; + padding: 0 3px 0 10px; + background-color: #fff; + padding-left: 15px; + padding-right: 5px; + margin-left: 40px; +} + +ol.ltLighter .ltfirst { + padding-top: 5px; +} + +ol.ltLighter .ltlast { + padding-bottom: 5px; +} + +ol.ltLighter .alt { + background-color: #edf5fc; +} + +ol.ltLighter span { + color: black; + font-size: 11px; +} + +ol.ltLighter .de1 { +} + +ol.ltLighter .de2 { +} + +ol.ltLighter .kw1 { + color: #1b609a; +} + +ol.ltLighter .kw2 { + color: #9a6f1b; +} + +ol.ltLighter .kw3 { + color: #784e0c; +} + +ol.ltLighter .co1 { + color: #888888; +} + +ol.ltLighter .co2 { + color: #888888; +} + +ol.ltLighter .st0 { + color: #489a1b; +} + +ol.ltLighter .st1 { + color: #70483d; +} + +ol.ltLighter .st2 { + color: #70483d; +} + +ol.ltLighter .nu0 { + color: #70483d; +} + +ol.ltLighter .me0 { + color: #666666; +} + +ol.ltLighter .br0 { + color: #444444; +} + +ol.ltLighter .sy0 { + color: #444444; +} + +ol.ltLighter .es0 { + color: #444444; +} + +ol.ltLighter .re0 { + color: #784e0c; +} \ No newline at end of file diff --git a/build/jsdoc-toolkit/templates/jsdoc/resources/css/reference.css b/build/jsdoc-toolkit/templates/jsdoc/resources/css/reference.css new file mode 100644 index 00000000..24ec3c7d --- /dev/null +++ b/build/jsdoc-toolkit/templates/jsdoc/resources/css/reference.css @@ -0,0 +1,120 @@ +.reference h1, .reference h2, .reference h3 { + font-size: 12px; + font-weight: normal; + display: block; + margin: 0 0 16px 0; + height: 17px; /* -1 for border */ + border-bottom: 1px solid black; +} + +.reference h3 { + margin-top: 16px; + border-bottom-style: dotted; +} + +.reference a tt, .reference a tt b { + padding-bottom: 1px; +} + +.reference pre { + margin: 0 0 18px 0; +} + +.reference ul { + margin: 0; + padding: 0; + list-style: none; +} + +.reference-list ul, .reference-inherited ul { + margin-left: 16px; +} + +.reference hr { + border: none; + border-bottom: 1px dotted #000; +} + +.reference-packages { + margin-left: 0px; +} + +.reference-packages h2, .reference-packages h3, .reference-packages hr { + margin: 10px 0 10px 0; +} + +.reference-packages .first h2 { + margin-top: 0; +} + +* html .reference-packages img { + margin-top: 5px; +} +.reference-packages li { + list-style: none; + list-style-image: none; /* needed for ie 6 */ +} + +.reference-end { + height: 600px; +} + +.reference-members { + padding-bottom: 16px; +} + +.member-group-text { + margin-bottom: 16px; +} + +.member-description { + border: 1px solid #999; + /* .member-header defines border-top for operator lists */ + border-top: 0; + margin: 16px 0 16px 0; +} + +.member-header { + border-top: 1px solid #999; + padding: 10px; +} + +.member-title { + float: left; + width: 400px; +} + +.member-close { + float: right; +} + +.member-text { + border-top: 1px dashed #999; + padding: 10px 10px 0 10px; +} + +.member-link { + text-indent: -30px; + padding-left: 30px; +} + +.reference-inherited ul li { + text-indent: -30px; + padding-left: 30px; +} + +.member-text ul { + padding-bottom: 10px; +} + +ul.package-classes { + padding-bottom: 4px; +} + +.package-classes li { + margin-left: 10px; +} + +.package-classes li h2 { + margin-left: -10px; +} diff --git a/build/jsdoc-toolkit/templates/jsdoc/resources/css/style.css b/build/jsdoc-toolkit/templates/jsdoc/resources/css/style.css new file mode 100644 index 00000000..2468415b --- /dev/null +++ b/build/jsdoc-toolkit/templates/jsdoc/resources/css/style.css @@ -0,0 +1,85 @@ +body { + background: #fff; + margin: 16px; + font-family: "Lucida Grande", Geneva, Verdana, Arial, sans-serif; + font-size: 12px; + line-height: 19px; + color: #000; + max-width: 540px; +} + +select, input, textarea { + font-family: "Lucida Grande", Geneva, Verdana, Arial, sans-serif; + font-size: 11px; + margin: 0; + color: #000; +} + +p tt, pre { + font-family: Menlo, Consolas, "Vera Mono", monospace; + font-size: 11px; + line-height: 19px; +} + +a { + color: #000; + text-decoration: none; + padding-bottom: 1px; + border-bottom: 1px solid #000; +} + +img { + border: 0; +} + +a:hover { + background: #e5e5e5; +} + +p { + margin: 0 0 19px 0; +} + +ul { + padding: 0; + margin: 0 0 19px 16px; + list-style: disc outside url(../assets/bullet.gif); +} + +ol { + padding: 0; + margin: 0 0 19px 0; +} + +.clear { + clear: both; +} + +.hidden { + display: none; +} + +.reference-packages, .reference-packages a { + color: #009dec; + border-bottom: 0px; +} + +/* border-bottom color for headers and ruler */ +.reference-packages h2, .reference-packages h3, .reference-packages hr { + border-color: #009dec; +} + +.reference-packages a:hover { + background: #e3f4fc; +} + +.reference h1 { + font-size: 18px; + font-weight: normal; + line-height: 24px; + border: none; +} + +.footer { + margin-top: 20px; +} diff --git a/build/jsdoc-toolkit/templates/jsdoc/resources/js/bootstrap.js b/build/jsdoc-toolkit/templates/jsdoc/resources/js/bootstrap.js new file mode 100644 index 00000000..e3082a21 --- /dev/null +++ b/build/jsdoc-toolkit/templates/jsdoc/resources/js/bootstrap.js @@ -0,0 +1,4003 @@ +new function() { + var fix = !this.__proto__ && [Function, Number, Boolean, String, Array, Date, RegExp]; + if (fix) + for (var i in fix) + fix[i].prototype.__proto__ = fix[i].prototype; + + var has = {}.hasOwnProperty + ? function(obj, name) { + return (!fix || name != '__proto__') && obj.hasOwnProperty(name); + } + : function(obj, name) { + return obj[name] !== (obj.__proto__ || Object.prototype)[name]; + }; + + function inject(dest, src, enumerable, base, generics) { + function field(name, dontCheck, generics) { + var val = src[name], func = typeof val == 'function', res = val, + prev = dest[name]; + if (generics && func && (!src.preserve || !generics[name])) generics[name] = function(bind) { + return bind && dest[name].apply(bind, + Array.prototype.slice.call(arguments, 1)); + } + if ((dontCheck || val !== undefined && has(src, name)) && (!prev || !src.preserve)) { + if (func) { + if (prev && /\bthis\.base\b/.test(val)) { + var fromBase = base && base[name] == prev; + res = (function() { + var tmp = this.base; + this.base = fromBase ? base[name] : prev; + try { return val.apply(this, arguments); } + finally { tmp ? this.base = tmp : delete this.base; } + }).pretend(val); + } + } + dest[name] = res; + } + } + if (src) { + for (var name in src) + if (has(src, name) && !/^(statics|generics|preserve|prototype|constructor|__proto__|toString|valueOf)$/.test(name)) + field(name, true, generics); + field('toString'); + field('valueOf'); + } + } + + function extend(obj) { + function ctor(dont) { + if (fix) this.__proto__ = obj; + if (this.initialize && dont !== ctor.dont) + return this.initialize.apply(this, arguments); + } + ctor.prototype = obj; + ctor.toString = function() { + return (this.prototype.initialize || function() {}).toString(); + } + return ctor; + } + + inject(Function.prototype, { + inject: function(src) { + if (src) { + var proto = this.prototype, base = proto.__proto__ && proto.__proto__.constructor; + inject(proto, src, false, base && base.prototype, src.generics && this); + inject(this, src.statics, true, base); + } + for (var i = 1, l = arguments.length; i < l; i++) + this.inject(arguments[i]); + return this; + }, + + extend: function(src) { + var proto = new this(this.dont), ctor = proto.constructor = extend(proto); + ctor.dont = {}; + inject(ctor, this, true); + return arguments.length ? this.inject.apply(ctor, arguments) : ctor; + }, + + pretend: function(fn) { + this.toString = function() { + return fn.toString(); + } + this.valueOf = function() { + return fn.valueOf(); + } + return this; + } + }); + + function each(obj, iter, bind) { + return obj ? (typeof obj.length == 'number' + ? Array : Hash).prototype.each.call(obj, iter, bind) : bind; + } + + Base = Object.extend({ + has: function(name) { + return has(this, name); + }, + + each: function(iter, bind) { + return each(this, iter, bind); + }, + + inject: function() { + for (var i = 0, l = arguments.length; i < l; i++) + inject(this, arguments[i]); + return this; + }, + + extend: function() { + var res = new (extend(this)); + return res.inject.apply(res, arguments); + }, + + statics: { + has: has, + each: each, + + type: function(obj) { + return (obj || obj === 0) && ( + obj._type || obj.nodeName && ( + obj.nodeType == 1 && 'element' || + obj.nodeType == 3 && 'textnode' || + obj.nodeType == 9 && 'document') + || obj.location && obj.frames && obj.history && 'window' + || typeof obj) || null; + }, + + check: function(obj) { + return !!(obj || obj === 0); + }, + + pick: function() { + for (var i = 0, l = arguments.length; i < l; i++) + if (arguments[i] !== undefined) + return arguments[i]; + return null; + }, + + iterator: function(iter) { + return !iter + ? function(val) { return val } + : typeof iter != 'function' + ? function(val) { return val == iter } + : iter; + }, + + stop: {} + } + }, { + generics: true, + + debug: function() { + return /^(string|number|function|regexp)$/.test(Base.type(this)) ? this + : Base.each(this, function(val, key) { this.push(key + ': ' + val); }, []).join(', '); + }, + + clone: function() { + return Base.each(this, function(val, i) { + this[i] = val; + }, new this.constructor()); + }, + + toQueryString: function() { + return Base.each(this, function(val, key) { + this.push(key + '=' + encodeURIComponent(val)); + }, []).join('&'); + } + }); +} + +$each = Base.each; +$type = Base.type; +$check = Base.check; +$pick = Base.pick; +$stop = $break = Base.stop; + +Enumerable = { + generics: true, + preserve: true, + + findEntry: function(iter, bind) { + var that = this, iter = Base.iterator(iter), ret = null; + Base.each(this, function(val, key) { + var res = iter.call(bind, val, key, that); + if (res) { + ret = { key: key, value: val, result: res }; + throw Base.stop; + } + }); + return ret; + }, + + find: function(iter, bind) { + var entry = this.findEntry(iter, bind); + return entry && entry.result; + }, + + contains: function(obj) { + return !!this.findEntry(obj); + }, + + remove: function(iter, bind) { + var entry = this.findEntry(iter, bind); + if (entry) { + delete this[entry.key]; + return entry.value; + } + }, + + filter: function(iter, bind) { + var that = this; + return Base.each(this, function(val, i) { + if (iter.call(bind, val, i, that)) + this[this.length] = val; + }, []); + }, + + map: function(iter, bind) { + var that = this; + return Base.each(this, function(val, i) { + this[this.length] = iter.call(bind, val, i, that); + }, []); + }, + + every: function(iter, bind) { + var that = this; + return this.find(function(val, i) { + return !iter.call(this, val, i, that); + }, bind || null) == null; + }, + + some: function(iter, bind) { + return this.find(iter, bind || null) != null; + }, + + collect: function(iter, bind) { + var that = this, iter = Base.iterator(iter); + return Base.each(this, function(val, i) { + val = iter.call(bind, val, i, that); + if (val != null) + this[this.length] = val; + }, []); + }, + + max: function(iter, bind) { + var that = this; + return Base.each(this, function(val, i) { + val = iter.call(bind, val, i, that); + if (val >= (this.max || val)) this.max = val; + }, {}).max; + }, + + min: function(iter, bind) { + var that = this; + return Base.each(this, function(val, i) { + val = iter.call(bind, val, i, that); + if (val <= (this.min || val)) this.min = val; + }, {}).min; + }, + + pluck: function(prop) { + return this.map(function(val) { + return val[prop]; + }); + }, + + sortBy: function(iter, bind) { + var that = this; + return this.map(function(val, i) { + return { value: val, compare: iter.call(bind, val, i, that) }; + }, bind).sort(function(left, right) { + var a = left.compare, b = right.compare; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + }, + + toArray: function() { + return this.map(function(value) { + return value; + }); + } +}; + +Hash = Base.extend(Enumerable, { + generics: true, + + initialize: function(arg) { + if (typeof arg == 'string') { + for (var i = 0, l = arguments.length; i < l; i += 2) + this[arguments[i]] = arguments[i + 1]; + } else { + this.append.apply(this, arguments); + } + return this; + }, + + each: function(iter, bind) { + var bind = bind || this, iter = Base.iterator(iter), has = Base.has; + try { + for (var i in this) + if (has(this, i)) + iter.call(bind, this[i], i, this); + } catch (e) { + if (e !== Base.stop) throw e; + } + return bind; + }, + + append: function() { + for (var i = 0, l = arguments.length; i < l; i++) { + var obj = arguments[i]; + for (var key in obj) + if (Base.has(obj, key)) + this[key] = obj[key]; + } + return this; + }, + + merge: function() { + return Array.each(arguments, function(obj) { + Base.each(obj, function(val, key) { + this[key] = Base.type(this[key]) == 'object' + ? Hash.prototype.merge.call(this[key], val) + : Base.type(val) == 'object' ? Base.clone(val) : val; + }, this); + }, this); + }, + + getKeys: function() { + return Hash.getKeys(this); + }, + + getValues: Enumerable.toArray, + + getSize: function() { + return this.each(function() { + this.size++; + }, { size: 0 }).size; + }, + + statics: { + create: function(obj) { + return arguments.length == 1 && obj.constructor == Hash + ? obj : Hash.prototype.initialize.apply(new Hash(), arguments); + }, + + getKeys: Object.keys || function(obj) { + return Hash.map(function(val, key) { + return key; + }); + } + } +}); + +$H = Hash.create; + +Array.inject({ + generics: true, + preserve: true, + _type: 'array', + + forEach: function(iter, bind) { + for (var i = 0, l = this.length; i < l; i++) + iter.call(bind, this[i], i, this); + }, + + indexOf: function(obj, i) { + i = i || 0; + if (i < 0) i = Math.max(0, this.length + i); + for (var l = this.length; i < l; i++) + if (this[i] == obj) return i; + return -1; + }, + + lastIndexOf: function(obj, i) { + i = i != null ? i : this.length - 1; + if (i < 0) i = Math.max(0, this.length + i); + for (; i >= 0; i--) + if (this[i] == obj) return i; + return -1; + }, + + filter: function(iter, bind) { + var res = []; + for (var i = 0, l = this.length; i < l; i++) { + var val = this[i]; + if (iter.call(bind, val, i, this)) + res[res.length] = val; + } + return res; + }, + + map: function(iter, bind) { + var res = new Array(this.length); + for (var i = 0, l = this.length; i < l; i++) + res[i] = iter.call(bind, this[i], i, this); + return res; + }, + + every: function(iter, bind) { + for (var i = 0, l = this.length; i < l; i++) + if (!iter.call(bind, this[i], i, this)) + return false; + return true; + }, + + some: function(iter, bind) { + for (var i = 0, l = this.length; i < l; i++) + if (iter.call(bind, this[i], i, this)) + return true; + return false; + }, + + reduce: function(fn, value) { + var i = 0; + if (arguments.length < 2 && this.length) value = this[i++]; + for (var l = this.length; i < l; i++) + value = fn.call(null, value, this[i], i, this); + return value; + } +}, Enumerable, { + generics: true, + + each: function(iter, bind) { + try { + Array.prototype.forEach.call(this, Base.iterator(iter), bind = bind || this); + } catch (e) { + if (e !== Base.stop) throw e; + } + return bind; + }, + + collect: function(iter, bind) { + var that = this; + return this.each(function(val, i) { + if ((val = iter.call(bind, val, i, that)) != null) + this[this.length] = val; + }, []); + }, + + findEntry: function(iter, bind) { + if (typeof iter != 'function') { + var i = this.indexOf(iter); + return i == -1 ? null : { key: i, value: iter, result: iter }; + } + return Enumerable.findEntry.call(this, iter, bind); + }, + + remove: function(iter, bind) { + var entry = this.findEntry(iter, bind); + if (entry.key != null) + this.splice(entry.key, 1); + return entry.value; + }, + + remove: function(iter, bind) { + var entry = this.findEntry(iter, bind); + if (entry) { + this.splice(entry.key, 1); + return entry.value; + } + }, + + toArray: function() { + return Array.prototype.slice.call(this); + }, + + clone: function() { + return this.toArray(); + }, + + clear: function() { + this.length = 0; + }, + + compact: function() { + return this.filter(function(value) { + return value != null; + }); + }, + + append: function(items) { + for (var i = 0, l = items.length; i < l; i++) + this[this.length++] = items[i]; + return this; + }, + + subtract: function(items) { + for (var i = 0, l = items.length; i < l; i++) + Array.remove(this, items[i]); + return this; + }, + + intersect: function(items) { + for (var i = this.length - 1; i >= 0; i--) + if (!items.find(this[i])) + this.splice(i, 1); + return this; + }, + + associate: function(obj) { + if (!obj) + obj = this; + else if (typeof obj == 'function') + obj = this.map(obj); + if (obj.length != null) { + var that = this; + return Base.each(obj, function(name, index) { + this[name] = that[index]; + if (index == that.length) + throw Base.stop; + }, {}); + } else { + obj = Hash.append({}, obj); + return Array.each(this, function(val) { + var type = Base.type(val); + Base.each(obj, function(hint, name) { + if (hint == 'any' || type == hint) { + this[name] = val; + delete obj[name]; + throw Base.stop; + } + }, this); + }, {}); + } + }, + + flatten: function() { + return Array.each(this, function(val) { + if (val != null && val.flatten) this.append(val.flatten()); + else this.push(val); + }, []); + }, + + swap: function(i, j) { + var tmp = this[j]; + this[j] = this[i]; + this[i] = tmp; + return tmp; + }, + + shuffle: function() { + var res = this.clone(); + var i = this.length; + while (i--) res.swap(i, Math.rand(i + 1)); + return res; + }, + + pick: function() { + return this[Math.rand(this.length)]; + }, + + getFirst: function() { + return this[0]; + }, + + getLast: function() { + return this[this.length - 1]; + } +}); + +Array.inject(new function() { + var proto = Array.prototype, fields = ['push','pop','shift','unshift','sort', + 'reverse','join','slice','splice','forEach','indexOf','lastIndexOf', + 'filter','map','every','some','reduce','concat'].each(function(name) { + this[name] = proto[name]; + }, { generics: true, preserve: true }); + + Array.inject(fields); + + Hash.append(fields, proto, { + clear: function() { + for (var i = 0, l = this.length; i < l; i++) + delete this[i]; + this.length = 0; + }, + + concat: function(list) { + return Browser.WEBKIT + ? new Array(this.length + list.length).append(this).append(list) + : Array.concat(this, list); + }, + + toString: proto.join, + + length: 0 + }); + + return { + statics: { + create: function(obj) { + if (obj == null) + return []; + if (obj.toArray) + return obj.toArray(); + if (typeof obj.length == 'number') + return Array.prototype.slice.call(obj); + return [obj]; + }, + + convert: function(obj) { + return Base.type(obj) == 'array' ? obj : Array.create(obj); + }, + + extend: function(src) { + var ret = Base.extend(fields, src); + ret.extend = Function.extend; + return ret; + } + } + }; +}); + +$A = Array.create; + +Function.inject(new function() { + + function timer(set) { + return function(delay, bind, args) { + var func = this.wrap(bind, args); + if (delay === undefined) + return func(); + var timer = set(func, delay); + func.clear = function() { + clearTimeout(timer); + clearInterval(timer); + }; + return func; + }; + } + + return { + generics: true, + preserve: true, + + delay: timer(setTimeout), + periodic: timer(setInterval), + + bind: function(bind) { + var that = this, slice = Array.prototype.slice, + args = arguments.length > 1 ? slice.call(arguments, 1) : null; + return function() { + return that.apply(bind, args ? arguments.length > 0 + ? args.concat(slice.call(arguments)) : args : arguments); + } + }, + + wrap: function(bind, args) { + var that = this; + return function() { + return that.apply(bind, args || arguments); + } + } + } +}); + +Number.inject({ + _type: 'number', + + limit: function(min, max) { + return Math.min(max, Math.max(min, this)); + }, + + times: function(func, bind) { + for (var i = 0; i < this; i++) + func.call(bind, i); + return bind || this; + }, + + toInt: function(base) { + return parseInt(this, base || 10); + }, + + toFloat: function() { + return parseFloat(this); + }, + + toPaddedString: function(length, base, prefix) { + var str = this.toString(base || 10); + return (prefix || '0').times(length - str.length) + str; + } +}); + +String.inject({ + _type: 'string', + + test: function(exp, param) { + return new RegExp(exp, param || '').test(this); + }, + + toArray: function() { + return this ? this.split(/\s+/) : []; + }, + + toInt: Number.prototype.toInt, + + toFloat: Number.prototype.toFloat, + + camelize: function(separator) { + return this.replace(separator ? new RegExp('[' + separator + '](\\w)', 'g') : /-(\w)/g, function(all, chr) { + return chr.toUpperCase(); + }); + }, + + uncamelize: function(separator) { + separator = separator || ' '; + return this.replace(/[a-z][A-Z0-9]|[0-9][a-zA-Z]|[A-Z]{2}[a-z]/g, function(match) { + return match.charAt(0) + separator + match.substring(1); + }); + }, + + hyphenate: function(separator) { + return this.uncamelize(separator || '-').toLowerCase(); + }, + + capitalize: function() { + return this.replace(/\b[a-z]/g, function(match) { + return match.toUpperCase(); + }); + }, + + escapeRegExp: function() { + return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); + }, + + trim: function(exp) { + exp = exp ? '[' + exp + ']' : '\\s'; + return this.replace(new RegExp('^' + exp + '+|' + exp + '+$', 'g'), ''); + }, + + clean: function() { + return this.replace(/\s{2,}/g, ' ').trim(); + }, + + contains: function(string, sep) { + return (sep ? (sep + this + sep).indexOf(sep + string + sep) : this.indexOf(string)) != -1; + }, + + times: function(count) { + return count < 1 ? '' : new Array(count + 1).join(this); + }, + + isHtml: function() { + return /^[^<]*(<(.|\s)+>)[^>]*$/.test(this); + } +}); + +RegExp.inject({ + _type: 'regexp' +}); + +Date.inject({ + statics: { + SECOND: 1000, + MINUTE: 60000, + HOUR: 3600000, + DAY: 86400000, + WEEK: 604800000, + MONTH: 2592000000, + YEAR: 31536000000, + + now: Date.now || function() { + return +new Date(); + } + } +}); + +Math.rand = function(first, second) { + return second == undefined + ? Math.rand(0, first) + : Math.floor(Math.random() * (second - first) + first); +} + +Array.inject({ + hexToRgb: function(toArray) { + if (this.length >= 3) { + var rgb = []; + for (var i = 0; i < 3; i++) + rgb.push((this[i].length == 1 ? this[i] + this[i] : this[i]).toInt(16)); + return toArray ? rgb : 'rgb(' + rgb.join(',') + ')'; + } + }, + + rgbToHex: function(toArray) { + if (this.length >= 3) { + if (this.length == 4 && this[3] == 0 && !toArray) return 'transparent'; + var hex = []; + for (var i = 0; i < 3; i++) { + var bit = (this[i] - 0).toString(16); + hex.push(bit.length == 1 ? '0' + bit : bit); + } + return toArray ? hex : '#' + hex.join(''); + } + }, + + rgbToHsb: function() { + var r = this[0], g = this[1], b = this[2]; + var hue, saturation, brightness; + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var delta = max - min; + brightness = max / 255; + saturation = (max != 0) ? delta / max : 0; + if (saturation == 0) { + hue = 0; + } else { + var rr = (max - r) / delta; + var gr = (max - g) / delta; + var br = (max - b) / delta; + if (r == max) hue = br - gr; + else if (g == max) hue = 2 + rr - br; + else hue = 4 + gr - rr; + hue /= 6; + if (hue < 0) hue++; + } + return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)]; + }, + + hsbToRgb: function() { + var br = Math.round(this[2] / 100 * 255); + if (this[1] == 0) { + return [br, br, br]; + } else { + var hue = this[0] % 360; + var f = hue % 60; + var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255); + var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255); + var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255); + switch (Math.floor(hue / 60)) { + case 0: return [br, t, p]; + case 1: return [q, br, p]; + case 2: return [p, br, t]; + case 3: return [p, q, br]; + case 4: return [t, p, br]; + case 5: return [br, p, q]; + } + } + } +}); + +String.inject({ + hexToRgb: function(toArray) { + var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); + return hex && hex.slice(1).hexToRgb(toArray); + }, + + rgbToHex: function(toArray) { + var rgb = this.match(/\d{1,3}/g); + return rgb && rgb.rgbToHex(toArray); + } +}); + +Json = function(JSON) { + var special = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', "'" : "\\'", '\\': '\\\\' }; + return { + encode: JSON + ? JSON.stringify + : function(obj, properties) { + if (Base.type(properties) == 'array') { + properties = properties.each(function(val) { + this[val] = true; + }, {}); + } + switch (Base.type(obj)) { + case 'string': + return '"' + obj.replace(/[\x00-\x1f\\"]/g, function(chr) { + return special[chr] || '\\u' + chr.charCodeAt(0).toPaddedString(4, 16); + }) + '"'; + case 'array': + return '[' + obj.collect(function(val) { + return Json.encode(val, properties); + }) + ']'; + case 'object': + case 'hash': + return '{' + Hash.collect(obj, function(val, key) { + if (!properties || properties[key]) { + val = Json.encode(val, properties); + if (val !== undefined) + return Json.encode(key) + ':' + val; + } + }) + '}'; + case 'function': + return undefined; + default: + return obj + ''; + } + return null; + }, + + decode: JSON + ? function(str, secure) { + try { + return JSON.parse(str); + } catch (e) { + return null; + } + } + : function(str, secure) { + try { + return Base.type(str) == 'string' && (str = str.trim()) && + (!secure || /^[\],:{}\s]*$/.test( + str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@") + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]") + .replace(/(?:^|:|,)(?:\s*\[)+/g, ""))) + ? (new Function('return ' + str))() : null; + } catch (e) { + return null; + } + } + }; +}(this.JSON); + +Browser = new function() { + var name = window.orientation != undefined ? 'ipod' + : (navigator.platform.match(/mac|win|linux|nix/i) || ['other'])[0].toLowerCase(); + var fields = { + PLATFORM: name, + XPATH: !!document.evaluate, + QUERY: !!document.querySelector + }; + fields[name.toUpperCase()] = true; + + function getVersion(prefix, min, max) { + var ver = (new RegExp(prefix + '([\\d.]+)', 'i').exec(navigator.userAgent) || [0, '0'])[1].split('.'); + return (ver.slice(0, min).join('') + '.' + ver.slice(min, max || ver.length).join('')).toFloat(); + } + + var engines = { + presto: function() { + return !window.opera ? false : getVersion('Presto/', 2) || getVersion('Opera[/ ]', 1); + }, + + trident: function() { + return !window.ActiveXObject ? false : getVersion('MSIE ', 1); + }, + + webkit: function() { + return navigator.taintEnabled ? false : getVersion('WebKit/', 1, 2); + }, + + gecko: function() { + return !document.getBoxObjectFor && window.mozInnerScreenX == null ? false : getVersion('rv:', 2); + } + }; + + for (var engine in engines) { + var version = engines[engine](); + if (version) { + fields.ENGINE = engine; + fields.VERSION = version; + engine = engine.toUpperCase(); + fields[engine] = true; + fields[(engine + version).replace(/\./g, '')] = true; + break; + } + } + + fields.log = function() { + if (!Browser.TRIDENT && window.console && console.log) + console.log.apply(console, arguments); + else + (window.console && console.log + || window.opera && opera.postError + || alert)(Array.join(arguments, ' ')); + } + + return fields; +}; + +DomNodes = Array.extend(new function() { + var unique = 0; + return { + initialize: function(nodes) { + this._unique = unique++; + this.append(nodes && nodes.length != null && !nodes.nodeType + ? nodes : arguments); + }, + + push: function() { + this.append(arguments); + return this.length; + }, + + append: function(items) { + for (var i = 0, l = items.length; i < l; i++) { + var el = items[i]; + if ((el = el && (el._wrapper || DomNode.wrap(el))) && el._unique != this._unique) { + el._unique = this._unique; + this[this.length++] = el; + } + } + return this; + }, + + toNode: function() { + return this; + }, + + statics: { + inject: function(src) { + var proto = this.prototype; + this.base(Base.each(src || {}, function(val, key) { + if (typeof val == 'function') { + var func = val, prev = proto[key]; + var count = func.length, prevCount = prev && prev.length; + val = function() { + var args = arguments, values; + if (prev && args.length == prevCount + || (args.length > count && args.length <= prevCount)) + return prev.apply(this, args); + this.each(function(obj) { + var ret = (obj[key] || func).apply(obj, args); + if (ret !== undefined && ret != obj) { + values = values || (DomNode.isNode(ret) + ? new obj._collection() : []); + values.push(ret); + } + }); + return values || this; + } + } + this[key] = val; + }, {})); + for (var i = 1, l = arguments.length; i < l; i++) + this.inject(arguments[i]); + return this; + } + } + }; +}); + +DomNode = Base.extend(new function() { + var nodes = []; + var tags = {}, classes = {}, classCheck, unique = 0; + + function dispose(force) { + for (var i = nodes.length - 1; i >= 0; i--) { + var el = nodes[i]; + if (force || (!el || el != window && el != document && + (!el.parentNode || !el.offsetParent))) { + if (el) { + var obj = el._wrapper; + if (obj && obj.finalize) obj.finalize(); + el._wrapper = el._unique = null; + } + if (!force) nodes.splice(i, 1); + } + } + } + + function inject(src) { + src = src || {}; + (src._methods || []).each(function(name) { + src[name] = function(arg) { + var ret = this.$[name] && this.$[name](arg); + return ret === undefined ? this : ret; + } + }); + (src._properties || []).each(function(name) { + var part = name.capitalize(), prop = name.toLowerCase(); + src['get' + part] = function() { + return this.getProperty(prop); + } + src['set' + part] = function(value) { + return this.setProperty(prop, value); + } + }); + delete src._methods; + delete src._properties; + return Function.inject.call(this, src); + } + + function getConstructor(el) { + var match; + return classCheck && el.className && (match = el.className.match(classCheck)) && match[2] && classes[match[2]] || + el.tagName && tags[el.tagName] || + el.className !== undefined && HtmlElement || + el.nodeType == 1 && DomElement || + el.nodeType == 3 && DomTextNode || + el.nodeType == 9 && (el.documentElement.nodeName.toLowerCase() == 'html' && HtmlDocument || DomDocument) || + el.location && el.frames && el.history && DomWindow || + DomNode; + } + + var dont = {}; + + return { + _type: 'node', + _collection: DomNodes, + _initialize: true, + + initialize: function(el, props, doc) { + if (!el) return null; + if (this._tag && Base.type(el) == 'object') { + props = el; + el = this._tag; + } + if (typeof(el) == 'string') { + el = DomElement.create(el, props, doc); + } else if (el._wrapper) { + return el._wrapper; + } + if (props === dont) { + props = null; + } else { + var ctor = getConstructor(el); + if (ctor != this.constructor) + return new ctor(el, props); + } + this.$ = el; + try { + el._wrapper = this; + nodes[nodes.length] = el; + } catch (e) {} + if (props) this.set(props); + }, + + statics: { + inject: function(src) { + if (src) { + var proto = this.prototype, that = this; + src.statics = Base.each(src, function(val, name) { + if (typeof val == 'function' && !this[name] && !that[name]) { + this[name] = function(el, param1, param2) { + if (el) try { + proto.$ = el.$ || el; + return proto[name](param1, param2); + } finally { + delete proto.$; + } + } + } + }, src.statics || {}); + inject.call(this, src); + delete src.toString; + proto._collection.inject(src); + } + for (var i = 1, l = arguments.length; i < l; i++) + this.inject(arguments[i]); + return this; + }, + + extend: function(src) { + var ret = this.base(); + var init = src.initialize; + if (init) src.initialize = function(el, props) { + var ret = this._initialize && this.base(el, props); + if (ret) return ret; + init.apply(this, arguments); + } + inject.call(ret, src); + if (ret.prototype._collection == this.prototype._collection) + ret.inject = inject; + if (src) { + if (src._tag) + tags[src._tag.toLowerCase()] = tags[src._tag.toUpperCase()] = ret; + if (src._class) { + classes[src._class] = ret; + classCheck = new RegExp('(^|\\s)(' + Base.each(classes, function(val, name) { + this.push(name); + }, []).join('|') + ')(\\s|$)'); + if (!src._lazy && src.initialize) Browser.document.addEvent('domready', function() { + this.getElements('.' + src._class); + }); + } + } + return ret; + }, + + wrap: function(el) { + return el ? typeof el == 'string' + ? DomElement.get(el) + : el._wrapper || el._collection && el || new (getConstructor(el))(el, dont) + : null; + }, + + unwrap: function(el) { + return el && el.$ || el; + }, + + unique: function(el) { + if (!el._unique) { + nodes.push(el); + el._unique = ++unique; + } + }, + + isNode: function(obj) { + return /^(element|node|textnode|document)$/.test( + typeof obj == 'string' ? obj : Base.type(obj)); + }, + + dispose: function() { + dispose(true); + } + } + } +}); + +DomNode.inject(new function() { + var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', + 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer' + ].associate(); + var properties = Hash.append({ + text: Browser.TRIDENT || Browser.WEBKIT && Browser.VERSION < 420 || Browser.PRESTO && Browser.VERSION < 9 + ? function(node) { + return node.$.innerText !== undefined ? 'innerText' : 'nodeValue' + } : 'textContent', + html: 'innerHTML', 'class': 'className', className: 'className', 'for': 'htmlFor' + }, [ + 'value', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', + 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', + 'selectedIndex', 'useMap', 'width', 'height' + ].associate(function(name) { + return name.toLowerCase(); + }), bools); + + var clones = { input: 'checked', option: 'selected', textarea: Browser.WEBKIT && Browser.VERSION < 420 ? 'innerHTML' : 'value' }; + + function handle(that, prefix, name, value) { + var ctor = that.__proto__.constructor; + var handlers = ctor.handlers = ctor.handlers || { get: {}, set: {} }; + var list = handlers[prefix]; + var fn = name == 'events' && prefix == 'set' ? that.addEvents : list[name]; + if (fn === undefined) + fn = list[name] = that[prefix + name.capitalize()] || null; + return fn + ? fn[Base.type(value) == 'array' ? 'apply' : 'call'](that, value) + : that[prefix + 'Property'](name, value); + } + + function toNodes(elements) { + var els = Base.type(elements) == 'array' ? elements : Array.create(arguments); + var created = els.find(function(el) { + return !DomNode.isNode(el); + }); + var result = els.toNode(this.getDocument()); + return { + array: result ? (Base.type(result) == 'array' ? result : [result]) : [], + result: created && result + }; + } + + var fields = { + _properties: ['text'], + + set: function(name, value) { + switch (Base.type(name)) { + case 'string': + return handle(this, 'set', name, value); + case 'object': + return Base.each(name, function(value, key) { + handle(this, 'set', key, value); + }, this); + } + return this; + }, + + get: function(name) { + return handle(this, 'get', name); + }, + + getDocument: function() { + return DomNode.wrap(this.$.ownerDocument); + }, + + getWindow: function() { + return this.getDocument().getWindow(); + }, + + getPreviousNode: function() { + return DomNode.wrap(this.$.previousSibling); + }, + + getNextNode: function() { + return DomNode.wrap(this.$.nextSibling); + }, + + getFirstNode: function() { + return DomNode.wrap(this.$.firstChild); + }, + + getLastNode: function() { + return DomNode.wrap(this.$.lastChild); + }, + + getParentNode: function() { + return DomNode.wrap(this.$.parentNode); + }, + + getChildNodes: function() { + return new DomNodes(this.$.childNodes); + }, + + hasChildNodes: function() { + return this.$.hasChildNodes(); + }, + + appendChild: function(el) { + if (el = DomNode.wrap(el)) { + var text = Browser.TRIDENT && el.$.text; + if (text) el.$.text = ''; + this.$.appendChild(el.$); + if (text) el.$.text = text; + } + return this; + }, + + appendChildren: function() { + return Array.flatten(arguments).each(function(el) { + this.appendChild($(DomNode.wrap(el))); + }, this); + }, + + appendText: function(text) { + return this.injectBottom(this.getDocument().createTextNode(text)); + }, + + prependText: function(text) { + return this.injectTop(this.getDocument().createTextNode(text)); + }, + + remove: function() { + if (this.$.parentNode) + this.$.parentNode.removeChild(this.$); + return this; + }, + + removeChild: function(el) { + el = DomNode.wrap(el); + this.$.removeChild(el.$); + return el; + }, + + removeChildren: function() { + var nodes = this.getChildNodes(); + nodes.remove(); + return nodes; + }, + + replaceWith: function(el) { + if (this.$.parentNode) { + el = toNodes.apply(this, arguments); + var els = el.array; + if (els.length > 0) + this.$.parentNode.replaceChild(els[0].$, this.$); + for (var i = els.length - 1; i > 0; i--) + els[i].insertAfter(els[0]); + return el.result; + } + return null; + }, + + wrap: function() { + var el = this.injectBefore.apply(this, arguments), last; + do { + last = el; + el = el.getFirst(); + } while(el); + last.appendChild(this); + return last; + }, + + clone: function(contents) { + var clone = this.$.cloneNode(!!contents); + function clean(left, right) { + if (Browser.TRIDENT) { + left.clearAttributes(); + left.mergeAttributes(right); + left.removeAttribute('_wrapper'); + left.removeAttribute('_unique'); + if (left.options) + for (var l = left.options, r = right.options, i = l.length; i--;) + l[i].selected = r[i].selected; + } + var name = clones[right.tagName.toLowerCase()]; + if (name && right[name]) + left[name] = right[name]; + if (contents) + for (var l = left.childNodes, r = right.childNodes, i = l.length; i--;) + clean(l[i], r[i]); + } + clean(clone, this.$); + return DomNode.wrap(clone); + }, + + getProperty: function(name) { + var key = properties[name], value; + key = key && typeof key == 'function' ? key(this) : key; + var value = key ? this.$[key] : this.$.getAttribute(name); + return bools[name] ? !!value : value; + }, + + setProperty: function(name, value) { + var key = properties[name], defined = value !== undefined; + key = key && typeof key == 'function' ? key(this) : key; + if (key && bools[name]) value = value || !defined ? true : false; + else if (!defined) return this.removeProperty(name); + key ? this.$[key] = value : this.$.setAttribute(name, value); + return this; + }, + + removeProperty: function(name) { + var key = properties[name], bool = key && bools[name]; + key = key && typeof key == 'function' ? key(this) : key; + key ? this.$[key] = bool ? false : '' : this.$.removeAttribute(name); + return this; + }, + + getProperties: function() { + var props = {}; + for (var i = 0; i < arguments.length; i++) + props[arguments[i]] = this.getProperty(arguments[i]); + return props; + }, + + setProperties: function(src) { + return Base.each(src, function(value, name) { + this.setProperty(name, value); + }, this); + }, + + removeProperties: function() { + return Array.each(arguments, this.removeProperty, this); + } + }; + + var inserters = { + before: function(source, dest) { + if (source && dest && dest.$.parentNode) { + var text = Browser.TRIDENT && dest.$.text; + if (text) dest.$.text = ''; + dest.$.parentNode.insertBefore(source.$, dest.$); + if (text) dest.$.text = text; + } + }, + + after: function(source, dest) { + if (source && dest && dest.$.parentNode) { + var next = dest.$.nextSibling; + if (next) source.insertBefore(next); + else dest.getParent().appendChild(source); + } + }, + + bottom: function(source, dest) { + if (source && dest) + dest.appendChild(source); + }, + + top: function(source, dest) { + if (source && dest) { + var first = dest.$.firstChild; + if (first) source.insertBefore(first); + else dest.appendChild(source); + } + } + }; + + inserters.inside = inserters.bottom; + + Base.each(inserters, function(inserter, name) { + var part = name.capitalize(); + fields['insert' + part] = function(el) { + el = toNodes.apply(this, arguments); + for (var i = 0, list = el.array, l = list.length; i < l; i++) + inserter(i == 0 ? this : this.clone(true), list[i]); + return el.result || this; + } + + fields['inject' + part] = function(el) { + el = toNodes.apply(this, arguments); + for (var i = 0, list = el.array, l = list.length; i < l; i++) + inserter(list[i], this); + return el.result || this; + } + }); + + return fields; +}); + +DomElements = DomNodes.extend(); + +DomElement = DomNode.extend({ + _type: 'element', + _collection: DomElements, + + statics: { + get: function(selector, root) { + return (root && DomNode.wrap(root) || Browser.document).getElement(selector); + }, + + getAll: function(selector, root) { + return (root && DomNode.wrap(root) || Browser.document).getElements(selector); + }, + + create: function(tag, props, doc) { + if (Browser.TRIDENT && props) { + ['name', 'type', 'checked'].each(function(key) { + if (props[key]) { + tag += ' ' + key + '="' + props[key] + '"'; + if (key != 'checked') + delete props[key]; + } + }); + tag = '<' + tag + '>'; + } + return (DomElement.unwrap(doc) || document).createElement(tag); + }, + + isAncestor: function(el, parent) { + return !el ? false : el.ownerDocument == parent ? true + : Browser.WEBKIT && Browser.VERSION < 420 + ? Array.contains(parent.getElementsByTagName(el.tagName), el) + : parent.contains + ? parent != el && parent.contains(el) + : !!(parent.compareDocumentPosition(el) & 16) + } + } +}); + +DomElement.inject(new function() { + function walk(el, walk, start, match, all) { + var elements = all && new el._collection(); + el = el.$[start || walk]; + while (el) { + if (el.nodeType == 1 && (!match || DomElement.match(el, match))) { + if (!all) return DomNode.wrap(el); + elements.push(el); + } + el = el[walk]; + } + return elements; + } + + return { + _properties: ['id'], + + getTag: function() { + return (this.$.tagName || '').toLowerCase(); + }, + + getPrevious: function(match) { + return walk(this, 'previousSibling', null, match); + }, + + getAllPrevious: function(match) { + return walk(this, 'previousSibling', null, match, true); + }, + + getNext: function(match) { + return walk(this, 'nextSibling', null, match); + }, + + getAllNext: function(match) { + return walk(this, 'nextSibling', null, match, true); + }, + + getFirst: function(match) { + return walk(this, 'nextSibling', 'firstChild', match); + }, + + getLast: function(match) { + return walk(this, 'previousSibling', 'lastChild', match); + }, + + hasChild: function(match) { + return DomNode.isNode(match) + ? DomElement.isAncestor(DomElement.unwrap(match), this.$) + : !!this.getFirst(match); + }, + + getParent: function(match) { + return walk(this, 'parentNode', null, match); + }, + + getParents: function(match) { + return walk(this, 'parentNode', null, match, true); + }, + + hasParent: function(match) { + return DomNode.isNode(match) + ? DomElement.isAncestor(this.$, DomElement.unwrap(match)) + : !!this.getParent(match); + }, + + getChildren: function(match) { + return walk(this, 'nextSibling', 'firstChild', match, true); + }, + + hasChildren: function(match) { + return !!this.getChildren(match).length; + }, + + toString: function() { + return (this.$.tagName || this._type).toLowerCase() + + (this.$.id ? '#' + this.$.id : ''); + }, + + toNode: function() { + return this; + } + }; +}); + +$ = DomElement.get; +$$ = DomElement.getAll; + +DomTextNode = DomNode.extend({ + _type: 'textnode' +}); + +DomDocument = DomElement.extend({ + _type: 'document', + + initialize: function() { + if(Browser.TRIDENT && Browser.VERSION < 7) + try { + this.$.execCommand('BackgroundImageCache', false, true); + } catch (e) {} + }, + + createElement: function(tag, props) { + return DomNode.wrap(DomElement.create(tag, props, this.$)).set(props); + }, + + createTextNode: function(text) { + return $(this.$.createTextNode(text)); + }, + + getDocument: function() { + return this; + }, + + getWindow: function() { + return DomNode.wrap(this.$.defaultView || this.$.parentWindow); + }, + + open: function() { + this.$.open(); + }, + + close: function() { + this.$.close(); + }, + + write: function(markup) { + this.$.write(markup); + }, + + writeln: function(markup) { + this.$.writeln(markup); + } +}); + +Window = DomWindow = DomElement.extend({ + _type: 'window', + _initialize: false, + _methods: ['close', 'alert', 'prompt', 'confirm', 'blur', 'focus', 'reload'], + + getDocument: function() { + return DomNode.wrap(this.$.document); + }, + + getWindow: function() { + return this; + }, + + initialize: function(param) { + var win; + if (param.location && param.frames && param.history) { + win = this.base(param) || this; + } else { + if (typeof param == 'string') + param = { url: param }; + (['toolbar','menubar','location','status','resizable','scrollbars']).each(function(key) { + param[key] = param[key] ? 1 : 0; + }); + if (param.width && param.height) { + if (param.left == null) param.left = Math.round( + Math.max(0, (screen.width - param.width) / 2)); + if (param.top == null) param.top = Math.round( + Math.max(0, (screen.height - param.height) / 2 - 40)); + } + var str = Base.each(param, function(val, key) { + if (!/^(focus|confirm|url|name)$/.test(key)) + this.push(key + '=' + (val + 0)); + }, []).join(); + win = this.base(window.open(param.url, param.name.replace(/\s+|\.+|-+/gi, ''), str)) || this; + if (win && param.focus) + win.focus(); + } + return ['location', 'frames', 'history'].each(function(key) { + this[key] = this.$[key]; + }, win); + } +}); + +DomElement.inject(new function() { + function cumulate(name, parent, iter) { + var left = name + 'Left', top = name + 'Top'; + return function(that) { + var cur, next = that, x = 0, y = 0; + do { + cur = next; + x += cur.$[left] || 0; + y += cur.$[top] || 0; + } while((next = DomNode.wrap(cur.$[parent])) && (!iter || iter(cur, next))) + return { x: x, y: y }; + } + } + + function bounds(fields, offset) { + return function(values) { + var vals = /^(object|array)$/.test(Base.type(values)) ? values : arguments; + if (offset) { + if (vals.x) vals.left = vals.x; + if (vals.y) vals.top = vals.y; + } + var i = 0; + return fields.each(function(name) { + var val = vals.length ? vals[i++] : vals[name]; + if (val != null) this.setStyle(name, val); + }, this); + } + } + + function body(that) { + return that.getTag() == 'body'; + } + + var getAbsolute = cumulate('offset', 'offsetParent', Browser.WEBKIT ? function(cur, next) { + return next.$ != document.body || cur.getStyle('position') != 'absolute'; + } : null, true); + + var getPositioned = cumulate('offset', 'offsetParent', function(cur, next) { + return next.$ != document.body && !/^(relative|absolute)$/.test(next.getStyle('position')); + }); + + var getScrollOffset = cumulate('scroll', 'parentNode'); + + var fields = { + + getSize: function() { + return body(this) + ? this.getWindow().getSize() + : { width: this.$.offsetWidth, height: this.$.offsetHeight }; + }, + + getOffset: function(relative) { + if (body(this)) + return this.getWindow().getOffset(); + if (relative && !DomNode.isNode(relative)) + return getPositioned(this); + var off = getAbsolute(this); + if (relative) { + var rel = getAbsolute(DomNode.wrap(relative)); + off = { x: off.x - rel.x, y: off.y - rel.y }; + } + return off; + }, + + getScrollOffset: function() { + return body(this) + ? this.getWindow().getScrollOffset() + : getScrollOffset(this); + }, + + getScrollSize: function() { + return body(this) + ? this.getWindow().getScrollSize() + : { width: this.$.scrollWidth, height: this.$.scrollHeight }; + }, + + getBounds: function(relative) { + if (body(this)) + return this.getWindow().getBounds(); + var off = this.getOffset(relative), el = this.$; + return { + left: off.x, + top: off.y, + right: off.x + el.offsetWidth, + bottom: off.y + el.offsetHeight, + width: el.offsetWidth, + height: el.offsetHeight + }; + }, + + setBounds: bounds(['left', 'top', 'width', 'height', 'clip'], true), + + setOffset: bounds(['left', 'top'], true), + + setSize: bounds(['width', 'height', 'clip']), + + setScrollOffset: function(x, y) { + if (body(this)) { + this.getWindow().setScrollOffset(x, y); + } else { + var off = typeof x == 'object' ? x : { x: x, y: y }; + this.$.scrollLeft = off.x; + this.$.scrollTop = off.y; + } + return this; + }, + + scrollTo: function(x, y) { + return this.setScrollOffset(x, y); + }, + + contains: function(pos) { + var bounds = this.getBounds(); + return pos.x >= bounds.left && pos.x < bounds.right && + pos.y >= bounds.top && pos.y < bounds.bottom; + } + }; + + ['left', 'top', 'right', 'bottom', 'width', 'height'].each(function(name) { + var part = name.capitalize(); + fields['get' + part] = function() { + return this.$['offset' + part]; + }; + fields['set' + part] = function(value) { + this.$.style[name] = isNaN(value) ? value : value + 'px'; + }; + }); + + return fields; +}); + +[DomDocument, DomWindow].each(function(ctor) { + ctor.inject(this); +}, { + + getSize: function() { + if (Browser.PRESTO || Browser.WEBKIT) { + var win = this.getWindow().$; + return { width: win.innerWidth, height: win.innerHeight }; + } + var doc = this.getCompatElement(); + return { width: doc.clientWidth, height: doc.clientHeight }; + }, + + getScrollOffset: function() { + var win = this.getWindow().$, doc = this.getCompatElement(); + return { x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop }; + }, + + getScrollSize: function() { + var doc = this.getCompatElement(), min = this.getSize(); + return { width: Math.max(doc.scrollWidth, min.width), height: Math.max(doc.scrollHeight, min.height) }; + }, + + getOffset: function() { + return { x: 0, y: 0 }; + }, + + getBounds: function() { + var size = this.getSize(); + return { + left: 0, top: 0, + right: size.width, bottom: size.height, + width: size.width, height: size.height + }; + }, + + setScrollOffset: function(x, y) { + var off = typeof x == 'object' ? x : { x: x, y: y }; + this.getWindow().$.scrollTo(off.x, off.y); + return this; + }, + + getElementAt: function(pos, exclude) { + var el = this.getDocument().getElement('body'); + while (true) { + var max = -1; + var ch = el.getFirst(); + while (ch) { + if (ch.contains(pos) && ch != exclude) { + var z = ch.$.style.zIndex.toInt() || 0; + if (z >= max) { + el = ch; + max = z; + } + } + ch = ch.getNext(); + } + if (max < 0) break; + } + return el; + }, + + getCompatElement: function() { + var doc = this.getDocument(); + return doc.getElement(!doc.$.compatMode + || doc.$.compatMode == 'CSS1Compat' ? 'html' : 'body').$; + } +}); + +DomEvent = Base.extend(new function() { + var keys = { + '8': 'backspace', + '13': 'enter', + '27': 'escape', + '32': 'space', + '37': 'left', + '38': 'up', + '39': 'right', + '40': 'down', + '46': 'delete' + }; + + function hover(name, type) { + return { + type: type, + listener: function(event) { + if (event.relatedTarget != this && !this.hasChild(event.relatedTarget)) + this.fireEvent(name, [event]); + } + } + } + + return { + initialize: function(event) { + this.event = event = event || window.event; + this.type = event.type; + this.target = DomNode.wrap(event.target || event.srcElement); + if (this.target && this.target.$.nodeType == 3) + this.target = this.target.getParent(); + this.shift = event.shiftKey; + this.control = event.ctrlKey; + this.alt = event.altKey; + this.meta = event.metaKey; + if (/^(mousewheel|DOMMouseScroll)$/.test(this.type)) { + this.wheel = event.wheelDelta ? + event.wheelDelta / (window.opera ? -120 : 120) : + - (event.detail || 0) / 3; + } else if (/^key/.test(this.type)) { + this.code = event.which || event.keyCode; + this.key = keys[this.code] || String.fromCharCode(this.code).toLowerCase(); + } else if (/^mouse|^click$/.test(this.type)) { + this.page = { + x: event.pageX || event.clientX + document.documentElement.scrollLeft, + y: event.pageY || event.clientY + document.documentElement.scrollTop + }; + this.client = { + x: event.pageX ? event.pageX - window.pageXOffset : event.clientX, + y: event.pageY ? event.pageY - window.pageYOffset : event.clientY + }; + var offset = this.target.getOffset(); + this.offset = { + x: this.page.x - offset.x, + y: this.page.y - offset.y + } + this.rightClick = event.which == 3 || event.button == 2; + if (/^mouse(over|out)$/.test(this.type)) + this.relatedTarget = DomNode.wrap(event.relatedTarget || + this.type == 'mouseout' ? event.toElement : event.fromElement); + } + }, + + stop: function() { + this.stopPropagation(); + this.preventDefault(); + return this; + }, + + stopPropagation: function() { + if (this.event.stopPropagation) this.event.stopPropagation(); + else this.event.cancelBubble = true; + this.stopped = true; + return this; + }, + + preventDefault: function() { + if (this.event.preventDefault) this.event.preventDefault(); + else this.event.returnValue = false; + return this; + }, + + statics: { + events: new Hash({ + mouseenter: hover('mouseenter', 'mouseover'), + + mouseleave: hover('mouseleave', 'mouseout'), + + mousewheel: { type: Browser.GECKO ? 'DOMMouseScroll' : 'mousewheel' }, + + domready: function(func) { + var win = this.getWindow(), doc = this.getDocument(); + if (Browser.loaded) { + func.call(this); + } else if (!doc.onDomReady) { + doc.onDomReady = function() { + if (!Browser.loaded) { + Browser.loaded = true; + doc.fireEvent('domready'); + win.fireEvent('domready'); + } + } + if (Browser.TRIDENT) { + var temp = doc.createElement('div'); + (function() { + try { + temp.$.doScroll('left'); + temp.insertBottom(DomElement.get('body')).setHtml('temp').remove(); + doc.onDomReady(); + } catch (e) { + arguments.callee.delay(50); + } + }).delay(0); + } else if (Browser.WEBKIT && Browser.VERSION < 525) { + (function() { + /^(loaded|complete)$/.test(doc.$.readyState) + ? doc.onDomReady() : arguments.callee.delay(50); + })(); + } else { + win.addEvent('load', doc.onDomReady); + doc.addEvent('DOMContentLoaded', doc.onDomReady); + } + } + } + }), + + add: function(events) { + this.events.append(events); + } + } + }; +}); + +DomElement.inject(new function() { + function callEvent(fire) { + return function(type, args, delay) { + var entries = (this.events || {})[type]; + if (entries) { + var event = args && args[0]; + if (event) + args[0] = event.event ? event : new DomEvent(event); + entries.each(function(entry) { + entry[fire ? 'func' : 'bound'].delay(delay, this, args); + }, this); + } + return !!entries; + } + } + + return { + addEvent: function(type, func) { + this.events = this.events || {}; + var entries = this.events[type] = this.events[type] || []; + if (func && !entries.find(function(entry) { return entry.func == func })) { + var listener = func, name = type, pseudo = DomEvent.events[type]; + if (pseudo) { + if (typeof pseudo == 'function') pseudo = pseudo.call(this, func); + listener = pseudo && pseudo.listener || listener; + name = pseudo && pseudo.type; + } + var that = this, bound = function(event) { + if (event || window.event) + event = event && event.event ? event : new DomEvent(event); + if (listener.call(that, event) === false && event) + event.stop(); + }; + if (name) { + if (this.$.addEventListener) { + this.$.addEventListener(name, bound, false); + } else if (this.$.attachEvent) { + this.$.attachEvent('on' + name, bound); + } + } + entries.push({ func: func, name: name, bound: bound }); + } + return this; + }, + + removeEvent: function(type, func) { + var entries = (this.events || {})[type], entry; + if (func && entries) { + if (entry = entries.remove(function(entry) { return entry.func == func })) { + var name = entry.name, pseudo = DomEvent.events[type]; + if (pseudo && pseudo.remove) pseudo.remove.call(this, func); + if (name) { + if (this.$.removeEventListener) { + this.$.removeEventListener(name, entry.bound, false); + } else if (this.$.detachEvent) { + this.$.detachEvent('on' + name, entry.bound); + } + } + } + } + return this; + }, + + addEvents: function(events) { + return Base.each(events || [], function(fn, type) { + this.addEvent(type, fn); + }, this); + }, + + removeEvents: function(type) { + if (this.events) { + if (type) { + (this.events[type] || []).each(function(fn) { + this.removeEvent(type, fn); + }, this); + delete this.events[type]; + } else { + Base.each(this.events, function(ev, type) { + this.removeEvents(type); + }, this); + this.events = null; + } + } + return this; + }, + + fireEvent: callEvent(true), + + triggerEvent: callEvent(false), + + finalize: function() { + this.removeEvents(); + } + }; +}); + +DomEvent.add(new function() { + var object, last; + + function dragStart(event) { + if (object != this) { + event.type = 'dragstart'; + last = event.page; + this.fireEvent('dragstart', [event]); + if (!event.stopped) { + event.stop(); + var doc = this.getDocument(); + doc.addEvent('mousemove', drag); + doc.addEvent('mouseup', dragEnd); + object = this; + } + } + } + + function drag(event) { + event.type = 'drag'; + event.delta = { + x: event.page.x - last.x, + y: event.page.y - last.y + } + last = event.page; + object.fireEvent('drag', [event]); + event.preventDefault(); + } + + function dragEnd(event) { + if (object) { + event.type = 'dragend'; + object.fireEvent('dragend', [event]); + event.preventDefault(); + var doc = object.getDocument(); + doc.removeEvent('mousemove', drag); + doc.removeEvent('mouseup', dragEnd); + object = null; + } + } + + return { + dragstart: { + type: 'mousedown', + listener: dragStart + }, + + drag: { + type: 'mousedown', + listener: dragStart + }, + + dragend: {} + }; +}); + +DomElement.inject(new function() { + var XPATH= 0, FILTER = 1; + + var methods = [{ + getParam: function(items, separator, context, params) { + var str = context.namespaceURI ? 'xhtml:' + params.tag : params.tag; + if (separator && (separator = DomElement.separators[separator])) + str = separator[XPATH] + str; + for (var i = params.pseudos.length; i--;) { + var pseudo = params.pseudos[i]; + str += pseudo.handler[XPATH](pseudo.argument); + } + if (params.id) str += '[@id="' + params.id + '"]'; + for (var i = params.classes.length; i--;) + str += '[contains(concat(" ", @class, " "), " ' + params.classes[i] + ' ")]'; + for (var i = params.attributes.length; i--;) { + var attribute = params.attributes[i]; + var operator = DomElement.operators[attribute[1]]; + if (operator) str += operator[XPATH](attribute[0], attribute[2]); + else str += '[@' + attribute[0] + ']'; + } + items.push(str); + return items; + }, + + getElements: function(items, elements, context) { + function resolver(prefix) { + return prefix == 'xhtml' ? 'http://www.w3.org/1999/xhtml' : false; + } + var res = (context.ownerDocument || context).evaluate('.//' + items.join(''), context, + resolver, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); + for (var i = 0, l = res.snapshotLength; i < l; i++) + elements.push(res.snapshotItem(i)); + } + }, { + getParam: function(items, separator, context, params, data) { + var found = []; + var tag = params.tag; + if (separator && (separator = DomElement.separators[separator])) { + separator = separator[FILTER]; + var uniques = {}; + function add(item) { + if (!item._unique) + DomNode.unique(item); + if (!uniques[item._unique] && match(item, params, data)) { + uniques[item._unique] = true; + found.push(item); + return true; + } + } + for (var i = 0, l = items.length; i < l; i++) + separator(items[i], params, add); + if (params.clearTag) + params.tag = params.clearTag = null; + return found; + } + if (params.id) { + var el = (context.ownerDocument || context).getElementById(params.id); + params.id = null; + return el && DomElement.isAncestor(el, context) + && match(el, params, data) ? [el] : null; + } else { + if (!items.length) { + items = context.getElementsByTagName(tag); + params.tag = null; + } + for (var i = 0, l = items.length; i < l; i++) + if (match(items[i], params, data)) + found.push(items[i]); + } + return found; + }, + + getElements: function(items, elements, context) { + elements.append(items); + } + }]; + + function parse(selector) { + var params = { tag: '*', id: null, classes: [], attributes: [], pseudos: [] }; + selector.replace(/:([^:(]+)*(?:\((["']?)(.*?)\2\))?|\[(\w+)(?:([!*^$~|]?=)(["']?)(.*?)\6)?\]|\.[\w-]+|#[\w-]+|\w+|\*/g, function(part) { + switch (part.charAt(0)) { + case '.': params.classes.push(part.slice(1)); break; + case '#': params.id = part.slice(1); break; + case '[': params.attributes.push([arguments[4], arguments[5], arguments[7]]); break; + case ':': + var handler = DomElement.pseudos[arguments[1]]; + if (!handler) { + params.attributes.push([arguments[1], arguments[3] ? '=' : '', arguments[3]]); + break; + } + params.pseudos.push({ + name: arguments[1], + argument: handler && handler.parser + ? (handler.parser.apply ? handler.parser(arguments[3]) : handler.parser) + : arguments[3], + handler: handler.handler || handler + }); + break; + default: params.tag = part; + } + return ''; + }); + return params; + } + + function match(el, params, data) { + if (params.id && params.id != el.id) + return false; + + if (params.tag && params.tag != '*' && params.tag != (el.tagName || '').toLowerCase()) + return false; + + for (var i = params.classes.length; i--;) + if (!el.className || !el.className.contains(params.classes[i], ' ')) + return false; + + var proto = DomElement.prototype; + for (var i = params.attributes.length; i--;) { + var attribute = params.attributes[i]; + proto.$ = el; + var val = proto.getProperty(attribute[0]); + if (!val) return false; + var operator = DomElement.operators[attribute[1]]; + operator = operator && operator[FILTER]; + if (operator && (!val || !operator(val, attribute[2]))) + return false; + } + + for (var i = params.pseudos.length; i--;) { + var pseudo = params.pseudos[i]; + if (!pseudo.handler[FILTER](el, pseudo.argument, data)) + return false; + } + + return true; + } + + function filter(items, selector, context, elements, data) { + var method = methods[!Browser.XPATH || items.length || + typeof selector == 'string' && selector.contains('option[') + ? FILTER : XPATH]; + var separators = []; + selector = selector.trim().replace(/\s*([+>~\s])[a-zA-Z#.*\s]/g, function(match) { + if (match.charAt(2)) match = match.trim(); + separators.push(match.charAt(0)); + return ':)' + match.charAt(1); + }).split(':)'); + for (var i = 0, l = selector.length; i < l; i++) { + var params = parse(selector[i]); + if (!params) return elements; + var next = method.getParam(items, separators[i - 1], context, params, data); + if (!next) break; + items = next; + } + method.getElements(items, elements, context); + return elements; + } + + return { + + getElements: function(selectors, nowrap) { + var elements = nowrap ? [] : new this._collection(); + selectors = !selectors ? ['*'] : typeof selectors == 'string' + ? selectors.split(',') + : selectors.length != null ? selectors : [selectors]; + for (var i = 0, l = selectors.length; i < l; i++) { + var selector = selectors[i]; + if (Base.type(selector) == 'element') elements.push(selector); + else filter([], selector, this.$, elements, {}); + } + return elements; + }, + + getElement: function(selector) { + var el, type = Base.type(selector), match; + if (type == 'window') { + el = selector; + } else { + if (type == 'string' && (match = selector.match(/^#?([\w-]+)$/))) + el = this.getDocument().$.getElementById(match[1]); + else if (DomNode.isNode(type)) + el = DomElement.unwrap(selector); + if (el && el != this.$ && !DomElement.isAncestor(el, this.$)) + el = null; + if (!el) + el = this.getElements(selector, true)[0]; + } + return DomNode.wrap(el); + }, + + hasElement: function(selector) { + return !!this.getElement(selector); + }, + + match: function(selector) { + return !selector || match(this.$, parse(selector), {}); + }, + + filter: function(elements, selector) { + return filter(elements, selector, this.$, new this._collection(), {}); + }, + + statics: { + match: function(el, selector) { + return !selector || match(DomElement.unwrap(el), parse(selector), {}); + } + } + }; +}); + +DomElement.separators = { + '~': [ + '/following-sibling::', + function(item, params, add) { + while (item = item.nextSibling) + if (item.nodeType == 1 && add(item)) + break; + } + ], + + '+': [ + '/following-sibling::*[1]/self::', + function(item, params, add) { + while (item = item.nextSibling) { + if (item.nodeType == 1) { + add(item); + break; + } + } + } + ], + + '>': [ + '/', + function(item, params, add) { + var children = item.childNodes; + for (var i = 0, l = children.length; i < l; i++) + if (children[i].nodeType == 1) + add(children[i]); + } + ], + + ' ': [ + '//', + function(item, params, add) { + var children = item.getElementsByTagName(params.tag); + params.clearTag = true; + for (var i = 0, l = children.length; i < l; i++) + add(children[i]); + } + ] +}; + +DomElement.operators = new function() { + function contains(sep) { + return [ + function(a, v) { + return '[contains(' + (sep ? 'concat("' + sep + '", @' + a + ', "' + sep + '")' : '@' + a) + ', "' + sep + v + sep + '")]'; + }, + function(a, v) { + return a.contains(v, sep); + } + ] + } + + return { + '=': [ + function(a, v) { + return '[@' + a + '="' + v + '"]'; + }, + function(a, v) { + return a == v; + } + ], + + '^=': [ + function(a, v) { + return '[starts-with(@' + a + ', "' + v + '")]'; + }, + function(a, v) { + return a.substring(0, v.length) == v; + } + ], + + '$=': [ + function(a, v) { + return '[substring(@' + a + ', string-length(@' + a + ') - ' + v.length + ' + 1) = "' + v + '"]'; + }, + function(a, v) { + return a.substring(a.length - v.length) == v; + } + ], + + '!=': [ + function(a, v) { + return '[@' + a + '!="' + v + '"]'; + }, + function(a, v) { + return a != v; + } + ], + + '*=': contains(''), + + '|=': contains('-'), + + '~=': contains(' ') + }; +}; + +DomElement.pseudos = new function() { + var nthChild = [ + function(argument) { + switch (argument.special) { + case 'n': return '[count(preceding-sibling::*) mod ' + argument.a + ' = ' + argument.b + ']'; + case 'first': return '[count(preceding-sibling::*) = 0]'; + case 'last': return '[count(following-sibling::*) = 0]'; + case 'only': return '[not(preceding-sibling::* or following-sibling::*)]'; + case 'index': return '[count(preceding-sibling::*) = ' + argument.a + ']'; + } + }, + function(el, argument, data) { + var count = 0; + switch (argument.special) { + case 'n': + data.indices = data.indices || {}; + if (!data.indices[el._unique]) { + var children = el.parentNode.childNodes; + for (var i = 0, l = children.length; i < l; i++) { + var child = children[i]; + if (child.nodeType == 1) { + if (!child._unique) + DomNode.unique(item); + data.indices[child._unique] = count++; + } + } + } + return data.indices[el._unique] % argument.a == argument.b; + case 'first': + while (el = el.previousSibling) + if (el.nodeType == 1) + return false; + return true; + case 'last': + while (el = el.nextSibling) + if (el.nodeType == 1) + return false; + return true; + case 'only': + var prev = el; + while(prev = prev.previousSibling) + if (prev.nodeType == 1) + return false; + var next = el; + while (next = next.nextSibling) + if (next.nodeType == 1) + return false; + return true; + case 'index': + while (el = el.previousSibling) + if (el.nodeType == 1 && ++count > argument.a) + return false; + return true; + } + return false; + } + ]; + + function contains(caseless) { + var abc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + return [ + function(argument) { + return '[contains(' + (caseless ? 'translate(text(), "' + abc + + '", "' + abc.toLowerCase() + '")' : 'text()') + ', "' + + (caseless && argument ? argument.toLowerCase() : argument) + '")]'; + }, + function(el, argument) { + if (caseless && argument) argument = argument.toLowerCase(); + var nodes = el.childNodes; + for (var i = nodes.length - 1; i >= 0; i--) { + var child = nodes[i]; + if (child.nodeName && child.nodeType == 3 && + (caseless ? child.nodeValue.toLowerCase() : child.nodeValue).contains(argument)) + return true; + } + return false; + } + ]; + } + + return { + 'nth-child': { + parser: function(argument) { + var match = argument ? argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/) : [null, 1, 'n', 0]; + if (!match) return null; + var i = parseInt(match[1]), + a = isNaN(i) ? 1 : i, + special = match[2], + b = parseInt(match[3]) || 0; + if (a != 0) { + b--; + while (b < 1) b += a; + while (b >= a) b -= a; + } else { + a = b; + special = 'index'; + } + switch (special) { + case 'n': return { a: a, b: b, special: 'n' }; + case 'odd': return { a: 2, b: 0, special: 'n' }; + case 'even': return { a: 2, b: 1, special: 'n' }; + case 'first': return { special: 'first' }; + case 'last': return { special: 'last' }; + case 'only': return { special: 'only' }; + default: return { a: a - 1, special: 'index' }; + } + }, + handler: nthChild + }, + + 'even': { + parser: { a: 2, b: 1, special: 'n' }, + handler: nthChild + }, + + 'odd': { + parser: { a: 2, b: 0, special: 'n' }, + handler: nthChild + }, + + 'first-child': { + parser: { special: 'first' }, + handler: nthChild + }, + + 'last-child': { + parser: { special: 'last' }, + handler: nthChild + }, + + 'only-child': { + parser: { special: 'only' }, + handler: nthChild + }, + + 'enabled': [ + function() { + return '[not(@disabled)]'; + }, + function(el) { + return !el.disabled; + } + ], + + 'empty': [ + function() { + return '[not(node())]'; + }, + function(el) { + return !(el.innerText || el.textContent || '').length; + } + ], + + 'contains': contains(false), + + 'contains-caseless': contains(true) + }; +}; + +HtmlElements = DomElements.extend(); + +HtmlElement = DomElement.extend({ + _collection: HtmlElements +}); + +HtmlElement.inject({ + _properties: ['html'], + + getClass: function() { + return this.$.className; + }, + + setClass: function(cls) { + this.$.className = cls; + }, + + modifyClass: function(name, add) { + if (!this.hasClass(name) ^ !add) + this.$.className = (add ? this.$.className + ' ' + name : + this.$.className.replace(name, '')).clean(); + return this; + }, + + addClass: function(name) { + return this.modifyClass(name, true); + }, + + removeClass: function(name) { + return this.modifyClass(name, false); + }, + + toggleClass: function(name) { + return this.modifyClass(name, !this.hasClass(name)); + }, + + hasClass: function(name) { + return this.$.className.contains(name, ' '); + } +}); + +Array.inject({ + toNode: function(doc) { + doc = DomNode.wrap(doc || document); + var elements = new HtmlElements(); + for (var i = 0; i < this.length;) { + var value = this[i++], element = null, type = Base.type(value); + if (type == 'string') { + var props = /^(object|hash)$/.test(Base.type(this[i])) && this[i++]; + element = value.isHtml() + ? value.toNode(doc).set(props) + : doc.createElement(value, props); + if (Base.type(this[i]) == 'array') + element.injectBottom(this[i++].toNode(doc)); + } else if (DomNode.isNode(type)) { + element = value; + } else if (value && value.toNode) { + element = value.toNode(doc); + } + if (element) + elements[Base.type(element) == 'array' ? 'append' : 'push'](element); + } + return elements.length == 1 ? elements[0] : elements; + } +}); + +String.inject({ + toNode: function(doc) { + var doc = doc || document, elements; + if (this.isHtml()) { + var str = this.trim().toLowerCase(); + var div = DomElement.unwrap(doc).createElement('div'); + + var wrap = + !str.indexOf('', ''] || + !str.indexOf('', ''] || + (!str.indexOf('', ''] || + !str.indexOf('', ''] || + (!str.indexOf('', ''] || + !str.indexOf('', ''] || + [0,'','']; + + div.innerHTML = wrap[1] + this + wrap[2]; + while (wrap[0]--) + div = div.firstChild; + if (Browser.TRIDENT) { + var els = []; + if (!str.indexOf('' && str.indexOf('= 0 ; --i) { + var el = els[i]; + if (el.nodeName.toLowerCase() == 'tbody' && !el.childNodes.length) + el.parentNode.removeChild(el); + } + } + elements = new HtmlElements(div.childNodes); + } else { + elements = DomNode.wrap(doc).getElements(this); + } + return elements.length == 1 ? elements[0] : elements; + } +}); + +HtmlDocument = DomDocument.extend({ + _collection: HtmlElements +}); + +HtmlElement.inject(new function() { + var styles = { + all: { + width: '@px', height: '@px', left: '@px', top: '@px', right: '@px', bottom: '@px', + color: 'rgb(@, @, @)', backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', + fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', textIndent: '@px', + margin: '@px @px @px @px', padding: '@px @px @px @px', + border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)', + borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', + borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)', + clip: 'rect(@px, @px, @px, @px)', opacity: '@' + }, + part: { + 'border': {}, 'borderWidth': {}, 'borderStyle': {}, 'borderColor': {}, + 'margin': {}, 'padding': {} + } + }; + + ['Top', 'Right', 'Bottom', 'Left'].each(function(dir) { + ['margin', 'padding'].each(function(style) { + var sd = style + dir; + styles.part[style][sd] = styles.all[sd] = '@px'; + }); + var bd = 'border' + dir; + styles.part.border[bd] = styles.all[bd] = '@px @ rgb(@, @, @)'; + var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color'; + styles.part[bd] = {}; + styles.part.borderWidth[bdw] = styles.part[bd][bdw] = '@px'; + styles.part.borderStyle[bds] = styles.part[bd][bds] = '@'; + styles.part.borderColor[bdc] = styles.part[bd][bdc] = 'rgb(@, @, @)'; + }); + + Base.each(styles.all, function(val, name) { + this[name] = val.split(' '); + }); + + var fields = { + + getComputedStyle: function(name) { + if (this.$.currentStyle) return this.$.currentStyle[name.camelize()]; + var style = this.getWindow().$.getComputedStyle(this.$, null); + return style ? style.getPropertyValue(name.hyphenate()) : null; + }, + + getStyle: function(name) { + if (name === undefined) return this.getStyles(); + if (name == 'opacity') { + var op = this.opacity; + return op || op == 0 ? op : this.getVisibility() ? 1 : 0; + } + var el = this.$; + name = name.camelize(); + var style = el.style[name]; + if (!Base.check(style)) { + if (styles.part[name]) { + style = Hash.map(styles.part[name], function(val, key) { + return this.getStyle(key); + }, this); + return style.every(function(val) { + return val == style[0]; + }) ? style[0] : style.join(' '); + } + style = this.getComputedStyle(name); + } + if (name == 'visibility') + return /^(visible|inherit(|ed))$/.test(style); + var color = style && style.match(/rgb[a]?\([\d\s,]+\)/); + if (color) return style.replace(color[0], color[0].rgbToHex()); + if (Browser.PRESTO || Browser.TRIDENT && isNaN(parseInt(style))) { + if (/^(width|height)$/.test(name)) { + var size = 0; + (name == 'width' ? ['left', 'right'] : ['top', 'bottom']).each(function(val) { + size += this.getStyle('border-' + val + '-width').toInt() + this.getStyle('padding-' + val).toInt(); + }, this); + return this.$['offset' + name.capitalize()] - size + 'px'; + } + if (Browser.PRESTO && /px/.test(style)) return style; + if (/border(.+)[wW]idth|margin|padding/.test(name)) return '0px'; + } + return style; + }, + + setStyle: function(name, value) { + if (value === undefined) return this.setStyles(name); + var el = this.$; + switch (name) { + case 'float': + name = Browser.TRIDENT ? 'styleFloat' : 'cssFloat'; + break; + case 'clip': + if (value == true) + value = [0, el.offsetWidth, el.offsetHeight, 0]; + break; + default: + name = name.camelize(); + } + var type = Base.type(value); + if (value != undefined && type != 'string') { + var parts = styles.all[name] || ['@'], index = 0; + value = (type == 'array' ? value.flatten() : [value]).map(function(val) { + var part = parts[index++]; + if (!part) + throw Base.stop; + return Base.type(val) == 'number' ? part.replace('@', name == 'opacity' ? val : Math.round(val)) : val; + }).join(' '); + } + switch (name) { + case 'visibility': + if (!isNaN(value)) value = !!value.toInt() + ''; + value = value == 'true' && 'visible' || value == 'false' && 'hidden' || value; + break; + case 'opacity': + this.opacity = value = parseFloat(value); + this.setStyle('visibility', !!value); + if (!value) value = 1; + if (!el.currentStyle || !el.currentStyle.hasLayout) el.style.zoom = 1; + if (Browser.TRIDENT) el.style.filter = value > 0 && value < 1 ? 'alpha(opacity=' + value * 100 + ')' : ''; + el.style.opacity = value; + return this; + } + el.style[name] = value; + return this; + }, + + getStyles: function() { + return arguments.length ? Array.each(arguments, function(name) { + this[name] = that.getStyle(name); + }, {}) : this.$.style.cssText; + }, + + setStyles: function(styles) { + switch (Base.type(styles)) { + case 'object': + Base.each(styles, function(style, name) { + if (style !== undefined) + this.setStyle(name, style); + }, this); + break; + case 'string': + this.$.style.cssText = styles; + } + return this; + } + }; + + ['opacity', 'color', 'background', 'visibility', 'clip', 'zIndex', + 'border', 'margin', 'padding', 'display'].each(function(name) { + var part = name.capitalize(); + fields['get' + part] = function() { + return this.getStyle(name); + }; + fields['set' + part] = function(value) { + return this.setStyle(name, arguments.length > 1 + ? Array.create(arguments) : value); + }; + }); + + return fields; +}); + +HtmlElement.inject({ + + getFormElements: function() { + return this.getElements(['input', 'select', 'textarea']); + }, + + getValue: function(name) { + var el = this.getElement(name); + return el && el.getValue && el.getValue(); + }, + + setValue: function(name, val) { + var el = this.getElement(name); + if (!el) el = this.injectTop('input', { type: 'hidden', id: name, name: name }); + return el.setValue(val); + }, + + getValues: function() { + return this.getFormElements().each(function(el) { + var name = el.getName(), value = el.getValue(); + if (name && value !== undefined && !el.getDisabled()) + this[name] = value; + }, new Hash()); + }, + + setValues: function(values) { + return Base.each(values, function(val, name) { + this.setValue(name, val); + }, this); + }, + + toQueryString: function() { + return Base.toQueryString(this.getValues()); + } +}); + +Form = HtmlElement.extend({ + _tag: 'form', + _properties: ['action', 'method', 'target'], + _methods: ['submit'], + + blur: function() { + return this.getFormElements().each(function(el) { + el.blur(); + }, this); + }, + + enable: function(enable) { + return this.getFormElements().each(function(el) { + el.enable(enable); + }, this); + } +}); + +FormElement = HtmlElement.extend({ + _properties: ['name', 'disabled'], + _methods: ['focus', 'blur'], + + enable: function(enable) { + var disabled = !enable && enable !== undefined; + if (disabled) this.$.blur(); + this.$.disabled = disabled; + return this; + } +}); + +Input = FormElement.extend({ + _tag: 'input', + _properties: ['type', 'checked', 'defaultChecked', 'readOnly', 'maxLength'], + _methods: ['click'], + + getValue: function() { + if (this.$.checked && /^(checkbox|radio)$/.test(this.$.type) || + /^(hidden|text|password|button|search)$/.test(this.$.type)) + return this.$.value; + }, + + setValue: function(val) { + if (/^(checkbox|radio)$/.test(this.$.type)) this.$.checked = this.$.value == val; + else this.$.value = val != null ? val : ''; + return this; + } +}); + +TextArea = FormElement.extend({ + _tag: 'textarea', + _properties: ['value'] +}); + +Select = FormElement.extend({ + _tag: 'select', + _properties: ['type', 'selectedIndex'], + + getOptions: function() { + return this.getElements('option'); + }, + + getSelected: function() { + return this.getElements('option[selected]'); + }, + + setSelected: function(values) { + this.$.selectedIndex = -1; + if (values) { + Array.each(values.length != null ? values : [values], function(val) { + val = DomElement.unwrap(val); + if (val != null) + this.getElements('option[value="' + (val.value || val) + '"]').setProperty('selected', true); + }, this); + } + return this; + }, + + getValue: function() { + return this.getSelected().getProperty('value'); + }, + + setValue: function(values) { + return this.setSelected(values); + } +}); + +SelectOption = FormElement.extend({ + _tag: 'option', + _properties: ['text', 'value', 'selected', 'defaultSelected', 'index'] +}); + +FormElement.inject({ + setSelection: function(start, end) { + var sel = end == undefined ? start : { start: start, end: end }; + this.focus(); + if(this.$.setSelectionRange) { + this.$.setSelectionRange(sel.start, sel.end); + } else { + var value = this.getValue(); + var len = value.substring(sel.start, sel.end).replace(/\r/g, '').length; + var pos = value.substring(0, sel.start).replace(/\r/g, '').length; + var range = this.$.createTextRange(); + range.collapse(true); + range.moveEnd('character', pos + len); + range.moveStart('character', pos); + range.select(); + } + return this; + }, + + getSelection: function() { + if (this.$.selectionStart !== undefined) { + return { start: this.$.selectionStart, end: this.$.selectionEnd }; + } else { + this.focus(); + var pos = { start: 0, end: 0 }; + var range = this.getDocument().$.selection.createRange(); + var dup = range.duplicate(); + if (this.$.type == 'text') { + pos.start = 0 - dup.moveStart('character', -100000); + pos.end = pos.start + range.text.length; + } else { + var value = this.getValue(); + dup.moveToElementText(this.$); + dup.setEndPoint('StartToEnd', range); + pos.end = value.length - dup.text.length; + dup.setEndPoint('StartToStart', range); + pos.start = value.length - dup.text.length; + } + return pos; + } + }, + + getSelectedText: function() { + var range = this.getSelection(); + return this.getValue().substring(range.start, range.end); + }, + + replaceSelectedText: function(value, select) { + var range = this.getSelection(), current = this.getValue(); + var top = this.$.scrollTop, height = this.$.scrollHeight; + this.setValue(current.substring(0, range.start) + value + current.substring(range.end, current.length)); + if(top != null) + this.$.scrollTop = top + this.$.scrollHeight - height; + return select || select == undefined + ? this.setSelection(range.start, range.start + value.length) + : this.setCaret(range.start + value.length); + }, + + getCaret: function() { + return this.getSelection().start; + }, + + setCaret: function(pos) { + return this.setSelection(pos, pos); + } +}); + +$document = Browser.document = DomNode.wrap(document); +$window = Browser.window = DomNode.wrap(window).addEvent('unload', DomNode.dispose); + +Chain = { + chain: function(fn) { + (this._chain = this._chain || []).push(fn); + return this; + }, + + callChain: function() { + if (this._chain && this._chain.length) + this._chain.shift().apply(this, arguments); + return this; + }, + + clearChain: function() { + this._chain = []; + return this; + } +}; + +Callback = { + addEvent: function(type, fn) { + var ref = this.events = this.events || {}; + ref = ref[type] = ref[type] || []; + if (!ref.find(function(val) { return val == fn })) ref.push(fn); + return this; + }, + + addEvents: function(events) { + return Base.each((events || []), function(fn, type) { + this.addEvent(type, fn); + }, this); + }, + + fireEvent: function(type, args, delay) { + return (this.events && this.events[type] || []).each(function(fn) { + fn.delay(delay, this, args); + }, this); + }, + + removeEvent: function(type, fn) { + if (this.events && this.events[type]) + this.events[type].remove(function(val) { return fn == val; }); + return this; + }, + + setOptions: function(opts) { + return (this.options = Hash.create(this.options, opts)).each(function(val, i) { + if (typeof val == 'function' && (i = i.match(/^on([A-Z]\w*)/))) + this.addEvent(i[1].toLowerCase(), val); + }, this); + }, + + statics: { + inject: function() { + var proto = this.prototype, options = proto.options; + this.base.apply(this, arguments); + if (proto.options != options) + proto.options = Hash.merge({}, options, proto.options); + return this; + } + } +}; + +Request = Base.extend(Chain, Callback, new function() { + var unique = 0; + + function createRequest(that) { + if (!that.transport) + that.transport = window.XMLHttpRequest && new XMLHttpRequest() + || Browser.TRIDENT && new ActiveXObject('Microsoft.XMLHTTP'); + } + + function createFrame(that) { + var id = 'request_' + unique++, load = that.onFrameLoad.bind(that); + var div = DomElement.get('body').injectBottom('div', { + styles: { + position: 'absolute', width: 0, height: 0, top: 0, marginLeft: '-10000px' + } + }, [ + 'iframe', { + name: id, id: id, events: { load: load, readystatechange: load } + } + ] + ); + that.frame = { + id: id, div: div, + iframe: window.frames[id] || document.getElementById(id), + element: DomElement.get(id) + }; + div.offsetWidth; + } + + return { + options: { + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' + }, + method: 'post', + async: true, + urlEncoded: true, + encoding: 'utf-8', + emulation: true, + secure: false + }, + + initialize: function() { + var params = Array.associate(arguments, { url: 'string', options: 'object', handler: 'function' }); + this.setOptions(params.options); + if (params.handler) + this.addEvent('complete', params.handler); + if (this.options.update) + this.options.type = 'html'; + this.headers = new Hash(this.options.headers); + if (this.options.type == 'json') { + this.setHeader('Accept', 'application/json'); + this.setHeader('X-Request', 'JSON'); + } + if (this.options.urlEncoded && /^(post|put)$/.test(this.options.method)) { + this.setHeader('Content-Type', 'application/x-www-form-urlencoded' + + (this.options.encoding ? '; charset=' + this.options.encoding : '')); + } + this.headers.append(this.options.headers); + }, + + onStateChange: function() { + if (this.transport.readyState == 4 && this.running) { + this.running = false; + this.status = 0; + try { + this.status = this.transport.status; + delete this.transport.onreadystatechange; + } catch (e) {} + if (!this.status || this.status >= 200 && this.status < 300) { + this.success(this.transport.responseText, this.transport.responseXML); + } else { + this.fireEvent('complete').fireEvent('failure'); + } + } + }, + + onFrameLoad: function() { + var frame = this.frame && this.frame.iframe, loc = frame && frame.location, + doc = frame && (frame.contentDocument || frame.contentWindow || frame).document; + if (this.running && frame && loc && (!loc.href || loc.href.indexOf(this.url) != -1) + && /^(loaded|complete|undefined)$/.test(doc.readyState)) { + this.running = false; + var html = this.options.type == 'html', area = !html + && doc.getElementsByTagName('textarea')[0]; + var text = doc && (area && area.value || doc.body + && (html && doc.body.innerHTML || doc.body.textContent + || doc.body.innerText)) || ''; + this.frame.element.setProperty('src', ''); + this.success(text); + if (!this.options.link) { + var div = this.frame.div; + div.insertBottom(DomElement.get('body')); + div.remove.delay(5000, div); + this.frame = null; + } + } + }, + + success: function(text, xml) { + var args; + switch (this.options.type) { + case 'html': + var match = text.match(/]*>([\u0000-\uffff]*?)<\/body>/i); + var stripped = this.stripScripts(match ? match[1] : text); + if (this.options.update) + DomElement.get(this.options.update).setHtml(stripped.html); + if (this.options.evalScripts) + this.executeScript(stripped.script); + args = [ stripped.html, text ]; + break; + case 'json': + args = [ Json.decode(text, this.options.secure), text ]; + break; + default: + args = [ this.processScripts(text), xml ] + } + this.fireEvent('complete', args) + .fireEvent('success', args) + .callChain(); + }, + + stripScripts: function(html) { + var script = ''; + html = html.replace(/]*>([\u0000-\uffff]*?)<\/script>/gi, function() { + script += arguments[1] + '\n'; + return ''; + }); + return { html: html, script: script }; + }, + + processScripts: function(text) { + if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) { + this.executeScript(text); + return text; + } else { + var stripped = this.stripScripts(text); + if (this.options.evalScripts) + this.executeScript(stripped.script); + return stripped.html; + } + }, + + executeScript: function(script) { + if (window.execScript) { + window.execScript(script); + } else { + DomElement.get('head').injectBottom('script', { + type: 'text/javascript', text: script + }).remove(); + } + }, + + setHeader: function(name, value) { + this.headers[name] = value; + return this; + }, + + getHeader: function(name) { + try { + if (this.transport) + return this.transport.getResponseHeader(name); + } catch (e) {} + return null; + }, + + send: function() { + var params = Array.associate(arguments, { url: 'string', options: 'object', handler: 'function' }); + var opts = params.options ? Hash.merge(params.options, this.options) : this.options; + if (params.handler) + this.addEvent('complete', function() { + params.handler.apply(this, arguments); + this.removeEvent('complete', arguments.callee); + }); + if (this.running) { + switch (opts.link) { + case 'cancel': + this.cancel(); + break; + case 'chain': + this.chain(this.send.wrap(this, arguments)); + default: + return this; + } + } + var data = opts.data || ''; + var url = params.url || opts.url; + switch (Base.type(data)) { + case 'element': + data = DomNode.wrap(data); + if (data.getTag() != 'form' || !data.hasElement('input[type=file]')) + data = data.toQueryString(); + break; + case 'object': + data = Base.toQueryString(data); + break; + default: + data = data.toString(); + } + var string = typeof data == 'string', method = opts.method; + if (opts.emulation && /^(put|delete)$/.test(method)) { + if (string) data += '&_method=' + method; + else data.setValue('_method', method); + method = 'post'; + } + if (string && !this.options.iframe) { + createRequest(this); + if (!this.transport) { + if (!this.frame) + createFrame(this); + method = 'get'; + } + } else if (!this.frame) { + createFrame(this); + } + if (string && data && method == 'get') { + url += (url.contains('?') ? '&' : '?') + data; + data = null; + } + this.running = true; + this.url = url; + if (this.frame) { + var form = !string && data; + if (form) { + form.set({ + target: this.frame.id, action: url, method: method, + enctype: method == 'get' + ? 'application/x-www-form-urlencoded' + : 'multipart/form-data', + 'accept-charset': opts.encoding || '' + }).submit(); + } else { + this.frame.element.setProperty('src', url); + } + } else if (this.transport) { + try { + this.transport.open(method.toUpperCase(), url, opts.async); + this.transport.onreadystatechange = this.onStateChange.bind(this); + new Hash(this.headers, opts.headers).each(function(header, name) { + try{ + this.transport.setRequestHeader(name, header); + } catch (e) { + this.fireEvent('exception', [e, name, header]); + } + }, this); + this.fireEvent('request'); + this.transport.send(data); + if (!opts.async) + this.onStateChange(); + } catch (e) { + this.fireEvent('failure', [e]); + } + } + return this; + }, + + cancel: function() { + if (this.running) { + this.running = false; + if (this.transport) { + this.transport.abort(); + this.transport.onreadystatechange = null; + this.transport = null; + } else if (this.frame) { + this.frame.div.remove(); + this.frame = null; + } + this.fireEvent('cancel'); + } + return this; + } + }; +}); + +Form.inject({ + send: function(url) { + if (!this.sender) + this.sender = new Request({ link: 'cancel' }); + this.sender.send({ + url: url || this.getProperty('action'), + data: this, method: this.getProperty('method') || 'post' + }); + } +}); + +HtmlElement.inject({ + load: function() { + if (!this.loader) + this.loader = new Request({ link: 'cancel', update: this, method: 'get' }); + this.loader.send(Array.associate(arguments, { data: 'object', url: 'string' })); + return this; + } +}); + +Asset = new function() { + function getProperties(props) { + return props ? Hash.create(props).each(function(val, key) { + if (/^on/.test(key)) delete this[key]; + }) : {}; + } + + function createMultiple(type, sources, options) { + var props = getProperties(options), count = 0; + options = options || {}; + return sources.each(function(src) { + props.onLoad = function() { + if (options.onProgress) + options.onProgress(src); + if (++count == sources.length && options.onComplete) + options.onComplete(); + } + this.push(Asset[type](src, props)); + }, new HtmlElements()); + } + + return { + script: function(src, props) { + var script = DomElement.get('head').injectBottom('script', Hash.merge({ + events: { + load: props.onLoad && function() { + if (!this.loaded) { + this.loaded = true; + props.onLoad.call(this); + } + }, + readystatechange: function() { + if (/loaded|complete/.test(this.$.readyState)) + this.fireEvent('load'); + } + }, + src: src + }, getProperties(props))); + if (Browser.WEBKIT && Browser.VERSION < 420) + new Request({ url: src, method: 'get' }).addEvent('success', function() { + script.fireEvent('load', [], 1); + }).send(); + return script; + }, + + stylesheet: function(src, props) { + return new HtmlElement('link', new Hash({ + rel: 'stylesheet', media: 'screen', type: 'text/css', href: src + }, props)).insertInside(DomElement.get('head')); + }, + + image: function(src, props) { + props = props || {}; + var image = new Image(); + image.src = src; + var element = new HtmlElement('img', { src: src }); + ['load', 'abort', 'error'].each(function(type) { + var name = 'on' + type.capitalize(); + if (props[name]) element.addEvent(type, function() { + this.removeEvent(type, arguments.callee); + props[name].call(this); + }); + }); + if (image.width && image.height) + element.fireEvent('load', [], 1); + return element.setProperties(getProperties(props)); + }, + + scripts: function(sources, options) { + return createMultiple('script', sources, options); + }, + + stylesheets: function(sources, options) { + return createMultiple('stylesheet', sources, options); + }, + + images: function(sources, options) { + return createMultiple('image', sources, options); + } + } +}; + +Cookie = { + set: function(name, value, expires, path) { + document.cookie = name + '=' + encodeURIComponent(value) + (expires ? ';expires=' + + expires.toGMTString() : '') + ';path=' + (path || '/'); + }, + get: function(name) { + var res = document.cookie.match('(?:^|;)\\s*' + name + '=([^;]*)'); + if (res) return decodeURIComponent(res[1]); + }, + + remove: function(name) { + this.set(name, '', -1); + } +}; + +Fx = Base.extend(Chain, Callback, { + options: { + fps: 50, + unit: false, + duration: 500, + wait: true, + transition: function(p) { + return -(Math.cos(Math.PI * p) - 1) / 2; + } + }, + + initialize: function(element, options) { + this.element = DomElement.get(element); + this.setOptions(options); + }, + + step: function() { + var time = Date.now(); + if (time < this.time + this.options.duration) { + this.delta = this.options.transition((time - this.time) / this.options.duration); + this.update(this.get()); + } else { + this.stop(true); + this.update(this.to); + this.fireEvent('complete', [this.element]).callChain(); + } + }, + + set: function(to) { + this.update(to); + this.fireEvent('set', [this.element]); + return this; + }, + + get: function() { + return this.compute(this.from, this.to); + }, + + compute: function(from, to) { + return (to - from) * this.delta + from; + }, + + start: function(from, to) { + if (!this.options.wait) this.stop(); + else if (this.timer) return this; + this.from = from; + this.to = to; + this.time = Date.now(); + if (!this.slave) { + this.timer = this.step.periodic(Math.round(1000 / this.options.fps), this); + this.fireEvent('start', [this.element]); + } + this.step(); + return this; + }, + + stop: function(end) { + if (this.timer) { + this.timer = this.timer.clear(); + if (!end) this.fireEvent('cancel', [this.element]).clearChain(); + } + return this; + } +}); + +Fx.Scroll = Fx.extend({ + options: { + offset: { x: 0, y: 0 }, + wheelStops: true + }, + + initialize: function(element, options) { + this.base(element, options); + if (this.options.wheelStops) { + var stop = this.stop.bind(this), stopper = this.element; + this.addEvent('start', function() { + stopper.addEvent('mousewheel', stop); + }, true); + this.addEvent('complete', function() { + stopper.removeEvent('mousewheel', stop); + }, true); + } + }, + + update: function(x, y) { + var now = Array.flatten(arguments); + this.element.setScrollOffset(now[0], now[1]); + }, + + get: function() { + var now = []; + for (var i = 0; i < 2; i++) + now.push(this.compute(this.from[i], this.to[i])); + return now; + }, + + start: function(x, y) { + var offsetSize = this.element.getSize(), + scrollSize = this.element.getScrollSize(), + scroll = this.element.getScrollOffset(), + values = { x: x, y: y }; + var lookup = { x: 'width', y: 'height' }; + for (var i in values) { + var s = lookup[i]; + var max = scrollSize[s] - offsetSize[s]; + if (Base.check(values[i])) + values[i] = Base.type(values[i]) == 'number' + ? values[i].limit(0, max) : max; + else values[i] = scroll[i]; + values[i] += this.options.offset[i]; + } + return this.base([scroll.x, scroll.y], [values.x, values.y]); + }, + + toTop: function() { + return this.start(false, 0); + }, + + toLeft: function() { + return this.start(0, false); + }, + + toRight: function() { + return this.start('right', false); + }, + + toBottom: function() { + return this.start(false, 'bottom'); + }, + + toElement: function(el) { + var offset = DomElement.get(el).getOffset(); + return this.start(offset.x, offset.y); + } +}); + +Fx.SmoothScroll = Fx.Scroll.extend({ + initialize: function(options, context) { + context = DomElement.get(context || document); + var doc = context.getDocument(), win = context.getWindow(); + this.base(doc, options); + var links = this.options.links ? $$(this.options.links) : $$('a', context); + var loc = win.location.href.match(/^[^#]*/)[0] + '#'; + links.each(function(link) { + if (link.$.href.indexOf(loc) != 0) return; + var hash = link.$.href.substring(loc.length); + var anchor = hash && DomElement.get('#' + hash, context); + if (anchor) { + link.addEvent('click', function(event) { + this.toElement(anchor); + var props = anchor.getProperties('name', 'id'); + anchor.removeProperties('name', 'id'); + win.location.hash = hash; + anchor.setProperties(props); + event.stop(); + }.bind(this)); + } + }, this); + } +}); + +Fx.CSS = new function() { + + var parsers = new Hash({ + color: { + match: function(value) { + if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true); + return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false; + }, + + compute: function(from, to, fx) { + return from.map(function(value, i) { + return Math.round(fx.compute(value, to[i])); + }); + }, + + get: function(value) { + return value.map(Number); + } + }, + + number: { + match: function(value) { + return parseFloat(value); + }, + + compute: function(from, to, fx) { + return fx.compute(from, to); + }, + + get: function(value, unit) { + return (unit) ? value + unit : value; + } + } + }); + + return { + start: function(element, property, values) { + values = Array.convert(values); + if (!Base.check(values[1])) + values = [ element.getStyle(property), values[0] ]; + var parsed = values.map(Fx.CSS.set); + return { from: parsed[0], to: parsed[1] }; + }, + + set: function(value) { + return Array.convert(value).map(function(val) { + val = val + ''; + var res = parsers.find(function(parser, key) { + var value = parser.match(val); + if (Base.check(value)) return { value: value, parser: parser }; + }) || { + value: val, + parser: { + compute: function(from, to) { + return to; + } + } + }; + return res; + }); + }, + + compute: function(from, to, fx) { + return from.map(function(obj, i) { + return { + value: obj.parser.compute(obj.value, to[i].value, fx), + parser: obj.parser + }; + }); + }, + + get: function(now, unit) { + return now.reduce(function(prev, cur) { + var get = cur.parser.get; + return prev.concat(get ? get(cur.value, unit) : cur.value); + }, []); + } + } +}; + +Fx.Style = Fx.extend({ + initialize: function(element, property, options) { + this.base(element, options); + this.property = property; + }, + + hide: function() { + return this.set(0); + }, + + get: function() { + return Fx.CSS.compute(this.from, this.to, this); + }, + + set: function(to) { + return this.base(Fx.CSS.set(to)); + }, + + start: function(from, to) { + if (this.timer && this.options.wait) return this; + var parsed = Fx.CSS.start(this.element, this.property, [from, to]); + return this.base(parsed.from, parsed.to); + }, + + update: function(val) { + this.element.setStyle(this.property, Fx.CSS.get(val, this.options.unit)); + } +}); + +HtmlElement.inject({ + effect: function(prop, opts) { + return new Fx.Style(this, prop, opts); + } +}); + +Fx.Styles = Fx.extend({ + get: function() { + var that = this; + return Base.each(this.from, function(from, key) { + this[key] = Fx.CSS.compute(from, that.to[key], that); + }, {}); + }, + + set: function(to) { + return this.base(Base.each(to, function(val, key) { + this[key] = Fx.CSS.set(val); + }, {})); + }, + + start: function(obj) { + if (this.timer && this.options.wait) return this; + var from = {}, to = {}; + Base.each(obj, function(val, key) { + var parsed = Fx.CSS.start(this.element, key, val); + from[key] = parsed.from; + to[key] = parsed.to; + }, this); + return this.base(from, to); + }, + + update: function(val) { + Base.each(val, function(val, key) { + this.element.setStyle(key, Fx.CSS.get(val, this.options.unit)); + }, this); + } + +}); + +HtmlElement.inject({ + effects: function(opts) { + return new Fx.Styles(this, opts); + } +}); + +Fx.Elements = Fx.extend({ + initialize: function(elements, options) { + this.base(null, options); + this.elements = DomElement.getAll(elements); + }, + + start: function(obj) { + if (this.timer && this.options.wait) return this; + this.effects = {}; + + function start(that, key, val) { + var fx = that.effects[key] = new Fx.Styles(that.elements[key], that.options); + fx.slave = true; + fx.start(val); + } + + Base.each(obj, function(val, key) { + if (key == '*') { + this.elements.each(function(el, key) { + start(this, key, val); + }, this); + } else if (isNaN(parseInt(key))) { + var els = DomElement.getAll(key); + this.elements.append(els); + els.each(function(el) { + start(this, this.elements.indexOf(el), val); + }, this); + } else { + start(this, key, val); + } + }, this); + return this.base(); + }, + + set: function(to) { + }, + + update: function(to) { + Base.each(this.effects, function(fx) { + fx.step(); + }); + } +}); + +Fx.Transitions = new Base().inject({ + inject: function(src) { + return this.base(Base.each(src, function(func, name) { + func.In = func; + + func.Out = function(pos) { + return 1 - func(1 - pos); + } + + func.InOut = function(pos) { + return pos <= 0.5 ? func(2 * pos) / 2 : (2 - func(2 * (1 - pos))) / 2; + } + })); + }, + + Linear: function(p) { + return p; + } +}); + +Fx.Transitions.inject({ + Pow: function(p, x) { + return Math.pow(p, x[0] || 6); + }, + + Expo: function(p) { + return Math.pow(2, 8 * (p - 1)); + }, + + Circ: function(p) { + return 1 - Math.sin(Math.acos(p)); + }, + + Sine: function(p) { + return 1 - Math.sin((1 - p) * Math.PI / 2); + }, + + Back: function(p, x) { + x = x[0] || 1.618; + return Math.pow(p, 2) * ((x + 1) * p - x); + }, + + Bounce: function(p) { + var value; + for (var a = 0, b = 1; 1; a += b, b /= 2) { + if (p >= (7 - 4 * a) / 11) { + value = - Math.pow((11 - 6 * a - 11 * p) / 4, 2) + b * b; + break; + } + } + return value; + }, + + Elastic: function(p, x) { + return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3); + } + +}); + +Fx.Transitions.inject(['Quad', 'Cubic', 'Quart', 'Quint'].each(function(name, i) { + this[name] = function(p) { + return Math.pow(p, i + 2); + } +}, {})); + diff --git a/build/jsdoc-toolkit/templates/jsdoc/resources/js/lighter.js b/build/jsdoc-toolkit/templates/jsdoc/resources/js/lighter.js new file mode 100644 index 00000000..53a9deaa --- /dev/null +++ b/build/jsdoc-toolkit/templates/jsdoc/resources/js/lighter.js @@ -0,0 +1,826 @@ +/** + * Script: + * Fuel.js - Language definition engine for Lighter.js + * + * License: + * MIT-style license. + * + * Author: + * José Prado + * + * Copyright: + * Copyright (©) 2009 [Jose Prado](http://pradador.com/). + * + * Changelog: + * 2009/03/21 (1.0.0) + * - Initial Release + * + */ +Fuel = Base.extend(Callback, { + + options: { + matchType: 'standard', + strict: false + }, + language: '', + defaultFlame: 'standard', + + patterns: new Hash(), + keywords: new Hash(), + rules: new Hash(), + delimiters: new Hash({ + start: null, + end: null + }), + + /************************ + * Common Regex Rules + ***********************/ + common: { + slashComments: /(?:^|[^\\])\/\/.*$/gm, // Matches a C style single-line comment. + poundComments: /#.*$/gm, // Matches a Perl style single-line comment. + multiComments: /\/\*[\s\S]*?\*\//gm, // Matches a C style multi-line comment. + aposStrings: /'[^'\\]*(?:\\.[^'\\]*)*'/gm, // Matches a string enclosed by single quotes. + quotedStrings: /"[^"\\]*(?:\\.[^"\\]*)*"/gm, // Matches a string enclosed by double quotes. + strings: /'[^'\\]*(?:\\.[^'\\]*)*'|"[^"\\]*(?:\\.[^"\\]*)*"/gm, // Matches both. + properties: /\.([\w]+)\s*/gi, // Matches a property: .property style. + methodCalls: /\.([\w]+)\s*\(/gm, // Matches a method call: .methodName() style. + functionCalls: /\b([\w]+)\s*\(/gm, // Matches a function call: functionName() style. + brackets: /\{|\}|\(|\)|\[|\]/g, // Matches any of the common brackets. + numbers: /\b((?:(\d+)?\.)?[0-9]+|0x[0-9A-F]+)\b/gi // Matches integers, decimals, hexadecimals. + }, + + /************************ + * Fuel Constructor + ***********************/ + initialize: function(lighter, flame, options, wicks) { + this.setOptions(options); + this.wicks = wicks || []; + // Set Lighter/Fuel/Flame relationship. + this.lighter = lighter; + this.flame = flame; + + // Set builder object for matchType. + this.builder = new Hash({ + 'standard': this.findMatches, + 'lazy': this.findMatchesLazy + }); + + // Add delimiter rules if not in strict mode + if (!options.strict) { + if (this.delimiters.start) this.addFuel('delimBeg', this.delimiters.start, 'de1'); + if (this.delimiters.end) this.addFuel('delimEnd', this.delimiters.end, 'de2'); + } + + // Set Keyword Rules from this.keywords object. + this.keywords.each(function(keywordSet, ruleName) { + if (keywordSet.csv != '') { + this.addFuel(ruleName, this.csvToRegExp(keywordSet.csv, "g"), keywordSet.alias); + } + }, this); + + // Set Rules from this.patterns object. + this.patterns.each(function(regex, ruleName) { + this.addFuel(ruleName, regex.pattern, regex.alias); + }, this); + + /** Process source code based on match type. */ + var codeBeg = 0, + codeEnd = lighter.code.length, + codeSeg = '', + delim = this.delimiters, + matches = [], + match = null, + endMatch = null; + + if (!options.strict) { + // Find matches through the complete source code. + matches.append(this.builder[options.matchType].bind(this, lighter.code)()); + } else if (delim.start && delim.end) { + // Find areas between language delimiters and find matches there. + while ((match = delim.start.exec(lighter.code)) != null ) { + delim.end.lastIndex = delim.start.lastIndex; + if ((endMatch = delim.end.exec(lighter.code)) != null ) { + matches.push(new Wick(match[0], 'de1', match.index)); + codeBeg = delim.start.lastIndex; + codeEnd = endMatch.index-1; + codeSeg = lighter.code.substring(codeBeg, codeEnd); + matches.append(this.builder[options.matchType].bind(this, codeSeg, codeBeg)()); + matches.push(new Wick(endMatch[0], 'de2', endMatch.index)); + } + } + } + this.wicks = matches; + }, + + /************************ + * Regex Helper methods. + ***********************/ + addFuel: function(fuelName, RegEx, className) { + this.rules[fuelName] = RegEx; + this.flame.addAlias(fuelName, className); + }, + csvToRegExp: function(csv, mod) {return new RegExp('\\b(' + csv.replace(/,\s*/g, '|') + ')\\b', mod);}, + delimitedRegex: function(beg, esc, end, mod, suffix) { + beg = beg.escapeRegExp(); + if (esc) esc = esc.escapeRegExp(); + end = (end) ? end.escapeRegExp() : beg; + pat = (esc) ? beg+"[^"+end+esc+'\\n]*(?:'+esc+'.[^'+end+esc+'\\n]*)*'+end : beg+"[^"+end+'\\n]*'+end; + return new RegExp(pat+(suffix || ''), mod || ''); + }, + strictRegex: function() { + var regex = '('; + for (var i = 0; i < arguments.length; i++) { + regex += arguments[i].escapeRegExp(); + regex += (i < arguments.length - 1) ? '|' : ''; + } + regex += ')'; + return new RegExp(regex, "gim"); + }, + + /************************ + * Match finding Methods + ***********************/ + findMatches: function(code, offset) { + var wicks = [], + startIndex = 0, + matchIndex = code.length + insertIndex = 0, + match = null, + type = null, + newWick = null, + rule = null, + rules = {}, + currentMatch = null, + futureMatch = null; + + offset = (offset) ? offset : 0; + this.rules.each(function(regex, rule) { + rules[rule] = {pattern: regex, enabled: true, lastIndex: 0}; + }, this); + + while(startIndex < code.length) { + matchIndex = code.length; + match = null; + for (rule in rules) { + rules[rule].pattern.lastIndex = startIndex; + currentMatch = rules[rule].pattern.exec(code); + if (currentMatch === null) { + delete rules[rule]; + } else { + if (currentMatch.index < matchIndex) { + match = currentMatch; + type = rule; + matchIndex = currentMatch.index; + } else if (currentMatch.index == matchIndex && match[0].length < currentMatch[0].length) { + match = currentMatch; + type = rule; + matchIndex = currentMatch.index; + } + rules[rule].nextIndex = rules[rule].pattern.lastIndex - currentMatch[0].length; + } + } + if (match != null) { + index = (match[1] && match[0].contains(match[1])) ? match.index + match[0].indexOf(match[1]) : match.index; + newWick = new Wick(match[1] || match[0], type, index+offset); + wicks.push(newWick); + + futureMatch = rules[type].pattern.exec(code); + if (!futureMatch) { + rules[type].nextIndex = code.length; + } else { + rules[type].nextIndex = rules[type].pattern.lastIndex - futureMatch[0].length; + } + + var min = code.length; + for (rule in rules) { + if (rules[rule].nextIndex < min) { + min = rules[rule].nextIndex; + } + } + startIndex = Math.max(min, newWick.end - offset); + } else { + break; + } + } + return wicks; + }, + findMatchesLazy: function(code, offset) { + var wicks = this.wicks, + match = null + index = 0; + + offset = (offset) ? offset : 0; + + this.rules.each(function(regex, rule) { + while ((match = regex.exec(code)) != null) { + index = (match[1] && match[0].contains(match[1])) ? match.index + match[0].indexOf(match[1]) : match.index; + wicks.push(new Wick(match[1] || match[0], rule, index + offset)); + } + }, this); + return this.purgeWicks(wicks); + }, + purgeWicks: function(wicks) { + wicks = wicks.sort(this.compareWicks); + for (var i = 0, j = 0; i < wicks.length; i++) { + if (wicks[i] == null) continue; + for (j = i+1; j < wicks.length && wicks[i] != null; j++) { + if (wicks[j] == null) {continue;} + else if (wicks[j].isBeyond(wicks[i])) {break;} + else if (wicks[j].overlaps(wicks[i])) {wicks[i] = null;} + else if (wicks[i].contains(wicks[j])) {wicks[j] = null;} + } + } + return wicks.compact(); + }, + compareWicks: function(wick1, wick2) {return wick1.index - wick2.index;} +}); + +Fuel.standard = Fuel.extend({initialize: function(lighter, flame, options, wicks) {this.base(lighter, flame, options, wicks);}}); + +var Wick = Base.extend({ + + initialize: function(match, type, index) { + this.text = match; + this.type = type; + this.index = index; + this.length = this.text.length; + this.end = this.index + this.length; + }, + contains: function(wick) {return (wick.index >= this.index && wick.index < this.end);}, + isBeyond: function(wick) {return (this.index >= wick.end);}, + overlaps: function(wick) {return (this.index == wick.index && this.length > wick.length);}, + toString: function() {return this.index+' - '+this.text+' - '+this.end;} +}); + + +/** + * Script: + * Flame.js - Theme Engine for Lighter.js + * + * License: + * MIT-style license. + * + * Author: + * José Prado + * + * Copyright: + * Copyright (©) 2009 [Jose Prado](http://pradador.com/). + * + * Changelog: + * 2009/03/21 (1.0.0) + * - Initial Release + * + */ +Flame = Base.extend(Callback, { + + shortName: 'lt', + aliases: new Hash(), + containers: new Hash(), + common: new Hash(), + layout: new Hash(), + styles: new Hash(), + + + defaultStyles: new Hash({ + de1: new Hash({}), // Beginning delimiter + de2: new Hash({}), // End delimiter + kw1: new Hash({'color': '#1b609a'}), // Keywords 1 + kw2: new Hash({'color': '#9a6f1b'}), // Keywords 2 + kw3: new Hash({'color': '#784e0c'}), // Keywords 3 + co1: new Hash({'color': '#888888'}), // Comments 1 + co2: new Hash({'color': '#888888'}), // Comments 2 + st0: new Hash({'color': '#489a1b'}), // Strings 1 + st1: new Hash({'color': '#70483d'}), // Strings 2 + st2: new Hash({'color': '#70483d'}), // Strings 3 + nu0: new Hash({'color': '#70483d'}), // Numbers + me0: new Hash({'color': '#666666'}), // Methods and Functions + br0: new Hash({'color': '#444444'}), // Brackets + sy0: new Hash({'color': '#444444'}), // Symbols + es0: new Hash({'color': '#444444'}), // Escape characters + re0: new Hash({'color': '#784e0c'}) // Regular Expressions + }), + defaultCommon: new Hash({ + 'font-family': 'Monaco, Courier, Monospace', + 'font-size': '10px', + 'overflow': 'auto', + 'white-space': 'pre-wrap', + 'word-wrap': 'break-word' + }), + layout: new Hash({ + 'numColor': new Hash({'background-color': '#f2f2f2'}), + 'lineColor': new Hash({'background-color': '#fff'}), + 'numStyles': new Hash({ + 'color': '#939393', + 'font-size': '10px', + 'list-style': 'decimal-leading-zero' + }), + 'lineStyles': new Hash({ + 'border-top': '1px solid #fff', + 'border-bottom': '1px solid #fff', + 'border-left': '1px solid #939393', + 'padding': '0 3px 0 10px' + }), + 'alt': new Hash({ + 'border-top': '1px solid #eee', + 'border-bottom': '1px solid #eee', + 'background-color': '#F4F8FC' + }), + 'top': new Hash({'padding-top': '5px'}), + 'right': new Hash({'padding-right': '5px'}), + 'bottom': new Hash({'padding-bottom': '5px'}), + 'left': new Hash({'padding-left': '15px'}), + 'codeStyles': new Hash({ + 'color': 'black', + 'font-size': '10px' + }) + }), + fixes: new Hash({ + 'div': new Hash({ + 'div': new Hash({ + 'clear': 'left', + 'overflow': 'auto' + }), + 'num': new Hash({ + 'display': 'block', + 'float': 'left', + 'text-align': 'center', + 'width': '30px' + }), + 'line': new Hash({ + 'display': 'block', + 'margin-left': '30px' + }) + }), + 'table': new Hash({ + 'num': new Hash({ + 'text-align': 'center', + 'width': '30px' + }) + }), + 'ol': new Hash({ + 'ol': new Hash({ + 'margin-left': '0', + 'padding-left': '0' + }), + 'li': new Hash({ + 'margin-left': '40px' + }) + }) + }), + + initialize: function(lighter, fuel) { + + // Setup Lighter/Fuel/Flame trio. + this.lighter = lighter; + this.fuel = fuel; + + this.common.merge(this.defaultCommon); + this.styles.merge(this.defaultStyles); + + // Map general styles to their aliases. + this.defaultStyles.each(function(style, key) { + this.addAlias(key); + }, this); + + // Insert stylesheet if in jsStyles mode + if (this.lighter.options.jsStyles && !$('style#lighter_' + this.lighter.options.mode)) this.injectTag(); + }, + addAlias: function(key, alias) {this.aliases[key] = alias || key;}, + injectTag: function() { + var type = this.lighter.options.mode, + pfx = type+'.'+this.shortName+this.lighter.name, + pfx2 = pfx+' .'+this.shortName, + + numCSS = this.layout['numStyles'].merge(this.layout.numColor), + lineCSS = this.layout['lineStyles'].merge(this.layout.lineColor), + padCSS = this.layout.left.merge(this.layout.right); + this.styleTag = new HtmlElement("style").setProperty('type','text/css').setProperty('id', 'lighter_' + this.lighter.options.mode).insertInside(DomElement.get('head')); + + // General white-space/font styles. + this.addCSS(pfx, this.common); + this.addCSS(pfx, new Hash({'white-space': '-moz-pre-wrap'})); + this.addCSS(pfx, new Hash({'white-space': '-pre-wrap'})); + this.addCSS(pfx, new Hash({'white-space': '-o-pre-wrap'})); + + // Case specific styles for a common general style. + switch (type) { + case "pre": + padCSS = padCSS.merge(this.layout.top).merge(this.layout.bottom); + this.addCSS(pfx, this.layout.lineColor.merge(padCSS)); + this.addCSS(pfx+' span', this.layout['codeStyles']); + break; + case "ol": + this.addCSS(pfx, numCSS.merge(this.fixes['ol']['ol'])); + this.addCSS(pfx+' li', lineCSS.merge(padCSS).merge(this.fixes['ol']['li'])); + this.addCSS(pfx2+'first', this.layout['top']); + this.addCSS(pfx2+'last', this.layout['bottom']); + this.addCSS(pfx+' .alt', this.layout['alt']); + this.addCSS(pfx+' span', this.layout['codeStyles']); + break; + case "div": + numCSS = numCSS.merge(this.fixes.div.num), + lineCSS = lineCSS.merge(this.fixes.div.line); + this.addCSS(pfx2+'num', numCSS); + this.addCSS(pfx2+'line', lineCSS.merge(padCSS)); + this.addCSS(pfx+' div', this.fixes['div']['div'].merge(this.layout.numColor)); + this.addCSS(pfx2+'first', this.layout['top']); + this.addCSS(pfx2+'last', this.layout['bottom']); + this.addCSS(pfx+' .alt', this.layout['alt']); + this.addCSS(pfx+' span', this.layout['codeStyles']); + break; + case "table": + numCSS = numCSS.merge(this.fixes['table']['num']); + this.addCSS(pfx2+'num', numCSS); + this.addCSS(pfx2+'line', lineCSS.merge(padCSS)); + this.addCSS(pfx2+'first', this.layout['top']); + this.addCSS(pfx2+'last', this.layout['bottom']); + this.addCSS(pfx+' .alt', this.layout['alt']); + this.addCSS(pfx+' span', this.layout['codeStyles']); + break; + default: + } + + this.styles.each(function(stylesHash, styleName) { + this.addCSS(pfx+' .'+styleName, stylesHash); + }, this); + }, + /** Code from horseweapon @ http://forum.mootools.net/viewtopic.php?id=6635 */ + addCSS: function(styleName, stylesHash) { + //Create the CSS rule + var newStyle = "\n" + styleName + " {\n"; + if (stylesHash) { + stylesHash.each(function(value, attribute) { + newStyle += "\t" + attribute + ": " + value + ";\n"; + }); + } + newStyle += "}\n"; + // Insert into Flame's styleTag. + if (browserTrident()) { + this.styleTag.$.styleSheet.cssText += newStyle; + } else { + this.styleTag.appendText(newStyle); + } + } + +}); + +Flame.standard = Flame.extend({initialize: function(lighter, fuel) {this.base(lighter, fuel);}}); + + +/** + * Script: + * Flame.js - Syntax Highlighter written in MooTools. + * + * License: + * MIT-style license. + * + * Author: + * José Prado + * + * Copyright: + * Copyright (©) 2009 [Jose Prado](http://pradador.com/). + * + * Changelog: + * 2009/03/21 (1.0.0) + * - Initial Release + * + */ + +var $time = Date.now || function(){ + return +new Date; +}; + +function browserTrident(){ + return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? 5 : 4); +} + +Lighter = Base.extend(Callback, { + name: 'Lighter', + options: { + altLines: '', // Pseudo-selector enabled. + container: null, + editable: false, + flame: 'standard', + fuel: 'standard', + id: null, + indent: -1, + jsStyles: true, + matchType: "standard", + mode: "pre", + path: "./", + strict: false + }, + + /*************************** + * Lighter Initialization + **************************/ + initialize: function(codeblock, options) { + this.setOptions(options); + options = this.options; + this.id = options.id || this.name + '_' + $time(); + this.codeblock = $(codeblock) + this.code = codeblock.get('html').chop().replace(/</gim, '<').replace(/>/gim, '>').replace(/&/gim, '&'); + if(this.options.container) + this.container = $(this.options.container); + // Indent code if user option is set. + if (options.indent > -1) this.code = this.code.tabToSpaces(options.indent); + + // Set builder options. + this.builder = new Hash({ + 'pre': this.createLighter.bind(this), + 'ol': this.createLighterWithLines.bind(this, ['ol'], ['li']), + 'div': this.createLighterWithLines.bind(this, ['div'], ['div', 'span'], true, 'span'), + 'table': this.createLighterWithLines.bind(this, ['table', 'tbody'], ['tr', 'td'], true, 'td') + }); + + // Extract fuel/flame names. Precedence: className > options > standard. + var ff = this.codeblock.get('class').split(':'); + if (!ff[0]) ff[0] = this.options.fuel; + if (!ff[1]) ff[1] = this.options.flame; + + // Load flame to start chain of loads. + this.loadFlameSrc(ff); + }, + loadFlameSrc: function(ff) { + if (!$check(Flame[ff[1]])) { + var flameScript = Asset.script(this.options.path+'Flames/Flame.'+ff[1]+'.js', { + onLoad: function() { + this.loadFlame(ff); + }.bind(this), + onError: function() { + ff[1] = 'standard'; + this.loadFlame(ff) + }.bind(this) + }); + } else { + this.loadFlame(ff); + } + }, + loadFlame: function(ff) { + this.flame = new Flame[ff[1]](this); + this.loadFuelSrc(ff); + }, + loadFuelSrc: function(ff) { + if (!$check(Fuel[ff[0]])) { + var fuelScript = Asset.script(this.options.path+'Fuels/Fuel.'+ff[0]+'.js', { + onLoad: function() { + this.loadFuel(ff); + }.bind(this), + onError: function() { + ff[0] = 'standard'; + this.loadFuel(ff); + }.bind(this) + }); + } else { + this.loadFuel(ff); + } + }, + loadFuel: function(ff) { + this.fuel = new Fuel[ff[0]](this, this.flame, { + matchType: this.options.matchType, + strict: this.options.strict + }); + this.light(); + }, + light: function() { + // Build highlighted code object. + this.element = this.toElement(); + // Insert lighter in the right spot. + if (this.container) { + this.container.removeChildren(); + this.element.insertInside(this.container); + } else { + this.codeblock.setStyle('display', 'none'); + this.codeblock.injectAfter(this.element); + } + }, + + /*************************** + * Lighter creation methods + **************************/ + createLighter: function() { + var lighter = new HtmlElement('pre', {'class': this.flame.shortName + this.name}), + pointer = 0; + + // If no matches were found, insert code plain text. + if (this.fuel.wicks[0] == undefined) { + lighter.appendText(this.code); + } else { + this.fuel.wicks.each(function(match) { + lighter.appendText(this.code.substring(pointer, match.index)); + this.insertAndKeepEl(lighter, match.text, match.type); + pointer = match.index + match.text.length; + }, this); + // Add last unmatched code segment if it exists. + if (pointer < this.code.length) { + lighter.appendText(this.code.substring(pointer, this.code.length)); + } + } + + //lighter.set('text', lighter.get('html')); + return lighter; + }, + createLighterWithLines: function(parent, child, addLines, lineType) { + var lighter = new HtmlElement(parent[0], {'class': this.flame.shortName + this.name, 'id': this.id}), + newLine = new HtmlElement(child[0]), + lineNum = 1, + pointer = 0, + text = null; + if (parent[0] == "table") lighter.set("cellpadding", 0).set("cellspacing", 0).set("border", 0); + if (parent[1]) lighter = new HtmlElement(parent[1]).insertInside(lighter); + if (child[1]) newLine = new HtmlElement(child[1]).insertInside(newLine); + newLine.addClass(this.flame.shortName + 'line'); + if (addLines) lineNum = this.insertLineNum(newLine, lineNum, lineType); + this.fuel.wicks.each(function(match) { + // Create and insert un-matched source code bits. + if (pointer != match.index) { + text = this.code.substring(pointer, match.index).split('\n'); + for (var i = 0; i < text.length; i++) { + if (i < text.length - 1) { + if (text[i] == '') text[i] = ' '; + newLine = this.insertAndMakeEl(newLine, lighter, text[i], child); + if (addLines) lineNum = this.insertLineNum(newLine, lineNum, lineType); + } else { + this.insertAndKeepEl(newLine, text[i]); + } + } + } + + // Create and insert matched symbol. + text = match.text.split('\n'); + for (i = 0; i < text.length; i++) { + if (i < text.length - 1) { + newLine = this.insertAndMakeEl(newLine, lighter, text[i], child, match.type); + + if (addLines) lineNum = this.insertLineNum(newLine, lineNum, lineType); + } else { + this.insertAndKeepEl(newLine, text[i], match.type); + } + } + + pointer = match.end; + }, this); + + // Add last unmatched code segment if it exists. + if (pointer <= this.code.length) { + text = this.code.substring(pointer, this.code.length).split('\n'); + for (var i = 0; i < text.length; i++) { + newLine = this.insertAndMakeEl(newLine, lighter, text[i], child); + if (addLines) lineNum = this.insertLineNum(newLine, lineNum, lineType); + } + } + + // Add alternate line styles based on pseudo-selector. + if (this.options.altLines !== '') { + if (this.options.altLines == 'hover') { + lighter.getElements('.'+this.flame.shortName+'line').addEvents({ + 'mouseover': function() {this.toggleClass('alt');}, + 'mouseout': function() {this.toggleClass('alt');} + }); + } else { + if (child[1]) { + lighter.getChildren(':'+this.options.altLines).getElement('.'+this.flame.shortName+'line').addClass('alt'); + } else { + lighter.getChildren(':'+this.options.altLines).addClass('alt'); + } + } + } + // Add first/last line classes based on mode. + if (child[1]) { + lighter.getFirst().getChildren().addClass(this.flame.shortName+'first'); + lighter.getLast().getChildren().addClass(this.flame.shortName+'last'); + } else { + lighter.getFirst().addClass(this.flame.shortName+'first'); + lighter.getLast().addClass(this.flame.shortName+'last'); + } + + if (parent[1]) lighter = lighter.getParent(); + return lighter; + }, + /** Helper function to insert new code segment into existing line. */ + insertAndKeepEl: function(el, text, alias) { + if (text.length > 0) { + var span = new HtmlElement('span'); + span.set('text', text); + if (alias) {span.addClass(this.flame.aliases[alias]);} + span.insertInside(el); + } + }, + /** Helper function to insert new code segment into existing line and create new line. */ + insertAndMakeEl: function(el, group, text, child, alias) { + this.insertAndKeepEl(el, text, alias); + if (child[1]) el = el.getParent(); + el.insertInside(group); + + var newLine = new HtmlElement(child[0]); + if (child[1]) newLine = new HtmlElement(child[1]).insertInside(newLine); + newLine.addClass(this.flame.shortName+'line'); + return newLine; + }, + /** Helper funciton to insert line number into line. */ + insertLineNum: function(el, lineNum, lineType) { + var newNum = new HtmlElement(lineType, { + 'text': lineNum++, + 'class': this.flame.shortName+ 'num' + }); + newNum.insertTop(el.getParent()); + return lineNum; + }, + + /****************** + * Element Methods + ******************/ + toElement: function() { + if (!this.element) { + this.element = this.builder[this.options.mode](); + if (this.options.editable) {this.element.set('contenteditable', 'true');} + } + + return this.element; + }, + replaces: function(element){ + element = $(element, true); + element.parentNode.replaceChild(this.toElement(), element); + + return this; + } + +}); +/** Element Native extensions */ +HtmlElement.inject({ + light: function(options) { + return new Lighter(this, options); + } +}); + +/** String Native extensions */ +String.inject({ + chop: function() {return this.replace(/(^\s*\n|\n\s*$)/gi, '');}, + tabToSpaces: function(spaces) { + for (var i = 0, indent = ''; i < spaces; i++) {indent += ' ';} + return this.replace(/\t/g, indent); + } +}); + + +/** + * Script: + * Fuel.js.js - JavaScript language file for Lighter.js + * + * License: + * MIT-style license. + * + * Author: + * José Prado + * + * Copyright: + * Copyright (©) 2009 [Jose Prado](http://pradador.com/). + * + * Changelog: + * 2009/03/21 (1.0.0) + * - Initial Release + * + */ +Fuel.js = Fuel.extend({ + language: 'js', + + initialize: function(lighter, flame, options) { + + // Keywords Rule Set + this.keywords = new Hash({ + commonKeywords: { + csv: "as, break, case, catch, continue, delete, do, else, eval, finally, for, if, in, is, instanceof, return, switch, this, throw, try, typeof, void, while, write, with", + alias: 'kw1' + }, + langKeywords: { + csv: "class, const, default, debugger, export, extends, false, function, import, namespace, new, null, package, private, protected, public, super, true, use, var", + alias: 'kw2' + }, + windowKeywords: { + csv: "alert, back, blur, close, confirm, focus, forward, home, navigate, onblur, onerror, onfocus, onload, onmove, onresize, onunload, open, print, prompt, scroll, status, stop", + alias: 'kw3' + } + }); + + // RegEx Rule Set + this.patterns = new Hash({ + 'slashComments': {pattern: this.common.slashComments, alias: 'co1'}, + 'multiComments': {pattern: this.common.multiComments, alias: 'co2'}, + 'strings': {pattern: this.common.strings, alias: 'st0'}, + 'methodCalls': {pattern: this.common.properties, alias: 'me0'}, + 'brackets': {pattern: this.common.brackets, alias: 'br0'}, + 'numbers': {pattern: /\b((([0-9]+)?\.)?[0-9_]+([e][-+]?[0-9]+)?|0x[A-F0-9]+)\b/gi, alias: 'nu0'}, + 'regex': {pattern: this.delimitedRegex("/", "\\", "/", "g", "[gimy]*"), alias: 're0'}, + 'symbols': {pattern: /\+|-|\*|\/|%|!|@|&|\||\^|\<|\>|=|,|\.|;|\?|:/g, alias: 'sy0'} + }); + + // Delimiters + this.delimiters = new Hash({ + start: this.strictRegex('') + }); + + this.base(lighter, flame, options); + } +}); \ No newline at end of file diff --git a/build/jsdoc-toolkit/templates/jsdoc/resources/js/reference.js b/build/jsdoc-toolkit/templates/jsdoc/resources/js/reference.js new file mode 100644 index 00000000..72c7e4df --- /dev/null +++ b/build/jsdoc-toolkit/templates/jsdoc/resources/js/reference.js @@ -0,0 +1,72 @@ +// This is identical with server sided code +var lighterSettings = { + altLines: 'hover', + indent: 4, + mode: 'ol', + fuel: 'js', + jsStyles: false +}; + +$document.addEvent('domready', function() { + var h = unescape(document.location.hash); + if (h) scrollToElement(h.substring(1)); + var code = $$('.text pre, .reference-class pre'); + if (code.length) { + code.light(lighterSettings).each(function(obj, i) { + var start =code[i].getProperty('start'); + if (start) + obj.element.setProperty('start', start); + }); + } +}); + +var lastMemberId = null; +function toggleMember(id, scrollTo) { + if (lastMemberId && lastMemberId != id) { + var prevId = lastMemberId; + lastMemberId = null; + toggleMember(prevId); + } + var link = $('#' + id + '-link'); + if (link) { + var desc = $('#' + id + '-description'); + var v = !link.hasClass('hidden'); + lastMemberId = v && id; + link.modifyClass('hidden', v); + desc.modifyClass('hidden', !v); + if (!desc.code && v) + desc.code = $$('pre', desc).light(lighterSettings); + if (scrollTo) + scrollToMember(id); + return false; + } + return true; +} + +function scrollToElement(id) { + var e = $('#' + id); + if (e) { + var offs = e.getOffset(); + $window.setScrollOffset(offs); + if (e.hasClass('member')) + toggleMember(id); + } else { + document.location.hash = id; + } +} + +function togglePackage(id, def) { + var e = $('#package-' + id); + if (e) { + var v = !e.hasClass('hidden'); + e.modifyClass('hidden', v); + var img = document.images['arrow-' + id]; + if (img) img.src = img.src.replace(/open|close/, v ? 'close' : 'open'); + } + return false; +} + +function toggleThumbnail(id, over) { + $('#' + id).modifyClass('hidden', over); + $('#' + id + '_over').modifyClass('hidden', !over); +} diff --git a/src/basic/Point.js b/src/basic/Point.js index 2450aeda..b391d842 100644 --- a/src/basic/Point.js +++ b/src/basic/Point.js @@ -98,6 +98,14 @@ var Point = this.Point = Base.extend({ return Point.create(this.x, this.y); }, + /** + * @return {string} A string representation of the point. + */ + toString: function() { + var format = Base.formatNumber; + return '{ x: ' + format(this.x) + ', y: ' + format(this.y) + ' }'; + }, + /** * Returns the addition of the supplied value to both coordinates of * the point as a new point. @@ -279,12 +287,23 @@ var Point = this.Point = Base.extend({ return Point.create(-this.x, -this.y); }, + /** + * Transforms the point by the matrix as a new point. The object itself + * is not modified! + * + * @param {Matrix} matrix + * @return {Point} the transformed point + */ transform: function(matrix) { return matrix._transformPoint(this); }, /** + * {@grouptitle Distance & Length} + * * Returns the distance between the point and another point. + * + * @param {Point} point * @return {number} */ getDistance: function(point) { @@ -297,7 +316,7 @@ var Point = this.Point = Base.extend({ /** * The length of the vector that is represented by this point's coordinates. * Each point can be interpreted as a vector that points from the origin - * ({@code x = 0},{@code y = 0}) to the point's location. + * ({@code x = 0}, {@code y = 0}) to the point's location. * Setting the length changes the location but keeps the vector's angle. * * @type number @@ -346,15 +365,8 @@ var Point = this.Point = Base.extend({ return point; }, - // DOCS: Point#getQuadrant - /** - * @return {number} - */ - getQuadrant: function() { - return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3; - }, - /** + * {@grouptitle Angle & Rotation} * Returns the smaller angle between two vectors. The angle is unsigned, no * information about rotational direction is given. * @@ -430,6 +442,14 @@ var Point = this.Point = Base.extend({ return this.getAngle(arguments[0]); }, + // DOCS: Point#getQuadrant + /** + * @return {number} + */ + getQuadrant: function() { + return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3; + }, + /** * Returns the angle between two vectors. The angle is directional and * signed, giving information about the rotational direction. @@ -438,6 +458,7 @@ var Point = this.Point = Base.extend({ * {@link #angle} property. * * @param {Point} point + * @return {number} the angle between the two vectors */ getDirectedAngle: function(point) { point = Point.read(arguments); @@ -478,7 +499,7 @@ var Point = this.Point = Base.extend({ * console.log(point == new Point(1, 1)); // false * console.log(point != new Point(1, 1)); // true * - * @param {Point} + * @param {Point} point * @return {boolean} */ equals: function(point) { @@ -487,6 +508,8 @@ var Point = this.Point = Base.extend({ }, /** + * {@grouptitle Tests} + * * Checks whether the point is inside the boundaries of the rectangle. * * @param {Rectangle} rect the rectangle to check against @@ -552,6 +575,7 @@ var Point = this.Point = Base.extend({ }, /** + * {@grouptitle Vectorial Math Functions} * Returns the dot product of the point and another point. * * @param {Point} point @@ -594,12 +618,14 @@ var Point = this.Point = Base.extend({ }, /** - * @return {string} A string representation of the point. + * This property is only present if the point is an anchor or control point + * of a {@link Segment} or a {@link Curve}. In this case, it returns + * true if it is selected, false otherwise + * + * @name Point#selected + * @property + * @return {boolean} true if the point is selected, false otherwise */ - toString: function() { - var format = Base.formatNumber; - return '{ x: ' + format(this.x) + ', y: ' + format(this.y) + ' }'; - }, statics: { /** @lends Point */ diff --git a/src/color/Gradient.js b/src/color/Gradient.js index acf36038..04ac4f75 100644 --- a/src/color/Gradient.js +++ b/src/color/Gradient.js @@ -15,15 +15,29 @@ */ var Gradient = this.Gradient = Base.extend({ + /** @lends Gradient# */ + beans: true, // TODO: Should type here be called 'radial' and have it // receive a boolean value? + /** + * Creates a gradient object + * + * @param {GradientStop[]} stops + * @param {string} [type='linear'] 'linear' or 'radial' + * @constructs Gradient + * + * @class The Gradient object. + */ initialize: function(stops, type) { this.setStops(stops || ['white', 'black']); this.type = type || 'linear'; }, + /** + * @return {Gradient} a copy of the gradient + */ clone: function() { var stops = []; for (var i = 0, l = this._stops.length; i < l; i++) @@ -31,6 +45,12 @@ var Gradient = this.Gradient = Base.extend({ return new Gradient(stops, this.type); }, + /** + * The gradient stops on the gradient ramp. + * + * @type GradientStop[] + * @bean + */ getStops: function() { return this._stops; }, @@ -48,6 +68,12 @@ var Gradient = this.Gradient = Base.extend({ } }, + /** + * Checks whether the gradient is equal to the supplied gradient. + * + * @param {Gradient} gradient + * @return {boolean} true if they are equal, false otherwise + */ equals: function(gradient) { if (gradient.type != this.type) return false; diff --git a/src/color/GradientColor.js b/src/color/GradientColor.js index 6cf6f1bf..5e568345 100644 --- a/src/color/GradientColor.js +++ b/src/color/GradientColor.js @@ -15,8 +15,21 @@ */ var GradientColor = this.GradientColor = Color.extend({ + /** @lends GradientColor# */ + beans: true, + /** + * Creates a gradient color object. + * + * @param {Gradient} gradient + * @param {Point} origin + * @param {Point} destination + * @param {Point} [hilite] + * @constructs GradientColor + * + * @class The GradientColor object. + */ initialize: function(gradient, origin, destination, hilite) { this.gradient = gradient || new Gradient(); this.setOrigin(origin); @@ -25,11 +38,20 @@ var GradientColor = this.GradientColor = Color.extend({ this.setHilite(hilite); }, + /** + * @return {GradientColor} a copy of the gradient color + */ clone: function() { return new GradientColor(this.gradient, this._origin, this._destination, this._hilite); }, + /** + * The origin point of the gradient. + * + * @type Point + * @bean + */ getOrigin: function() { return this._origin; }, @@ -43,6 +65,12 @@ var GradientColor = this.GradientColor = Color.extend({ return this; }, + /** + * The destination point of the gradient. + * + * @type Point + * @bean + */ getDestination: function() { return this._destination; }, @@ -55,6 +83,12 @@ var GradientColor = this.GradientColor = Color.extend({ return this; }, + /** + * The hilite point of the gradient. + * + * @type Point + * @bean + */ getHilite: function() { return this._hilite; }, @@ -88,13 +122,13 @@ var GradientColor = this.GradientColor = Color.extend({ } return gradient; }, - + /** - * Checks if the component color values of the color are the - * same as those of the supplied one. + * Checks if the gradient color has the same properties as that of the + * supplied one. * - * @param obj the GrayColor to compare with - * @return true if the GrayColor is the same, false otherwise. + * @param {GradientColor} color + * @return true if the GradientColor is the same, false otherwise */ equals: function(color) { return color == this || color && color._colorType === this._colorType @@ -102,7 +136,12 @@ var GradientColor = this.GradientColor = Color.extend({ && this._origin.equals(color._origin) && this._destination.equals(color._destination); }, - + + /** + * Transform the gradient color by the specified matrix. + * + * @param {Matrix} matrix the matrix to transform the gradient color by + */ transform: function(matrix) { matrix._transformPoint(this._origin, this._origin, true); matrix._transformPoint(this._destination, this._destination, true); diff --git a/src/color/GradientStop.js b/src/color/GradientStop.js index baf8dfac..5a37d4c4 100644 --- a/src/color/GradientStop.js +++ b/src/color/GradientStop.js @@ -16,8 +16,20 @@ // TODO: Support midPoint? (initial tests didn't look nice) var GradientStop = this.GradientStop = Base.extend({ + /** @lends GradientStop# */ + beans: true, + /** + * Creates a GradientStop object. + * + * @param {Color} [color=new RGBColor(0, 0, 0)] the color of the stop + * @param {number} [rampPoint=0] the position of the stop on the gradient + * ramp {@default 0} + * @constructs GradientStop + * + * @class The GradientStop object. + */ initialize: function(arg0, arg1) { if (arg1 === undefined && Array.isArray(arg0)) { // [color, rampPoint] @@ -34,10 +46,19 @@ var GradientStop = this.GradientStop = Base.extend({ } }, + /** + * @return {GradientColor} a copy of the gradient-stop + */ clone: function() { return new GradientStop(this._color.clone(), this._rampPoint); }, + /** + * The ramp-point of the gradient stop as a value between 0 and 1. + * + * @type number + * @bean + */ getRampPoint: function() { return this._rampPoint; }, @@ -47,6 +68,12 @@ var GradientStop = this.GradientStop = Base.extend({ this._rampPoint = rampPoint || 0; }, + /** + * The color of the gradient stop. + * + * @type Color + * @bean + */ getColor: function() { return this._color; }, diff --git a/src/item/Item.js b/src/item/Item.js index 024465be..f3c99c74 100644 --- a/src/item/Item.js +++ b/src/item/Item.js @@ -36,42 +36,6 @@ var Item = this.Item = Base.extend({ this.setStyle(this._project.getCurrentStyle()); }, - /** - * Clones the item within the same project and places the copy above the - * item. - * - * @return the newly cloned item - */ - clone: function() { - return this._clone(new this.constructor()); - }, - - _clone: function(copy) { - // Copy over style - copy.setStyle(this._style); - // If this item has children, clone and append each of them: - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) - copy.appendTop(this._children[i].clone()); - } - // Only copy over these fields if they are actually defined in 'this' - // TODO: Consider moving this to Base once it's useful in more than one - // place - var keys = ['locked', 'visible', 'opacity', 'blendMode', '_clipMask']; - for (var i = 0, l = keys.length; i < l; i++) { - var key = keys[i]; - if (this.hasOwnProperty(key)) - copy[key] = this[key]; - } - // Move the clone above the original, at the same position. - copy.moveAbove(this); - // Only set name once the copy is moved, to avoid setting and unsettting - // name related structures. - if (this._name) - copy.setName(this._name); - return copy; - }, - /** * Private notifier that is called whenever a change occurs in this item or * its sub-elements, such as Segments, Curves, PathStyles, etc. @@ -97,7 +61,13 @@ var Item = this.Item = Base.extend({ }, /** - * The name of the item. + * The name of the item. If the item has a name, it can be accessed by name + * through its parent's children list. + * + * @example + * var path = new Path(); + * path.name = 'example'; + * project.activeLayer.children['example'].remove(); * * @type string * @bean @@ -123,7 +93,332 @@ var Item = this.Item = Base.extend({ delete children[name]; } }, - + + /** + * The item's position within the project. This is the + * {@link Rectangle#center} of the {@link #bounds} rectangle. + * + * @example + * // Create a circle at position { x: 10, y: 10 } + * var circle = new Path.Circle(new Point(10, 10), 10); + * circle.fillColor = 'red'; + * + * // Move the circle to { x: 20, y: 20 } + * circle.position = new Point(20, 20); + * + * // Move the circle 10 points to the right and 10 points down + * circle.position += new Point(10, 10); + * console.log(circle.position); // { x: 30, y: 30 } + * + * @example + * // Create a circle at position { x: 10, y: 10 } + * var circle = new Path.Circle(new Point(10, 10), 10); + * circle.fillColor = 'red'; + * + * // Move the circle 10 points to the right + * circle.position.x += 10; + * console.log(circle.position); // { x: 20, y: 10 } + * + * @type Point + * @bean + */ + getPosition: function() { + // Cache position value + if (!this._position) { + // Center is a LinkedPoint as well, so we can use _x and _y + var center = this.getBounds().getCenter(); + this._position = LinkedPoint.create(this, 'setPosition', + center._x, center._y); + } + return this._position; + }, + + setPosition: function(point) { + point = Point.read(arguments); + if (point) + this.translate(point.subtract(this.getPosition())); + }, + + /** + * The path style of the item. + * + * @example + * var circle = new Path.Circle(new Point(10, 10), 10); + * circle.style = { + * fillColor: new RGBColor(1, 0, 0), + * strokeColor: new RGBColor(0, 1, 0), + * strokeWidth: 5 + * }; + * + * @type PathStyle + * @bean + */ + getStyle: function() { + return this._style; + }, + + setStyle: function(style) { + this._style.initialize(style); + }, + + /** + * Specifies whether an item is selected and will also return {@code true} if + * the item is partially selected (groups with some selected items/partially + * selected paths). + * + * Paper.js draws the visual outlines of selected items on top of your + * project. This can be useful for debugging, as it allows you to see the + * construction of paths, position of path curves, individual segment points + * and bounding boxes of symbol and raster items. + * + * @example + * console.log(project.selectedItems.length); // 0 + * var path = new Path.Circle(new Size(50, 50), 25); + * path.selected = true; // Select the path + * console.log(project.selectedItems.length) // 1 + * + * @type boolean true if the item is selected, false otherwise + * @bean + */ + isSelected: function() { + if (this._children) { + for (var i = 0, l = this._children.length; i < l; i++) { + if (this._children[i].isSelected()) { + return true; + } + } + } else { + return !!this._selected; + } + return false; + }, + + setSelected: function(selected) { + if (this._children) { + for (var i = 0, l = this._children.length; i < l; i++) { + this._children[i].setSelected(selected); + } + } else { + if ((selected = !!selected) != this._selected) { + this._selected = selected; + this._project._selectItem(this, selected); + } + } + }, + + // TODO: isFullySelected / setFullySelected + // TODO: Change to getter / setters for these below that notify of changes + // through _changed() + + // TODO: Item#isLocked is currently ignored in the documentation, as + // locking an item currently has no effect + /** + * Specifies whether the item is locked. + * + * @type boolean + * @default false + * @ignore + */ + locked: false, + + /** + * Specifies whether the item is visible. When set to {@code false}, the + * item won't be drawn. + * + * @example + * var path = new Path.Circle(new Point(50, 50), 20); + * path.fillColor = 'red'; + * console.log(path.visible) // true + * path.visible = false; // Hides the path + * + * @type boolean true if the item is visible, false otherwise + * @default true + */ + visible: true, + + /** + * Specifies whether the item defines a clip mask. This can only be set on + * paths, compound paths, and text frame objects, and only if the item is + * already contained within a clipping group. + * + * @type boolean + * @default false + * @bean + */ + isClipMask: function() { + return this._clipMask; + }, + + setClipMask: function(clipMask) { + this._clipMask = clipMask; + if (this._clipMask) { + this.setFillColor(null); + this.setStrokeColor(null); + } + }, + + /** + * The blend mode of the item. + * + * @example + * var circle = new Path.Circle(new Point(50, 50), 10); + * circle.fillColor = 'red'; + * + * // Change the blend mode of the path item: + * circle.blendMode = 'multiply'; + * + * @type String('normal','screen','multiply','difference','src-in','add','overlay','hard-light','dodge','burn','darken','lighten','exclusion') + * @default 'normal' + */ + blendMode: 'normal', + + /** + * The opacity of the item as a value between 0 and 1. + * + * @example + * // Create a circle at position { x: 50, y: 50 } + * var circle = new Path.Circle(new Point(50, 50), 20); + * circle.fillColor = 'red'; + * + * // Change the opacity of the circle to 50%: + * circle.opacity = 0.5; + * + * @type number + * @default 1 + */ + opacity: 1, + + // TODO: get/setIsolated (print specific feature) + // TODO: get/setKnockout (print specific feature) + // TODO: get/setAlphaIsShape + // TODO: get/setData + + /** + * {@grouptitle Project Hierarchy} + * The project that this item belongs to. + * + * @type Project + * @bean + */ + getProject: function() { + return this._project; + }, + + _setProject: function(project) { + if (this._project != project) { + this._project = project; + if (this._children) { + for (var i = 0, l = this._children.length; i < l; i++) { + this._children[i]._setProject(project); + } + } + } + }, + + // TODO: #getLayer() + + /** + * The item that this item is contained within. + * + * @example + * var path = new Path(); + * // New items are placed in the active layer: + * console.log(path.parent == project.activeLayer); // true + * + * var group = new Group(); + * group.appendTop(path); + * // Now the parent of the path has become the group: + * console.log(path.parent == group); // true + * + * @type Item + * @bean + */ + getParent: function() { + return this._parent; + }, + + /** + * The children items contained within this item. Items that define a + * {@link #name} can also be accessed by name. + * + * @example + * var path = new Path(); + * var group = new Group(); + * group.appendTop(path); + * + * // The path has been placed in the children list of the group: + * console.log(group.children[0] == path); + * + * path.name = 'example'; + * // Now the path can also be accessed by name: + * console.log(group.children['example'] == path); // true + * + * @type Item[] + * @bean + */ + getChildren: function() { + return this._children; + }, + + setChildren: function(items) { + this.removeChildren(); + for (var i = 0, l = items && items.length; i < l; i++) + this.appendTop(items[i]); + }, + + /** + * The first item contained within this item. This is a shortcut for + * accessing {@code item.children[0]}. + * + * @type Item + * @bean + */ + getFirstChild: function() { + return this._children && this._children[0] || null; + }, + + /** + * The last item contained within this item.This is a shortcut for + * accessing {@code item.children[item.children.length - 1]}. + * + * @type Item + * @bean + */ + getLastChild: function() { + return this._children && this._children[this._children.length - 1] + || null; + }, + + /** + * The next item on the same level as this item. + * + * @type Item + * @bean + */ + getNextSibling: function() { + return this._parent && this._parent._children[this._index + 1] || null; + }, + + /** + * The previous item on the same level as this item. + * + * @type Item + * @bean + */ + getPreviousSibling: function() { + return this._parent && this._parent._children[this._index - 1] || null; + }, + + /** + * The index of this item within the list of its parent's children. + * + * @type number + * @bean + */ + getIndex: function() { + return this._index; + }, + _removeFromNamed: function() { var children = this._parent._children, namedChildren = this._parent._namedChildren, @@ -157,7 +452,10 @@ var Item = this.Item = Base.extend({ }, /** - * Removes the item from the project. + * Removes the item from the project. If the item has children, they are also + * removed. + * + * @return {boolean} true if the item was removed, false otherwise */ remove: function() { if (this.isSelected()) @@ -165,6 +463,20 @@ var Item = this.Item = Base.extend({ return this._removeFromParent(); }, + /** + * Removes all of the item's children (if any). + * + * @return {boolean} true if removing was successful, false otherwise + */ + removeChildren: function() { + var removed = false; + if (this._children) { + for (var i = this._children.length - 1; i >= 0; i--) + removed = this._children[i].remove() || removed; + } + return removed; + }, + /** * When passed a project, copies the item to the project, * or duplicates it within the same project. When passed an item, @@ -185,172 +497,39 @@ var Item = this.Item = Base.extend({ }, /** - * Specifies whether an item is selected and will also return true if - * the item is partially selected (groups with - * some selected items/partially selected paths). + * Clones the item within the same project and places the copy above the + * item. * - * @example - * console.log(project.selectedItems.length); // 0 - * var path = new Path.Circle(new Size(50, 50), 25); - * path.selected = true; // Select the path - * console.log(project.selectedItems.length) // 1 - * - * @type boolean - * @bean - */ - isSelected: function() { + * @return {Item} the newly cloned item + */ + clone: function() { + return this._clone(new this.constructor()); + }, + + _clone: function(copy) { + // Copy over style + copy.setStyle(this._style); + // If this item has children, clone and append each of them: if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) { - if (this._children[i].isSelected()) { - return true; - } - } - } else { - return !!this._selected; + for (var i = 0, l = this._children.length; i < l; i++) + copy.appendTop(this._children[i].clone()); } - return false; - }, - - setSelected: function(selected) { - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) { - this._children[i].setSelected(selected); - } - } else { - if ((selected = !!selected) != this._selected) { - this._selected = selected; - this._project._selectItem(this, selected); - } + // Only copy over these fields if they are actually defined in 'this' + // TODO: Consider moving this to Base once it's useful in more than one + // place + var keys = ['locked', 'visible', 'opacity', 'blendMode', '_clipMask']; + for (var i = 0, l = keys.length; i < l; i++) { + var key = keys[i]; + if (this.hasOwnProperty(key)) + copy[key] = this[key]; } - }, - - /** - * The project that this item belongs to. - * - * @type Project - * @bean - */ - getProject: function() { - return this._project; - }, - - _setProject: function(project) { - if (this._project != project) { - this._project = project; - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) { - this._children[i]._setProject(project); - } - } - } - }, - - // TODO: isFullySelected / setFullySelected - // TODO: Change to getter / setters for these below that notify of changes - // through _changed() - /** - * Specifies whether the item is locked. - * - * @type boolean - * @default false - */ - locked: false, - - /** - * Specifies whether the item is visible. - * - * @type boolean - * @default true - */ - visible: true, - - /** - * The opacity of the item as a value between 0 and 1. - * - * @type number - * @default 1 - */ - opacity: 1, - - // DOCS: list the different blend modes that are possible. - /** - * The blend mode of the item. - * @type string - * @default 'normal' - */ - blendMode: 'normal', - - /** - * Specifies whether the item defines a clip mask. This can only be set on - * paths, compound paths, and text frame objects, and only if the item is - * already contained within a clipping group. - * - * @type boolean - * @default false - * @bean - */ - isClipMask: function() { - return this._clipMask; - }, - - setClipMask: function(clipMask) { - this._clipMask = clipMask; - if (this._clipMask) { - this.setFillColor(null); - this.setStrokeColor(null); - } - }, - - // TODO: get/setIsolated (print specific feature) - // TODO: get/setKnockout (print specific feature) - // TODO: get/setAlphaIsShape - // TODO: get/setData - - /** - * The item that this item is contained within. - * - * @type Item - * @bean - */ - getParent: function() { - return this._parent; - }, - - // TODO: #getLayer() - - /** - * The index of this item within the list of its parent's children. - * - * @type number - * @bean - */ - getIndex: function() { - return this._index; - }, - - /** - * The children items contained within this item. - * - * @type array - * @bean - */ - getChildren: function() { - return this._children; - }, - - setChildren: function(items) { - this.removeChildren(); - for (var i = 0, l = items && items.length; i < l; i++) - this.appendTop(items[i]); - }, - - /** - * Checks if the item contains any children items. - * - * @return {boolean} true if it has one or more children, false otherwise. - */ - hasChildren: function() { - return this._children && this._children.length > 0; + // Move the clone above the original, at the same position. + copy.moveAbove(this); + // Only set name once the copy is moved, to avoid setting and unsettting + // name related structures. + if (this._name) + copy.setName(this._name); + return copy; }, /** @@ -368,63 +547,48 @@ var Item = this.Item = Base.extend({ }, /** - * Removes all of the item's children, if it has any + * Rasterizes the item into a newly created Raster object. The item itself + * is not removed after rasterization. + * + * @param {number} [resolution=72] the resolution of the raster in dpi + * @return {Raster} the newly created raster item */ - removeChildren: function() { - var removed = false; - if (this._children) { - for (var i = this._children.length - 1; i >= 0; i--) - removed = this._children[i].remove() || removed; - } - return removed; + rasterize: function(resolution) { + // TODO: why would we want to pass a size to rasterize? Seems to produce + // weird results on Scriptographer. Also we can't use antialiasing, since + // Canvas doesn't support it yet. Project colorMode is also out of the + // question for now. + var bounds = this.getStrokeBounds(), + scale = (resolution || 72) / 72, + canvas = CanvasProvider.getCanvas(bounds.getSize().multiply(scale)), + ctx = canvas.getContext('2d'), + matrix = new Matrix().scale(scale).translate(-bounds.x, -bounds.y); + matrix.applyToContext(ctx); + this.draw(ctx, {}); + var raster = new Raster(canvas); + raster.setPosition(this.getPosition()); + raster.scale(1 / scale); + return raster; }, /** - * The first item contained within this item. + * {@grouptitle Tests} + * Checks if the item contains any children items. * - * @type Item - * @bean + * @return {boolean} true if it has one or more children, false otherwise. */ - getFirstChild: function() { - return this._children && this._children[0] || null; - }, - - /** - * The last item contained within this item. - * - * @type Item - * @bean - */ - getLastChild: function() { - return this._children && this._children[this._children.length - 1] - || null; - }, - - /** - * The next item on the same level as this item. - * - * @type Item - * @bean - */ - getNextSibling: function() { - return this._parent && this._parent._children[this._index + 1] || null; - }, - - /** - * The previous item on the same level as this item. - * - * @type Item - * @bean - */ - getPreviousSibling: function() { - return this._parent && this._parent._children[this._index - 1] || null; + hasChildren: function() { + return this._children && this._children.length > 0; }, + // TODO: Item#isEditable is currently ignored in the documentation, as + // locking an item currently has no effect /** * Checks whether the item is editable. * * @return {boolean} true when neither the item, nor its parents are * locked or hidden, false otherwise. + * @ignore */ isEditable: function() { var parent = this; @@ -464,6 +628,7 @@ var Item = this.Item = Base.extend({ // TODO: isBelow /** + * {@grouptitle Hierarchy Tests} * Checks whether the specified item is the parent of the item. * * @param {Item} item The item to check against @@ -614,100 +779,10 @@ var Item = this.Item = Base.extend({ */ // TODO: getControlBounds - /** - * Rasterizes the item into a newly created Raster object. The item itself - * is not removed after rasterization. - * - * @param {number} [resolution=72] the resolution of the raster in dpi - * @return {Raster} the newly created raster item - */ - rasterize: function(resolution) { - // TODO: why would we want to pass a size to rasterize? Seems to produce - // weird results on Scriptographer. Also we can't use antialiasing, since - // Canvas doesn't support it yet. Project colorMode is also out of the - // question for now. - var bounds = this.getStrokeBounds(), - scale = (resolution || 72) / 72, - canvas = CanvasProvider.getCanvas(bounds.getSize().multiply(scale)), - ctx = canvas.getContext('2d'), - matrix = new Matrix().scale(scale).translate(-bounds.x, -bounds.y); - matrix.applyToContext(ctx); - this.draw(ctx, {}); - var raster = new Raster(canvas); - raster.setPosition(this.getPosition()); - raster.scale(1 / scale); - return raster; - }, - - /** - * The item's position within the project. This is the - * {@link Rectangle#center} of the {@link Item#bounds} rectangle. - * - * @type Point - * @bean - */ - getPosition: function() { - // Cache position value - if (!this._position) { - // Center is a LinkedPoint as well, so we can use _x and _y - var center = this.getBounds().getCenter(); - this._position = LinkedPoint.create(this, 'setPosition', - center._x, center._y); - } - return this._position; - }, - - setPosition: function(point) { - point = Point.read(arguments); - if (point) - this.translate(point.subtract(this.getPosition())); - }, - - /** - * @param flags: Array of any of the following: 'objects', 'children', - * 'fill-gradients', 'fill-patterns', 'stroke-patterns', 'lines'. - * Default: ['objects', 'children'] - * - * @ignore - */ - transform: function(matrix, flags) { - // TODO: Handle flags, add TransformFlag class and convert to bit mask - // for quicker checking - // TODO: Call transform on chidren only if 'children' flag is provided - if (this._transform) - this._transform(matrix, flags); - // Transform position as well. Do not modify _position directly, - // since it's a LinkedPoint and would cause recursion! - if (this._position) - matrix._transformPoint(this._position, this._position, true); - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) { - var child = this._children[i]; - child.transform(matrix, flags); - } - } - // PORT: Return 'this' in all chainable commands - return this; - }, - -/* - _transform: function(matrix, flags) { - // The code that performs the actual transformation of content, - // if defined. Item itself does not define this. - }, -*/ - /** - * Translates (moves) the item by the given offset point. - * - * @param {Point} delta the offset to translate the item by - */ - translate: function(delta) { - var mx = new Matrix(); - return this.transform(mx.translate.apply(mx, arguments)); - }, - // DOCS: document the different arguments that this function can receive. /** + * {@grouptitle Transform Functions} + * * Scales the item by the given value from its center point, or optionally * by a supplied point. * @@ -728,9 +803,36 @@ var Item = this.Item = Base.extend({ * // Scale the path 200% from its bottom left corner * circle.scale(2, circle.bounds.bottomLeft); * + * @name Item#scale^1 + * @function * @param {number} scale the scale factor * @param {Point} [center=the center point of the item] */ + /** + * Scales the item by the given values from its center point, or optionally + * by a supplied point. + * + * @example + * // Create a circle at position { x: 10, y: 10 } + * var circle = new Path.Circle(new Point(10, 10), 10); + * console.log(circle.bounds.width); // 20 + * + * // Scale the path horizontally by 200% + * circle.scale(1, 2); + * + * console.log(circle.bounds.width); // 40 + * + * @example + * // Create a circle at position { x: 10, y: 10 } + * var circle = new Path.Circle(new Point(10, 10), 10); + * + * // Scale the path 200% horizontally from its bottom left corner + * circle.scale(1, 2, circle.bounds.bottomLeft); + * + * @param {number} sx the horizontal scale factor + * @param {number} sy the vertical scale factor + * @param {Point} [center=the center point of the item] + */ scale: function(sx, sy /* | scale */, center) { // See Matrix#scale for explanation of this: if (arguments.length < 2 || typeof sy === 'object') { @@ -741,6 +843,16 @@ var Item = this.Item = Base.extend({ center || this.getPosition())); }, + /** + * Translates (moves) the item by the given offset point. + * + * @param {Point} delta the offset to translate the item by + */ + translate: function(delta) { + var mx = new Matrix(); + return this.transform(mx.translate.apply(mx, arguments)); + }, + /** * Rotates the item by a given angle around the given point. * @@ -777,18 +889,39 @@ var Item = this.Item = Base.extend({ }, /** - * The path style of the item. - * - * @type PathStyle - * @bean + * Transform the item. + * + * @param {Matrix} matrix + * @param {array} flags Array of any of the following: 'objects', 'children', + * 'fill-gradients', 'fill-patterns', 'stroke-patterns', 'lines'. + * Default: ['objects', 'children'] */ - getStyle: function() { - return this._style; + transform: function(matrix, flags) { + // TODO: Handle flags, add TransformFlag class and convert to bit mask + // for quicker checking + // TODO: Call transform on chidren only if 'children' flag is provided + if (this._transform) + this._transform(matrix, flags); + // Transform position as well. Do not modify _position directly, + // since it's a LinkedPoint and would cause recursion! + if (this._position) + matrix._transformPoint(this._position, this._position, true); + if (this._children) { + for (var i = 0, l = this._children.length; i < l; i++) { + var child = this._children[i]; + child.transform(matrix, flags); + } + } + // PORT: Return 'this' in all chainable commands + return this; }, - setStyle: function(style) { - this._style.initialize(style); - }, + /* + _transform: function(matrix, flags) { + // The code that performs the actual transformation of content, + // if defined. Item itself does not define this. + }, + */ // TODO: toString @@ -924,6 +1057,7 @@ var Item = this.Item = Base.extend({ /** @lends Item# */ /** + * {@grouptitle Hierarchy Operations} * Inserts the specified item as a child of the item by appending it to * the list of children and moving it above all other children. You can * use this function for groups, compound paths and layers. @@ -965,6 +1099,7 @@ var Item = this.Item = Base.extend({ //DOCS: document removeOn(param) /** + * {@grouptitle Remove On Event} * Removes the item when the next {@link Tool#onMouseMove} event is fired. * * @name Item#removeOnMove diff --git a/src/item/PlacedSymbol.js b/src/item/PlacedSymbol.js index b05d10f3..78500370 100644 --- a/src/item/PlacedSymbol.js +++ b/src/item/PlacedSymbol.js @@ -15,8 +15,51 @@ */ var PlacedSymbol = this.PlacedSymbol = Item.extend({ + /** @lends PlacedSymbol# */ + beans: true, + /** + * Creates a new PlacedSymbol Item. + * + * @example + * var path = new Path.Star(new Point(0, 0), 6, 5, 13); + * path.style = { + * fillColor: 'white', + * strokeColor: 'black' + * }; + * + * // Create a symbol from the path: + * // (the original path is removed from the project) + * var symbol = new Symbol(path); + * + * // Place 100 instances of the symbol: + * for (var i = 0; i < 100; i++) { + * // Place an instance of the symbol in the project: + * var instance = new PlacedSymbol(symbol); + * + * // Move the instance to a random position within the view: + * instance.position = Point.random() * view.size; + * + * // Rotate the instance by a random amount between + * // 0 and 360 degrees: + * instance.rotate(Math.random() * 360); + * + * // Scale the instance between 0.25 and 1: + * instance.scale(0.25 + Math.random() * 0.75); + * } + * + * @param {Symbol} symbol the symbol to place + * @param {Point|Matrix} matrixOrOffset the center point of the placed + * symbol or a {@link Matrix} transformation to transform the placed symbol + * with. + * + * @class A PlacedSymbol represents a symbol which has been placed in a + * Paper.js project. + * + * @extends Item + * @constructs PlacedSymbol + */ initialize: function(symbol, matrixOrOffset) { this.base(); this.symbol = symbol instanceof Symbol ? symbol : new Symbol(symbol); @@ -27,6 +70,13 @@ var PlacedSymbol = this.PlacedSymbol = Item.extend({ : new Matrix(); }, + /** + * The symbol contained within the placed symbol. + * + * @name PlacedSymbol#symbol + * @type Symbol + */ + clone: function() { return this._clone(new PlacedSymbol(this.symbol, this.matrix.clone())); }, @@ -60,6 +110,5 @@ var PlacedSymbol = this.PlacedSymbol = Item.extend({ } } - // TODO: - // embed() + // TODO: PlacedSymbol#embed() }); diff --git a/src/path/Path.js b/src/path/Path.js index 474385cd..32b683cb 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -34,7 +34,7 @@ var Path = this.Path = PathItem.extend({ * path.moveTo(30, 30); * path.lineTo(100, 100); * - * @param {array} [segments] An optional array of segments (or points to be + * @param {Segment[]} [segments] An optional array of segments (or points to be * converted to segments) that will be added to the path. * * @class The Path item represents a path in a Paper.js project. @@ -77,7 +77,7 @@ var Path = this.Path = PathItem.extend({ /** * The segments contained within the path. * - * @type array + * @type Segment[] * @bean */ getSegments: function() { @@ -120,7 +120,7 @@ var Path = this.Path = PathItem.extend({ /** * The curves contained within the path. * - * @type array + * @type Curve[] * @bean */ getCurves: function() { @@ -282,6 +282,7 @@ var Path = this.Path = PathItem.extend({ * @param {Segment|Point} segment the segment or point to be added. * @return {Segment} the added segment. This is not necessarily the same * object, e.g. if the segment to be added already belongs to another path. + * @operator none */ add: function(segment1 /*, segment2, ... */) { return arguments.length > 1 && typeof segment1 !== 'number' diff --git a/src/project/Project.js b/src/project/Project.js index 4d73eac4..189ab137 100644 --- a/src/project/Project.js +++ b/src/project/Project.js @@ -98,7 +98,7 @@ var Project = this.Project = Base.extend({ /** * The selected items contained within the project. * - * @type array + * @type Item[] * @bean */ getSelectedItems: function() { @@ -142,7 +142,45 @@ var Project = this.Project = Base.extend({ for (var i in this._selectedItems) this._selectedItems[i].setSelected(false); }, - + + /** + * {@grouptitle Project Hierarchy} + * + * The layers contained within the project. + * + * @name Project#layers + * @type Layer[] + */ + + /** + * The layer which is currently active. New items will be created on this + * layer by default. + * + * @name Project#activeLayer + * @type Layer + */ + + /** + * The symbols contained within the project. + * + * @name Project#symbols + * @type Symbol[] + */ + + /** + * The views contained within the project. + * + * @name Project#views + * @type View[] + */ + + /** + * The view which is currently active. + * + * @name Project#activeView + * @type View + */ + draw: function(ctx) { ctx.save(); var param = { offset: new Point(0, 0) }; diff --git a/src/project/Symbol.js b/src/project/Symbol.js index 779b99dd..889d44b4 100644 --- a/src/project/Symbol.js +++ b/src/project/Symbol.js @@ -15,20 +15,59 @@ */ var Symbol = this.Symbol = Base.extend({ + /** @lends Symbol# */ + beans: true, + /** + * Creates a Symbol item. + * + * Sample code: + * @example + * var circlePath = new Path.Circle(new Point(100, 100), 50); + * circlePath.fillColor = 'red'; + * + * var circleSymbol = new Symbol(circlePath); + * circleSymbol.name = 'Circle'; + * + * // The original item is still contained in the document: + * circlePath.remove(); + * + * // The symbol can now also be accessed + * // through project.symbols: + * console.log(project.symbols['Circle']); + * + * // To place instances of the symbol in the document: + * var placedCircle = new PlacedSymbol(circleSymbol); + * placedCircle.position = new Point(150, 150); + * + * @param {Item} item the source item which is copied as the definition of + * the symbol + * + * @name Symbol + * @constructor + * + * @class Symbols allow you to place multiple instances of an item in your + * project. This can save memory, since all instances of a symbol + * simply refer to the original item and it can speed up moving + * around complex objects, since internal properties such as segment + * lists and gradient positions don't need to be updated with every + * transformation. + */ initialize: function(item) { this.project = paper.project; this.project.symbols.push(this); this.setDefinition(item); }, - clone: function() { - return new Symbol(this._definition.clone()); - }, - // TODO: remove() + /** + * The symbol definition. + * + * @type Item + * @bean + */ getDefinition: function() { return this._definition; }, @@ -40,5 +79,14 @@ var Symbol = this.Symbol = Base.extend({ item._removeFromParent(); // Move position to 0, 0. TODO: Why? item.setPosition(new Point()); + }, + + /** + * Returns a copy of the symbol. + * + * @return {Symbol} + */ + clone: function() { + return new Symbol(this._definition.clone()); } }); diff --git a/src/text/CharacterStyle.js b/src/text/CharacterStyle.js index d44e683d..450182f1 100644 --- a/src/text/CharacterStyle.js +++ b/src/text/CharacterStyle.js @@ -15,6 +15,27 @@ */ var CharacterStyle = this.CharacterStyle = PathStyle.extend({ + /** @lends CharacterStyle# */ + + /** + * CharacterStyle objects don't need to be created directly. Just pass an + * object to {@link TextItem#characterStyle}, it will be converted to a + * CharacterStyle object internally. + * + * @constructs CharacterStyle + * @param {object} style + * + * @constructs CharacterStyle + * + * @class The CharacterStyle object represents the character style of a text + * item ({@link TextItem#characterStyle}) + * + * @example + * var text = new PointText(new Point(50, 50)); + * text.fillColor = 'black'; + * text.content = 'Hello world.'; + * text.characterStyle.fontSize = 50; + */ initialize: function(style) { Base.initialize(this, style, { fontSize: 10, @@ -23,6 +44,22 @@ var CharacterStyle = this.CharacterStyle = PathStyle.extend({ this.base(style); }, + /** + * The font of the character style. + * + * @name CharacterStyle#font + * @default 'sans-serif' + * @type string + */ + + /** + * The font size of the character style in points. + * + * @name CharacterStyle#fontSize + * @default 10 + * @type number + */ + statics: { create: function(item) { var style = new CharacterStyle(CharacterStyle.dont); diff --git a/src/text/ParagraphStyle.js b/src/text/ParagraphStyle.js index 6fcff771..c598a9c4 100644 --- a/src/text/ParagraphStyle.js +++ b/src/text/ParagraphStyle.js @@ -15,12 +15,45 @@ */ var ParagraphStyle = this.ParagraphStyle = Base.extend({ + /** @lends ParagraphStyle# */ + + /** + * ParagraphStyle objects don't need to be created directly. Just pass an + * object to {@link TextItem#paragraphStyle}, it will be converted to a + * ParagraphStyle object internally. + * + * Currently, the ParagraphStyle object may seem a bit empty, with just the + * {@link #justification} property. Yet, we have lots in store for Paper.js + * when it comes to typography. Please stay tuned. + * + * @constructs ParagraphStyle + * @param {object} style + * + * @constructs ParagraphStyle + * + * @class The ParagraphStyle object represents the paragraph style of a text + * item ({@link TextItem#paragraphStyle}) + * + * @example + * var text = new PointText(new Point(0,0)); + * text.fillColor = 'black'; + * text.content = 'Hello world.'; + * text.paragraphStyle.justification = 'center'; + */ initialize: function(style) { Base.initialize(this, style, { justification: 'left' }); }, + /** + * The justification of the paragraph. + * + * @name ParagraphStyle#justification + * @default 'left' + * @type string + */ + statics: { create: function(item) { var style = new ParagraphStyle(ParagraphStyle.dont); diff --git a/src/text/PointText.js b/src/text/PointText.js index d77898b5..79d21bd2 100644 --- a/src/text/PointText.js +++ b/src/text/PointText.js @@ -15,8 +15,30 @@ */ var PointText = this.PointText = TextItem.extend({ + /** @lends PointText# */ + beans: true, + /** + * Creates a point text item + * + * @example + * var text = new PointText(new Point(50, 100)); + * text.paragraphStyle.justification = 'center'; + * text.content = 'The contents of the point text'; + * text.fillColor = 'black'; + * + * @param {Point} point the position where the text will start + * + * @constructs PointText + * + * @class A PointText item represents a piece of typography in your Paper.js + * project which starts from a certain point and extends by the amount of + * characters contained in it. + * + * @extends TextItem + * @extends Item + */ initialize: function(point) { this.base(); var point = Point.read(arguments); @@ -33,6 +55,12 @@ var PointText = this.PointText = TextItem.extend({ return copy; }, + /** + * The PointText's anchor point + * + * @type Point + * @bean + */ getPoint: function() { return this._point; }, diff --git a/src/text/TextItem.js b/src/text/TextItem.js index 0f6bc1d8..8909c609 100644 --- a/src/text/TextItem.js +++ b/src/text/TextItem.js @@ -15,8 +15,21 @@ */ var TextItem = this.TextItem = Item.extend({ + /** @lends TextItem# */ + beans: true, + /** + * @constructs TextItem + * + * @class The TextItem type allows you to create typography. Its + * functionality is inherited by different text item types such as + * {@link PointText}, and {@link AreaText} (coming soon). They each add a + * layer of functionality that is unique to their type, but share the + * underlying properties and functions that they inherit from TextItem. + * + * @extends Item + */ initialize: function() { this.base(); this.content = null; @@ -26,12 +39,27 @@ var TextItem = this.TextItem = Item.extend({ this.setParagraphStyle(); }, + /** + * The text contents of the text item. + * + * @name TextItem#content + * @type string + */ + _clone: function(copy) { copy.setCharacterStyle(this._characterStyle); copy.setParagraphStyle(this._paragraphStyle); return this.base(copy); }, + /** + * {@grouptitle Style Properties} + * + * The character style of the text item. + * + * @type CharacterStyle + * @bean + */ getCharacterStyle: function() { return this._characterStyle; }, @@ -39,7 +67,13 @@ var TextItem = this.TextItem = Item.extend({ setCharacterStyle: function(style) { this._characterStyle.initialize(style); }, - + + /** + * The paragraph style of the text item. + * + * @type ParagraphStyle + * @bean + */ getParagraphStyle: function() { return this._paragraphStyle; }, diff --git a/src/tool/ToolEvent.js b/src/tool/ToolEvent.js index ae7a7d23..f4078c40 100644 --- a/src/tool/ToolEvent.js +++ b/src/tool/ToolEvent.js @@ -20,7 +20,6 @@ var ToolEvent = this.ToolEvent = Base.extend({ beans: true, /** - * * @name ToolEvent * @constructor * diff --git a/src/ui/KeyEvent.js b/src/ui/KeyEvent.js index d63eb315..e01cabc7 100644 --- a/src/ui/KeyEvent.js +++ b/src/ui/KeyEvent.js @@ -16,13 +16,50 @@ var KeyEvent = this.KeyEvent = Event.extend(new function() { return { + /** @lends KeyEvent# */ + + beans: true, + + /** + * @name KeyEvent + * @constructor + * + * @class KeyEvent The KeyEvent object is received by the {@link Tool}'s + * keyboard handlers {@link Tool#onKeyDown}, {@link Tool#onKeyUp}, + * The KeyEvent object is the only parameter passed to these functions + * and contains information about the keyboard event. + */ initialize: function(down, key, character, event) { this.base(event); this.type = down ? 'keydown' : 'keyup'; this.key = key; this.character = character; }, - + + /** + * The type of key event. + * + * @name KeyEvent#type + * @type String('keydown', 'keyup') + */ + + /** + * The string character of the key that caused this key event. + * + * @name KeyEvent#character + * @type string + */ + + /** + * The key that caused this key event. + * + * @name KeyEvent#key + * @type string + */ + + /** + * @return {string} A string representation of the key event. + */ toString: function() { return '{ type: ' + this.type + ', key: ' + this.key diff --git a/src/ui/View.js b/src/ui/View.js index 4332de0d..3a7d7c27 100644 --- a/src/ui/View.js +++ b/src/ui/View.js @@ -15,11 +15,21 @@ */ var View = this.View = Base.extend({ + /** @lends View# */ + beans: true, + // DOCS: View: there is alot left to document // TODO: Add bounds parameter that defines position within canvas? // Find a good name for these bounds, since #bounds is already the artboard // bounds of the visible area. + /** + * Creates a view object + * @param {Canvas} canvas + * @constructs View + * + * @class The View object.. + */ initialize: function(canvas) { // Associate this view with the active paper scope. this._scope = paper; @@ -88,6 +98,13 @@ var View = this.View = Base.extend({ View.focused = this; }, + /** + * The bounds of the view, i.e. the bounds of the part of the project which + * is visible in the window. + * + * @type Rectangle + * @bean + */ getViewBounds: function() { return this._viewBounds; }, @@ -112,6 +129,10 @@ var View = this.View = Base.extend({ this._bounds = null; }, + /** + * @type Size + * @bean + */ getViewSize: function() { return this._viewBounds.getSize(); }, @@ -120,16 +141,28 @@ var View = this.View = Base.extend({ this._viewBounds.setSize.apply(this._viewBounds, arguments); }, + /** + * @type Size + * @bean + */ getBounds: function() { if (!this._bounds) this._bounds = this._matrix._transformBounds(this._viewBounds); return this._bounds; }, + /** + * @type Size + * @bean + */ getSize: function() { return this.getBounds().getSize(); }, + /** + * @type Point + * @bean + */ getCenter: function() { return this.getBounds().getCenter(); }, @@ -138,6 +171,10 @@ var View = this.View = Base.extend({ this.scrollBy(Point.read(arguments).subtract(this.getCenter())); }, + /** + * @type number + * @bean + */ getZoom: function() { return this._zoom; }, @@ -148,6 +185,9 @@ var View = this.View = Base.extend({ this._zoom = zoom; }, + /** + * @param {Point} point + */ scrollBy: function(point) { this._transform(new Matrix().translate(Point.read(arguments).negate())); }, @@ -214,12 +254,17 @@ var View = this.View = Base.extend({ }, /** - * Handler to be called whenever a view gets resized. + * Handler function that is called whenever a view gets resized. + * + * @type function */ onResize: null, /** - * Handler to be called on each frame of an animation. + * Handler function to be called on each frame of an animation. + * + * @type function + * @bean */ getOnFrame: function() { return this._onFrame;