From f8dfcbee55d5aa4ef8fa2270ff9c7db3300c457e Mon Sep 17 00:00:00 2001 From: Jon-Carlos Rivera Date: Sat, 3 Apr 2010 22:51:31 -0400 Subject: [PATCH] Initial N-Tree Commit. Passes tests. Examples work. --- README | 3 + examples/canvas_test_generate.html | 485 +++++++++ examples/index.html | 31 + examples/numbers.html | 121 +++ examples/simple_test.html | 469 +++++++++ examples/test.html | 198 ++++ examples/tree.html | 146 +++ jsspec/JSSpec.css | 224 ++++ jsspec/JSSpec.js | 1548 ++++++++++++++++++++++++++++ jsspec/diff_match_patch.js | 1 + libs/firebug-lite-compressed.js | 124 +++ libs/jquery.min.js | 19 + libs/json2.js | 479 +++++++++ src/ntree.js | 613 +++++++++++ tests/unit.html | 70 ++ 15 files changed, 4531 insertions(+) create mode 100644 README create mode 100644 examples/canvas_test_generate.html create mode 100644 examples/index.html create mode 100644 examples/numbers.html create mode 100644 examples/simple_test.html create mode 100644 examples/test.html create mode 100644 examples/tree.html create mode 100644 jsspec/JSSpec.css create mode 100644 jsspec/JSSpec.js create mode 100644 jsspec/diff_match_patch.js create mode 100644 libs/firebug-lite-compressed.js create mode 100644 libs/jquery.min.js create mode 100644 libs/json2.js create mode 100644 src/ntree.js create mode 100644 tests/unit.html diff --git a/README b/README new file mode 100644 index 0000000..1f753ae --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +N-Tree Library for Javascript + +TODO: Make More ReadMe \ No newline at end of file diff --git a/examples/canvas_test_generate.html b/examples/canvas_test_generate.html new file mode 100644 index 0000000..724b4bc --- /dev/null +++ b/examples/canvas_test_generate.html @@ -0,0 +1,485 @@ + + + +More Canvas Fun + + + + + + This test generate 20k squares over a an area several times larger than the canvas window. Once insertion has completely, the canvas begins scrolling diagonally. This test shows canvas rendering and event handling of a small subset (<300 objects) of a much larger dataset (20k objects) at interactive rates.
+ + + + + \ No newline at end of file diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 0000000..1147e4e --- /dev/null +++ b/examples/index.html @@ -0,0 +1,31 @@ + + + R-Tree Madness + + + +

R-Tree Library for Javascript

+

by Jon-Carlos Rivera

+

Released under an MIT license

+
+

This page contains examples of my N-Tree Library (ntree.js) for Javascript. The library is an open-source, high performance, and non-recursive implementation of a multidimensional N-Tree volume indexing data structure.

+

To contribute to, watch, or fork the N-Tree Library, see the project on GitHub at http://github.com/imbcmdth/NTree

+

Tests:

+

Simple Unit Tests

+

Preliminary unit testing examples. +

+

Simple Playground

+

A simple interactive example of the R-Tree. Great for understanding R-Tree principles. +

+

Area Effect

+

Generates a group of 6500 random small non-overlapping rectangles and shows the resultant tree-structure using a "heat-map". +

+

Benchmark

+

Inserts 10k objects. Performs some searches. Inserts another 10k objects. Performs some searches. Deletes and reinserts some objects. Performs some final searches. +Screenshot of various benchmark results

+

Super-Harcore Example

+

ONLY run on Opera, Chrome, Safari, or Firefox 3.6 nightlies!
+This test generates 20k dense non-overlapping rectangles of various size and color. +

+ + \ No newline at end of file diff --git a/examples/numbers.html b/examples/numbers.html new file mode 100644 index 0000000..548cc70 --- /dev/null +++ b/examples/numbers.html @@ -0,0 +1,121 @@ + + + R-Tree for Javascript - Just the numbers + + + + + + +

Raw freaking R-Tree numbers!

+

Note the faster searches after re-insertion of the same elements that were deleted.

+
+
+ + \ No newline at end of file diff --git a/examples/simple_test.html b/examples/simple_test.html new file mode 100644 index 0000000..a46a19c --- /dev/null +++ b/examples/simple_test.html @@ -0,0 +1,469 @@ + + + +More Canvas Fun + + + + The black outline is the tree's root node. Each pink outline is a sub-node.
+ Click on a rectangle to delete it and see the result in the R-Tree.
+ Click on any blank area to create a new rectangle and add it to the R-Tree.
+ + + + + + + \ No newline at end of file diff --git a/examples/test.html b/examples/test.html new file mode 100644 index 0000000..ac34bcf --- /dev/null +++ b/examples/test.html @@ -0,0 +1,198 @@ + + + +Slowdowns.. bleh + + + + + + + + \ No newline at end of file diff --git a/examples/tree.html b/examples/tree.html new file mode 100644 index 0000000..dee4693 --- /dev/null +++ b/examples/tree.html @@ -0,0 +1,146 @@ + + + + + + + + This test generates 6500 small and non-overlapping rectangles. Each left mouse click searches a 100x100 pixel window deleting any elements it funds. The time taken is reported in the firebug-lite console.
+ + + \ No newline at end of file diff --git a/jsspec/JSSpec.css b/jsspec/JSSpec.css new file mode 100644 index 0000000..0eac818 --- /dev/null +++ b/jsspec/JSSpec.css @@ -0,0 +1,224 @@ +@CHARSET "UTF-8"; + +/* -------------------- + * @Layout + */ + +html { + overflow: hidden; +} + +body, #jsspec_container { + overflow: hidden; + padding: 0; + margin: 0; + width: 100%; + height: 100%; + background-color: white; +} + +#title { + padding: 0; + margin: 0; + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 40px; + overflow: hidden; +} + +#list { + padding: 0; + margin: 0; + position: absolute; + top: 40px; + left: 0px; + bottom: 0px; + overflow: auto; + width: 250px; + _height:expression(document.body.clientHeight-40); +} + +#log { + padding: 0; + margin: 0; + position: absolute; + top: 40px; + left: 250px; + right: 0px; + bottom: 0px; + overflow: auto; + _height:expression(document.body.clientHeight-40); + _width:expression(document.body.clientWidth-250); +} + + + +/* -------------------- + * @Decorations and colors + */ +* { + padding: 0; + margin: 0; + font-family: "Lucida Grande", Helvetica, sans-serif; +} + +li { + list-style: none; +} + +/* hiding subtitles */ +h2 { + display: none; +} + +/* title section */ +div#title { + padding: 0em 0.5em; +} + +div#title h1 { + font-size: 1.5em; + float: left; +} + +div#title ul li { + float: left; + padding: 0.5em 0em 0.5em 0.75em; +} + +div#title p { + float:right; + margin-right:1em; + font-size: 0.75em; +} + +/* spec container */ +ul.specs { + margin: 0.5em; +} +ul.specs li { + margin-bottom: 0.1em; +} + +/* spec title */ +ul.specs li h3 { + font-weight: bold; + font-size: 0.75em; + padding: 0.2em 1em; + cursor: pointer; + _cursor: hand; +} + +/* example container */ +ul.examples li { + border-style: solid; + border-width: 0px 0px 1px 5px; + margin: 0.2em 0em 0.2em 1em; +} + +/* example title */ +ul.examples li h4 { + font-weight: normal; + font-size: 0.75em; + margin-left: 1em; +} + +pre.examples-code { + margin: 0.5em 2em; + padding: 0.5em; + background: white; + border: solid 1px #CCC; +} + +/* example explaination */ +ul.examples li div { + padding: 1em 2em; + font-size: 0.75em; +} + +/* styles for ongoing, success, failure, error */ +div.success, div.success a { + color: #FFFFFF; + background-color: #65C400; +} + +ul.specs li.success h3, ul.specs li.success h3 a { + color: #FFFFFF; + background-color: #65C400; +} + +ul.examples li.success, ul.examples li.success a { + color: #3D7700; + background-color: #DBFFB4; + border-color: #65C400; +} + +div.exception, div.exception a { + color: #FFFFFF; + background-color: #C20000; +} + +ul.specs li.exception h3, ul.specs li.exception h3 a { + color: #FFFFFF; + background-color: #C20000; +} + +ul.examples li.exception, ul.examples li.exception a { + color: #C20000; + background-color: #FFFBD3; + border-color: #C20000; +} + +div.ongoing, div.ongoing a { + color: #000000; + background-color: #FFFF80; +} + +ul.specs li.ongoing h3, ul.specs li.ongoing h3 a { + color: #000000; + background-color: #FFFF80; +} + +ul.examples li.ongoing, ul.examples li.ongoing a { + color: #000000; + background-color: #FFFF80; + border-color: #DDDD00; +} + + + +/* -------------------- + * values + */ +.number_value, .string_value, .regexp_value, .boolean_value, .dom_value { + font-family: monospace; + color: blue; +} +.object_value, .array_value { + line-height: 2em; + padding: 0.1em 0.2em; + margin: 0.1em 0; +} +.date_value { + font-family: monospace; + color: olive; +} +.undefined_value, .null_value { + font-style: italic; + color: blue; +} +.dom_attr_name { +} +.dom_attr_value { + color: red; +} +.dom_path { + font-size: 0.75em; + color: gray; +} +strong { + font-weight: normal; + background-color: #FFC6C6; +} \ No newline at end of file diff --git a/jsspec/JSSpec.js b/jsspec/JSSpec.js new file mode 100644 index 0000000..1a337df --- /dev/null +++ b/jsspec/JSSpec.js @@ -0,0 +1,1548 @@ +/** + * JSSpec + * + * Copyright 2007 Alan Kang + * - mailto:jania902@gmail.com + * - http://jania.pe.kr + * + * http://jania.pe.kr/aw/moin.cgi/JSSpec + * + * Dependencies: + * - diff_match_patch.js ( http://code.google.com/p/google-diff-match-patch ) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * Namespace + */ + +var JSSpec = { + specs: [], + + EMPTY_FUNCTION: function() {}, + + Browser: { + // By Rendering Engines + Trident: navigator.appName === "Microsoft Internet Explorer", + Webkit: navigator.userAgent.indexOf('AppleWebKit/') > -1, + Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') === -1, + KHTML: navigator.userAgent.indexOf('KHTML') !== -1, + Presto: navigator.appName === "Opera", + + // By Platforms + Mac: navigator.userAgent.indexOf("Macintosh") !== -1, + Ubuntu: navigator.userAgent.indexOf('Ubuntu') !== -1, + Win: navigator.userAgent.indexOf('Windows') !== -1, + + // By Browsers + IE: navigator.appName === "Microsoft Internet Explorer", + IE6: navigator.userAgent.indexOf('MSIE 6') !== -1, + IE7: navigator.userAgent.indexOf('MSIE 7') !== -1, + IE8: navigator.userAgent.indexOf('MSIE 8') !== -1, + + FF: navigator.userAgent.indexOf('Firefox') !== -1, + FF2: navigator.userAgent.indexOf('Firefox/2') !== -1, + FF3: navigator.userAgent.indexOf('Firefox/3') !== -1, + Safari: navigator.userAgent.indexOf('Safari') !== -1 + } +}; + + + +/** + * Executor + */ +JSSpec.Executor = function(target, onSuccess, onException) { + this.target = target; + this.onSuccess = typeof onSuccess == 'function' ? onSuccess : JSSpec.EMPTY_FUNCTION; + this.onException = typeof onException == 'function' ? onException : JSSpec.EMPTY_FUNCTION; + + if(JSSpec.Browser.Trident) { + // Exception handler for Trident. It helps to collect exact line number where exception occured. + window.onerror = function(message, fileName, lineNumber) { + var self = window._curExecutor; + var ex = {message:message, fileName:fileName, lineNumber:lineNumber}; + + if(JSSpec._secondPass) { + ex = self.mergeExceptions(JSSpec._assertionFailure, ex); + delete JSSpec._secondPass; + delete JSSpec._assertionFailure; + + ex.type = "failure"; + self.onException(self, ex); + } else if(JSSpec._assertionFailure) { + JSSpec._secondPass = true; + self.run(); + } else { + self.onException(self, ex); + } + + return true; + }; + } +}; +JSSpec.Executor.prototype.mergeExceptions = function(assertionFailure, normalException) { + var merged = { + message:assertionFailure.message, + fileName:normalException.fileName, + lineNumber:normalException.lineNumber + }; + + return merged; +}; + +JSSpec.Executor.prototype.run = function() { + var self = this; + var target = this.target; + var onSuccess = this.onSuccess; + var onException = this.onException; + + window.setTimeout( + function() { + var result; + if(JSSpec.Browser.Trident) { + window._curExecutor = self; + + result = self.target(); + self.onSuccess(self, result); + } else { + try { + result = self.target(); + self.onSuccess(self, result); + } catch(ex) { + if(JSSpec.Browser.Webkit) ex = {message:ex.message, fileName:ex.sourceURL, lineNumber:ex.line}; + + if(JSSpec._secondPass) { + ex = self.mergeExceptions(JSSpec._assertionFailure, ex); + delete JSSpec._secondPass; + delete JSSpec._assertionFailure; + + ex.type = "failure"; + self.onException(self, ex); + } else if(JSSpec._assertionFailure) { + JSSpec._secondPass = true; + self.run(); + } else { + self.onException(self, ex); + } + } + } + }, + 0 + ); +}; + + + +/** + * CompositeExecutor composites one or more executors and execute them sequencially. + */ +JSSpec.CompositeExecutor = function(onSuccess, onException, continueOnException) { + this.queue = []; + this.onSuccess = typeof onSuccess == 'function' ? onSuccess : JSSpec.EMPTY_FUNCTION; + this.onException = typeof onException == 'function' ? onException : JSSpec.EMPTY_FUNCTION; + this.continueOnException = !!continueOnException; +}; + +JSSpec.CompositeExecutor.prototype.addFunction = function(func) { + this.addExecutor(new JSSpec.Executor(func)); +}; + +JSSpec.CompositeExecutor.prototype.addExecutor = function(executor) { + var last = this.queue.length == 0 ? null : this.queue[this.queue.length - 1]; + if(last) { + last.next = executor; + } + + executor.parent = this; + executor.onSuccessBackup = executor.onSuccess; + executor.onSuccess = function(result) { + this.onSuccessBackup(result); + if(this.next) { + this.next.run(); + } else { + this.parent.onSuccess(); + } + }; + executor.onExceptionBackup = executor.onException; + executor.onException = function(executor, ex) { + this.onExceptionBackup(executor, ex); + + if(this.parent.continueOnException) { + if(this.next) { + this.next.run(); + } else { + this.parent.onSuccess(); + } + } else { + this.parent.onException(executor, ex); + } + }; + + this.queue.push(executor); +}; + +JSSpec.CompositeExecutor.prototype.run = function() { + if(this.queue.length > 0) { + this.queue[0].run(); + } +}; + +/** + * Spec is a set of Examples in a specific context + */ +JSSpec.Spec = function(context, entries) { + this.id = JSSpec.Spec.id++; + this.context = context; + this.url = location.href; + + this.filterEntriesByEmbeddedExpressions(entries); + this.extractOutSpecialEntries(entries); + this.examples = this.makeExamplesFromEntries(entries); + this.examplesMap = this.makeMapFromExamples(this.examples); +}; + +JSSpec.Spec.id = 0; +JSSpec.Spec.prototype.getExamples = function() { + return this.examples; +}; + +JSSpec.Spec.prototype.hasException = function() { + return this.getTotalFailures() > 0 || this.getTotalErrors() > 0; +}; + +JSSpec.Spec.prototype.getTotalFailures = function() { + var examples = this.examples; + var failures = 0; + for(var i = 0; i < examples.length; i++) { + if(examples[i].isFailure()) failures++; + } + return failures; +}; + +JSSpec.Spec.prototype.getTotalErrors = function() { + var examples = this.examples; + var errors = 0; + for(var i = 0; i < examples.length; i++) { + if(examples[i].isError()) errors++; + } + return errors; +}; + +JSSpec.Spec.prototype.filterEntriesByEmbeddedExpressions = function(entries) { + var isTrue; + for(name in entries) if(entries.hasOwnProperty(name)) { + var m = name.match(/\[\[(.+)\]\]/); + if(m && m[1]) { + eval("isTrue = (" + m[1] + ")"); + if(!isTrue) delete entries[name]; + } + } +}; + +JSSpec.Spec.prototype.extractOutSpecialEntries = function(entries) { + this.beforeEach = JSSpec.EMPTY_FUNCTION; + this.beforeAll = JSSpec.EMPTY_FUNCTION; + this.afterEach = JSSpec.EMPTY_FUNCTION; + this.afterAll = JSSpec.EMPTY_FUNCTION; + + for(name in entries) if(entries.hasOwnProperty(name)) { + if(name == 'before' || name == 'before each' || name == 'before_each') { + this.beforeEach = entries[name]; + } else if(name == 'before all' || name == 'before_all') { + this.beforeAll = entries[name]; + } else if(name == 'after' || name == 'after each' || name == 'after_each') { + this.afterEach = entries[name]; + } else if(name == 'after all' || name == 'after_all') { + this.afterAll = entries[name]; + } + } + + delete entries['before']; + delete entries['before each']; + delete entries['before_each']; + delete entries['before all']; + delete entries['before_all']; + delete entries['after']; + delete entries['after each']; + delete entries['after_each']; + delete entries['after all']; + delete entries['after_all']; +}; + +JSSpec.Spec.prototype.makeExamplesFromEntries = function(entries) { + var examples = []; + for(name in entries) if(entries.hasOwnProperty(name)) { + examples.push(new JSSpec.Example(name, entries[name], this.beforeEach, this.afterEach)); + } + return examples; +}; + +JSSpec.Spec.prototype.makeMapFromExamples = function(examples) { + var map = {}; + for(var i = 0; i < examples.length; i++) { + var example = examples[i]; + map[example.id] = examples[i]; + } + return map; +}; + +JSSpec.Spec.prototype.getExampleById = function(id) { + return this.examplesMap[id]; +}; + +JSSpec.Spec.prototype.getExecutor = function() { + var self = this; + var onException = function(executor, ex) { + self.exception = ex; + }; + + var composite = new JSSpec.CompositeExecutor(); + composite.addFunction(function() {JSSpec.log.onSpecStart(self);}); + composite.addExecutor(new JSSpec.Executor(this.beforeAll, null, function(exec, ex) { + self.exception = ex; + JSSpec.log.onSpecEnd(self); + })); + + var exampleAndAfter = new JSSpec.CompositeExecutor(null,null,true); + for(var i = 0; i < this.examples.length; i++) { + exampleAndAfter.addExecutor(this.examples[i].getExecutor()); + } + exampleAndAfter.addExecutor(new JSSpec.Executor(this.afterAll, null, onException)); + exampleAndAfter.addFunction(function() {JSSpec.log.onSpecEnd(self);}); + composite.addExecutor(exampleAndAfter); + + return composite; +}; + +/** + * Example + */ +JSSpec.Example = function(name, target, before, after) { + this.id = JSSpec.Example.id++; + this.name = name; + this.target = target; + this.before = before; + this.after = after; +}; + +JSSpec.Example.id = 0; +JSSpec.Example.prototype.isFailure = function() { + return this.exception && this.exception.type == "failure"; +}; + +JSSpec.Example.prototype.isError = function() { + return this.exception && !this.exception.type; +}; + +JSSpec.Example.prototype.getExecutor = function() { + var self = this; + var onException = function(executor, ex) { + self.exception = ex; + }; + + var composite = new JSSpec.CompositeExecutor(); + composite.addFunction(function() {JSSpec.log.onExampleStart(self);}); + composite.addExecutor(new JSSpec.Executor(this.before, null, function(exec, ex) { + self.exception = ex; + JSSpec.log.onExampleEnd(self); + })); + + var targetAndAfter = new JSSpec.CompositeExecutor(null,null,true); + + targetAndAfter.addExecutor(new JSSpec.Executor(this.target, null, onException)); + targetAndAfter.addExecutor(new JSSpec.Executor(this.after, null, onException)); + targetAndAfter.addFunction(function() {JSSpec.log.onExampleEnd(self);}); + + composite.addExecutor(targetAndAfter); + + return composite; +}; + +/** + * Runner + */ +JSSpec.Runner = function(specs, logger) { + JSSpec.log = logger; + + this.totalExamples = 0; + this.specs = []; + this.specsMap = {}; + this.addAllSpecs(specs); +}; + +JSSpec.Runner.prototype.addAllSpecs = function(specs) { + for(var i = 0; i < specs.length; i++) { + this.addSpec(specs[i]); + } +}; + +JSSpec.Runner.prototype.addSpec = function(spec) { + this.specs.push(spec); + this.specsMap[spec.id] = spec; + this.totalExamples += spec.getExamples().length; +}; + +JSSpec.Runner.prototype.getSpecById = function(id) { + return this.specsMap[id]; +}; + +JSSpec.Runner.prototype.getSpecByContext = function(context) { + for(var i = 0; i < this.specs.length; i++) { + if(this.specs[i].context == context) return this.specs[i]; + } + return null; +}; + +JSSpec.Runner.prototype.getSpecs = function() { + return this.specs; +}; + +JSSpec.Runner.prototype.hasException = function() { + return this.getTotalFailures() > 0 || this.getTotalErrors() > 0; +}; + +JSSpec.Runner.prototype.getTotalFailures = function() { + var specs = this.specs; + var failures = 0; + for(var i = 0; i < specs.length; i++) { + failures += specs[i].getTotalFailures(); + } + return failures; +}; + +JSSpec.Runner.prototype.getTotalErrors = function() { + var specs = this.specs; + var errors = 0; + for(var i = 0; i < specs.length; i++) { + errors += specs[i].getTotalErrors(); + } + return errors; +}; + + +JSSpec.Runner.prototype.run = function() { + JSSpec.log.onRunnerStart(); + var executor = new JSSpec.CompositeExecutor(function() {JSSpec.log.onRunnerEnd()},null,true); + for(var i = 0; i < this.specs.length; i++) { + executor.addExecutor(this.specs[i].getExecutor()); + } + executor.run(); +}; + + +JSSpec.Runner.prototype.rerun = function(context) { + JSSpec.runner = new JSSpec.Runner([this.getSpecByContext(context)], JSSpec.log); + JSSpec.runner.run(); +}; + +/** + * Logger + */ +JSSpec.Logger = function() { + this.finishedExamples = 0; + this.startedAt = null; +}; + +JSSpec.Logger.prototype.onRunnerStart = function() { + this._title = document.title; + + this.startedAt = new Date(); + var container = document.getElementById('jsspec_container'); + if(container) { + container.innerHTML = ""; + } else { + container = document.createElement("DIV"); + container.id = "jsspec_container"; + document.body.appendChild(container); + } + + var title = document.createElement("DIV"); + title.id = "title"; + title.innerHTML = [ + '

JSSpec

', + '', + '

JSSpec homepage

', + ].join(""); + container.appendChild(title); + + var list = document.createElement("DIV"); + list.id = "list"; + list.innerHTML = [ + '

List

', + '' + ].join(""); + container.appendChild(list); + + var log = document.createElement("DIV"); + log.id = "log"; + log.innerHTML = [ + '

Log

', + '' + ].join(""); + + container.appendChild(log); + + // add event handler for toggling + var specs = JSSpec.runner.getSpecs(); + var sb = []; + for(var i = 0; i < specs.length; i++) { + var spec = document.getElementById("spec_" + specs[i].id); + var title = spec.getElementsByTagName("H3")[0]; + title.onclick = function(e) { + var target = document.getElementById(this.parentNode.id + "_examples"); + target.style.display = target.style.display == "none" ? "block" : "none"; + return true; + } + } +}; + +JSSpec.Logger.prototype.onRunnerEnd = function() { + if(JSSpec.runner.hasException()) { + var times = 4; + var title1 = "*" + this._title; + var title2 = "*F" + JSSpec.runner.getTotalFailures() + " E" + JSSpec.runner.getTotalErrors() + "* " + this._title; + } else { + var times = 2; + var title1 = this._title; + var title2 = "Success"; + } + this.blinkTitle(times,title1,title2); +}; + +JSSpec.Logger.prototype.blinkTitle = function(times, title1, title2) { + var times = times * 2; + var mode = true; + + var f = function() { + if(times > 0) { + document.title = mode ? title1 : title2; + mode = !mode; + times--; + window.setTimeout(f, 500); + } else { + document.title = title1; + } + }; + + f(); +}; + +JSSpec.Logger.prototype.onSpecStart = function(spec) { + var spec_list = document.getElementById("spec_" + spec.id + "_list"); + var spec_log = document.getElementById("spec_" + spec.id); + + spec_list.className = "ongoing"; + spec_log.className = "ongoing"; +}; + +JSSpec.Logger.prototype.onSpecEnd = function(spec) { + var spec_list = document.getElementById("spec_" + spec.id + "_list"); + var spec_log = document.getElementById("spec_" + spec.id); + var examples = document.getElementById("spec_" + spec.id + "_examples"); + var className = spec.hasException() ? "exception" : "success"; + + spec_list.className = className; + spec_log.className = className; + + if(JSSpec.options.autocollapse && !spec.hasException()) examples.style.display = "none"; + + if(spec.exception) { + spec_log.appendChild(document.createTextNode(" - " + spec.exception.message)); + } +}; + +JSSpec.Logger.prototype.onExampleStart = function(example) { + var li = document.getElementById("example_" + example.id); + li.className = "ongoing"; +}; + +JSSpec.Logger.prototype.onExampleEnd = function(example) { + var li = document.getElementById("example_" + example.id); + li.className = example.exception ? "exception" : "success"; + + if(example.exception) { + var div = document.createElement("DIV"); + div.innerHTML = example.exception.message + "


" + " at " + example.exception.fileName + ", line " + example.exception.lineNumber + "

"; + li.appendChild(div); + } + + var title = document.getElementById("title"); + var runner = JSSpec.runner; + + title.className = runner.hasException() ? "exception" : "success"; + + this.finishedExamples++; + document.getElementById("total_failures").innerHTML = runner.getTotalFailures(); + document.getElementById("total_errors").innerHTML = runner.getTotalErrors(); + var progress = parseInt(this.finishedExamples / runner.totalExamples * 100); + document.getElementById("progress").innerHTML = progress; + document.getElementById("total_elapsed").innerHTML = (new Date().getTime() - this.startedAt.getTime()) / 1000; + + document.title = progress + "%: " + this._title; +}; + +/** + * IncludeMatcher + */ +JSSpec.IncludeMatcher = function(actual, expected, condition) { + this.actual = actual; + this.expected = expected; + this.condition = condition; + this.match = false; + this.explaination = this.makeExplain(); +}; + +JSSpec.IncludeMatcher.createInstance = function(actual, expected, condition) { + return new JSSpec.IncludeMatcher(actual, expected, condition); +}; + +JSSpec.IncludeMatcher.prototype.matches = function() { + return this.match; +}; + +JSSpec.IncludeMatcher.prototype.explain = function() { + return this.explaination; +}; + +JSSpec.IncludeMatcher.prototype.makeExplain = function() { + if(typeof this.actual.length == 'undefined') { + return this.makeExplainForNotArray(); + } else { + return this.makeExplainForArray(); + } +}; + +JSSpec.IncludeMatcher.prototype.makeExplainForNotArray = function() { + if(this.condition) { + this.match = !!this.actual[this.expected]; + } else { + this.match = !this.actual[this.expected]; + } + + var sb = []; + sb.push('

actual value:

'); + sb.push('

' + JSSpec.util.inspect(this.actual, false, this.expected) + '

'); + sb.push('

should ' + (this.condition ? '' : 'not') + ' include:

'); + sb.push('

' + JSSpec.util.inspect(this.expected) + '

'); + return sb.join(""); +}; + +JSSpec.IncludeMatcher.prototype.makeExplainForArray = function() { + var matches; + if(this.condition) { + for(var i = 0; i < this.actual.length; i++) { + matches = JSSpec.EqualityMatcher.createInstance(this.expected, this.actual[i]).matches(); + if(matches) { + this.match = true; + break; + } + } + } else { + for(var i = 0; i < this.actual.length; i++) { + matches = JSSpec.EqualityMatcher.createInstance(this.expected, this.actual[i]).matches(); + if(matches) { + this.match = false; + break; + } + } + } + + if(this.match) return ""; + + var sb = []; + sb.push('

actual value:

'); + sb.push('

' + JSSpec.util.inspect(this.actual, false, this.condition ? null : i) + '

'); + sb.push('

should ' + (this.condition ? '' : 'not') + ' include:

'); + sb.push('

' + JSSpec.util.inspect(this.expected) + '

'); + return sb.join(""); +}; + +/** + * PropertyLengthMatcher + */ +JSSpec.PropertyLengthMatcher = function(num, property, o, condition) { + this.num = num; + this.o = o; + this.property = property; + if((property == 'characters' || property == 'items') && typeof o.length != 'undefined') { + this.property = 'length'; + } + + this.condition = condition; + this.conditionMet = function(x) { + if(condition == 'exactly') return x.length == num; + if(condition == 'at least') return x.length >= num; + if(condition == 'at most') return x.length <= num; + + throw "Unknown condition '" + condition + "'"; + }; + this.match = false; + this.explaination = this.makeExplain(); +}; + +JSSpec.PropertyLengthMatcher.prototype.makeExplain = function() { + if(this.o._type == 'String' && this.property == 'length') { + this.match = this.conditionMet(this.o); + return this.match ? '' : this.makeExplainForString(); + } else if(typeof this.o.length != 'undefined' && this.property == "length") { + this.match = this.conditionMet(this.o); + return this.match ? '' : this.makeExplainForArray(); + } else if(typeof this.o[this.property] != 'undefined' && this.o[this.property] != null) { + this.match = this.conditionMet(this.o[this.property]); + return this.match ? '' : this.makeExplainForObject(); + } else if(typeof this.o[this.property] == 'undefined' || this.o[this.property] == null) { + this.match = false; + return this.makeExplainForNoProperty(); + } + + this.match = true; +}; + +JSSpec.PropertyLengthMatcher.prototype.makeExplainForString = function() { + var sb = []; + + var exp = this.num == 0 ? + 'be an empty string' : + 'have ' + this.condition + ' ' + this.num + ' characters'; + + sb.push('

actual value has ' + this.o.length + ' characters:

'); + sb.push('

' + JSSpec.util.inspect(this.o) + '

'); + sb.push('

but it should ' + exp + '.

'); + + return sb.join(""); +}; + +JSSpec.PropertyLengthMatcher.prototype.makeExplainForArray = function() { + var sb = []; + + var exp = this.num == 0 ? + 'be an empty array' : + 'have ' + this.condition + ' ' + this.num + ' items'; + + sb.push('

actual value has ' + this.o.length + ' items:

'); + sb.push('

' + JSSpec.util.inspect(this.o) + '

'); + sb.push('

but it should ' + exp + '.

'); + + return sb.join(""); +}; + +JSSpec.PropertyLengthMatcher.prototype.makeExplainForObject = function() { + var sb = []; + + var exp = this.num == 0 ? + 'be empty' : + 'have ' + this.condition + ' ' + this.num + ' ' + this.property + '.'; + + sb.push('

actual value has ' + this.o[this.property].length + ' ' + this.property + ':

'); + sb.push('

' + JSSpec.util.inspect(this.o, false, this.property) + '

'); + sb.push('

but it should ' + exp + '.

'); + + return sb.join(""); +}; + +JSSpec.PropertyLengthMatcher.prototype.makeExplainForNoProperty = function() { + var sb = []; + + sb.push('

actual value:

'); + sb.push('

' + JSSpec.util.inspect(this.o) + '

'); + sb.push('

should have ' + this.condition + ' ' + this.num + ' ' + this.property + ' but there\'s no such property.

'); + + return sb.join(""); +}; + +JSSpec.PropertyLengthMatcher.prototype.matches = function() { + return this.match; +}; + +JSSpec.PropertyLengthMatcher.prototype.explain = function() { + return this.explaination; +}; + +JSSpec.PropertyLengthMatcher.createInstance = function(num, property, o, condition) { + return new JSSpec.PropertyLengthMatcher(num, property, o, condition); +}; + +/** + * EqualityMatcher + */ +JSSpec.EqualityMatcher = {}; + +JSSpec.EqualityMatcher.createInstance = function(expected, actual) { + if(expected == null || actual == null) { + return new JSSpec.NullEqualityMatcher(expected, actual); + } else if(expected._type && expected._type == actual._type) { + if(expected._type == "String") { + return new JSSpec.StringEqualityMatcher(expected, actual); + } else if(expected._type == "Date") { + return new JSSpec.DateEqualityMatcher(expected, actual); + } else if(expected._type == "Number") { + return new JSSpec.NumberEqualityMatcher(expected, actual); + } else if(expected._type == "Array") { + return new JSSpec.ArrayEqualityMatcher(expected, actual); + } else if(expected._type == "Boolean") { + return new JSSpec.BooleanEqualityMatcher(expected, actual); + } + } + + return new JSSpec.ObjectEqualityMatcher(expected, actual); +}; + +JSSpec.EqualityMatcher.basicExplain = function(expected, actual, expectedDesc, actualDesc) { + var sb = []; + + sb.push(actualDesc || '

actual value:

'); + sb.push('

' + JSSpec.util.inspect(actual) + '

'); + sb.push(expectedDesc || '

should be:

'); + sb.push('

' + JSSpec.util.inspect(expected) + '

'); + + return sb.join(""); +}; + +JSSpec.EqualityMatcher.diffExplain = function(expected, actual) { + var sb = []; + + sb.push('

diff:

'); + sb.push('

'); + + var dmp = new diff_match_patch(); + var diff = dmp.diff_main(expected, actual); + dmp.diff_cleanupEfficiency(diff); + + sb.push(JSSpec.util.inspect(dmp.diff_prettyHtml(diff), true)); + + sb.push('

'); + + return sb.join(""); +}; + +/** + * BooleanEqualityMatcher + */ +JSSpec.BooleanEqualityMatcher = function(expected, actual) { + this.expected = expected; + this.actual = actual; +}; + +JSSpec.BooleanEqualityMatcher.prototype.explain = function() { + var sb = []; + + sb.push('

actual value:

'); + sb.push('

' + JSSpec.util.inspect(this.actual) + '

'); + sb.push('

should be:

'); + sb.push('

' + JSSpec.util.inspect(this.expected) + '

'); + + return sb.join(""); +}; + +JSSpec.BooleanEqualityMatcher.prototype.matches = function() { + return this.expected == this.actual; +}; + +/** + * NullEqualityMatcher + */ +JSSpec.NullEqualityMatcher = function(expected, actual) { + this.expected = expected; + this.actual = actual; +}; + +JSSpec.NullEqualityMatcher.prototype.matches = function() { + return this.expected == this.actual && typeof this.expected == typeof this.actual; +}; + +JSSpec.NullEqualityMatcher.prototype.explain = function() { + return JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual); +}; + +JSSpec.DateEqualityMatcher = function(expected, actual) { + this.expected = expected; + this.actual = actual; +}; + +JSSpec.DateEqualityMatcher.prototype.matches = function() { + return this.expected.getTime() == this.actual.getTime(); +}; + +JSSpec.DateEqualityMatcher.prototype.explain = function() { + var sb = []; + + sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual)); + sb.push(JSSpec.EqualityMatcher.diffExplain(this.expected.toString(), this.actual.toString())); + + return sb.join(""); +}; + +/** + * ObjectEqualityMatcher + */ +JSSpec.ObjectEqualityMatcher = function(expected, actual) { + this.expected = expected; + this.actual = actual; + this.match = this.expected == this.actual; + this.explaination = this.makeExplain(); +}; + +JSSpec.ObjectEqualityMatcher.prototype.matches = function() {return this.match}; + +JSSpec.ObjectEqualityMatcher.prototype.explain = function() {return this.explaination}; + +JSSpec.ObjectEqualityMatcher.prototype.makeExplain = function() { + if(this.expected == this.actual) { + this.match = true; + return ""; + } + + if(JSSpec.util.isDomNode(this.expected)) { + return this.makeExplainForDomNode(); + } + + var key, expectedHasItem, actualHasItem; + + for(key in this.expected) { + expectedHasItem = this.expected[key] != null && typeof this.expected[key] != 'undefined'; + actualHasItem = this.actual[key] != null && typeof this.actual[key] != 'undefined'; + if(expectedHasItem && !actualHasItem) return this.makeExplainForMissingItem(key); + } + for(key in this.actual) { + expectedHasItem = this.expected[key] != null && typeof this.expected[key] != 'undefined'; + actualHasItem = this.actual[key] != null && typeof this.actual[key] != 'undefined'; + if(actualHasItem && !expectedHasItem) return this.makeExplainForUnknownItem(key); + } + + for(key in this.expected) { + var matcher = JSSpec.EqualityMatcher.createInstance(this.expected[key], this.actual[key]); + if(!matcher.matches()) return this.makeExplainForItemMismatch(key); + } + + this.match = true; +}; + +JSSpec.ObjectEqualityMatcher.prototype.makeExplainForDomNode = function(key) { + var sb = []; + + sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual)); + + return sb.join(""); +}; + +JSSpec.ObjectEqualityMatcher.prototype.makeExplainForMissingItem = function(key) { + var sb = []; + + sb.push('

actual value has no item named ' + JSSpec.util.inspect(key) + '

'); + sb.push('

' + JSSpec.util.inspect(this.actual, false, key) + '

'); + sb.push('

but it should have the item whose value is ' + JSSpec.util.inspect(this.expected[key]) + '

'); + sb.push('

' + JSSpec.util.inspect(this.expected, false, key) + '

'); + + return sb.join(""); +}; + +JSSpec.ObjectEqualityMatcher.prototype.makeExplainForUnknownItem = function(key) { + var sb = []; + + sb.push('

actual value has item named ' + JSSpec.util.inspect(key) + '

'); + sb.push('

' + JSSpec.util.inspect(this.actual, false, key) + '

'); + sb.push('

but there should be no such item

'); + sb.push('

' + JSSpec.util.inspect(this.expected, false, key) + '

'); + + return sb.join(""); +}; + +JSSpec.ObjectEqualityMatcher.prototype.makeExplainForItemMismatch = function(key) { + var sb = []; + + sb.push('

actual value has an item named ' + JSSpec.util.inspect(key) + ' whose value is ' + JSSpec.util.inspect(this.actual[key]) + '

'); + sb.push('

' + JSSpec.util.inspect(this.actual, false, key) + '

'); + sb.push('

but it\'s value should be ' + JSSpec.util.inspect(this.expected[key]) + '

'); + sb.push('

' + JSSpec.util.inspect(this.expected, false, key) + '

'); + + return sb.join(""); +}; + + + + +/** + * ArrayEqualityMatcher + */ +JSSpec.ArrayEqualityMatcher = function(expected, actual) { + this.expected = expected; + this.actual = actual; + this.match = this.expected == this.actual; + this.explaination = this.makeExplain(); +}; + +JSSpec.ArrayEqualityMatcher.prototype.matches = function() {return this.match}; + +JSSpec.ArrayEqualityMatcher.prototype.explain = function() {return this.explaination}; + +JSSpec.ArrayEqualityMatcher.prototype.makeExplain = function() { + if(this.expected.length != this.actual.length) return this.makeExplainForLengthMismatch(); + + for(var i = 0; i < this.expected.length; i++) { + var matcher = JSSpec.EqualityMatcher.createInstance(this.expected[i], this.actual[i]); + if(!matcher.matches()) return this.makeExplainForItemMismatch(i); + } + + this.match = true; +}; + +JSSpec.ArrayEqualityMatcher.prototype.makeExplainForLengthMismatch = function() { + return JSSpec.EqualityMatcher.basicExplain( + this.expected, + this.actual, + '

but it should be ' + this.expected.length + '

', + '

actual value has ' + this.actual.length + ' items

' + ); +}; + +JSSpec.ArrayEqualityMatcher.prototype.makeExplainForItemMismatch = function(index) { + var postfix = ["th", "st", "nd", "rd", "th"][Math.min((index + 1) % 10,4)]; + + var sb = []; + + sb.push('

' + (index + 1) + postfix + ' item (index ' + index + ') of actual value is ' + JSSpec.util.inspect(this.actual[index]) + ':

'); + sb.push('

' + JSSpec.util.inspect(this.actual, false, index) + '

'); + sb.push('

but it should be ' + JSSpec.util.inspect(this.expected[index]) + ':

'); + sb.push('

' + JSSpec.util.inspect(this.expected, false, index) + '

'); + + return sb.join(""); +}; + +/** + * NumberEqualityMatcher + */ +JSSpec.NumberEqualityMatcher = function(expected, actual) { + this.expected = expected; + this.actual = actual; +}; + +JSSpec.NumberEqualityMatcher.prototype.matches = function() { + if(this.expected == this.actual) return true; +}; + +JSSpec.NumberEqualityMatcher.prototype.explain = function() { + return JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual); +}; + +/** + * StringEqualityMatcher + */ +JSSpec.StringEqualityMatcher = function(expected, actual) { + this.expected = expected; + this.actual = actual; +}; + +JSSpec.StringEqualityMatcher.prototype.matches = function() { + return this.expected == this.actual; +}; + +JSSpec.StringEqualityMatcher.prototype.explain = function() { + var sb = []; + + sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual)); + sb.push(JSSpec.EqualityMatcher.diffExplain(this.expected, this.actual)); + return sb.join(""); +}; + +/** + * PatternMatcher + */ +JSSpec.PatternMatcher = function(actual, pattern, condition) { + this.actual = actual; + this.pattern = pattern; + this.condition = condition; + this.match = false; + this.explaination = this.makeExplain(); +}; + +JSSpec.PatternMatcher.createInstance = function(actual, pattern, condition) { + return new JSSpec.PatternMatcher(actual, pattern, condition); +}; + +JSSpec.PatternMatcher.prototype.makeExplain = function() { + var sb; + if(this.actual == null || this.actual._type != 'String') { + sb = []; + sb.push('

actual value:

'); + sb.push('

' + JSSpec.util.inspect(this.actual) + '

'); + sb.push('

should ' + (this.condition ? '' : 'not') + ' match with pattern:

'); + sb.push('

' + JSSpec.util.inspect(this.pattern) + '

'); + sb.push('

but pattern matching cannot be performed.

'); + return sb.join(""); + } else { + this.match = this.condition == !!this.actual.match(this.pattern); + if(this.match) return ""; + + sb = []; + sb.push('

actual value:

'); + sb.push('

' + JSSpec.util.inspect(this.actual) + '

'); + sb.push('

should ' + (this.condition ? '' : 'not') + ' match with pattern:

'); + sb.push('

' + JSSpec.util.inspect(this.pattern) + '

'); + return sb.join(""); + } +}; + +JSSpec.PatternMatcher.prototype.matches = function() { + return this.match; +}; + +JSSpec.PatternMatcher.prototype.explain = function() { + return this.explaination; +}; + +/** + * Domain Specific Languages + */ +JSSpec.DSL = {}; + +JSSpec.DSL.forString = { + normalizeHtml: function() { + var html = this; + + // Uniformize quotation, turn tag names and attribute names into lower case + html = html.replace(/<(\/?)(\w+)([^>]*?)>/img, function(str, closingMark, tagName, attrs) { + var sortedAttrs = JSSpec.util.sortHtmlAttrs(JSSpec.util.correctHtmlAttrQuotation(attrs).toLowerCase()) + return "<" + closingMark + tagName.toLowerCase() + sortedAttrs + ">" + }); + + // validation self-closing tags + html = html.replace(/<(br|hr|img)([^>]*?)>/mg, function(str, tag, attrs) { + return "<" + tag + attrs + " />"; + }); + + // append semi-colon at the end of style value + html = html.replace(/style="(.*?)"/mg, function(str, styleStr) { + styleStr = JSSpec.util.sortStyleEntries(styleStr.strip()); // for Safari + if(styleStr.charAt(styleStr.length - 1) != ';') styleStr += ";" + + return 'style="' + styleStr + '"' + }); + + // sort style entries + + // remove empty style attributes + html = html.replace(/ style=";"/mg, ""); + + // remove new-lines + html = html.replace(/\r/mg, ''); + html = html.replace(/\n/mg, ''); + + return html; + } +}; + + +JSSpec.DSL.describe = function(context, entries, base) { + if(base) { + for(var i = 0; i < JSSpec.specs.length; i++) { + if(JSSpec.specs[i].context === base) { + base = JSSpec.specs[i]; + break; + } + } + + for(var i = 0; i < base.examples.length; i++) { + var example = base.examples[i]; + + if(!entries[example.name]) entries[example.name] = example.target; + } + } + + JSSpec.specs.push(new JSSpec.Spec(context, entries)); +}; + +JSSpec.DSL.value_of = function(target) { + if(JSSpec._secondPass) return {}; + + var subject = new JSSpec.DSL.Subject(target); + return subject; +}; + +JSSpec.DSL.Subject = function(target) { + this.target = target; +}; + +JSSpec.DSL.Subject.prototype._type = 'Subject'; + +JSSpec.DSL.Subject.prototype.should_fail = function(message) { + JSSpec._assertionFailure = {message:message}; + throw JSSpec._assertionFailure; +}; + +JSSpec.DSL.Subject.prototype.should_be = function(expected) { + var matcher = JSSpec.EqualityMatcher.createInstance(expected, this.target); + if(!matcher.matches()) { + JSSpec._assertionFailure = {message:matcher.explain()}; + throw JSSpec._assertionFailure; + } +}; + +JSSpec.DSL.Subject.prototype.should_not_be = function(expected) { + // TODO JSSpec.EqualityMatcher should support 'condition' + var matcher = JSSpec.EqualityMatcher.createInstance(expected, this.target); + if(matcher.matches()) { + JSSpec._assertionFailure = {message:"'" + this.target + "' should not be '" + expected + "'"}; + throw JSSpec._assertionFailure; + } +}; + +JSSpec.DSL.Subject.prototype.should_be_empty = function() { + this.should_have(0, this.getType() == 'String' ? 'characters' : 'items'); +}; + +JSSpec.DSL.Subject.prototype.should_not_be_empty = function() { + this.should_have_at_least(1, this.getType() == 'String' ? 'characters' : 'items'); +}; + +JSSpec.DSL.Subject.prototype.should_be_true = function() { + this.should_be(true); +}; + +JSSpec.DSL.Subject.prototype.should_be_false = function() { + this.should_be(false); +}; + +JSSpec.DSL.Subject.prototype.should_be_null = function() { + this.should_be(null); +}; + +JSSpec.DSL.Subject.prototype.should_be_undefined = function() { + this.should_be(undefined); +}; + +JSSpec.DSL.Subject.prototype.should_not_be_null = function() { + this.should_not_be(null); +}; + +JSSpec.DSL.Subject.prototype.should_not_be_undefined = function() { + this.should_not_be(undefined); +}; + +JSSpec.DSL.Subject.prototype._should_have = function(num, property, condition) { + var matcher = JSSpec.PropertyLengthMatcher.createInstance(num, property, this.target, condition); + if(!matcher.matches()) { + JSSpec._assertionFailure = {message:matcher.explain()}; + throw JSSpec._assertionFailure; + } +}; + +JSSpec.DSL.Subject.prototype.should_have = function(num, property) { + this._should_have(num, property, "exactly"); +}; + +JSSpec.DSL.Subject.prototype.should_have_exactly = function(num, property) { + this._should_have(num, property, "exactly"); +}; + +JSSpec.DSL.Subject.prototype.should_have_at_least = function(num, property) { + this._should_have(num, property, "at least"); +}; + +JSSpec.DSL.Subject.prototype.should_have_at_most = function(num, property) { + this._should_have(num, property, "at most"); +}; + +JSSpec.DSL.Subject.prototype.should_include = function(expected) { + var matcher = JSSpec.IncludeMatcher.createInstance(this.target, expected, true); + if(!matcher.matches()) { + JSSpec._assertionFailure = {message:matcher.explain()}; + throw JSSpec._assertionFailure; + } +}; + +JSSpec.DSL.Subject.prototype.should_not_include = function(expected) { + var matcher = JSSpec.IncludeMatcher.createInstance(this.target, expected, false); + if(!matcher.matches()) { + JSSpec._assertionFailure = {message:matcher.explain()}; + throw JSSpec._assertionFailure; + } +}; + +JSSpec.DSL.Subject.prototype.should_match = function(pattern) { + var matcher = JSSpec.PatternMatcher.createInstance(this.target, pattern, true); + if(!matcher.matches()) { + JSSpec._assertionFailure = {message:matcher.explain()}; + throw JSSpec._assertionFailure; + } +} +JSSpec.DSL.Subject.prototype.should_not_match = function(pattern) { + var matcher = JSSpec.PatternMatcher.createInstance(this.target, pattern, false); + if(!matcher.matches()) { + JSSpec._assertionFailure = {message:matcher.explain()}; + throw JSSpec._assertionFailure; + } +}; + +JSSpec.DSL.Subject.prototype.getType = function() { + if(typeof this.target == 'undefined') { + return 'undefined'; + } else if(this.target == null) { + return 'null'; + } else if(this.target._type) { + return this.target._type; + } else if(JSSpec.util.isDomNode(this.target)) { + return 'DomNode'; + } else { + return 'object'; + } +}; + +/** + * Utilities + */ +JSSpec.util = { + escapeTags: function(string) { + return string.replace(//img, '>'); + }, + escapeMetastring: function(string) { + return string.replace(/\r/img, '\\r').replace(/\n/img, '\\n').replace(/\¶\;\/img, '\\n').replace(/\t/img, '\\t'); + }, + parseOptions: function(defaults) { + var options = defaults; + + var url = location.href; + var queryIndex = url.indexOf('?'); + if(queryIndex == -1) return options; + + var query = url.substring(queryIndex + 1).split('#')[0]; + var pairs = query.split('&'); + for(var i = 0; i < pairs.length; i++) { + var tokens = pairs[i].split('='); + options[tokens[0]] = tokens[1]; + } + + return options; + }, + correctHtmlAttrQuotation: function(html) { + html = html.replace(/(\w+)=['"]([^'"]+)['"]/mg,function (str, name, value) {return name + '=' + '"' + value + '"';}); + html = html.replace(/(\w+)=([^ '"]+)/mg,function (str, name, value) {return name + '=' + '"' + value + '"';}); + html = html.replace(/'/mg, '"'); + + return html; + }, + sortHtmlAttrs: function(html) { + var attrs = []; + html.replace(/((\w+)="[^"]+")/mg, function(str, matched) { + attrs.push(matched); + }); + return attrs.length == 0 ? "" : " " + attrs.sort().join(" "); + }, + sortStyleEntries: function(styleText) { + var entries = styleText.split(/; /); + return entries.sort().join("; "); + }, + escapeHtml: function(str) { + if(!this._div) { + this._div = document.createElement("DIV"); + this._text = document.createTextNode(''); + this._div.appendChild(this._text); + } + this._text.data = str; + return this._div.innerHTML; + }, + isDomNode: function(o) { + // TODO: make it more stricter + return (typeof o.nodeName == 'string') && (typeof o.nodeType == 'number'); + }, + inspectDomPath: function(o) { + var sb = []; + while(o && o.nodeName != '#document' && o.parent) { + var siblings = o.parentNode.childNodes; + for(var i = 0; i < siblings.length; i++) { + if(siblings[i] == o) { + sb.push(o.nodeName + (i == 0 ? '' : '[' + i + ']')); + break; + } + } + o = o.parentNode; + } + return sb.join(" > "); + }, + inspectDomNode: function(o) { + if(o.nodeType == 1) { + var nodeName = o.nodeName.toLowerCase(); + var sb = []; + sb.push(''); + sb.push("<"); + sb.push(nodeName); + + var attrs = o.attributes; + for(var i = 0; i < attrs.length; i++) { + if( + attrs[i].nodeValue && + attrs[i].nodeName != 'contentEditable' && + attrs[i].nodeName != 'style' && + typeof attrs[i].nodeValue != 'function' + ) sb.push(' ' + attrs[i].nodeName.toLowerCase() + '="' + attrs[i].nodeValue + '"'); + } + if(o.style && o.style.cssText) { + sb.push(' style="' + o.style.cssText + '"'); + } + sb.push('>'); + sb.push(JSSpec.util.escapeHtml(o.innerHTML)); + sb.push('</' + nodeName + '>'); + sb.push(' (' + JSSpec.util.inspectDomPath(o) + ')' ); + sb.push(''); + return sb.join(""); + } else if(o.nodeType == 3) { + return '#text ' + o.nodeValue + ''; + } else { + return 'UnknownDomNode'; + } + }, + inspect: function(o, dontEscape, emphasisKey) { + var sb, inspected; + + if(typeof o == 'undefined') return 'undefined'; + if(o == null) return 'null'; + if(o._type == 'String') return '"' + (dontEscape ? JSSpec.util.escapeMetastring(o) : JSSpec.util.escapeHtml(JSSpec.util.escapeMetastring(o))) + '"'; + + if(o._type == 'Date') { + return '"' + o.toString() + '"'; + } + + if(o._type == 'Number') return '' + (dontEscape ? o : JSSpec.util.escapeHtml(o)) + ''; + + if(o._type == 'Boolean') return '' + o + ''; + + if(o._type == 'RegExp') return '' + JSSpec.util.escapeHtml(o.toString()) + ''; + + if(JSSpec.util.isDomNode(o)) return JSSpec.util.inspectDomNode(o); + + if(o._type == 'Array' || typeof o.length != 'undefined') { + sb = []; + for(var i = 0; i < o.length; i++) { + inspected = JSSpec.util.inspect(o[i]); + sb.push(i == emphasisKey ? ('' + inspected + '') : inspected); + } + return '[' + sb.join(', ') + ']'; + } + + // object + sb = []; + for(var key in o) { + if(key == 'should') continue; + + inspected = JSSpec.util.inspect(key) + ":" + JSSpec.util.inspect(o[key]); + sb.push(key == emphasisKey ? ('' + inspected + '') : inspected); + } + return '{' + sb.join(', ') + '}'; + } +}; + +describe = JSSpec.DSL.describe; +behavior_of = JSSpec.DSL.describe; +value_of = JSSpec.DSL.value_of; +expect = JSSpec.DSL.value_of; // @deprecated + +String.prototype._type = "String"; +Number.prototype._type = "Number"; +Date.prototype._type = "Date"; +Array.prototype._type = "Array"; +Boolean.prototype._type = "Boolean"; +RegExp.prototype._type = "RegExp"; + +var targets = [Array.prototype, Date.prototype, Number.prototype, String.prototype, Boolean.prototype, RegExp.prototype]; + +String.prototype.normalizeHtml = JSSpec.DSL.forString.normalizeHtml; +String.prototype.asHtml = String.prototype.normalizeHtml; //@deprecated +String.prototype.strip = function() {return this.replace(/^\s+/, '').replace(/\s+$/, '');} + + +/** + * Main + */ +JSSpec.defaultOptions = { + autorun: 1, + specIdBeginsWith: 0, + exampleIdBeginsWith: 0, + autocollapse: 1 +}; +JSSpec.options = JSSpec.util.parseOptions(JSSpec.defaultOptions); + +JSSpec.Spec.id = JSSpec.options.specIdBeginsWith; +JSSpec.Example.id = JSSpec.options.exampleIdBeginsWith; + + + +window.onload = function() { + if(JSSpec.specs.length > 0) { + if(!JSSpec.options.inSuite) { + JSSpec.runner = new JSSpec.Runner(JSSpec.specs, new JSSpec.Logger()); + if(JSSpec.options.rerun) { + JSSpec.runner.rerun(decodeURIComponent(JSSpec.options.rerun)); + } else { + JSSpec.runner.run(); + } + } else { + // in suite, send all specs to parent + var parentWindow = window.frames.parent.window; + for(var i = 0; i < JSSpec.specs.length; i++) { + parentWindow.JSSpec.specs.push(JSSpec.specs[i]); + } + } + } else { + var links = document.getElementById('list').getElementsByTagName('A'); + var frameContainer = document.createElement('DIV'); + frameContainer.style.display = 'none'; + document.body.appendChild(frameContainer); + + for(var i = 0; i < links.length; i++) { + var frame = document.createElement('IFRAME'); + frame.src = links[i].href + '?inSuite=0&specIdBeginsWith=' + (i * 10000) + '&exampleIdBeginsWith=' + (i * 10000); + frameContainer.appendChild(frame); + } + } +} \ No newline at end of file diff --git a/jsspec/diff_match_patch.js b/jsspec/diff_match_patch.js new file mode 100644 index 0000000..885e051 --- /dev/null +++ b/jsspec/diff_match_patch.js @@ -0,0 +1 @@ +function diff_match_patch(){this.Diff_Timeout=1.0;this.Diff_EditCost=4;this.Diff_DualThreshold=32;this.Match_Balance=0.5;this.Match_Threshold=0.5;this.Match_MinLength=100;this.Match_MaxLength=1000;this.Patch_Margin=4;function getMaxBits(){var maxbits=0;var oldi=1;var newi=2;while(oldi!=newi){maxbits++;oldi=newi;newi=newi<<1}return maxbits}this.Match_MaxBits=getMaxBits()}var DIFF_DELETE=-1;var DIFF_INSERT=1;var DIFF_EQUAL=0;diff_match_patch.prototype.diff_main=function(text1,text2,opt_checklines){if(text1==text2){return[[DIFF_EQUAL,text1]]}if(typeof opt_checklines=='undefined'){opt_checklines=true}var checklines=opt_checklines;var commonlength=this.diff_commonPrefix(text1,text2);var commonprefix=text1.substring(0,commonlength);text1=text1.substring(commonlength);text2=text2.substring(commonlength);commonlength=this.diff_commonSuffix(text1,text2);var commonsuffix=text1.substring(text1.length-commonlength);text1=text1.substring(0,text1.length-commonlength);text2=text2.substring(0,text2.length-commonlength);var diffs=this.diff_compute(text1,text2,checklines);if(commonprefix){diffs.unshift([DIFF_EQUAL,commonprefix])}if(commonsuffix){diffs.push([DIFF_EQUAL,commonsuffix])}this.diff_cleanupMerge(diffs);return diffs};diff_match_patch.prototype.diff_compute=function(text1,text2,checklines){var diffs;if(!text1){return[[DIFF_INSERT,text2]]}if(!text2){return[[DIFF_DELETE,text1]]}var longtext=text1.length>text2.length?text1:text2;var shorttext=text1.length>text2.length?text2:text1;var i=longtext.indexOf(shorttext);if(i!=-1){diffs=[[DIFF_INSERT,longtext.substring(0,i)],[DIFF_EQUAL,shorttext],[DIFF_INSERT,longtext.substring(i+shorttext.length)]];if(text1.length>text2.length){diffs[0][0]=diffs[2][0]=DIFF_DELETE}return diffs}longtext=shorttext=null;var hm=this.diff_halfMatch(text1,text2);if(hm){var text1_a=hm[0];var text1_b=hm[1];var text2_a=hm[2];var text2_b=hm[3];var mid_common=hm[4];var diffs_a=this.diff_main(text1_a,text2_a,checklines);var diffs_b=this.diff_main(text1_b,text2_b,checklines);return diffs_a.concat([[DIFF_EQUAL,mid_common]],diffs_b)}if(checklines&&text1.length+text2.length<250){checklines=false}var linearray;if(checklines){var a=this.diff_linesToChars(text1,text2);text1=a[0];text2=a[1];linearray=a[2]}diffs=this.diff_map(text1,text2);if(!diffs){diffs=[[DIFF_DELETE,text1],[DIFF_INSERT,text2]]}if(checklines){this.diff_charsToLines(diffs,linearray);this.diff_cleanupSemantic(diffs);diffs.push([DIFF_EQUAL,'']);var pointer=0;var count_delete=0;var count_insert=0;var text_delete='';var text_insert='';while(pointer=1&&count_insert>=1){var a=this.diff_main(text_delete,text_insert,false);diffs.splice(pointer-count_delete-count_insert,count_delete+count_insert);pointer=pointer-count_delete-count_insert;for(var j=a.length-1;j>=0;j--){diffs.splice(pointer,0,a[j])}pointer=pointer+a.length}count_insert=0;count_delete=0;text_delete='';text_insert=''}pointer++}diffs.pop()}return diffs};diff_match_patch.prototype.diff_linesToChars=function(text1,text2){var linearray=[];var linehash={};linearray.push('');function diff_linesToCharsMunge(text){var chars='';while(text){var i=text.indexOf('\n');if(i==-1){i=text.length}var line=text.substring(0,i+1);text=text.substring(i+1);if(linehash.hasOwnProperty?linehash.hasOwnProperty(line):(linehash[line]!==undefined)){chars+=String.fromCharCode(linehash[line])}else{linearray.push(line);linehash[line]=linearray.length-1;chars+=String.fromCharCode(linearray.length-1)}}return chars}var chars1=diff_linesToCharsMunge(text1);var chars2=diff_linesToCharsMunge(text2);return[chars1,chars2,linearray]};diff_match_patch.prototype.diff_charsToLines=function(diffs,linearray){for(var x=0;x0&&(new Date()).getTime()>ms_end){return null}v_map1[d]={};for(var k=-d;k<=d;k+=2){if(k==-d||k!=d&&v1[k-1]=0;d--){while(1){if(v_map[d].hasOwnProperty?v_map[d].hasOwnProperty((x-1)+','+y):(v_map[d][(x-1)+','+y]!==undefined)){x--;if(last_op===DIFF_DELETE){path[0][1]=text1.charAt(x)+path[0][1]}else{path.unshift([DIFF_DELETE,text1.charAt(x)])}last_op=DIFF_DELETE;break}else if(v_map[d].hasOwnProperty?v_map[d].hasOwnProperty(x+','+(y-1)):(v_map[d][x+','+(y-1)]!==undefined)){y--;if(last_op===DIFF_INSERT){path[0][1]=text2.charAt(y)+path[0][1]}else{path.unshift([DIFF_INSERT,text2.charAt(y)])}last_op=DIFF_INSERT;break}else{x--;y--;if(last_op===DIFF_EQUAL){path[0][1]=text1.charAt(x)+path[0][1]}else{path.unshift([DIFF_EQUAL,text1.charAt(x)])}last_op=DIFF_EQUAL}}}return path};diff_match_patch.prototype.diff_path2=function(v_map,text1,text2){var path=[];var x=text1.length;var y=text2.length;var last_op=null;for(var d=v_map.length-2;d>=0;d--){while(1){if(v_map[d].hasOwnProperty?v_map[d].hasOwnProperty((x-1)+','+y):(v_map[d][(x-1)+','+y]!==undefined)){x--;if(last_op===DIFF_DELETE){path[path.length-1][1]+=text1.charAt(text1.length-x-1)}else{path.push([DIFF_DELETE,text1.charAt(text1.length-x-1)])}last_op=DIFF_DELETE;break}else if(v_map[d].hasOwnProperty?v_map[d].hasOwnProperty(x+','+(y-1)):(v_map[d][x+','+(y-1)]!==undefined)){y--;if(last_op===DIFF_INSERT){path[path.length-1][1]+=text2.charAt(text2.length-y-1)}else{path.push([DIFF_INSERT,text2.charAt(text2.length-y-1)])}last_op=DIFF_INSERT;break}else{x--;y--;if(last_op===DIFF_EQUAL){path[path.length-1][1]+=text1.charAt(text1.length-x-1)}else{path.push([DIFF_EQUAL,text1.charAt(text1.length-x-1)])}last_op=DIFF_EQUAL}}}return path};diff_match_patch.prototype.diff_commonPrefix=function(text1,text2){if(!text1||!text2||text1.charCodeAt(0)!==text2.charCodeAt(0)){return 0}var pointermin=0;var pointermax=Math.min(text1.length,text2.length);var pointermid=pointermax;var pointerstart=0;while(pointermintext2.length?text1:text2;var shorttext=text1.length>text2.length?text2:text1;if(longtext.length<10||shorttext.length<1){return null}var dmp=this;function diff_halfMatchI(longtext,shorttext,i){var seed=longtext.substring(i,i+Math.floor(longtext.length/4));var j=-1;var best_common='';var best_longtext_a,best_longtext_b,best_shorttext_a,best_shorttext_b;while((j=shorttext.indexOf(seed,j+1))!=-1){var prefixLength=dmp.diff_commonPrefix(longtext.substring(i),shorttext.substring(j));var suffixLength=dmp.diff_commonSuffix(longtext.substring(0,i),shorttext.substring(0,j));if(best_common.length=longtext.length/2){return[best_longtext_a,best_longtext_b,best_shorttext_a,best_shorttext_b,best_common]}else{return null}}var hm1=diff_halfMatchI(longtext,shorttext,Math.ceil(longtext.length/4));var hm2=diff_halfMatchI(longtext,shorttext,Math.ceil(longtext.length/2));var hm;if(!hm1&&!hm2){return null}else if(!hm2){hm=hm1}else if(!hm1){hm=hm2}else{hm=hm1[4].length>hm2[4].length?hm1:hm2}var text1_a,text1_b,text2_a,text2_b;if(text1.length>text2.length){text1_a=hm[0];text1_b=hm[1];text2_a=hm[2];text2_b=hm[3]}else{text2_a=hm[0];text2_b=hm[1];text1_a=hm[2];text1_b=hm[3]}var mid_common=hm[4];return[text1_a,text1_b,text2_a,text2_b,mid_common]};diff_match_patch.prototype.diff_cleanupSemantic=function(diffs){var changes=false;var equalities=[];var lastequality=null;var pointer=0;var length_changes1=0;var length_changes2=0;while(pointer=bestScore){bestScore=score;bestEquality1=equality1;bestEdit=edit;bestEquality2=equality2}}if(diffs[pointer-1][1]!=bestEquality1){diffs[pointer-1][1]=bestEquality1;diffs[pointer][1]=bestEdit;diffs[pointer+1][1]=bestEquality2}}pointer++}};diff_match_patch.prototype.diff_cleanupEfficiency=function(diffs){var changes=false;var equalities=[];var lastequality='';var pointer=0;var pre_ins=false;var pre_del=false;var post_ins=false;var post_del=false;while(pointer0&&diffs[pointer-count_delete-count_insert-1][0]==DIFF_EQUAL){diffs[pointer-count_delete-count_insert-1][1]+=text_insert.substring(0,commonlength)}else{diffs.splice(0,0,[DIFF_EQUAL,text_insert.substring(0,commonlength)]);pointer++}text_insert=text_insert.substring(commonlength);text_delete=text_delete.substring(commonlength)}commonlength=this.diff_commonSuffix(text_insert,text_delete);if(commonlength!==0){diffs[pointer][1]=text_insert.substring(text_insert.length-commonlength)+diffs[pointer][1];text_insert=text_insert.substring(0,text_insert.length-commonlength);text_delete=text_delete.substring(0,text_delete.length-commonlength)}}if(count_delete===0){diffs.splice(pointer-count_delete-count_insert,count_delete+count_insert,[DIFF_INSERT,text_insert])}else if(count_insert===0){diffs.splice(pointer-count_delete-count_insert,count_delete+count_insert,[DIFF_DELETE,text_delete])}else{diffs.splice(pointer-count_delete-count_insert,count_delete+count_insert,[DIFF_DELETE,text_delete],[DIFF_INSERT,text_insert])}pointer=pointer-count_delete-count_insert+(count_delete?1:0)+(count_insert?1:0)+1}else if(pointer!==0&&diffs[pointer-1][0]==DIFF_EQUAL){diffs[pointer-1][1]+=diffs[pointer][1];diffs.splice(pointer,1)}else{pointer++}count_insert=0;count_delete=0;text_delete='';text_insert=''}}if(diffs[diffs.length-1][1]===''){diffs.pop()}var changes=false;pointer=1;while(pointerloc){break}last_chars1=chars1;last_chars2=chars2}if(diffs.length!=x&&diffs[x][0]===DIFF_DELETE){return last_chars2}return last_chars2+(loc-last_chars1)};diff_match_patch.prototype.diff_prettyHtml=function(diffs){this.diff_addIndex(diffs);var html=[];for(var x=0;x/g,'>');t=t.replace(/\n/g,'¶
');if(m===DIFF_DELETE){html.push('',t,'')}else if(m===DIFF_INSERT){html.push('',t,'')}else{html.push('',t,'')}}return html.join('')};diff_match_patch.prototype.diff_text1=function(diffs){var txt=[];for(var x=0;xthis.Match_MaxBits){return alert('Pattern too long for this browser.')}var s=this.match_alphabet(pattern);var score_text_length=text.length;score_text_length=Math.max(score_text_length,this.Match_MinLength);score_text_length=Math.min(score_text_length,this.Match_MaxLength);var dmp=this;function match_bitapScore(e,x){var d=Math.abs(loc-x);return(e/pattern.length/dmp.Match_Balance)+(d/score_text_length/(1.0-dmp.Match_Balance))}var score_threshold=this.Match_Threshold;var best_loc=text.indexOf(pattern,loc);if(best_loc!=-1){score_threshold=Math.min(match_bitapScore(0,best_loc),score_threshold)}best_loc=text.lastIndexOf(pattern,loc+pattern.length);if(best_loc!=-1){score_threshold=Math.min(match_bitapScore(0,best_loc),score_threshold)}var matchmask=1<<(pattern.length-1);best_loc=null;var bin_min,bin_mid;var bin_max=Math.max(loc+loc,text.length);var last_rd;for(var d=0;d=start;j--){if(d===0){rd[j]=((rd[j+1]<<1)|1)&s[text.charAt(j)]}else{rd[j]=((rd[j+1]<<1)|1)&s[text.charAt(j)]|((last_rd[j+1]<<1)|1)|((last_rd[j]<<1)|1)|last_rd[j+1]}if(rd[j]&matchmask){var score=match_bitapScore(d,j);if(score<=score_threshold){score_threshold=score;best_loc=j;if(j>loc){start=Math.max(0,loc-(j-loc))}else{break}}}}if(match_bitapScore(d+1,loc)>score_threshold){break}last_rd=rd}return best_loc};diff_match_patch.prototype.match_alphabet=function(pattern){var s=Object();for(var i=0;i2){this.diff_cleanupSemantic(diffs);this.diff_cleanupEfficiency(diffs)}}if(diffs.length===0){return[]}var patches=[];var patch=new patch_obj();var char_count1=0;var char_count2=0;var prepatch_text=text1;var postpatch_text=text1;for(var x=0;x=2*this.Patch_Margin){if(patch.diffs.length!==0){this.patch_addContext(patch,prepatch_text);patches.push(patch);patch=new patch_obj();prepatch_text=postpatch_text}}if(diff_type!==DIFF_INSERT){char_count1+=diff_text.length}if(diff_type!==DIFF_DELETE){char_count2+=diff_text.length}}if(patch.diffs.length!==0){this.patch_addContext(patch,prepatch_text);patches.push(patch)}return patches};diff_match_patch.prototype.patch_apply=function(patches,text){this.patch_splitMax(patches);var results=[];var delta=0;for(var x=0;xthis.Match_MaxBits){var bigpatch=patches[x];patches.splice(x,1);var patch_size=this.Match_MaxBits;var start1=bigpatch.start1;var start2=bigpatch.start2;var precontext='';while(bigpatch.diffs.length!==0){var patch=new patch_obj();var empty=true;patch.start1=start1-precontext.length;patch.start2=start2-precontext.length;if(precontext!==''){patch.length1=patch.length2=precontext.length;patch.diffs.push([DIFF_EQUAL,precontext])}while(bigpatch.diffs.length!==0&&patch.length1>>"));el.left.console.input=new lib.element("INPUT").attribute.set("type","text").attribute.addClass("Input").event.addListener("keydown",listen.consoleTextbox).insert(new lib.element("DIV").attribute.addClass("InputContainer").insert(el.left.console.container));el.right.console={};el.right.console.container=new lib.element("DIV").attribute.addClass("Console Container").insert(el.right.container);el.right.console.mlButton=new lib.element("A").attribute.addClass("MLButton CloseML").event.addListener("click",d.console.toggleML).insert(el.right.console.container);el.right.console.input=new lib.element("TEXTAREA").attribute.addClass("Input").insert(el.right.console.container);el.right.console.input.event.addListener("keydown",lib.util.Curry(tab,window,el.right.console.input.element));el.right.console.run=new lib.element("A").attribute.addClass("Button").event.addListener("click",listen.runMultiline).update("Run").insert(el.right.console.container);el.right.console.clear=new lib.element("A").attribute.addClass("Button").event.addListener("click",lib.util.Curry(d.clean,window,el.right.console.input)).update("Clear").insert(el.right.console.container);el.button.console={};el.button.console.container=new lib.element("DIV").attribute.addClass("ButtonSet").insert(el.button.container);el.button.console.clear=new lib.element("A").attribute.addClass("Button").event.addListener("click",d.console.clear).update("Clear").insert(el.button.console.container);el.left.html={};el.left.html.container=new lib.element("DIV").attribute.addClass("HTML").insert(el.left.container);el.right.html={};el.right.html.container=new lib.element("DIV").attribute.addClass("HTML Container").insert(el.right.container);el.right.html.nav={};el.right.html.nav.container=new lib.element("DIV").attribute.addClass("Nav").insert(el.right.html.container);el.right.html.nav.computedStyle=new lib.element("A").attribute.addClass("Tab Selected").event.addListener("click",lib.util.Curry(d.html.navigate,firebug,"computedStyle")).update("Computed Style").insert(el.right.html.nav.container);el.right.html.nav.dom=new lib.element("A").attribute.addClass("Tab").event.addListener("click",lib.util.Curry(d.html.navigate,firebug,"dom")).update("DOM").insert(el.right.html.nav.container);el.right.html.content=new lib.element("DIV").attribute.addClass("Content").insert(el.right.html.container);el.button.html={};el.button.html.container=new lib.element("DIV").attribute.addClass("ButtonSet HTML").insert(el.button.container);el.left.css={};el.left.css.container=new lib.element("DIV").attribute.addClass("CSS").insert(el.left.container);el.right.css={};el.right.css.container=new lib.element("DIV").attribute.addClass("CSS Container").insert(el.right.container);el.right.css.nav={};el.right.css.nav.container=new lib.element("DIV").attribute.addClass("Nav").insert(el.right.css.container);el.right.css.nav.runCSS=new lib.element("A").attribute.addClass("Tab Selected").update("Run CSS").insert(el.right.css.nav.container);el.right.css.mlButton=new lib.element("A").attribute.addClass("MLButton CloseML").event.addListener("click",d.console.toggleML).insert(el.right.css.container);el.right.css.input=new lib.element("TEXTAREA").attribute.addClass("Input").insert(el.right.css.container);el.right.css.input.event.addListener("keydown",lib.util.Curry(firebug.tab,window,el.right.css.input.element));el.right.css.run=new lib.element("A").attribute.addClass("Button").event.addListener("click",listen.runCSS).update("Run").insert(el.right.css.container);el.right.css.clear=new lib.element("A").attribute.addClass("Button").event.addListener("click",lib.util.Curry(d.clean,window,el.right.css.input)).update("Clear").insert(el.right.css.container);el.button.css={};el.button.css.container=new lib.element("DIV").attribute.addClass("ButtonSet CSS").insert(el.button.container);el.button.css.selectbox=new lib.element("SELECT").event.addListener("change",listen.cssSelectbox).insert(el.button.css.container);el.left.scripts={};el.left.scripts.container=new lib.element("DIV").attribute.addClass("Scripts").insert(el.left.container);el.right.scripts={};el.right.scripts.container=new lib.element("DIV").attribute.addClass("Scripts Container").insert(el.right.container);el.button.scripts={};el.button.scripts.container=new lib.element("DIV").attribute.addClass("ButtonSet Scripts").insert(el.button.container);el.button.scripts.selectbox=new lib.element("SELECT").event.addListener("change",listen.scriptsSelectbox).insert(el.button.scripts.container);el.button.scripts.lineNumbers=new lib.element("A").attribute.addClass("Button").event.addListener("click",d.scripts.toggleLineNumbers).update("Show Line Numbers").insert(el.button.scripts.container);el.left.dom={};el.left.dom.container=new lib.element("DIV").attribute.addClass("DOM").insert(el.left.container);el.right.dom={};el.right.dom.container=new lib.element("DIV").attribute.addClass("DOM Container").insert(el.right.container);el.button.dom={};el.button.dom.container=new lib.element("DIV").attribute.addClass("ButtonSet DOM").insert(el.button.container);el.button.dom.label=new lib.element("LABEL").update("Object Path:").insert(el.button.dom.container);el.button.dom.textbox=new lib.element("INPUT").event.addListener("keydown",listen.domTextbox).update(internal.isPopup?"window.opener":"window").insert(el.button.dom.container);el.left.str={};el.left.str.container=new lib.element("DIV").attribute.addClass("STR").insert(el.left.container);el.right.str={};el.right.str.container=new lib.element("DIV").attribute.addClass("STR").insert(el.left.container);el.button.str={};el.button.str.container=new lib.element("DIV").attribute.addClass("ButtonSet XHR").insert(el.button.container);el.button.str.watch=new lib.element("A").attribute.addClass("Button").event.addListener("click",lib.util.Curry(d.navigate,window,"xhr")).update("Back").insert(el.button.str.container);el.left.xhr={};el.left.xhr.container=new lib.element("DIV").attribute.addClass("XHR").insert(el.left.container);el.right.xhr={};el.right.xhr.container=new lib.element("DIV").attribute.addClass("XHR").insert(el.left.container);el.button.xhr={};el.button.xhr.container=new lib.element("DIV").attribute.addClass("ButtonSet XHR").insert(el.button.container);el.button.xhr.label=new lib.element("LABEL").update("XHR Path:").insert(el.button.xhr.container);el.button.xhr.textbox=new lib.element("INPUT").event.addListener("keydown",listen.xhrTextbox).insert(el.button.xhr.container);el.button.xhr.watch=new lib.element("A").attribute.addClass("Button").event.addListener("click",listen.addXhrObject).update("Watch").insert(el.button.xhr.container);el.settings={};el.settings.container=new lib.element("DIV").child.add(new lib.element("DIV").attribute.addClass("Header").child.add(new lib.element().attribute.addClass("Title").update('Firebug Lite Settings'))).attribute.addClass("SettingsDiv").insert(el.main);el.settings.content=new lib.element("DIV").attribute.addClass("Content").insert(el.settings.container);el.settings.progressDiv=new lib.element("DIV").attribute.addClass("ProgressDiv").insert(el.settings.content);el.settings.progress=new lib.element("DIV").attribute.addClass("Progress").insert(el.settings.progressDiv);el.settings.cbxDebug=new lib.element("INPUT").attribute.set("type","checkbox").attribute.addClass("SettingsCBX").insert(el.settings.content);el.settings.content.child.add(document.createTextNode("Start visible"));new lib.element("BR").insert(el.settings.content);el.settings.cbxDetectFirebug=new lib.element("INPUT").attribute.set("type","checkbox").attribute.addClass("SettingsCBX").insert(el.settings.content);el.settings.content.child.add(document.createTextNode("Hide when Firebug active"));new lib.element("BR").insert(el.settings.content);el.settings.cbxHideDOMFunctions=new lib.element("INPUT").attribute.set("type","checkbox").attribute.addClass("SettingsCBX").insert(el.settings.content);el.settings.content.child.add(document.createTextNode("Hide DOM functions"));new lib.element("BR").insert(el.settings.content);el.settings.cbxOverride=new lib.element("INPUT").attribute.set("type","checkbox").attribute.addClass("SettingsCBX").insert(el.settings.content);el.settings.content.child.add(document.createTextNode("Override window.console"));new lib.element("BR").insert(el.settings.content);el.settings.cbxShowIcon=new lib.element("INPUT").attribute.set("type","checkbox").attribute.addClass("SettingsCBX").insert(el.settings.content);el.settings.content.child.add(document.createTextNode("Show icon when hidden"));new lib.element("BR").insert(el.settings.content);el.settings.cbxOpenInPopup=new lib.element("INPUT").attribute.set("type","checkbox").attribute.addClass("SettingsCBX").insert(el.settings.content);el.settings.content.child.add(document.createTextNode("Open in popup"));new lib.element("BR").insert(el.settings.content);el.settings.content.child.add(document.createTextNode("Trim textnode to "));el.settings.textNodeChars=new lib.element("INPUT").attribute.set("type","text").attribute.addClass("SettingsTextbox").insert(el.settings.content);el.settings.content.child.add(document.createTextNode(" chars"));el.settings.buttonDiv=new lib.element("DIV").insert(el.settings.content);el.settings.buttonLeftDiv=new lib.element("DIV").attribute.addClass("ButtonsLeft").insert(el.settings.buttonDiv);el.settings.resetButton=new lib.element("INPUT").attribute.set("type","button").update("Reset").event.addListener("click",settings.reset).insert(el.settings.buttonLeftDiv);el.settings.buttonRightDiv=new lib.element("DIV").attribute.addClass("ButtonsRight").insert(el.settings.buttonDiv);el.settings.cancelButton=new lib.element("INPUT").attribute.set("type","button").update("Cancel").event.addListener("click",settings.hide).insert(el.settings.buttonRightDiv);el.settings.buttonRightDiv.child.add(document.createTextNode(" "));el.settings.saveButton=new lib.element("INPUT").attribute.set("type","button").update("Save").event.addListener("click",settings.saveClicked).insert(el.settings.buttonRightDiv);lib.util.AddEvent(document,"mousemove",listen.mouse)("mousemove",win.resizer.resize)("mouseup",win.resizer.stop)("keydown",listen.keyboard);internal.init=true;for(var i=0,len=d.console.cache.length;i0){fe.textNodeChars=elSet.textNodeChars.element.value;}else{fe.textNodeChars=0;} +if(firebug.internal.isPopup){window.opener.firebug.env=firebug.lib.util.Hash.clone(fe);} +with(firebug){settings.writeCookie();settings.hide();win.refreshDOM();d.html.openHtmlTree();if(internal.isPopup){with(opener.firebug){win.refreshDOM();d.html.openHtmlTree();}}}},reset:function(){var exdate=new Date();exdate.setTime(exdate.getTime()-1);document.cookie='FBLiteSettings=;expires='+exdate.toGMTString();location.reload(true);},readCookie:function(){var i,cookieArr,valueArr,item,value;with(firebug.env){if(firebug.internal.targetWindow.document.cookie.length>0){cookieArr=firebug.internal.targetWindow.document.cookie.split('; ');for(i=0;i-1){scriptPath=scripts[i].src;break;}} +if(scriptPath){done=false;script=fi.popupWin.document.createElement('script');script.type='text/javascript';script.src=scriptPath;script[firebug.lib.env.ie?"onreadystatechange":"onload"]=function(){if(!done&&(!firebug.lib.env.ie||this.readyState=="complete"||this.readyState=="loaded")){done=true;if(fi.popupWin.firebug){with(fi.popupWin.firebug){internal.isPopup=true;env.css=fe.css;init();el.button.dock.environment.addStyle({"display":"block"});el.button.newWindow.environment.addStyle({"display":"none"});}}}};if(!done&&firebug.lib.env.webkit){interval=setInterval(function(){if(firebug.internal.popupWin.firebug){clearInterval(interval);done=true;with(firebug.internal.popupWin.firebug){internal.isPopup=true;env.css=fe.css;init();el.button.dock.environment.addStyle({"display":"block"});el.button.newWindow.environment.addStyle({"display":"none"});}}},10);};if(!firebug.lib.env.ie){firebug.internal.popupWin.document.write('Firebug Lite - '+document.location.href+'');} +if(!done){firebug.internal.popupWin.document.getElementsByTagName('head')[0].appendChild(script);firebug.el.main.environment.addStyle({"display":"none"});firebug.el.mainiframe.environment.addStyle({"display":"none"});}}else{alert("Unable to detect the following script \""+firebug.internal.liteFilename+"\" ... if the script has been renamed then please set the value of firebug.internal.liteFilename to reflect this change");firebug.internal.popupWin.close();firebug.internal.popupWin=null;}}}},dock:function(){with(opener.firebug){internal.popupWin=null;el.main.environment.addStyle({"display":"block"});el.mainiframe.environment.addStyle({"display":"block"});settings.readCookie();window.close();};},unload:function(){with(firebug){if(internal.isPopup){win.dock();}else if(internal.popupWin){internal.popupWin.close();}}},fitToPopup:function(){with(firebug){var viewport=lib.util.GetViewport(window);win.setHeight((window.innerHeight||viewport.height)-38);el.main.environment.addStyle({"width":(viewport.width)+"px"});el.mainiframe.environment.addStyle({"width":(viewport.width)+"px"});}},resizer:{y:[],enabled:false,start:function(_event){with(firebug){if(internal.minimized)return;win.resizer.y=[el.main.element.offsetHeight,_event.clientY];if(lib.env.ie6){win.resizer.y[3]=parseInt(el.main.environment.getPosition().top);} +win.resizer.enabled=true;}},resize:function(_event){with(firebug){if(!win.resizer.enabled)return;win.resizer.y[2]=(win.resizer.y[0]+(win.resizer.y[1]-_event.clientY));el.main.environment.addStyle({"height":win.resizer.y[2]+"px"});el.mainiframe.environment.addStyle({"height":win.resizer.y[2]+"px"});if(lib.env.ie6){el.main.environment.addStyle({"top":win.resizer.y[3]-(win.resizer.y[1]-_event.clientY)+"px"});el.mainiframe.environment.addStyle({"top":win.resizer.y[3]-(win.resizer.y[1]-_event.clientY)+"px"});}}},stop:function(_event){with(firebug){if(win.resizer.enabled){win.resizer.enabled=false;win.setHeight(win.resizer.y[2]-35);}}}},setHeight:function(_height){with(firebug){env.height=_height;el.left.container.environment.addStyle({"height":_height+"px"});el.right.container.environment.addStyle({"height":_height+"px"});el.main.environment.addStyle({"height":_height+38+"px"});el.mainiframe.environment.addStyle({"height":_height+38+"px"});win.refreshSize();el.left.console.monitor.element.parentNode.style.height=_height-47+"px";el.left.console.mlButton.environment.addStyle({"top":_height+19+"px"});el.right.console.mlButton.environment.addStyle({"top":_height+19+"px"});el.right.console.input.environment.addStyle({"height":_height-29+"px"});el.left.html.container.environment.addStyle({"height":_height-23+"px"});el.right.html.content.environment.addStyle({"height":_height-23+"px"});el.left.css.container.environment.addStyle({"height":_height-33+"px"});el.right.css.input.environment.addStyle({"height":_height-55+"px"});el.left.scripts.container.environment.addStyle({"height":_height-23+"px"});el.left.dom.container.environment.addStyle({"height":_height-31+"px"});el.left.xhr.container.environment.addStyle({"height":_height-32+"px"});el.left.str.container.environment.addStyle({"height":_height-32+"px"});}},refreshDOM:function(){with(firebug){d.dom.open(eval(el.button.dom.textbox.environment.getElement().value),el.left.dom.container);if(d.html.nIndex=="dom"){firebug.d.html.navigate("dom")}}},refreshSize:function(){with(firebug){if(!internal.init) +return;var dim=lib.util.GetViewport();el.main.environment.addStyle({"width":dim.width+"px"});el.mainiframe.environment.addStyle({"width":dim.width+"px"});if(lib.env.ie6) +win.setVerticalPosition(dim);}},setVerticalPosition:function(_dim,_event){with(firebug){var dim=_dim||lib.util.GetViewport();el.main.environment.addStyle({"top":dim.height-el.main.environment.getSize().offsetHeight+Math.max(document.documentElement.scrollTop,document.body.scrollTop)+"px"});el.mainiframe.environment.addStyle({"top":dim.height-el.main.environment.getSize().offsetHeight+Math.max(document.documentElement.scrollTop,document.body.scrollTop)+"px"});}}},d:{clean:function(_element){with(firebug){_element.update("");}},console:{addLine:function(){with(firebug){return new lib.element("DIV").attribute.addClass("Row").insert(el.left.console.monitor);}},cache:[],clear:function(){with(firebug){d.clean(el.left.console.monitor);d.console.cache=[];}},formatArgs:function(){with(firebug){var content=[];for(var i=0,len=arguments.length;i>> "+_cmd);d.console.addLine().update(d.highlight(_text,false,false,true));d.console.scroll();}},printException:function(_exception){with(firebug){var message=_exception.description||_exception.message||_exception;if(_exception.fileName){message+=' ('+(_exception.name&&(_exception.name+', ')||'')+getFileName(_exception.fileName)+', '+_exception.lineNumber+')';} +d.console.addLine().attribute.addClass("Error").update("Error: "+message,true);}},eval:function(_cmd){var result;with(firebug){if(_cmd.length==0) +return;el.left.console.input.environment.getElement().value="";d.console.historyIndex=d.console.history.push(_cmd);try{if(_cmd==='console.firebug'){d.console.addLine().attribute.addClass("Arrow").update(firebug.version);}else{result=eval.call(window,_cmd);d.console.print(_cmd,result);}}catch(e){d.console.addLine().attribute.addClass("Arrow").update(">>> "+_cmd);d.console.printException(e);} +d.console.scroll();}},scroll:function(){with(firebug){el.left.console.monitor.environment.getElement().parentNode.scrollTop=Math.abs(el.left.console.monitor.environment.getSize().offsetHeight-(el.left.console.monitor.element.parentNode.offsetHeight-11));}},run:function(_command){with(firebug){if(!internal.init){d.console.cache.push({"command":_command,"arg":Array.prototype.slice.call(arguments,1)});}else{d.console.cmd[_command].apply(window,Array.prototype.slice.call(arguments,1));}}},toggleML:function(){with(firebug){var open=!env.ml;env.ml=!env.ml;d.navigateRightColumn("console",open);el[open?"left":"right"].console.mlButton.environment.addStyle({display:"none"});el[!open?"left":"right"].console.mlButton.environment.addStyle({display:"block"});el.left.console.mlButton.attribute[(open?"add":"remove")+"Class"]("CloseML");}},countMap:{},timeMap:{},cmd:{log:function(_value){with(firebug){var args=d.console.formatArgs.apply(window,arguments);d.console.addLine().attribute.addClass("Log").update(args);d.console.scroll();}},warn:function(_value){with(firebug){var args=d.console.formatArgs.apply(window,arguments);d.console.addLine().attribute.addClass("Warn").update(args);d.console.scroll();}},info:function(_value){with(firebug){var args=d.console.formatArgs.apply(window,arguments);d.console.addLine().attribute.addClass("Info").update(args);d.console.scroll();}},debug:function(_value){with(firebug){var args=d.console.formatArgs.apply(window,arguments);d.console.addLine().attribute.addClass("Debug").update(args);d.console.scroll();}},error:function(_value){with(firebug){var args=d.console.formatArgs.apply(window,arguments);d.console.addLine().attribute.addClass("Error").update(args);d.console.scroll();}},trace:function(_value){with(firebug){var stackAmt=3,f=arguments.caller,isArray=lib.util.IsArray(f);if((!isArray&&f)||(isArray&&f.length>0)){d.console.addLine().attribute.addClass("Arrow").update(">>> console.trace(stack)");for(var i=0;i>> console.dir("+_value+")");d.dom.open(_value,d.console.addLine());}},dirxml:function(){with(firebug){d.console.cmd.log.apply(this,arguments);}},time:function(_name){with(firebug){d.console.timeMap[_name]=new Date().getTime();}},timeEnd:function(_name){with(firebug){if(_name in d.console.timeMap){var delta=new Date().getTime()-d.console.timeMap[_name],args=d.console.formatArgs.apply(window,[_name+":",delta+"ms"]);d.console.addLine().attribute.addClass("log").update(args);delete d.console.timeMap[_name];}}},count:function(_name){with(firebug){if(!d.console.countMap[_name]) +d.console.countMap[_name]=0;d.console.countMap[_name]++;d.console.cmd.log.apply(window,[_name,d.console.countMap[_name]]);}},group:function(){with(firebug){d.console.cmd.log.apply(this,["console.group is not supported"]);}},groupEnd:function(){with(firebug){d.console.cmd.log.apply(this,["console.groupEnd is not supported"]);}},profile:function(){with(firebug){d.console.cmd.log.apply(this,["console.profile is not supported"]);}},profileEnd:function(){with(firebug){d.console.cmd.log.apply(this,["console.profileEnd is not supported"]);}}}},css:{index:-1,open:function(_index){with(firebug){var item=internal.targetWindow.document.styleSheets[_index],uri=item.href;try{var rules=item[lib.env.ie?"rules":"cssRules"],str="";for(var i=0;iAccess to restricted URI denied";} +el.left.css.container.update(str);}},printRule:function(_selector,_css,_layer){with(firebug){var str="
"+_selector+" {
";for(var i=0,len=_css.length;i"+item.replace(/(.+\:)(.+)/,"$1$2;")+"";} +str+="
}
";return str;}},refresh:function(){with(firebug){el.button.css.selectbox.update("");var collection=internal.targetWindow.document.styleSheets;for(var i=0,len=collection.length;iundefined");}else{result.push("null");}}else if(vtype=="regexp"){result.push(""+_value+"");}else if(vtype=="date"){result.push("'"+_value+"'");}else if(vtype=="boolean"||vtype=="number"){result.push(""+_value+"");}else if(vtype=="function"){result.push("function()");}else{result.push("\""+(!_inObject&&!_inArray?_value:_value.substring(0,35)+(_value.length>35?" ...":"")).replace(/\n/g,"\\n").replace(/\s/g," ").replace(/>/g,">").replace(/");}} +else if(isElement){if(_value.nodeType==3) +result.push(d.highlight(_value.nodeValue));else if(_inObject){result.push(""+_value.nodeName.toLowerCase()+"");}else{result.push("");if(_inArray){result.push(_value.nodeName.toLowerCase());if(_value.getAttribute){if(_value.getAttribute&&_value.getAttribute("id")) +result.push("#"+_value.getAttribute("id")+"");var elClass=_value.getAttribute(lib.env.ie&&!lib.env.ie8?"className":"class")||"";result.push(!elClass?"":"."+elClass.split(" ")[0]+"");} +result.push("");}else{result.push("<"+_value.nodeName.toLowerCase());if(_value.attributes){for(var i=0,len=_value.attributes.length;i"+item.nodeName+"=\""+item.nodeValue+"\"");}} +result.push(">");}}} +else if(isArray||isHash){if(isArray){if(_inObject){result.push("["+_value.length+"]");}else{result.push("[ ");for(var i=0,len=_value.length;i3){result.push(", "+(len-4)+" More...");break;} +result.push((i>0?", ":"")+d.highlight(_value[i],false,true,true));} +result.push(" ]");}}else if(_inObject){result.push("Object");}else{result.push("Object");var i=0;for(var key in _value){var value=_value[key];if((_inObject||_inArray)&&i>3){result.push(" More...");break;} +result.push(" "+key+"="+d.highlight(value,true));i++;} +result.push("");}}else{result.push([""+_value+""]);}}catch(e){result.push("..");} +return result.join("");}},html:{nIndex:"computedStyle",current:null,highlight:function(_element,_clear,_event){with(firebug){if(_element.firebugElement){return;} +if(_clear){internal.targetWindow.firebug.el.bgInspector.environment.addStyle({"display":"none"});return;} +d.inspector.inspect(_element,true);}},inspect:function(_element){var map=[],parentLayer,t,link,tagName,searchEl,parent=_element;while(parent){map.push(parent);if(parent==firebug.internal.targetWindow.document.body)break;parent=parent.parentNode;} +map=map.reverse();with(firebug){if(env.dIndex!="html"){internal.targetWindow.firebug.d.navigate("html");} +internal.targetWindow.firebug.d.inspector.toggle(false);for(t=0;t<html>").insert(parent);};for(var i=0;i<=len;i++){if(i==len){new lib.element("A").attribute.addClass("Block").update("</"+element.nodeName.toLowerCase()+">").insert(container);break;} +var item=element.childNodes[i];if(item.nodeType!=Node.TEXT_NODE&&!item.getAttribute('firebugIgnore')){var container=new lib.element().attribute.addClass("Block").insert(parent),link=new lib.element("A").attribute.addClass("Link").insert(container),spacer=new lib.element("SPAN").attribute.addClass("Spacer").update(" ").insert(link),html=new lib.element("SPAN").attribute.addClass("Content").update(d.highlight(item)).insert(link),subContainer=new lib.element("DIV").attribute.addClass("SubContainer").insert(container),view;if(item.nodeType==Node.COMMENT_NODE){continue;} +view=lib.util.Element.getView(item);link.event.addListener("click",lib.util.Curry(d.html.openHtmlTree,window,item,subContainer,false));link.event.addListener("mouseover",lib.util.Curry(d.html.highlight,window,item,false));link.event.addListener("mouseout",lib.util.Curry(d.html.highlight,window,item,true));returnParentVal=returnParentEl==item?subContainer:returnParentVal;if(d.html.current==null&&item==document.body){link.attribute.addClass("Selected");link.attribute.addClass("Parent");d.html.current=[item,link];d.html.openHtmlTree(item,subContainer);} +if(element.nodeName!="HEAD"&&element!=document.documentElement&&(view.visibility=="hidden"||view.display=="none")){container.attribute.addClass("Unvisible");};if(item.childNodes){var childLen=item.childNodes.length;if(childLen==1&&item.childNodes[0].nodeType==Node.TEXT_NODE){if(isFinite(env.textNodeChars)&&parseInt(env.textNodeChars)>0){html.child.add(document.createTextNode(item.childNodes[0].nodeValue.substring(0,env.textNodeChars)));}else{html.child.add(document.createTextNode(item.childNodes[0].nodeValue));} +html.child.add(document.createTextNode(""));continue;} +else if(childLen>0){link.attribute.addClass("Parent");}}}};return returnParentVal;}},openProperties:function(){with(firebug){var index=d.html.nIndex;var node=d.html.current[0];d.clean(el.right.html.content);var str="";switch(index){case"computedStyle":var property=["opacity","filter","azimuth","background","backgroundAttachment","backgroundColor","backgroundImage","backgroundPosition","backgroundRepeat","border","borderCollapse","borderColor","borderSpacing","borderStyle","borderTop","borderRight","borderBottom","borderLeft","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor","borderTopStyle","borderRightStyle","borderBottomStyle","borderLeftStyle","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderWidth","bottom","captionSide","clear","clip","color","content","counterIncrement","counterReset","cue","cueAfter","cueBefore","cursor","direction","display","elevation","emptyCells","cssFloat","font","fontFamily","fontSize","fontSizeAdjust","fontStretch","fontStyle","fontVariant","fontWeight","height","left","letterSpacing","lineHeight","listStyle","listStyleImage","listStylePosition","listStyleType","margin","marginTop","marginRight","marginBottom","marginLeft","markerOffset","marks","maxHeight","maxWidth","minHeight","minWidth","orphans","outline","outlineColor","outlineStyle","outlineWidth","overflow","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","page","pageBreakAfter","pageBreakBefore","pageBreakInside","pause","pauseAfter","pauseBefore","pitch","pitchRange","playDuring","position","quotes","richness","right","size","speak","speakHeader","speakNumeral","speakPunctuation","speechRate","stress","tableLayout","textAlign","textDecoration","textIndent","textShadow","textTransform","top","unicodeBidi","verticalAlign","visibility","voiceFamily","volume","whiteSpace","widows","width","wordSpacing","zIndex"].sort();var view=document.defaultView?document.defaultView.getComputedStyle(node,null):node.currentStyle;for(var i=0,len=property.length;i
"+item+"
"+d.highlight(view[item])+"
";} +el.right.html.content.update(str);break;case"dom":d.dom.open(node,el.right.html.content,lib.env.ie);break;}}}},inspector:{enabled:false,el:null,inspect:function(_element,_bgInspector){with(firebug){var pos=internal.targetWindow.firebug.lib.util.Element.getPosition(_element);internal.targetWindow.firebug.el[_bgInspector&&"bgInspector"||"borderInspector"].environment.addStyle({"width":_element.offsetWidth+"px","height":_element.offsetHeight+"px","top":pos.offsetTop-(_bgInspector?0:2)+"px","left":pos.offsetLeft-(_bgInspector?0:2)+"px","display":"block"});if(!_bgInspector){d.inspector.el=_element;}};},toggle:function(_absoluteValue,_event){with(firebug){if(_absoluteValue==d.inspector.enabled) +return;d.inspector.enabled=_absoluteValue!=undefined&&!_absoluteValue.clientX?_absoluteValue:!d.inspector.enabled;el.button.inspect.attribute[(d.inspector.enabled?"add":"remove")+"Class"]("Enabled");if(d.inspector.enabled==false){el.borderInspector.environment.addStyle({"display":"none"});d.inspector.el=null;}else if(lib.env.dIndex!="html"){if(internal.popupWin){internal.popupWin.firebug.d.navigate("html");}else{d.navigate("html");}}}}},scripts:{index:-1,lineNumbers:false,open:function(_index){with(firebug){d.scripts.index=_index;el.left.scripts.container.update("");var i=0,script=document.getElementsByTagName("script")[_index],uri=script.src||document.location.href,source;try{if(uri!=document.location.href){source=internal.cache[uri]||lib.xhr.get(uri).responseText;internal.cache[uri]=source;}else{source=script.innerHTML;} +source=source.replace(/<|>/g,function(_ch){return({"<":"<",">":">"})[_ch];});if(d.scripts.lineNumbers){source=source.replace(/(^)|\n/g,function(_ch){i++;return"\n"+i+" ";});} +el.left.scripts.container.update(source);}catch(e){el.left.scripts.container.child.add(new lib.element("DIV").attribute.addClass("CodeContainer").update("Access to restricted URI denied"));}}},toggleLineNumbers:function(){with(firebug){d.scripts.lineNumbers=!d.scripts.lineNumbers;el.button.scripts.lineNumbers.attribute[(d.scripts.lineNumbers?"add":"remove")+"Class"]("Enabled");d.scripts.open(d.scripts.index);}},refresh:function(){with(firebug){el.button.scripts.selectbox.clean();var collection=internal.targetWindow.document.getElementsByTagName("script");for(var i=0,len=collection.length;i"))}}},xhr:{objects:[],addObject:function(){with(firebug){for(var i=0,len=arguments.length;i0&&_object[0]!=undefined&&_object[len-1]!=undefined){return true;}else{for(var key in _object){if(key!="item"&&key!="length"&&key!="setNamedItemNS"&&key!="setNamedItem"&&key!="getNamedItem"&&key!="removeNamedItem"&&key!="getNamedItemNS"&&key!="removeNamedItemNS"&&key!="tags"){return false;}} +return true};},IsHash:function(_object){return _object&&typeof _object=="object"&&(_object==window||_object instanceof Object)&&!_object.nodeName&&!pi.util.IsArray(_object)},Init:[],AddEvent:function(_element,_eventName,_fn,_useCapture){_element[pi.env.ie?"attachEvent":"addEventListener"]((pi.env.ie?"on":"")+_eventName,_fn,_useCapture||false);return pi.util.Curry(pi.util.AddEvent,this,_element);},RemoveEvent:function(_element,_eventName,_fn,_useCapture){_element[pi.env.ie?"detachEvent":"removeEventListener"]((pi.env.ie?"on":"")+_eventName,_fn,_useCapture||false);return pi.util.Curry(pi.util.RemoveEvent,this,_element);},Element:{addClass:function(_element,_class){if(!pi.util.Element.hasClass(_element,_class)) +pi.util.Element.setClass(_element,pi.util.Element.getClass(_element)+" "+_class);},getClass:function(_element){return _element.getAttribute(pi.env.ie&&!pi.env.ie8?"className":"class")||"";},hasClass:function(_element,_class){return pi.util.Array.indexOf(pi.util.Element.getClass(_element).split(" "),_class)>-1;},removeClass:function(_element,_class){if(pi.util.Element.hasClass(_element,_class)){var names=pi.util.Element.getClass(_element,_class).split(" ");pi.util.Element.setClass(_element,pi.util.Array.remove(names,pi.util.Array.indexOf(names,_class)).join(" "));}},setClass:function(_element,_value){if(pi.env.ie8){_element.setAttribute("className",_value);_element.setAttribute("class",_value);}else{_element.setAttribute(pi.env.ie?"className":"class",_value);}},toggleClass:function(){if(pi.util.Element.hasClass.apply(this,arguments)) +pi.util.Element.removeClass.apply(this,arguments);else +pi.util.Element.addClass.apply(this,arguments);},getOpacity:function(_styleObject){var styleObject=_styleObject;if(!pi.env.ie) +return styleObject["opacity"];var alpha=styleObject["filter"].match(/opacity\=(\d+)/i);return alpha?alpha[1]/100:1;},setOpacity:function(_element,_value){if(!pi.env.ie) +return pi.util.Element.addStyle(_element,{"opacity":_value});_value*=100;pi.util.Element.addStyle(_element,{"filter":"alpha(opacity="+_value+")"});return this._parent_;},getPosition:function(_element){var parent=_element,offsetLeft=document.body.offsetLeft,offsetTop=document.body.offsetTop,view=pi.util.Element.getView(_element);while(parent&&parent!=document.body&&parent!=document.firstChild){offsetLeft+=parseInt(parent.offsetLeft);offsetTop+=parseInt(parent.offsetTop);parent=parent.offsetParent;};return{"bottom":view["bottom"],"clientLeft":_element.clientLeft,"clientTop":_element.clientTop,"left":view["left"],"marginTop":view["marginTop"],"marginLeft":view["marginLeft"],"offsetLeft":offsetLeft,"offsetTop":offsetTop,"position":view["position"],"right":view["right"],"top":view["top"],"zIndex":view["zIndex"]};},getSize:function(_element){var view=pi.util.Element.getView(_element);return{"height":view["height"],"clientHeight":_element.clientHeight,"clientWidth":_element.clientWidth,"offsetHeight":_element.offsetHeight,"offsetWidth":_element.offsetWidth,"width":view["width"]}},addStyle:function(_element,_style){for(var key in _style){key=key=="float"?pi.env.ie?"styleFloat":"cssFloat":key;if(key=="opacity"&&pi.env.ie){pi.util.Element.setOpacity(_element,_style[key]);continue;} +try{_element.style[key]=_style[key];}catch(e){}}},getStyle:function(_element,_property){_property=_property=="float"?pi.env.ie?"styleFloat":"cssFloat":_property;if(_property=="opacity"&&pi.env.ie) +return pi.util.Element.getOpacity(_element.style);return typeof _property=="string"?_element.style[_property]:_element.style;},getValue:function(_element){switch(_element.nodeName.toLowerCase()){case"input":case"textarea":return _element.value;case"select":return _element.options[_element.selectedIndex].value;default:return _element.innerHTML;break;}},getView:function(_element,_property){var view=document.defaultView?document.defaultView.getComputedStyle(_element,null):_element.currentStyle;_property=_property=="float"?pi.env.ie?"styleFloat":"cssFloat":_property;if(_property=="opacity"&&pi.env.ie) +return pi.util.Element.getOpacity(_element,view);return typeof _property=="string"?view[_property]:view;}},Hash:{clone:function(_hash,_undeep){var tmp={};for(var key in _hash){if(!_undeep&&pi.util.IsArray(_hash[key])){tmp[key]=pi.util.Array.clone(_hash[key]);}else if(!_undeep&&pi.util.IsHash(_hash[key])){tmp[key]=pi.util.Hash.clone(_hash[key]);}else{tmp[key]=_hash[key];}} +return tmp;},merge:function(_hash,_source,_undeep){for(var key in _source){var value=_source[key];if(!_undeep&&pi.util.IsArray(_source[key])){if(pi.util.IsArray(_hash[key])){Array.prototype.push.apply(_source[key],_hash[key])} +else +value=pi.util.Array.clone(_source[key]);} +else if(!_undeep&&pi.util.IsHash(_source[key])){if(pi.util.IsHash(_hash[key])){value=pi.util.Hash.merge(_hash[key],_source[key]);}else{value=pi.util.Hash.clone(_source[key]);}}else if(_hash[key]) +value=_hash[key];_hash[key]=value;};return _hash;}},String:{format:function(_str){var values=Array.prototype.slice.call(arguments,1);return _str.replace(/\{(\d)\}/g,function(){return values[arguments[1]];})}},GetViewport:function(){return{height:document.documentElement.clientHeight||document.body.clientHeight,width:document.documentElement.clientWidth||document.body.clientWidth}}};pi.base=function(){this.body={};this.init=null;this.build=function(_skipClonning){var base=this,skipClonning=_skipClonning||false,_private={},fn=function(){var _p=pi.util.Hash.clone(_private);if(!skipClonning){for(var key in this){if(pi.util.IsArray(this[key])){this[key]=pi.util.Array.clone(this[key]);}else +if(pi.util.IsHash(this[key])){this[key]=pi.util.Hash.clone(this[key],function(_key,_object){this[_key]._parent_=this;});}}};base.createAccessors(_p,this);if(base.init) +return base.init.apply(this,arguments);return this;};this.movePrivateMembers(this.body,_private);if(this.init){fn["$Init"]=this.init;};fn.prototype=this.body;return fn;};this.createAccessors=function(_p,_branch){var getter=function(_property){return this[_property];},setter=function(_property,_value){this[_property]=_value;return _branch._parent_||_branch;};for(var name in _p){var isPrivate=name.substring(0,1)=="_",title=name.substring(1,2).toUpperCase()+name.substring(2);if(isPrivate){_branch[(_branch["get"+title]?"_":"")+"get"+title]=pi.util.Curry(getter,_p,name);_branch[(_branch["set"+title]?"_":"")+"set"+title]=pi.util.Curry(setter,_p,name);} +else +if(pi.util.IsHash(_p[name])){_branch[name]._parent_=_branch;if(!_branch[name]) +_branch[name]={};this.createAccessors(_p[name],_branch[name]);}};};this.movePrivateMembers=function(_object,_branch){for(var name in _object){var isPrivate=name.substring(0,1)=="_";if(isPrivate){_branch[name]=_object[name];delete _object[name];} +else +if(pi.util.IsHash(_object[name])){_branch[name]={};this.movePrivateMembers(_object[name],_branch[name]);}};};};pi.element=new pi.base;pi.element.init=function(_val){this.environment.setElement(typeof _val=="string"||!_val?document.createElement(_val||"DIV"):_val);return this;};pi.element.body={"addStyle":function(){return this.environment.addStyle.apply(this.environment,arguments);},"clean":function(){var childs=this.child.get();while(childs.length){childs[0].parentNode.removeChild(childs[0]);}},"clone":function(_deep){return this.environment.getElement().cloneNode(_deep);},"insert":function(_element){_element=_element.environment?_element.environment.getElement():_element;_element.appendChild(this.environment.getElement());return this;},"insertAfter":function(_referenceElement){_referenceElement=_referenceElement.environment?_referenceElement.environment.getElement():_referenceElement;_referenceElement.nextSibling?this.insertBefore(_referenceElement.nextSibling):this.insert(_referenceElement.parentNode);return this;},"insertBefore":function(_referenceElement){_referenceElement=_referenceElement.environment?_referenceElement.environment.getElement():_referenceElement;_referenceElement.parentNode.insertBefore(this.environment.getElement(),_referenceElement);return this;},"query":function(_expression,_resultType,namespaceResolver,_result){return pi.xpath(_expression,_resultType||"ORDERED_NODE_SNAPSHOT_TYPE",this.environment.getElement(),_namespaceResolver,_result);},"remove":function(){if(this.environment.getParent()){this.environment.getParent().removeChild(this.environment.getElement());}},"update":function(_value){this.element[this.element.nodeName.toLowerCase()=="textarea"||this.element.nodeName.toLowerCase()=="input"?"value":"innerHTML"]=_value;return this;},"attribute":{"getAll":function(){return this._parent_.environment.getElement().attributes;},"clear":function(_name){this.set(_name,"");return this._parent_;},"get":function(_name){return this._parent_.environment.getElement().getAttribute(_name);},"has":function(_name){return pi.env.ie?(this.get(_name)!=null):this._parent_.environment.getElement().hasAttribute(_name);},"remove":function(_name){this._parent_.environment.getElement().removeAttribute(_name);return this._parent_;},"set":function(_name,_value){this._parent_.environment.getElement().setAttribute(_name,_value);return this._parent_;},"addClass":function(_classes){for(var i=0,len=arguments.length;i-1){callback[i].fn.apply(this);}}}};pi.xhr=pi.xhr.build();pi.xhr.get=function(_url,_returnPiObject){var request=new pi.xhr();request.environment.setAsync(false);request.environment.setUrl(_url);request.send();return _returnPiObject?request:request.environment.getApi();};pi.util.AddEvent(pi.env.ie?window:document,pi.env.ie?"load":"DOMContentLoaded",function(){for(var i=0,len=pi.util.Init.length;i=0;i--){var script=scriptsIncluded[i],src=getFileName(script.src);if(src){internal.liteFilename=src;break;}} +initConsole();lib.util.Init.push(firebug.init);}})(); \ No newline at end of file diff --git a/libs/jquery.min.js b/libs/jquery.min.js new file mode 100644 index 0000000..b1ae21d --- /dev/null +++ b/libs/jquery.min.js @@ -0,0 +1,19 @@ +/* + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.3 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/libs/json2.js b/libs/json2.js new file mode 100644 index 0000000..c422a6e --- /dev/null +++ b/libs/json2.js @@ -0,0 +1,479 @@ +/* + http://www.JSON.org/json2.js + 2009-09-29 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. +*/ + +/*jslint evil: true, strict: false */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (!this.JSON) { + this.JSON = {}; +} + +(function () { + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? + '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : + '"' + string + '"'; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : + gap ? '[\n' + gap + + partial.join(',\n' + gap) + '\n' + + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + k = rep[i]; + if (typeof k === 'string') { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : + gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + + mind + '}' : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with '()' and 'new' +// because they can cause invocation, and '=' because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we +// replace all simple value tokens with ']' characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or ']' or +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/. +test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). +replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). +replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + } +}()); diff --git a/src/ntree.js b/src/ntree.js new file mode 100644 index 0000000..d536d91 --- /dev/null +++ b/src/ntree.js @@ -0,0 +1,613 @@ +/****************************************************************************** + rtree.js - General-Purpose Non-Recursive Javascript R-Tree Library + Version 0.6.2, December 5st 2009 + + Copyright (c) 2009 Jon-Carlos Rivera + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jon-Carlos Rivera - imbcmdth@hotmail.com +******************************************************************************/ +var isArray = function(o) { + return Object.prototype.toString.call(o) === '[object Array]'; +}; + +/** + * RTree - A simple r-tree structure for great results. + * @constructor + */ +var NTree = function(dimensions, width){ + // Variables to control tree-dimensions + var _Min_Width = 3; // Minimum width of any node before a merge + var _Max_Width = 6; // Maximum width of any node before a split + var _Dimensions = 2; // Number of intervals per node + if(!isNaN(dimensions)){ _Dimensions = dimensions;} + if(!isNaN(width)){ _Min_Width = Math.floor(width/2.0); _Max_Width = width;} + // Start with an empty root-tree + + var _intervals = NTree.Interval.make_Empty(_Dimensions); + var _T = {d:_intervals, id:"root", nodes:[] }; + + /* @function + * @description Function to generate unique strings for element IDs + * @param {String} n The prefix to use for the IDs generated. + * @return {String} A guarenteed unique ID. + */ + var _name_to_id = (function() { + // hide our idCache inside this closure + var idCache = {}; + + // return the api: our function that returns a unique string with incrementing number appended to given idPrefix + return function(idPrefix) { + var idVal = 0; + if(idPrefix in idCache) { + idVal = idCache[idPrefix]++; + } else { + idCache[idPrefix] = 0; + } + return idPrefix + "_" + idVal; + } + })(); + + // This is my special addition to the world of r-trees + // every other (simple) method I found produced crap trees + // this skews insertions to prefering squarer and emptier nodes + NTree.Interval.jons_ratio = function(dimensions, intervals, count) { + // Area of new enlarged rectangle + var i, sum = 0, mul = 1; + for( i = 0; i < dimensions; i++ ) + { + sum += intervals[i].b; + mul *= intervals[i].b; + } + sum /= dimensions; + var lgeo = mul / Math.pow(sum, dimensions); + + // return the ratio of the perimeter to the area - the closer to 1 we are, + // the more "square" or "cubic" a volume is. conversly, when approaching zero the + // more elongated a rectangle is + + return(mul * count / lgeo); + }; + + /* find the best specific node(s) for object to be deleted from + * [ leaf node parent ] = _remove_subtree(rectangle, object, root) + * @private + */ + var _remove_subtree = function(intervals, obj, root) { + var hit_stack = []; // Contains the elements that overlap + var count_stack = []; // Contains the elements that overlap + var ret_array = []; + var current_depth = 1; + + if(!intervals || !NTree.Interval.overlap_intervals(intervals, root.d)) + return ret_array; + + var ret_obj = {d:NTree.Interval.make_Intervals(_Dimensions, intervals), target:obj}; + + count_stack.push(root.nodes.length); + hit_stack.push(root); + + do { + var tree = hit_stack.pop(); + var i = count_stack.pop()-1; + + if("target" in ret_obj) { // We are searching for a target + while(i >= 0) { + var ltree = tree.nodes[i]; + if(NTree.Interval.overlap_intervals(ret_obj.d, ltree.d)) { + if( (ret_obj.target && "leaf" in ltree && ltree.leaf === ret_obj.target) + ||(!ret_obj.target && ("leaf" in ltree || NTree.Interval.contains_intervals(ltree.d, ret_obj.d)))) { // A Match !! + // Yup we found a match... + // we can cancel search and start walking up the list + if("nodes" in ltree) {// If we are deleting a node not a leaf... + ret_array = _search_subtree(ltree.d, true, [], ltree); + tree.nodes.splice(i, 1); + } else { + ret_array = tree.nodes.splice(i, 1); + } + // Resize MBR down... + NTree.Interval.make_MBV(_Dimensions, tree.nodes, tree.d); + delete ret_obj.target; + if(tree.nodes.length < _Min_Width) { // Underflow + ret_obj.nodes = _search_subtree(tree.d, true, [], tree); + } + break; + }/* else if("load" in ltree) { // A load + }*/ else if("nodes" in ltree) { // Not a Leaf + current_depth += 1; + count_stack.push(i); + hit_stack.push(tree); + tree = ltree; + i = ltree.nodes.length; + } + } + i -= 1; + } + } else if("nodes" in ret_obj) { // We are unsplitting + tree.nodes.splice(i+1, 1); // Remove unsplit node + // ret_obj.nodes contains a list of elements removed from the tree so far + if(tree.nodes.length > 0) + NTree.Interval.make_MBV(_Dimensions, tree.nodes, tree.d); + for(var t = 0;t 0 && tree.nodes.length < _Min_Width) { // Underflow..AGAIN! + ret_obj.nodes = _search_subtree(tree.d, true, ret_obj.nodes, tree); + tree.nodes.length = 0; + }else { + delete ret_obj.nodes; // Just start resizing + } + } else { // we are just resizing + NTree.Interval.make_MBV(_Dimensions, tree.nodes, tree.d); + } + current_depth -= 1; + }while(hit_stack.length > 0); + + return(ret_array); + }; + + /* choose the best damn node for rectangle to be inserted into + * [ leaf node parent ] = _choose_leaf_subtree(rectangle, root to start search at) + * @private + */ + var _choose_leaf_subtree = function(intervals, root) { + var best_choice_index = -1; + var best_choice_stack = []; + var best_choice_area; + + var load_callback = function(local_tree, local_node){ + return(function(data) { + local_tree._attach_data(local_node, data); + }); + }; + + best_choice_stack.push(root); + var nodes = root.nodes; + + do { + if(best_choice_index != -1) { + best_choice_stack.push(nodes[best_choice_index]); + nodes = nodes[best_choice_index].nodes; + best_choice_index = -1; + } + + for(var i = nodes.length-1; i >= 0; i--) { + var ltree = nodes[i]; + if("leaf" in ltree) { + // Bail out of everything and start inserting + best_choice_index = -1; + break; + } /*else if(ltree.load) { + throw( "Can't insert into partially loaded tree ... yet!"); + //jQuery.getJSON(ltree.load, load_callback(this, ltree)); + //delete ltree.load; + }*/ + // Area of new enlarged rectangle + var old_lratio = NTree.Interval.jons_ratio(_Dimensions, ltree.d, ltree.nodes.length+1); + + var copy_of_intervals = NTree.Interval.make_Intervals(_Dimensions, ltree.d); + NTree.Interval.expand_intervals(copy_of_intervals, intervals); + + // Area of new enlarged rectangle + var lratio = NTree.Interval.jons_ratio(_Dimensions, copy_of_intervals, ltree.nodes.length+2); + + if(best_choice_index < 0 || Math.abs(lratio - old_lratio) < best_choice_area) { + best_choice_area = Math.abs(lratio - old_lratio); best_choice_index = i; + } + } + }while(best_choice_index != -1); + + return(best_choice_stack); + }; + + /* split a set of nodes into two roughly equally-filled nodes + * [ an array of two new arrays of nodes ] = linear_split(array of nodes) + * @private + */ + var _linear_split = function(nodes) { + var n = _pick_linear(nodes); + while(nodes.length > 0) { + _pick_next(nodes, n[0], n[1]); + } + return(n); + }; + + /* insert the best source rectangle into the best fitting parent node: a or b + * [] = pick_next(array of source nodes, target node array a, target node array b) + * @private + */ + var _pick_next = function(nodes, a, b) { + // Area of new enlarged rectangle + var area_a = NTree.Interval.jons_ratio(_Dimensions, a.d, a.nodes.length+1); + var area_b = NTree.Interval.jons_ratio(_Dimensions, b.d, b.nodes.length+1); + var high_area_delta; + var high_area_node; + var lowest_growth_group; + + for(var i = nodes.length-1; i>=0;i--) { + var l = nodes[i]; + + var copy_of_intervals = NTree.Interval.make_Intervals(_Dimensions, a.d); + NTree.Interval.expand_intervals(copy_of_intervals, l.d); + var change_new_area_a = Math.abs(NTree.Interval.jons_ratio(_Dimensions, copy_of_intervals, a.nodes.length+2) - area_a); + + copy_of_intervals = NTree.Interval.make_Intervals(_Dimensions, b.d); + NTree.Interval.expand_intervals(copy_of_intervals, l.d); + var change_new_area_b = Math.abs(NTree.Interval.jons_ratio(_Dimensions, copy_of_intervals, b.nodes.length+2) - area_b); + + if( !high_area_node || !high_area_delta || Math.abs( change_new_area_b - change_new_area_a ) < high_area_delta ) { + high_area_node = i; + high_area_delta = Math.abs(change_new_area_b-change_new_area_a); + lowest_growth_group = change_new_area_b < change_new_area_a ? b : a; + } + } + var temp_node = nodes.splice(high_area_node, 1)[0]; + if(a.nodes.length + nodes.length + 1 <= _Min_Width) { + a.nodes.push(temp_node); + NTree.Interval.expand_intervals(a.d, temp_node.d); + } else if(b.nodes.length + nodes.length + 1 <= _Min_Width) { + b.nodes.push(temp_node); + NTree.Interval.expand_intervals(b.d, temp_node.d); + } + else { + lowest_growth_group.nodes.push(temp_node); + NTree.Interval.expand_intervals(lowest_growth_group.d, temp_node.d); + } + }; + + /* pick the "best" two starter nodes to use as seeds using the "linear" criteria + * [ an array of two new arrays of nodes ] = pick_linear(array of source nodes) + * @private + */ + var _pick_linear = function(nodes) { + var lowest_high = new Array(_Dimensions); + for(i = 0; i < _Dimensions;i++) lowest_high[i] = nodes.length-1; + var highest_low = new Array(_Dimensions); + for(i = 0; i < _Dimensions;i++) highest_low[i] = 0; + var t1, t2, l, i, d; + + for(i = nodes.length-2; i>=0;i--) { + l = nodes[i]; + for(d = 0; d < _Dimensions;d++) { + if(l.d[d].a > nodes[highest_low[d]].d[d].a ) highest_low[d] = i; + else if(l.d[d].a+l.d[d].b < nodes[lowest_high[d]].d[d].a+nodes[lowest_high[d]].d[d].b) lowest_high[d] = i; + } + } + + d = 0; + var difference, last_difference = 0; + for(i = 0; i < _Dimensions;i++) { + difference = Math.abs((nodes[lowest_high[i]].d[i].a+nodes[lowest_high[i]].d[i].b) - nodes[highest_low[i]].d[i].a); + if(difference > last_difference) + { + d = i; + last_difference = difference; + } + } + + if(lowest_high[d] > highest_low[d]) { + t1 = nodes.splice(lowest_high[d], 1)[0]; + t2 = nodes.splice(highest_low[d], 1)[0]; + } else { + t2 = nodes.splice(highest_low[d], 1)[0]; + t1 = nodes.splice(lowest_high[d], 1)[0]; + } + + return([{d:NTree.Interval.make_Intervals(_Dimensions, t1.d), nodes:[t1]}, + {d:NTree.Interval.make_Intervals(_Dimensions, t2.d), nodes:[t2]} ]); + }; + + var _attach_data = function(node, more_tree){ + node.nodes = more_tree.nodes; + node.x = more_tree.x; node.y = more_tree.y; + node.w = more_tree.w; node.h = more_tree.h; + return(node); + }; + + /* non-recursive internal insert function + * [] = _insert_subtree(rectangle, object to insert, root to begin insertion at) + * @private + */ + var _insert_subtree = function(node, root) { + var bc; // Best Current node + // Initial insertion is special because we resize the Tree and we don't + // care about any overflow (seriously, how can the first object overflow?) + if(root.nodes.length == 0) { + NTree.Interval.make_Intervals(_Dimensions, node.d, root.d); + root.nodes.push(node); + return; + } + + // Find the best fitting leaf node + // choose_leaf returns an array of all tree levels (including root) + // that were traversed while trying to find the leaf + var tree_stack = _choose_leaf_subtree(node.d, root); + var ret_obj = node;//{x:rect.x,y:rect.y,w:rect.w,h:rect.h, leaf:obj}; + + // Walk back up the tree resizing and inserting as needed + do { + //handle the case of an empty node (from a split) + if(bc && "nodes" in bc && bc.nodes.length == 0) { + var pbc = bc; // Past bc + bc = tree_stack.pop(); + for(var t=0;t 0); + }; + + /* non-recursive internal search function + * [ nodes | objects ] = _search_subtree(intervals, [return node data], [array to fill], root to begin search at) + * @private + */ + var _search_subtree = function(intervals, return_node, return_array, root) { + var hit_stack = []; // Contains the elements that overlap + + if(!NTree.Interval.overlap_intervals(intervals, root.d)) + return(return_array); + + var load_callback = function(local_tree, local_node){ + return(function(data) { + local_tree._attach_data(local_node, data); + }); + }; + + hit_stack.push(root.nodes); + + do { + var nodes = hit_stack.pop(); + + for(var i = nodes.length-1; i >= 0; i--) { + var ltree = nodes[i]; + if(NTree.Interval.overlap_intervals(intervals, ltree.d)) { + if("nodes" in ltree) { // Not a Leaf + hit_stack.push(ltree.nodes); + } else if("leaf" in ltree) { // A Leaf !! + if(!return_node) + return_array.push(ltree.leaf); + else + return_array.push(ltree); + }/* else if("load" in ltree) { // We need to fetch a URL for some more tree data + jQuery.getJSON(ltree.load, load_callback(this, ltree)); + delete ltree.load; + // i++; // Replay this entry + }*/ + } + } + }while(hit_stack.length > 0); + + return(return_array); + }; + + /* quick 'n' dirty function for plugins or manually drawing the tree + * [ tree ] = RTree.get_tree(): returns the raw tree data. useful for adding + * @public + * !! DEPRECATED !! + */ + this.get_tree = function() { + return _T; + }; + + /* quick 'n' dirty function for plugins or manually loading the tree + * [ tree ] = RTree.set_tree(sub-tree, where to attach): returns the raw tree data. useful for adding + * @public + * !! DEPRECATED !! + */ + this.set_tree = function(new_tree, where) { + if(!where) + where = _T; + return(_attach_data(where, new_tree)); + }; + + /* non-recursive search function + * [ nodes | objects ] = NTree.search(intervals, [return node data], [array to fill]) + * @public + */ + this.search = function(intervals, return_node, return_array) { + if(arguments.length < 1) + throw "Wrong number of arguments. NT.Search requires at least a bounding set of intervals." + + switch(arguments.length) { + case 1: + arguments[1] = false;// Add an "return node" flag - may be removed in future + case 2: + arguments[2] = []; // Add an empty array to contain results + case 3: + arguments[3] = _T; // Add root node to end of argument list + default: + arguments.length = 4; + } + return(_search_subtree.apply(this, arguments)); + }; + + + /* non-recursive insert function + * [] = NTree.insert(intervals, object to insert) + */ + this.insert = function(intervals, obj) { + if(arguments.length < 2) + throw "Wrong number of arguments. RT.Insert requires at least a bounding rectangle and an object." + + return(_insert_subtree({d:NTree.Interval.make_Intervals(_Dimensions, intervals), leaf:obj}, _T)); + }; + + /* non-recursive function that deletes a specific + * [ number ] = NTree.remove(intervals, obj) + */ + this.remove = function(intervals, obj) { + if(arguments.length < 1) + throw "Wrong number of arguments. RT.remove requires at least a bounding rectangle." + + switch(arguments.length) { + case 1: + arguments[1] = false; // obj == false for conditionals + case 2: + arguments[2] = _T; // Add root node to end of argument list + default: + arguments.length = 3; + } + if(arguments[1] === false) { // Do area-wide delete + var numberdeleted = 0; + var ret_array = []; + do { + numberdeleted=ret_array.length; + ret_array = ret_array.concat(_remove_subtree.apply(this, arguments)); + }while( numberdeleted != ret_array.length); + return ret_array; + } + else { // Delete a specific item + return(_remove_subtree.apply(this, arguments)); + } + }; + +}; + +NTree.Interval = {}; + +/* returns true if intervals "a" overlaps intervals "b" + * [ boolean ] = overlap_intervals(intervals a, intervals b) + * @static function + */ +NTree.Interval.overlap_intervals = function(a, b) { + var d = a.length; + var i, ret_val = true; + if( b.length != d ) ret_val = false; // Should probably be an error. + for( i = 0; i < d; i++ ) + { + ret_val = ret_val && (a[i].a < (b[i].a+b[i].b) && (a[i].a+a[i].b) > b[i].a); + } + return ret_val; +}; + +/* returns true if intervals "a" overlaps intervals "b" + * [ boolean ] = contains_intervals(intervals a, intervals b) + * @static function + */ +NTree.Interval.contains_intervals = function(a, b) { + var d = a.length; + var i, ret_val = true; + if( b.length != d ) ret_val = false; // Should probably be an error. + for( i = 0; i < d; i++ ) + { + ret_val = ret_val && ((a[i].a+a[i].b) <= (b[i].a+b[i].b) && a[i].a >= b[i].a); + } + return ret_val; +}; + +/* expands intervals A to include intervals B, intervals B is untouched + * [ rectangle a ] = expand_rectangle(rectangle a, rectangle b) + * @static function + */ +NTree.Interval.expand_intervals = function(a, b) { + var d = a.length; + var i, n; + if( b.length != d ) return false; // Should probably be an error. + for( i = 0; i < d; i++ ) + { + n = Math.min(a[i].a, b[i].a); + a[i].b = Math.max(a[i].a+a[i].b, b[i].a+b[i].b) - n; + a[i].a = n; + } + return a; +}; + +/* generates a minimally bounding intervals for all intervals in + * array "nodes". If intervals is set, it is modified into the MBR. Otherwise, + * a new set of intervals is generated and returned. + * [ rectangle a ] = make_MBR(rectangle array nodes, rectangle rect) + * @static function + */ +NTree.Interval.make_MBV = function(dimensions, nodes, intervals) { + var d; + if(nodes.length < 1) + { + //throw "make_MBR: nodes must contain at least one rectangle!"; + return NTree.Interval.make_Empty(dimensions); + } + + if(!intervals) + intervals = NTree.Interval.make_Intervals(dimensions, nodes[0].d); + else + NTree.Interval.make_Intervals(dimensions, nodes[0].d, intervals); + + for(var i = nodes.length-1; i > 0; i--) + NTree.Interval.expand_intervals(intervals, nodes[i].d); + + return(intervals); +}; + +NTree.Interval.make_Empty = function(dimensions) { + var i, d = new Array(dimensions); + for( i = 0; i < dimensions; i++ ) + d[i] = {a:0, b:0}; + return d; +}; + +NTree.Interval.make_Intervals = function(dimensions, intervals, d) { + var i; + if(!isArray(d)) + d = new Array(dimensions); + for( i = 0; i < dimensions; i++ ) + d[i] = {a:intervals[i].a, b:intervals[i].b}; + return d; +}; \ No newline at end of file diff --git a/tests/unit.html b/tests/unit.html new file mode 100644 index 0000000..15aa79c --- /dev/null +++ b/tests/unit.html @@ -0,0 +1,70 @@ + + + + +JSSpec results + + + + + + + +