").append(x.parseHTML(e)).find(r):e)}).complete(n&&function(e,t){s.each(n,o||[e.responseText,t,e])}),this},x.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){x.fn[t]=function(e){return this.on(t,e)}}),x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Xt,type:"GET",isLocal:Qt.test(_t[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":on,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":x.parseJSON,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?ln(ln(e,x.ajaxSettings),t):ln(x.ajaxSettings,e)},ajaxPrefilter:an(nn),ajaxTransport:an(rn),ajax:function(e,t){"object"==typeof e&&(t=e,e=undefined),t=t||{};var n,r,i,o,s,a,u,l,c=x.ajaxSetup({},t),f=c.context||c,p=c.context&&(f.nodeType||f.jquery)?x(f):x.event,h=x.Deferred(),d=x.Callbacks("once memory"),g=c.statusCode||{},m={},y={},v=0,b="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===v){if(!o){o={};while(t=Jt.exec(i))o[t[1].toLowerCase()]=t[2]}t=o[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===v?i:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return v||(e=y[n]=y[n]||e,m[e]=t),this},overrideMimeType:function(e){return v||(c.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>v)for(t in e)g[t]=[g[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||b;return n&&n.abort(t),k(0,t),this}};if(h.promise(T).complete=d.add,T.success=T.done,T.error=T.fail,c.url=((e||c.url||Xt)+"").replace(Vt,"").replace(Zt,_t[1]+"//"),c.type=t.method||t.type||c.method||c.type,c.dataTypes=x.trim(c.dataType||"*").toLowerCase().match(w)||[""],null==c.crossDomain&&(a=en.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===_t[1]&&a[2]===_t[2]&&(a[3]||("http:"===a[1]?"80":"443"))===(_t[3]||("http:"===_t[1]?"80":"443")))),c.data&&c.processData&&"string"!=typeof c.data&&(c.data=x.param(c.data,c.traditional)),un(nn,c,t,T),2===v)return T;u=c.global,u&&0===x.active++&&x.event.trigger("ajaxStart"),c.type=c.type.toUpperCase(),c.hasContent=!Kt.test(c.type),r=c.url,c.hasContent||(c.data&&(r=c.url+=(Yt.test(r)?"&":"?")+c.data,delete c.data),c.cache===!1&&(c.url=Gt.test(r)?r.replace(Gt,"$1_="+Ut++):r+(Yt.test(r)?"&":"?")+"_="+Ut++)),c.ifModified&&(x.lastModified[r]&&T.setRequestHeader("If-Modified-Since",x.lastModified[r]),x.etag[r]&&T.setRequestHeader("If-None-Match",x.etag[r])),(c.data&&c.hasContent&&c.contentType!==!1||t.contentType)&&T.setRequestHeader("Content-Type",c.contentType),T.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+("*"!==c.dataTypes[0]?", "+on+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)T.setRequestHeader(l,c.headers[l]);if(c.beforeSend&&(c.beforeSend.call(f,T,c)===!1||2===v))return T.abort();b="abort";for(l in{success:1,error:1,complete:1})T[l](c[l]);if(n=un(rn,c,t,T)){T.readyState=1,u&&p.trigger("ajaxSend",[T,c]),c.async&&c.timeout>0&&(s=setTimeout(function(){T.abort("timeout")},c.timeout));try{v=1,n.send(m,k)}catch(C){if(!(2>v))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,t,o,a){var l,m,y,b,w,C=t;2!==v&&(v=2,s&&clearTimeout(s),n=undefined,i=a||"",T.readyState=e>0?4:0,l=e>=200&&300>e||304===e,o&&(b=cn(c,T,o)),b=fn(c,b,T,l),l?(c.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(x.lastModified[r]=w),w=T.getResponseHeader("etag"),w&&(x.etag[r]=w)),204===e?C="nocontent":304===e?C="notmodified":(C=b.state,m=b.data,y=b.error,l=!y)):(y=C,(e||!C)&&(C="error",0>e&&(e=0))),T.status=e,T.statusText=(t||C)+"",l?h.resolveWith(f,[m,C,T]):h.rejectWith(f,[T,C,y]),T.statusCode(g),g=undefined,u&&p.trigger(l?"ajaxSuccess":"ajaxError",[T,c,l?m:y]),d.fireWith(f,[T,C]),u&&(p.trigger("ajaxComplete",[T,c]),--x.active||x.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,t){return x.get(e,undefined,t,"script")}}),x.each(["get","post"],function(e,t){x[t]=function(e,n,r,i){return x.isFunction(n)&&(i=i||r,r=n,n=undefined),x.ajax({url:e,type:t,dataType:i,data:n,success:r})}});function cn(e,t,n){var r,i,o,s,a=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),r===undefined&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in a)if(a[i]&&a[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}s||(s=i)}o=o||s}return o?(o!==u[0]&&u.unshift(o),n[o]):undefined}function fn(e,t,n,r){var i,o,s,a,u,l={},c=e.dataTypes.slice();if(c[1])for(s in e.converters)l[s.toLowerCase()]=e.converters[s];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(s=l[u+" "+o]||l["* "+o],!s)for(i in l)if(a=i.split(" "),a[1]===o&&(s=l[u+" "+a[0]]||l["* "+a[0]])){s===!0?s=l[i]:l[i]!==!0&&(o=a[0],c.unshift(a[1]));break}if(s!==!0)if(s&&e["throws"])t=s(t);else try{t=s(t)}catch(f){return{state:"parsererror",error:s?f:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",function(e){e.cache===undefined&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),x.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,i){t=x("
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Add element to root
+
Add element Ajax
+
JavaScript dialect engines
+
+
+
+
+
+
+
jQuery(document).ready(function ($) {
+
+ var rowStr = "<ul>";
+
+ var wikipediaJsCategory = {
+ category: 'JavaScript',
+ subcategories: [
+ {category: 'Ajax (programming)'},
+ {category: 'JavaScript engines'},
+ {category: 'JavaScript programming languages family',
+ subcategories: [{
+ category: 'JavaScript dialect engines'
+ }]
+ },
+ {category: 'JavaScript based calendar components'},
+ {category: 'JavaScript based HTML editors'}
+ ]
+ };
+
+
+ var tree = Arboreal.parse(wikipediaJsCategory, 'subcategories');
+
+
+ function iterator(node) {
+ if (node.children.length > 0)
+ {
+ rowStr += '';
+ rowStr += '<li class="jstree-open">' + node.data.category + '<ul>';
+ } else
+ {
+ rowStr += '<li>' + node.data.category + '</li>';
+ }
+ }
+
+ function iteratorAfter(node) {
+ if (node.children.length > 0)
+ {
+
+ rowStr += '</ul></li>';
+ }
+ }
+
+ tree.traverseDown(iterator, iteratorAfter);
+
+ rowStr += "</ul>";
+
+ $("#container").html(rowStr);
+
+ $('#container').jstree();
+});
+
+
\ No newline at end of file
diff --git a/docs/examples/style.css b/docs/examples/style.css
new file mode 100644
index 0000000..67cd156
--- /dev/null
+++ b/docs/examples/style.css
@@ -0,0 +1,7 @@
+.codeblock
+{
+ margin : 10px;
+ border : 1px #000 solid;
+ max-height: 350px;
+ overflow: scroll;
+}
diff --git a/docs/lib/arboreal.min.js b/docs/lib/arboreal.min.js
new file mode 100644
index 0000000..1be2f3b
--- /dev/null
+++ b/docs/lib/arboreal.min.js
@@ -0,0 +1,5 @@
+/*! Arboreal.js - v0.0.1 - 2017-04-12
+* https://github.com/vasiliyaltunin/arboreal.js
+* Copyright (c) 2017 Vasiliy Altunin (skyr@altunin.online); Licensed MIT; See MIT-LICENSE.txt for more info*/
+
+!function(){function a(a,b){for(var c=0,d=a.length;c
-1}function c(a,b,c){var d=!0;!function a(e){var f,g;if(d)if(!1===b(e))d=!1;else for(f=0;fe?f.children[e]:null;return f},i.prototype.toArray=function(){var a=[];return this.traverseDown(function(b){a.push(b)}),a},i.prototype.root=function(){var a=this;if(!a.parent)return this;for(;a.parent;)a=a.parent;return a},i.prototype.isRoot=function(){return!this.parent},Object.defineProperty(i.prototype,"length",{get:function(){return this.toArray().length}}),"undefined"!=typeof module&&module.exports?module.exports=i:this.Arboreal=i}();
\ No newline at end of file
diff --git a/examples/index.html b/examples/index.html
new file mode 100644
index 0000000..995cda5
--- /dev/null
+++ b/examples/index.html
@@ -0,0 +1,194 @@
+
+
+
+ Arboreal tree list demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Add element to root
+ Add element Ajax
+ JavaScript dialect engines
+
+
+
+
+
+
+ jQuery(document).ready(function ($) {
+
+ var rowStr = "<ul>";
+
+ var wikipediaJsCategory = {
+ category: 'JavaScript',
+ subcategories: [
+ {category: 'Ajax (programming)'},
+ {category: 'JavaScript engines'},
+ {category: 'JavaScript programming languages family',
+ subcategories: [{
+ category: 'JavaScript dialect engines'
+ }]
+ },
+ {category: 'JavaScript based calendar components'},
+ {category: 'JavaScript based HTML editors'}
+ ]
+ };
+
+
+ var tree = Arboreal.parse(wikipediaJsCategory, 'subcategories');
+
+
+ function iterator(node) {
+ if (node.children.length > 0)
+ {
+ rowStr += '';
+ rowStr += '<li class="jstree-open">' + node.data.category + '<ul>';
+ } else
+ {
+ rowStr += '<li>' + node.data.category + '</li>';
+ }
+ }
+
+ function iteratorAfter(node) {
+ if (node.children.length > 0)
+ {
+
+ rowStr += '</ul></li>';
+ }
+ }
+
+ tree.traverseDown(iterator, iteratorAfter);
+
+ rowStr += "</ul>";
+
+ $("#container").html(rowStr);
+
+ $('#container').jstree();
+});
+
+
\ No newline at end of file
diff --git a/examples/style.css b/examples/style.css
new file mode 100644
index 0000000..67cd156
--- /dev/null
+++ b/examples/style.css
@@ -0,0 +1,7 @@
+.codeblock
+{
+ margin : 10px;
+ border : 1px #000 solid;
+ max-height: 350px;
+ overflow: scroll;
+}
diff --git a/jsdoc.json b/jsdoc.json
new file mode 100644
index 0000000..edcd243
--- /dev/null
+++ b/jsdoc.json
@@ -0,0 +1,39 @@
+{
+ "tags": {
+ "allowUnknownTags": true,
+ "dictionaries": ["jsdoc","closure"]
+ },
+ "source": {
+ "includePattern": ".+\\.js(doc|x)?$",
+ "excludePattern": "(^|\\/|\\\\)_"
+ },
+ "plugins": [
+ "plugins/markdown"
+ ],
+ "templates": {
+ "cleverLinks": true,
+ "monospaceLinks": true,
+ "logoFile": "",
+ "cleverLinks": false,
+ "monospaceLinks": false,
+ "dateFormat": "ddd MMM Do YYYY",
+ "outputSourceFiles": true,
+ "outputSourcePath": true,
+ "systemName": "Arboreal.js",
+ "footer": "",
+ "copyright": "Copyright (c) 2017 Vasiliy Altunin.",
+ "navType": "vertical",
+ "theme": "cerulean",
+ "linenums": true,
+ "collapseSymbols": false,
+ "inverseNav": true,
+ "protocol": "html://",
+ "methodHeadingReturns": false
+ },
+ "opts": {
+ "encoding": "utf8",
+ "destination": "./docs/doc",
+ "recurse": true
+ }
+}
+
diff --git a/lib/arboreal.js b/lib/arboreal.js
index 0135c0c..06f9faf 100644
--- a/lib/arboreal.js
+++ b/lib/arboreal.js
@@ -1,250 +1,786 @@
-!function () {
- 'use strict';
-
- function include (array, item) {
- return array.indexOf(item) > -1;
- }
-
- function _traverseDown (context, iterator) {
- var doContinue = true;
-
- (function walkDown (node) {
- var i, newContext;
-
- if (!doContinue) return;
-
- if (iterator(node) === false) {
- //break the traversal loop if the iterator returns a falsy value
- doContinue = false;
- }
- else {
- for (i = 0; i < node.children.length; i++) {
- newContext = node.children[i];
- walkDown(newContext);
+/**
+ * @file Arboreal.js
+ * @author Vasiliy Altunin
+ * @copyright 2017 Vasiliy Altunin
+ * @version 0.0.1
+ * @license MIT
+ * @description Javascript tree traversal and manipulation library (Forked from Nenad V. Nikolić, originally by Andrea Fiore)
+ * @module arboreal
+ */
+
+(function () {
+
+ /**
+ * @private
+ * @param {type} array
+ * @param {type} item
+ * @returns {Number|arboreal_L8.indexOf.i}
+ */
+ function indexOf(array, item) {
+ for (var i = 0, j = array.length; i < j; i++) {
+ if (array[i] === item) {
+ return i;
+ }
}
- }
- })(context);
- }
+ return -1;
+ }
+ /**
+ * @private
+ * @param {type} array
+ * @param {type} item
+ * @returns {Boolean}
+ */
+ function include(array, item) {
+ return indexOf(array, item) > -1;
+ }
- function _traverseUp (context, iterator) {
- var i, node, doContinue;
+ /**
+ * Traverse down tree iterator
+ * @private
+ * @param {type} context
+ * @param {function} iterator Iterator function to be called when traverse node
+ * @param {function} iteratorAfter Optional iterator function to be called AFTER node was traversed
+ */
+ function _traverseDown(context, iterator, iteratorAfter) {
+ var doContinue = true;
- while (context) {
- if ( iterator(context) === false ) return;
+ (function walkDown(node) {
+ var i, newContext;
- for (i = 0; i < context.children.length; i++) {
- node = context.children[i];
- if ( iterator(node) === false ) return;
- }
- context = context.parent;
- }
- }
-
-
- function _traverse (context, iterator, callback) {
- var visited = [],
- callIterator = function (node) {
- var id = node.id,
- returned;
-
- if (! include(visited, id)) {
- returned = iterator.call(node, node);
- visited.push(id);
-
- if (returned === false) {
- return returned;
+ if (!doContinue) {
+ return;
}
- }
- },
- i, node;
-
- callback(context, callIterator);
- }
-
-
- function _removeChild (node) {
- var parent = node.parent,
- child,
- i;
-
- for (i = 0; i < parent.children.length; i++) {
- child = parent.children[i];
-
- if (child === node) {
- return parent.children.splice(i, 1).shift();
- }
- }
- }
-
- function nodeId (parent, separator) {
- separator = separator || '/';
- if (parent) {
- return [parent.id, parent.children.length ].join(separator);
+
+ if (iterator(node) === false) {
+ //break the traversal loop if the iterator returns a falsy value
+ doContinue = false;
+ } else {
+ for (i = 0; i < node.children.length; i++) {
+ newContext = node.children[i];
+ walkDown(newContext);
+ if (iteratorAfter !== undefined)
+ {
+ iteratorAfter.call(newContext, newContext);
+ }
+ }
+ }
+ })(context);
}
- else {
- return '0';
+
+ /**
+ * Traverse up tree iterator
+ * @private
+ * @param {type} context
+ * @param {function} iterator Iterator function to be called when traverse node
+ */
+ function _traverseUp(context, iterator) {
+ var i, node;
+
+ while (context) {
+ if (iterator(context) === false) {
+ return;
+ }
+
+ for (i = 0; i < context.children.length; i++) {
+ node = context.children[i];
+ if (iterator(node) === false) {
+ return;
+ }
+ }
+ context = context.parent;
+ }
}
- }
-
-
- function Arboreal (parent, data, id) {
- this.depth = parent ? parent.depth + 1 : 0;
- this.data = data || {};
- this.parent = parent || null;
- this.id = id || nodeId(parent);
- this.children = [];
- }
-
- Arboreal.parse = function (object, childrenAttr) {
- var root, getNodeData = function (node) {
- var attr, nodeData = {};
- for (attr in node) {
- if (attr !== childrenAttr) nodeData[attr] = node[attr];
- }
- return nodeData;
- };
-
- (function walkDown(node, parent) {
- var newNode, i;
-
- if (!parent) {
- newNode = root = new Arboreal(null, getNodeData(node));
- } else {
- newNode = new Arboreal(parent, getNodeData(node));
- parent.children.push(newNode);
- }
- if (childrenAttr in node) {
- for (i = 0; i < node[childrenAttr].length; i++ ) {
- walkDown(node[childrenAttr][i], newNode);
+
+ /**
+ * Bubble up tree iterator
+ * @private
+ * @param {type} context
+ * @param {function} iterator Iterator function to be called when traverse node *
+ */
+ function _bubbleUp(context, iterator) {
+
+ while (context) {
+ if (iterator(context) === false) {
+ return;
+ }
+ context = context.parent;
}
- }
- })(object);
-
- return root;
-
- };
-
- Arboreal.prototype.appendChild = function (data, id) {
- var child = new Arboreal(this, data, id);
- this.children.push(child);
- return this;
- };
-
- Arboreal.prototype.removeChild = function (arg) {
- if (typeof arg === 'number' && this.children[arg]) {
- return this.children.splice(arg, 1).shift();
}
- if (arg instanceof Arboreal) {
- return _removeChild(arg);
+
+ /**
+ * Traverse node and calls callback
+ * @private
+ * @param {type} context
+ * @param {type} iterator Iterator function to be called when traverse node
+ * @param {type} callback Iterator callback
+ * @param {type} iteratorAfter Optional iterator function to be called AFTER node was traversed
+ */
+ function _traverse(context, iterator, callback, iteratorAfter) {
+ var visited = [],
+ callIterator = function (node) {
+ var id = node.id,
+ returned;
+
+ if (!include(visited, id)) {
+ returned = iterator.call(node, node);
+ visited.push(id);
+
+ if (returned === false) {
+ return returned;
+ }
+ }
+ };
+ // ,i, node;
+
+ callback(context, callIterator, iteratorAfter);
}
- throw new Error("Invalid argument "+ arg);
- };
-
- Arboreal.prototype.remove = function () {
- return _removeChild(this);
- };
-
-
- Arboreal.prototype.root = function () {
- var node = this;
-
- if (!node.parent) {
- return this;
+
+
+ /**
+ * Removes node
+ * @private
+ * @param {node} node
+ * @returns {Arboreal}
+ */
+ function _removeChild(node) {
+ var parent = node.parent,
+ child,
+ i;
+
+ for (i = 0; i < parent.children.length; i++) {
+ child = parent.children[i];
+
+ if (child === node) {
+ return parent.children.splice(i, 1).shift();
+ }
+ }
}
-
- while (node.parent) {
- node = node.parent;
+
+ /**
+ * Build Id for node by concat parent node id with separator and parent children length
+ * @private
+ * @param {Node} parent Parent node
+ * @param {Char} Separator for numbers in Id
+ * @returns {String} Id string
+ */
+ function _nodeId(parent, separator) {
+ separator = separator || '/';
+ if (parent) {
+ return [parent.id, parent.children.length].join(separator);
+ } else {
+ return '0';
+ }
}
- return node;
- };
-
- Arboreal.prototype.isRoot = function () {
- return !this.parent;
- };
-
- Arboreal.prototype.traverseUp = function (iterator) {
- _traverse(this, iterator, _traverseUp);
- };
-
- Arboreal.prototype.traverseDown = function (iterator) {
- _traverse(this, iterator, _traverseDown);
- };
-
- Arboreal.prototype.toString = function () {
- var lines = [];
-
- this.traverseDown(function (node) {
- var separator = '|- ', indentation = '', i;
-
- if (node.depth === 0) {
- lines.push(node.id);
- return;
- }
- for (i = 0; i < node.depth; i++) {
- indentation += ' ';
- }
- lines.push( indentation + separator + node.id);
- });
- return lines.join("\n");
- };
-
- Arboreal.prototype.find = function (finder) {
- var match = null,
- iterator = (typeof finder === 'function') ?
- finder : function (node) {
- if (node.id === finder) {
- match = node;
- return false;
+
+ /**
+ * Creates Arboreal root tree object or child node object
+ * @param {Arboreal=} parent Parent node
+ * @param {Object=} data Data to store in node
+ * @param {String=} id - node id
+ * @param {Char=} separator - separator for id numbers
+ * @memberOf module:arboreal
+ * @example *
+ * var tree = new Arboreal(null,{category: 'JavaScript'});
+ * console.log(tree.toString(true));
+ *
+ * Result:
+ *
+ * 0 {"category":"JavaScript"}
+ * @returns {Arboreal} Arboreal object
+ */
+ function Arboreal(parent, data, id, separator) {
+ this.depth = parent ? parent.depth + 1 : 0;
+ this.data = data || {};
+ this.parent = parent || null;
+
+ if (separator === undefined)
+ {
+ if ((parent !== undefined) && (parent !== null))
+ {
+ this.separator = this.parent.separator;
+ } else
+ {
+ this.separator = '/';
}
- };
-
- this.traverseDown(function (node) {
- if (iterator.call(this, node)) {
- match = node;
- return false;
- }
- });
-
- return match;
- };
-
- Arboreal.prototype.path = function (path, separator) {
- separator = separator || '/';
- //allow path to begin with
- if (path[0] === separator) path = path.substring(1);
-
- var indexes = path.split(separator),
- index = null,
- context = this,
- i;
-
- for (i = 0; i < indexes.length; i++) {
- index = parseInt(indexes[i], 10);
- context = (context.children.length && context.children.length > index) ?
- context.children[index] : null;
+ } else
+ {
+ this.separator = separator;
+ }
+ this.id = id || _nodeId(parent, this.separator);
+ this.children = [];
}
-
- return context;
- };
-
- Arboreal.prototype.toArray = function () {
- var nodeList = [];
- this.traverseDown(function (node) {
- nodeList.push(node);
- });
- return nodeList;
- };
- Arboreal.prototype.__defineGetter__("length", function () {
- return this.toArray().length;
- });
+ /**
+ * Parses Object and creates tree from it
+ * @param {Object} object Object to parse
+ * @param {String} childrenAttr - name of object attr to use as children attr
+ * @param {Arboreal} parent - Parent node, if null new tree created
+ * @memberOf module:arboreal
+ * @example
+ * var wikipediaJsCategory = {
+ * category: 'JavaScript',
+ * subcategories: [
+ * {category: 'Ajax (programming)'},
+ * {category: 'JavaScript engines'},
+ * {category: 'JavaScript programming languages family',
+ * subcategories: [{
+ * category: 'JavaScript dialect engines'
+ * }]
+ * },
+ * {category: 'JavaScript based calendar components'},
+ * {category: 'JavaScript based HTML editors'}
+ * ]
+ * };
+ * tree = Arboreal.parse(wikipediaJsCategory, 'subcategories');
+ *
+ * Result:
+ *
+ * 0 {"category":"JavaScript"}
+ * |- 0/0 {"category":"Ajax (programming)"}
+ * |- 0/1 {"category":"JavaScript engines"}
+ * |- 0/2 {"category":"JavaScript programming languages family"}
+ * |- 0/2/0 {"category":"JavaScript dialect engines"}
+ * |- 0/3 {"category":"JavaScript based calendar components"}
+ * |- 0/4 {"category":"JavaScript based HTML editors"}
+ * @returns {Arboreal} Arboreal root object or parent object
+ */
+ Arboreal.parse = function (object, childrenAttr, parent) {
+ var root, getNodeData = function (node) {
+ var attr, nodeData = {};
+ for (attr in node) {
+ if (attr !== childrenAttr) {
+ nodeData[attr] = node[attr];
+ }
+ }
+ return nodeData;
+ };
+
+ (function walkDown(node, parent) {
+ var newNode, i;
+
+ if (!parent) {
+ newNode = root = new Arboreal(null, getNodeData(node));
+ } else {
+ newNode = new Arboreal(parent, getNodeData(node));
+ parent.children.push(newNode);
+ }
+ if (childrenAttr in node) {
+ for (i = 0; i < node[childrenAttr].length; i++) {
+ walkDown(node[childrenAttr][i], newNode);
+ }
+ }
+ })(object, parent);
+ return root || parent;
- if (typeof module !== 'undefined' && module.exports) {
- module.exports = Arboreal;
- } else {
- this.Arboreal = Arboreal;
- }
+ };
+
+ /**
+ * Appends child node to tree
+ * @param {Object} data Data to store in node
+ * @param {String=} id - Node id
+ * @memberOf module:arboreal
+ * @example
+ * var tree = new Arboreal(null,{category: 'JavaScript'});
+ * tree.appendChild({category: 'Ajax (programming)'});
+ *
+ * Result
+ *
+ * 0 {"category":"JavaScript"}
+ * |- 0/0 {"category":"Ajax (programming)"}
+ * @returns {Arboreal} Arboreal root object or parent object
+ */
+ Arboreal.prototype.appendChild = function (data, id) {
+ var child = new Arboreal(this, data, id);
+ this.children.push(child);
+ return this;
+ };
+ /**
+ * Parse data and add it to `this` node
+ * @param {Object} data Object to be parsed
+ * @param {String} childrenAttr Attr to use as children attr
+ * @memberOf module:arboreal
+ * @example
+ *
+ * var wikipediaJsCategory = {
+ * category: 'JavaScript',
+ * subcategories: [
+ * {category: 'Ajax (programming)'},
+ * {category: 'JavaScript engines'},
+ * {category: 'JavaScript programming languages family',
+ * subcategories: [{
+ * category: 'JavaScript dialect engines'
+ * }]
+ * },
+ * {category: 'JavaScript based calendar components'},
+ * {category: 'JavaScript based HTML editors'}
+ * ]
+ * };
+ *
+ * tree = Arboreal.parse(wikipediaJsCategory, 'subcategories');
+ *
+ * var subCategory = {
+ * category: 'JavaScript ajax query',
+ * subcategories: [
+ * {category: 'JSON'},
+ * {category: 'jQuery'}
+ * ]};
+ *
+ * tree.children[0].appendChildren(subCategory, 'subcategories');
+ *
+ * console.log(tree.toString(true));
+ *
+ * Result:
+ *
+ * 0 {"category":"JavaScript"}
+ * |- 0/0 {"category":"Ajax (programming)"}
+ * |- 0/0/0 {"category":"JavaScript ajax query"}
+ * |- 0/0/0/0 {"category":"JSON"}
+ * |- 0/0/0/1 {"category":"jQuery"}
+ * |- 0/1 {"category":"JavaScript engines"}
+ * |- 0/2 {"category":"JavaScript programming languages family"}
+ * |- 0/2/0 {"category":"JavaScript dialect engines"}
+ * |- 0/3 {"category":"JavaScript based calendar components"}
+ * |- 0/4 {"category":"JavaScript based HTML editors"}
+ * @returns {Arboreal} Arboreal root object or parent object
+ */
+ Arboreal.prototype.appendChildren = function (data, childrenAttr) {
+ return Arboreal.parse(data, childrenAttr, this);
+ };
+
+ /**
+ * Remove child node from `this` node
+ * @param {String= | Node=} arg Node id or Arboreal
+ * @memberOf module:arboreal
+ * @example
+ *
+ * var wikipediaJsCategory = {
+ * category: 'JavaScript',
+ * subcategories: [
+ * {category: 'Ajax (programming)'},
+ * {category: 'JavaScript engines'},
+ * {category: 'JavaScript programming languages family',
+ * subcategories: [{
+ * category: 'JavaScript dialect engines'
+ * }]
+ * },
+ * {category: 'JavaScript based calendar components'},
+ * {category: 'JavaScript based HTML editors'}
+ * ]
+ * };
+ *
+ * tree.removeChild(2);
+ *
+ * console.log(tree.toString(true));
+ *
+ * Result:
+ *
+ * 0 {"category":"JavaScript"}
+ * |- 0/0 {"category":"Ajax (programming)"}
+ * |- 0/1 {"category":"JavaScript engines"}
+ * |- 0/3 {"category":"JavaScript based calendar components"}
+ * |- 0/4 {"category":"JavaScript based HTML editors"}
+ * @returns {Arboreal} Arboreal
+ */
+ Arboreal.prototype.removeChild = function (arg) {
+ if (typeof arg === 'number' && this.children[arg]) {
+ return this.children.splice(arg, 1).shift();
+ }
+ if (arg instanceof Arboreal) {
+ return _removeChild(arg);
+ }
+ throw new Error("Invalid argument " + arg);
+ };
+
+ /**
+ * Removes `this` node
+ * @memberOf module:arboreal
+ * @example
+ * var wikipediaJsCategory = {
+ * category: 'JavaScript',
+ * subcategories: [
+ * {category: 'Ajax (programming)'},
+ * {category: 'JavaScript engines'},
+ * {category: 'JavaScript programming languages family',
+ * subcategories: [{
+ * category: 'JavaScript dialect engines'
+ * }]
+ * },
+ * {category: 'JavaScript based calendar components'},
+ * {category: 'JavaScript based HTML editors'}
+ * ]
+ * };
+ *
+ * tree.children[0].remove();
+ *
+ * console.log(tree.toString(true));
+ *
+ * Result:
+ *
+ * 0 {"category":"JavaScript"}
+ * |- 0/1 {"category":"JavaScript engines"}
+ * |- 0/2 {"category":"JavaScript programming languages family"}
+ * |- 0/2/0 {"category":"JavaScript dialect engines"}
+ * |- 0/3 {"category":"JavaScript based calendar components"}
+ * |- 0/4 {"category":"JavaScript based HTML editors"}
+ * @returns {Arboreal}
+ */
+ Arboreal.prototype.remove = function () {
+ return _removeChild(this);
+ };
+
+ /**
+ * Traverse tree down
+ * @param {function} iterator Iterator function
+ * @memberOf module:arboreal
+ * @example
+ * //Traverse and creating HTML list discribed in example
+ * //https://vasiliyaltunin.github.io/arboreal.js/examples
+ *
+ * var wikipediaJsCategory = {
+ * category: 'JavaScript',
+ * subcategories: [
+ * {category: 'Ajax (programming)'},
+ * {category: 'JavaScript engines'},
+ * {category: 'JavaScript programming languages family',
+ * subcategories: [{
+ * category: 'JavaScript dialect engines'
+ * }]
+ * },
+ * {category: 'JavaScript based calendar components'},
+ * {category: 'JavaScript based HTML editors'}
+ * ]
+ * };
+ *
+ * function iterator (node) {
+ * var depth = "", i;
+ * for (i = 1; i <= node.depth; i++) depth += ">>";
+ * console.log([depth, node.data.category].join(" "));
+ * }
+ *
+ * tree.traverseDown(iterator);
+ *
+ * Result:
+ *
+ * JavaScript
+ * >> Ajax (programming)
+ * >> JavaScript engines
+ * >> JavaScript programming languages family
+ * >>>> JavaScript dialect engines
+ * >> JavaScript based calendar components
+ * >> JavaScript based HTML editors
+ */
+ Arboreal.prototype.traverseDown = function (iterator, iteratorAfter) {
+ _traverse(this, iterator, _traverseDown, iteratorAfter);
+ };
+
+
+
+ /**
+ * Traverse tree up
+ * @param {function} iterator Iterator function
+ * @memberOf module:arboreal
+ * @example
+ * var wikipediaJsCategory = {
+ * category: 'JavaScript',
+ * subcategories: [
+ * {category: 'Ajax (programming)'},
+ * {category: 'JavaScript engines'},
+ * {category: 'JavaScript programming languages family',
+ * subcategories: [{
+ * category: 'JavaScript dialect engines'
+ * }]
+ * },
+ * {category: 'JavaScript based calendar components'},
+ * {category: 'JavaScript based HTML editors'}
+ * ]
+ * };
+ *
+ * function iterator (node) {
+ * var depth = "", i;
+ * for (i = 1; i <= node.depth; i++) depth += ">>";
+ * console.log([depth, node.data.category].join(" "));
+ * }
+ *
+ * tree.children[2].traverseUp(iterator);
+ *
+ * Result:
+ *
+ * >> JavaScript programming languages family
+ * >>>> JavaScript dialect engines
+ * JavaScript
+ * >> Ajax (programming)
+ * >> JavaScript engines
+ * >> JavaScript based calendar components
+ * >> JavaScript based HTML editors
+ */
+ Arboreal.prototype.traverseUp = function (iterator) {
+ _traverse(this, iterator, _traverseUp);
+ };
+
+ /**
+ * Traverse tree bubble up
+ * @param {function} iterator Iterator function
+ * @memberOf module:arboreal
+ * @example
+ * var wikipediaJsCategory = {
+ * category: 'JavaScript',
+ * subcategories: [
+ * {category: 'Ajax (programming)'},
+ * {category: 'JavaScript engines'},
+ * {category: 'JavaScript programming languages family',
+ * subcategories: [{
+ * category: 'JavaScript dialect engines'
+ * }]
+ * },
+ * {category: 'JavaScript based calendar components'},
+ * {category: 'JavaScript based HTML editors'}
+ * ]
+ * };
+ *
+ * function iterator (node) {
+ * var depth = "", i;
+ * for (i = 1; i <= node.depth; i++) depth += ">>";
+ * console.log([depth, node.data.category].join(" "));
+ * }
+ *
+ * tree.children[2].children[0].bubbleUp(iterator);
+ *
+ * Result:
+ *
+ * >>>> JavaScript dialect engines
+ * >> JavaScript programming languages family
+ * JavaScript
+ */
+ Arboreal.prototype.bubbleUp = function (iterator) {
+ _traverse(this, iterator, _bubbleUp);
+ };
+
+ /**
+ * Retrns string representation of a tree
+ * @param {boolean=} isValue If true than data value printed
+ * @memberOf module:arboreal
+ * @example
+ * var wikipediaJsCategory = {
+ * category: 'JavaScript',
+ * subcategories: [
+ * {category: 'Ajax (programming)'},
+ * {category: 'JavaScript engines'},
+ * {category: 'JavaScript programming languages family',
+ * subcategories: [{
+ * category: 'JavaScript dialect engines'
+ * }]
+ * },
+ * {category: 'JavaScript based calendar components'},
+ * {category: 'JavaScript based HTML editors'}
+ * ]
+ * };
+ *
+ * console.log(tree.toString());
+ *
+ * Result:
+ *
+ * 0
+ * |- 0/0
+ * |- 0/1
+ * |- 0/2
+ * |- 0/2/0
+ * |- 0/3
+ * |- 0/4
+ *
+ * If True passed as arg result changed:
+ *
+ * 0 {"category":"JavaScript"}
+ * |- 0/0 {"category":"Ajax (programming)"}
+ * |- 0/1 {"category":"JavaScript engines"}
+ * |- 0/2 {"category":"JavaScript programming languages family"}
+ * |- 0/2/0 {"category":"JavaScript dialect engines"}
+ * |- 0/3 {"category":"JavaScript based calendar components"}
+ * |- 0/4 {"category":"JavaScript based HTML editors"}
+ * @returns {String}
+ */
+ Arboreal.prototype.toString = function (isValue) {
+ var lines = [];
+ isValue = isValue || false;
+
+ this.traverseDown(function (node) {
+ var separator = '|- ', indentation = '', i;
+
+ var value = "";
+ if (isValue)
+ {
+ value = JSON.stringify(node.data);
+ }
+
+ if (node.depth === 0) {
+ lines.push(node.id + " " + value);
+ return;
+ }
+ for (i = 0; i < node.depth; i++) {
+ indentation += ' ';
+ }
+
+ lines.push(indentation + separator + node.id + indentation + value);
+ });
+ return lines.join("\n");
+ };
+
+ /**
+ * Finds node in tree by using iterator
+ * @param {function} finder Iterator
+ * @memberOf module:arboreal
+ * @example
+ * var wikipediaJsCategory = {
+ * category: 'JavaScript',
+ * subcategories: [
+ * {category: 'Ajax (programming)'},
+ * {category: 'JavaScript engines'},
+ * {category: 'JavaScript programming languages family',
+ * subcategories: [{
+ * category: 'JavaScript dialect engines'
+ * }]
+ * },
+ * {category: 'JavaScript based calendar components'},
+ * {category: 'JavaScript based HTML editors'}
+ * ]
+ * };
+ *
+ * var result = tree.find(function (node) {
+ * return (/calendar/).test(node.data.category)
+ * }).data.category;
+ * console.log(result);
+ *
+ * Result:
+ *
+ * JavaScript based calendar components
+ * @returns {Arboreal} First match
+ */
+
+ Arboreal.prototype.find = function (finder) {
+ var match = null,
+ iterator = (typeof finder === 'function') ?
+ finder : function (node) {
+ if (node.id === finder) {
+ match = node;
+ return false;
+ }
+ };
+
+ this.traverseDown(function (node) {
+ if (iterator.call(this, node)) {
+ match = node;
+ return false;
+ }
+ });
+
+ return match;
+ };
+
+ /**
+ * Returns Arboreal for givent id path
+ * @param {String} path Id string
+ * @param {Char=} separator Separator to use
+ * @memberOf module:arboreal
+ * @example
+ * var wikipediaJsCategory = {
+ * category: 'JavaScript',
+ * subcategories: [
+ * {category: 'Ajax (programming)'},
+ * {category: 'JavaScript engines'},
+ * {category: 'JavaScript programming languages family',
+ * subcategories: [{
+ * category: 'JavaScript dialect engines'
+ * }]
+ * },
+ * {category: 'JavaScript based calendar components'},
+ * {category: 'JavaScript based HTML editors'}
+ * ]
+ * };
+ *
+ * var result = tree.path("/2/0").data.category;
+ * console.log(result);
+ *
+ * Result:
+ *
+ * JavaScript dialect engines
+ * @returns {Arboreal} Node finded by path
+ */
+ Arboreal.prototype.path = function (path, separator) {
+ separator = separator || '/';
+ //allow path to begin with
+ if (path[0] === separator) {
+ path = path.substring(1);
+ }
+
+ var indexes = path.split(separator),
+ index = null,
+ context = this,
+ i;
+
+ for (i = 0; i < indexes.length; i++) {
+ index = parseInt(indexes[i], 10);
+ context = (context.children.length && context.children.length > index) ?
+ context.children[index] : null;
+ }
+
+ return context;
+ };
+
+
+ /**
+ * Converts tree to array of objects
+ * @private
+ * @returns {array} Array pepresentation of tree
+ */
+ Arboreal.prototype.toArray = function () {
+ var nodeList = [];
+ this.traverseDown(function (node) {
+ nodeList.push(node);
+ });
+ return nodeList;
+ };
+
+ /**
+ * Returns root node for `this`
+ * @private
+ * @returns {Arboreal}
+ */
+ Arboreal.prototype.root = function () {
+ var node = this;
+
+ if (!node.parent) {
+ return this;
+ }
+
+ while (node.parent) {
+ node = node.parent;
+ }
+ return node;
+ };
+
+ /**
+ * Checks is root
+ * @private
+ * @returns {Arboreal}
+ */
+ Arboreal.prototype.isRoot = function () {
+ return !this.parent;
+ };
+
+ /**
+ * @private
+ */
+ Object.defineProperty(Arboreal.prototype, 'length', {
+ get: function () {
+ return this.toArray().length;
+ }
+ });
+
+ if (typeof module !== 'undefined' && module.exports) {
+ module.exports = Arboreal;
+ } else {
+ this.Arboreal = Arboreal;
+ }
-}(this);
+}(this));
diff --git a/lib/arboreal.min.js b/lib/arboreal.min.js
new file mode 100644
index 0000000..a66823f
--- /dev/null
+++ b/lib/arboreal.min.js
@@ -0,0 +1,5 @@
+/*! Arboreal.js - v0.0.3 - 2017-04-14
+* https://github.com/vasiliyaltunin/arboreal.js
+* Copyright (c) 2017 Vasiliy Altunin (skyr@altunin.online); Licensed MIT; See MIT-LICENSE.txt for more info*/
+
+!function(){function a(a,b){for(var c=0,d=a.length;c-1}function c(a,b,c){var d=!0;!function a(e){var f,g;if(d)if(!1===b(e))d=!1;else for(f=0;fe?f.children[e]:null;return f},i.prototype.toArray=function(){var a=[];return this.traverseDown(function(b){a.push(b)}),a},i.prototype.root=function(){var a=this;if(!a.parent)return this;for(;a.parent;)a=a.parent;return a},i.prototype.isRoot=function(){return!this.parent},Object.defineProperty(i.prototype,"length",{get:function(){return this.toArray().length}}),"undefined"!=typeof module&&module.exports?module.exports=i:this.Arboreal=i}();
\ No newline at end of file
diff --git a/make.js b/make.js
deleted file mode 100644
index 8071a91..0000000
--- a/make.js
+++ /dev/null
@@ -1 +0,0 @@
-require("smoosh").make("config.json");
diff --git a/package.json b/package.json
index 31ee6eb..acf0b64 100644
--- a/package.json
+++ b/package.json
@@ -1,18 +1,70 @@
{
- "name": "arboreal",
- "description": "Javascript tree traversal and manipulation library",
- "author": "Andrea Fiore",
- "version": "0.0.1",
- "contributors": [],
+ "name": "arboreal.js",
+ "description": "Javascript tree traversal and manipulation library (forked from Nenad V. Nikolić code, originally by Andrea Fiore)",
+ "author": "Vasiliy Altuin",
+ "version": "0.0.3",
+ "contributors": [
+ {
+ "name": "Alex Pernot",
+ "email": "alex_pernot@hotmail.com",
+ "url": "https://github.com/AlexPernot",
+ "contributions": 1,
+ "additions": 7,
+ "deletions": 5,
+ "hireable": null
+ },
+ {
+ "name": "Nenad V. Nikolić",
+ "email": null,
+ "url": "https://github.com/shonzilla",
+ "contributions": 11,
+ "additions": 37,
+ "deletions": 22,
+ "hireable": true
+ },
+ {
+ "name": "Andrea Fiore",
+ "email": null,
+ "url": "https://github.com/afiore",
+ "contributions": 1,
+ "additions": 20,
+ "deletions": 0,
+ "hireable": null
+ },
+ {
+ "name": "Evgeny Gusev",
+ "email": null,
+ "url": "https://github.com/EvgenyGusev",
+ "contributions": 6,
+ "additions": 124,
+ "deletions": 20,
+ "hireable": null
+ }
+ ],
"dependencies": {},
"devDependencies": {
- "jasmine-node": "1.0.6",
- "smoosh": "0.3.0"
- },
- "scripts": {
- "test": "jasmine-node test/specs"
+ "grunt": "^1.0.1",
+ "grunt-contrib-clean": "^1.0.0",
+ "grunt-contrib-concat": "^1.0.1",
+ "grunt-contrib-jshint": "^1.1.0",
+ "grunt-contrib-qunit": "^2.0.0",
+ "grunt-contrib-uglify": "^2.3.0",
+ "grunt-contrib-watch": "^1.0.0",
+ "grunt-jsdoc": "^2.1.0",
+ "ink-docstrap": "^1.3.0",
+ "phantomjs-prebuilt": "^2.1.7",
+ "qunit": "^1.0.0"
},
"engines": {
- "node": ">= 0.4.1 < 0.5.0"
+ "node": ">= 4.0.0"
+ },
+ "license": "MIT",
+ "main": "./lib/arboreal",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vasiliyaltunin/arboreal.js"
+ },
+ "scripts": {
+ "test": "grunt qunit"
}
}
diff --git a/test/.jshintrc b/test/.jshintrc
new file mode 100644
index 0000000..8a4a8e2
--- /dev/null
+++ b/test/.jshintrc
@@ -0,0 +1,33 @@
+{
+ "curly": true,
+ "eqeqeq": true,
+ "immed": true,
+ "latedef": true,
+ "newcap": true,
+ "noarg": true,
+ "sub": true,
+ "undef": true,
+ "unused": true,
+ "boss": true,
+ "eqnull": true,
+ "browser": true,
+ "predef": [
+ "jQuery",
+ "QUnit",
+ "module",
+ "test",
+ "asyncTest",
+ "expect",
+ "start",
+ "stop",
+ "ok",
+ "equal",
+ "notEqual",
+ "deepEqual",
+ "notDeepEqual",
+ "strictEqual",
+ "notStrictEqual",
+ "throws",
+ "console"
+ ]
+}
\ No newline at end of file
diff --git a/test/index.html b/test/index.html
index 294b518..180acd5 100644
--- a/test/index.html
+++ b/test/index.html
@@ -1,26 +1,32 @@
-
+
+
-
- Jasmine Test Runner for StateMachine.js
-
-
-
+
+ Arboreal Test Suite
+
+
+
+
+
+
+
-
-
+
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
diff --git a/test/specs/arboreal-spec.js b/test/specs/arboreal-spec.js
deleted file mode 100644
index 3be1055..0000000
--- a/test/specs/arboreal-spec.js
+++ /dev/null
@@ -1,173 +0,0 @@
-if (typeof require !== 'undefined') {
- util = require("util");
- Arboreal = require('../../lib/arboreal');
-}
-
-function appendSomeChildren (tree) {
- tree
- .appendChild()
- .appendChild()
- .appendChild()
- .appendChild();
-
- tree.children[0]
- .appendChild()
- .appendChild();
-}
-
-describe("Arboreal", function () {
- it("#new()", function () {
- var tree = new Arboreal();
- expect(tree.id).toBe('0');
- });
-
- it("#new(null, {myAttr: true}, 'myId')", function () {
- var tree = new Arboreal(null, {myCustomAttr:true}, "myId");
- expect(tree.id).toBe("myId");
- expect(tree.data.myCustomAttr).toBeTruthy();
- });
-
-
- it("::parse()", function () {
- var data = {
- category: 'JavaScript',
- subcategories: [
- {category: 'Ajax (programming)'},
- {category: 'JavaScript engines'},
- {category: 'JavaScript produgramming languages family',
- subcategories: [{
- category: 'JavaScript dialect engines'
- }]
- },
- {category: 'JavaScript based calendar components'},
- {category: 'JavaScript based HTML editors'}
- ]
- };
- var tree = Arboreal.parse(data, 'subcategories');
- expect(tree.length).toBe(7);
- });
-
- it("#appendChild(null, 'bla')", function () {
- var tree = new Arboreal();
- tree.appendChild({myCustomAttr:true}, "myId" );
- expect(tree.children.length).toBe(1);
- expect(tree.children[0].id).toBe("myId");
- expect(tree.children[0].parent).toEqual(tree);
- });
-
- it("#removeChild()", function () {
- var tree = new Arboreal(),
- thirdChild,
- secondChild;
-
- appendSomeChildren(tree);
-
- thirdChild = tree.children[2];
- secondChild = tree.children[0].children[1];
-
- expect( tree.removeChild(thirdChild)).toBe(thirdChild);
- expect(tree.children.length).toBe(3);
- expect(tree.children[2].id).toBe("0/3");
-
-
- expect(tree.children[0].removeChild(1).id).toBe(secondChild.id);
- });
-
- it("#traverseDown", function () {
- var tree = new Arboreal(),
- callbackCounter = 0;
-
- appendSomeChildren(tree);
-
- tree.traverseDown(function () {
- this.id = "_" + this.id;
- });
-
- expect(tree.id).toBe('_0');
- expect(tree.children[0].id).toBe('_0/0');
- expect(tree.children[3].id).toBe('_0/3');
-
- //expect to stop traversing when the iterator returns a falsy value
-
- tree.traverseDown(function (node) {
- callbackCounter++;
- if (callbackCounter === 3) return false;
- });
-
- expect(callbackCounter).toBe(3);
- });
-
- it("#traverseUp", function () {
- var tree = new Arboreal(),
- callbackCounter=0,
- treeIds = [],
- spy1 = jasmine.createSpy(),
- spy2 = jasmine.createSpy(),
- appendId = function (node) {
- treeIds.push(node.id);
- };
-
-
- appendSomeChildren(tree);
- tree.traverseUp(spy1);
- //should iterate over itself and its child elements
- expect(spy1.callCount).toBe(1 + 4);
-
- //should traverse all the nodes in the tree
- tree.children[0].children[1].traverseUp(spy2);
- expect(spy2.callCount).toBe(tree.toArray().length);
-
- //should traverse all the nodes in the right order
- tree.children[0].children[1].traverseUp(appendId);
-
- expect(treeIds.shift()).toBe("0/0/1");
- expect(treeIds.pop()).toBe("0/3");
-
- treeIds = [];
- //should break when iterator returns a falsy value
- tree.traverseUp(function (node) {
- appendId(node);
- if (treeIds.length === 3) return false;
- });
- expect(treeIds.length).toBe(3);
- });
-
-
- it("#toArray", function () {
- var tree = new Arboreal();
-
- appendSomeChildren(tree);
-
- expect(tree.toArray().length).toBe(7);
-
- expect(tree
- .toArray()
- .map(function (node) {return node.id;})
- .every(function (id) { return id.length;})
-
- ).toBeTruthy();
- });
-
- it("#find", function () {
- var tree = new Arboreal();
- appendSomeChildren(tree);
-
- expect(tree.find("0/3").id).toBe("0/3");
- expect(tree.find(function () {
- return this.depth == 2;
- }).id).toBe("0/0/0");
- });
-
- it("#path", function () {
- var tree = new Arboreal(),
- treeArry;
-
- appendSomeChildren(tree);
- treeArray = tree.toArray();
- lastNode = treeArray[treeArray.length -1 ];
-
- //should automatically strip out the '/' prefix
- expect(tree.path("/3").id).toBe("0/3");
- expect(tree.path("0/1").id).toBe("0/0/1");
- });
-});
diff --git a/test/test.js b/test/test.js
new file mode 100644
index 0000000..50b9a55
--- /dev/null
+++ b/test/test.js
@@ -0,0 +1,310 @@
+/*jslint node: true */
+/*global Arboreal*/
+
+'use strict';
+
+function appendSomeChildren(tree) {
+ tree
+ .appendChild()
+ .appendChild()
+ .appendChild()
+ .appendChild();
+
+ tree.children[0]
+ .appendChild()
+ .appendChild();
+}
+
+
+QUnit.test("#new()", function (assert) {
+
+ var tree = new Arboreal();
+ assert.ok(tree.id === '0', '#new()');
+});
+
+QUnit.test("#new(null, {myAttr: true}, 'myId')", function (assert) {
+
+
+ var tree = new Arboreal(null, {myCustomAttr: true}, "myId");
+ assert.ok(tree.id === 'myId', "id");
+ assert.ok(tree.data.myCustomAttr, "myCustomAttr");
+});
+
+QUnit.test("#parse(data, 'someId')", function (assert) {
+
+ var data = {
+ category: 'JavaScript',
+ subcategories: [
+ {category: 'Ajax (programming)'},
+ {category: 'JavaScript engines'},
+ {category: 'JavaScript produgramming languages family',
+ subcategories: [{
+ category: 'JavaScript dialect engines'
+ }]
+ },
+ {category: 'JavaScript based calendar components'},
+ {category: 'JavaScript based HTML editors'}
+ ]
+ };
+
+ var tree = Arboreal.parse(data, 'subcategories');
+ assert.ok(tree.length === 7, "tree.length");
+
+ var subData = {
+ category: 'C#',
+ subcategories: [
+ {category: 'WinForms'},
+ {category: 'WPF',
+ subcategories: [{
+ category: 'XAML markup'
+ }]
+ },
+ ]
+ };
+ tree = Arboreal.parse(data, 'subcategories');
+ var biggerTree = Arboreal.parse(subData, 'subcategories', tree.children[1]);
+
+ assert.ok(biggerTree.children[0].length === 4, "biggerTree.children[0].length");
+ assert.ok(tree.length === 11, "tree.length");
+
+ assert.ok(tree.children[1].children[0].data.category === 'C#', "tree.children[1].children[0].data.category");
+
+});
+
+QUnit.test("#appendChild(null, 'bla')", function (assert) {
+ var tree = new Arboreal();
+ tree.appendChild({myCustomAttr: true}, "myId");
+
+ assert.ok(tree.children.length === 1, "tree.children.length");
+ assert.ok(tree.children[0].id === "myId", "tree.children[0].id");
+ assert.ok(tree.children[0].parent === tree, "tree");
+
+});
+
+QUnit.test("#appendChildren to root", function (assert) {
+ var tree = new Arboreal();
+ tree.appendChildren({testAttr: 'subroot', subitems: [{testAttr: 'firstChild'}]}, "subitems");
+
+ assert.ok(tree.length === 3, "tree.children.length");
+ assert.ok(tree.children[0].data.testAttr === "subroot", "tree.children[0].data.testAttr");
+ assert.ok(tree.children[0].children[0].data.testAttr === "firstChild", "tree.children[0].children[0].data.testAttr");
+
+});
+
+QUnit.test("#appendChildren to children", function (assert) {
+ var tree = new Arboreal();
+ tree.appendChild().appendChild();
+ tree.children[1].appendChildren({testAttr: 'secondchild', subitems: [{testAttr: 'third child'}]}, "subitems");
+ assert.ok(tree.length === 5);
+ assert.ok(tree.children[1].children[0].data.testAttr === "secondchild");
+ assert.ok(tree.children[1].children[0].children[0].data.testAttr === 'third child');
+
+});
+
+QUnit.test("#removeChild()", function (assert) {
+
+ var tree = new Arboreal(),
+ thirdChild,
+ secondChild;
+
+ appendSomeChildren(tree);
+
+ thirdChild = tree.children[2];
+ secondChild = tree.children[0].children[1];
+
+ assert.ok(tree.removeChild(thirdChild) === thirdChild);
+ assert.ok(tree.children.length === 3);
+ assert.ok(tree.children[2].id === "0/3");
+
+
+ assert.ok(tree.children[0].removeChild(1).id === secondChild.id);
+
+});
+
+QUnit.test("#traverseDown", function (assert) {
+ var tree = new Arboreal(),
+ callbackCounter = 0,
+ callbackCounter2 = 0;
+
+ appendSomeChildren(tree);
+
+ tree.traverseDown(function () {
+ this.id = "_" + this.id;
+ });
+
+ assert.ok(tree.id === '_0');
+ assert.ok(tree.children[0].id === '_0/0');
+ assert.ok(tree.children[3].id === '_0/3');
+
+ tree = new Arboreal();
+ appendSomeChildren(tree);
+
+ tree.traverseDown(
+ function () {
+ this.id = "_" + this.id;
+ },
+ function () {
+ this.id = this.id + "=";
+ }
+ );
+
+ assert.ok(tree.id === '_0');
+ assert.ok(tree.children[0].id === '_0/0=');
+ assert.ok(tree.children[3].id === '_0/3=');
+
+
+ //expect to stop traversing when the iterator returns a falsy value
+
+ tree.traverseDown(function () {
+ callbackCounter++;
+ if (callbackCounter === 3) {
+ return false;
+ }
+ });
+
+ assert.ok(callbackCounter === 3);
+
+
+ callbackCounter = 0;
+ tree = new Arboreal();
+ appendSomeChildren(tree);
+
+ tree.traverseDown(
+ function () {
+ callbackCounter++;
+ if (callbackCounter === 3) {
+ return false;
+ }
+ },
+ function () {
+ callbackCounter2++;
+ }
+ );
+
+ assert.ok(callbackCounter === 3);
+ assert.ok(callbackCounter2 === 6);
+
+});
+
+
+QUnit.test("#traverseUp", function (assert) {
+
+ var tree = new Arboreal();
+
+ var treeIds = [];
+
+ var appendId = function (node) {
+ treeIds.push(node.id);
+ };
+
+ var iterations = 0;
+ var countIterations = function () {
+ iterations++;
+ };
+
+ appendSomeChildren(tree);
+ tree.traverseUp(countIterations);
+ //should iterate over itself and its child elements
+ assert.ok(iterations === 1 + 4);
+
+ //should traverse all the nodes in the tree
+ iterations = 0;
+ tree.children[0].children[1].traverseUp(countIterations);
+ assert.ok(iterations === tree.toArray().length);
+
+ //should traverse all the nodes in the right order
+ tree.children[0].children[1].traverseUp(appendId);
+
+ assert.ok(treeIds.shift() === "0/0/1");
+ assert.ok(treeIds.pop() === "0/3");
+
+ treeIds = [];
+ //should break when iterator returns a falsy value
+ tree.traverseUp(function (node) {
+ appendId(node);
+ if (treeIds.length === 3) {
+ return false;
+ }
+ });
+ assert.ok(treeIds.length === 3);
+});
+
+QUnit.test("#bubbleUp", function (assert) {
+
+ var tree = new Arboreal();
+ var treeIds = [];
+
+ var appendId = function (node) {
+ treeIds.push(node.id);
+ };
+
+ var iterations = 0;
+ var countIterations = function () {
+ iterations++;
+ };
+
+
+ appendSomeChildren(tree);
+ tree.bubbleUp(countIterations);
+ //should iterate over itself
+ assert.ok(iterations = 1);
+
+ //should bubble up to the root
+ iterations = 0;
+ tree.children[0].children[1].bubbleUp(countIterations);
+ assert.ok(iterations === 1 + 1 + 1);
+
+ //should bubble up in the right order
+ tree.children[0].children[1].bubbleUp(appendId);
+
+ assert.ok(treeIds.shift() === "0/0/1");
+
+ treeIds = [];
+ //should break when iterator returns a falsy value
+ tree.bubbleUp(function (node) {
+ appendId(node);
+ if (treeIds.length === 2) {
+ return false;
+ }
+ });
+ assert.ok(treeIds.length === 1);
+});
+
+
+QUnit.test("#toArray", function (assert) {
+ var tree = new Arboreal();
+
+ appendSomeChildren(tree);
+
+ assert.ok(tree.toArray().length === 7);
+
+ assert.ok(tree
+ .toArray()
+ .map(function (node) {
+ return node.id;
+ })
+ .every(function (id) {
+ return id.length;
+ })
+ );
+});
+
+QUnit.test("#find", function (assert) {
+ var tree = new Arboreal();
+ appendSomeChildren(tree);
+
+ assert.ok(tree.find("0/3").id === "0/3");
+ assert.ok(tree.find(function () {
+ return this.depth === 2;
+ }).id === "0/0/0");
+});
+
+QUnit.test("#path", function (assert) {
+ var tree = new Arboreal();
+
+ appendSomeChildren(tree);
+
+ //should automatically strip out the '/' prefix
+ assert.ok(tree.path("/3").id === "0/3");
+ assert.ok(tree.path("0/1").id === "0/0/1");
+});
\ No newline at end of file
diff --git a/test/vendor/jasmine-sinon.js b/test/vendor/jasmine-sinon.js
deleted file mode 100644
index 094aecf..0000000
--- a/test/vendor/jasmine-sinon.js
+++ /dev/null
@@ -1,41 +0,0 @@
-(function(global) {
-
- var spyMatchers = "called calledOnce calledTwice calledThrice calledBefore calledAfter calledOn alwaysCalledOn calledWith alwaysCalledWith calledWithExactly alwaysCalledWithExactly".split(" "),
- i = spyMatchers.length,
- spyMatcherHash = {},
- unusualMatchers = {
- "returned": "toHaveReturned",
- "alwaysReturned": "toHaveAlwaysReturned"
- },
-
- getMatcherFunction = function(sinonName) {
- return function() {
- var sinonProperty = this.actual[sinonName];
- return (typeof sinonProperty === 'function') ? sinonProperty.apply(this.actual, arguments) : sinonProperty;
- };
- };
-
- while(i--) {
- var sinonName = spyMatchers[i],
- matcherName = "toHaveBeen" + sinonName.charAt(0).toUpperCase() + sinonName.slice(1);
-
- spyMatcherHash[matcherName] = getMatcherFunction(sinonName);
- };
-
- for (var j in unusualMatchers) {
- spyMatcherHash[unusualMatchers[j]] = getMatcherFunction(j);
- }
-
- global.sinonJasmine = {
- getMatchers: function() {
- return spyMatcherHash;
- }
- };
-
-})(window);
-
-beforeEach(function() {
-
- this.addMatchers(sinonJasmine.getMatchers());
-
-});
\ No newline at end of file
diff --git a/test/vendor/jasmine/SpecRunner.html b/test/vendor/jasmine/SpecRunner.html
deleted file mode 100644
index 8fdfbe7..0000000
--- a/test/vendor/jasmine/SpecRunner.html
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
- Jasmine Test Runner
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/vendor/jasmine/jasmine-html.js b/test/vendor/jasmine/jasmine-html.js
deleted file mode 100644
index b405821..0000000
--- a/test/vendor/jasmine/jasmine-html.js
+++ /dev/null
@@ -1,182 +0,0 @@
-jasmine.TrivialReporter = function(doc) {
- this.document = doc || document;
- this.suiteDivs = {};
- this.logRunningSpecs = false;
-};
-
-jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
- var el = document.createElement(type);
-
- for (var i = 2; i < arguments.length; i++) {
- var child = arguments[i];
-
- if (typeof child === 'string') {
- el.appendChild(document.createTextNode(child));
- } else {
- if (child) { el.appendChild(child); }
- }
- }
-
- for (var attr in attrs) {
- if (attr == "className") {
- el[attr] = attrs[attr];
- } else {
- el.setAttribute(attr, attrs[attr]);
- }
- }
-
- return el;
-};
-
-jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
- var showPassed, showSkipped;
-
- this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
- this.createDom('div', { className: 'banner' },
- this.createDom('div', { className: 'logo' },
- "Jasmine",
- this.createDom('span', { className: 'version' }, runner.env.versionString())),
- this.createDom('div', { className: 'options' },
- "Show ",
- showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
- this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
- showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
- this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
- )
- ),
-
- this.runnerDiv = this.createDom('div', { className: 'runner running' },
- this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
- this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
- this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
- );
-
- this.document.body.appendChild(this.outerDiv);
-
- var suites = runner.suites();
- for (var i = 0; i < suites.length; i++) {
- var suite = suites[i];
- var suiteDiv = this.createDom('div', { className: 'suite' },
- this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
- this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
- this.suiteDivs[suite.id] = suiteDiv;
- var parentDiv = this.outerDiv;
- if (suite.parentSuite) {
- parentDiv = this.suiteDivs[suite.parentSuite.id];
- }
- parentDiv.appendChild(suiteDiv);
- }
-
- this.startedAt = new Date();
-
- var self = this;
- showPassed.onchange = function(evt) {
- if (evt.target.checked) {
- self.outerDiv.className += ' show-passed';
- } else {
- self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
- }
- };
-
- showSkipped.onchange = function(evt) {
- if (evt.target.checked) {
- self.outerDiv.className += ' show-skipped';
- } else {
- self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
- }
- };
-};
-
-jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
- var results = runner.results();
- var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
- this.runnerDiv.setAttribute("class", className);
- //do it twice for IE
- this.runnerDiv.setAttribute("className", className);
- var specs = runner.specs();
- var specCount = 0;
- for (var i = 0; i < specs.length; i++) {
- if (this.specFilter(specs[i])) {
- specCount++;
- }
- }
- var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
- message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
- this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
-
- this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
-};
-
-jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
- var results = suite.results();
- var status = results.passed() ? 'passed' : 'failed';
- if (results.totalCount == 0) { // todo: change this to check results.skipped
- status = 'skipped';
- }
- this.suiteDivs[suite.id].className += " " + status;
-};
-
-jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
- if (this.logRunningSpecs) {
- this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
- }
-};
-
-jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
- var results = spec.results();
- var status = results.passed() ? 'passed' : 'failed';
- if (results.skipped) {
- status = 'skipped';
- }
- var specDiv = this.createDom('div', { className: 'spec ' + status },
- this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
- this.createDom('a', {
- className: 'description',
- href: '?spec=' + encodeURIComponent(spec.getFullName()),
- title: spec.getFullName()
- }, spec.description));
-
-
- var resultItems = results.getItems();
- var messagesDiv = this.createDom('div', { className: 'messages' });
- for (var i = 0; i < resultItems.length; i++) {
- var result = resultItems[i];
-
- if (result.type == 'log') {
- messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
- } else if (result.type == 'expect' && result.passed && !result.passed()) {
- messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
-
- if (result.trace.stack) {
- messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
- }
- }
- }
-
- if (messagesDiv.childNodes.length > 0) {
- specDiv.appendChild(messagesDiv);
- }
-
- this.suiteDivs[spec.suite.id].appendChild(specDiv);
-};
-
-jasmine.TrivialReporter.prototype.log = function() {
- var console = jasmine.getGlobal().console;
- if (console && console.log) console.log.apply(console, arguments);
-};
-
-jasmine.TrivialReporter.prototype.getLocation = function() {
- return this.document.location;
-};
-
-jasmine.TrivialReporter.prototype.specFilter = function(spec) {
- var paramMap = {};
- var params = this.getLocation().search.substring(1).split('&');
- for (var i = 0; i < params.length; i++) {
- var p = params[i].split('=');
- paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
- }
-
- if (!paramMap["spec"]) return true;
- return spec.getFullName().indexOf(paramMap["spec"]) == 0;
-};
diff --git a/test/vendor/jasmine/jasmine.css b/test/vendor/jasmine/jasmine.css
deleted file mode 100644
index 6583fe7..0000000
--- a/test/vendor/jasmine/jasmine.css
+++ /dev/null
@@ -1,166 +0,0 @@
-body {
- font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
-}
-
-
-.jasmine_reporter a:visited, .jasmine_reporter a {
- color: #303;
-}
-
-.jasmine_reporter a:hover, .jasmine_reporter a:active {
- color: blue;
-}
-
-.run_spec {
- float:right;
- padding-right: 5px;
- font-size: .8em;
- text-decoration: none;
-}
-
-.jasmine_reporter {
- margin: 0 5px;
-}
-
-.banner {
- color: #303;
- background-color: #fef;
- padding: 5px;
-}
-
-.logo {
- float: left;
- font-size: 1.1em;
- padding-left: 5px;
-}
-
-.logo .version {
- font-size: .6em;
- padding-left: 1em;
-}
-
-.runner.running {
- background-color: yellow;
-}
-
-
-.options {
- text-align: right;
- font-size: .8em;
-}
-
-
-
-
-.suite {
- border: 1px outset gray;
- margin: 5px 0;
- padding-left: 1em;
-}
-
-.suite .suite {
- margin: 5px;
-}
-
-.suite.passed {
- background-color: #dfd;
-}
-
-.suite.failed {
- background-color: #fdd;
-}
-
-.spec {
- margin: 5px;
- padding-left: 1em;
- clear: both;
-}
-
-.spec.failed, .spec.passed, .spec.skipped {
- padding-bottom: 5px;
- border: 1px solid gray;
-}
-
-.spec.failed {
- background-color: #fbb;
- border-color: red;
-}
-
-.spec.passed {
- background-color: #bfb;
- border-color: green;
-}
-
-.spec.skipped {
- background-color: #bbb;
-}
-
-.messages {
- border-left: 1px dashed gray;
- padding-left: 1em;
- padding-right: 1em;
-}
-
-.passed {
- background-color: #cfc;
- display: none;
-}
-
-.failed {
- background-color: #fbb;
-}
-
-.skipped {
- color: #777;
- background-color: #eee;
- display: none;
-}
-
-
-/*.resultMessage {*/
- /*white-space: pre;*/
-/*}*/
-
-.resultMessage span.result {
- display: block;
- line-height: 2em;
- color: black;
-}
-
-.resultMessage .mismatch {
- color: black;
-}
-
-.stackTrace {
- white-space: pre;
- font-size: .8em;
- margin-left: 10px;
- max-height: 5em;
- overflow: auto;
- border: 1px inset red;
- padding: 1em;
- background: #eef;
-}
-
-.finished-at {
- padding-left: 1em;
- font-size: .6em;
-}
-
-.show-passed .passed,
-.show-skipped .skipped {
- display: block;
-}
-
-
-#jasmine_content {
- position:fixed;
- right: 100%;
-}
-
-.runner {
- border: 1px solid gray;
- display: block;
- margin: 5px 0;
- padding: 2px 0 2px 10px;
-}
diff --git a/test/vendor/jasmine/jasmine.js b/test/vendor/jasmine/jasmine.js
deleted file mode 100644
index 3ace3bc..0000000
--- a/test/vendor/jasmine/jasmine.js
+++ /dev/null
@@ -1,2421 +0,0 @@
-/**
- * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
- *
- * @namespace
- */
-var jasmine = {};
-
-/**
- * @private
- */
-jasmine.unimplementedMethod_ = function() {
- throw new Error("unimplemented method");
-};
-
-/**
- * Use jasmine.undefined instead of undefined, since undefined is just
- * a plain old variable and may be redefined by somebody else.
- *
- * @private
- */
-jasmine.undefined = jasmine.___undefined___;
-
-/**
- * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
- *
- */
-jasmine.DEFAULT_UPDATE_INTERVAL = 250;
-
-/**
- * Default timeout interval in milliseconds for waitsFor() blocks.
- */
-jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
-
-jasmine.getGlobal = function() {
- function getGlobal() {
- return this;
- }
-
- return getGlobal();
-};
-
-/**
- * Allows for bound functions to be compared. Internal use only.
- *
- * @ignore
- * @private
- * @param base {Object} bound 'this' for the function
- * @param name {Function} function to find
- */
-jasmine.bindOriginal_ = function(base, name) {
- var original = base[name];
- if (original.apply) {
- return function() {
- return original.apply(base, arguments);
- };
- } else {
- // IE support
- return jasmine.getGlobal()[name];
- }
-};
-
-jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
-jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
-jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
-jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
-
-jasmine.MessageResult = function(values) {
- this.type = 'log';
- this.values = values;
- this.trace = new Error(); // todo: test better
-};
-
-jasmine.MessageResult.prototype.toString = function() {
- var text = "";
- for(var i = 0; i < this.values.length; i++) {
- if (i > 0) text += " ";
- if (jasmine.isString_(this.values[i])) {
- text += this.values[i];
- } else {
- text += jasmine.pp(this.values[i]);
- }
- }
- return text;
-};
-
-jasmine.ExpectationResult = function(params) {
- this.type = 'expect';
- this.matcherName = params.matcherName;
- this.passed_ = params.passed;
- this.expected = params.expected;
- this.actual = params.actual;
-
- this.message = this.passed_ ? 'Passed.' : params.message;
- this.trace = this.passed_ ? '' : new Error(this.message);
-};
-
-jasmine.ExpectationResult.prototype.toString = function () {
- return this.message;
-};
-
-jasmine.ExpectationResult.prototype.passed = function () {
- return this.passed_;
-};
-
-/**
- * Getter for the Jasmine environment. Ensures one gets created
- */
-jasmine.getEnv = function() {
- return jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
-};
-
-/**
- * @ignore
- * @private
- * @param value
- * @returns {Boolean}
- */
-jasmine.isArray_ = function(value) {
- return jasmine.isA_("Array", value);
-};
-
-/**
- * @ignore
- * @private
- * @param value
- * @returns {Boolean}
- */
-jasmine.isString_ = function(value) {
- return jasmine.isA_("String", value);
-};
-
-/**
- * @ignore
- * @private
- * @param value
- * @returns {Boolean}
- */
-jasmine.isNumber_ = function(value) {
- return jasmine.isA_("Number", value);
-};
-
-/**
- * @ignore
- * @private
- * @param {String} typeName
- * @param value
- * @returns {Boolean}
- */
-jasmine.isA_ = function(typeName, value) {
- return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
-};
-
-/**
- * Pretty printer for expecations. Takes any object and turns it into a human-readable string.
- *
- * @param value {Object} an object to be outputted
- * @returns {String}
- */
-jasmine.pp = function(value) {
- var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
- stringPrettyPrinter.format(value);
- return stringPrettyPrinter.string;
-};
-
-/**
- * Returns true if the object is a DOM Node.
- *
- * @param {Object} obj object to check
- * @returns {Boolean}
- */
-jasmine.isDomNode = function(obj) {
- return obj['nodeType'] > 0;
-};
-
-/**
- * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
- *
- * @example
- * // don't care about which function is passed in, as long as it's a function
- * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
- *
- * @param {Class} clazz
- * @returns matchable object of the type clazz
- */
-jasmine.any = function(clazz) {
- return new jasmine.Matchers.Any(clazz);
-};
-
-/**
- * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
- *
- * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
- * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
- *
- * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
- *
- * Spies are torn down at the end of every spec.
- *
- * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
- *
- * @example
- * // a stub
- * var myStub = jasmine.createSpy('myStub'); // can be used anywhere
- *
- * // spy example
- * var foo = {
- * not: function(bool) { return !bool; }
- * }
- *
- * // actual foo.not will not be called, execution stops
- * spyOn(foo, 'not');
-
- // foo.not spied upon, execution will continue to implementation
- * spyOn(foo, 'not').andCallThrough();
- *
- * // fake example
- * var foo = {
- * not: function(bool) { return !bool; }
- * }
- *
- * // foo.not(val) will return val
- * spyOn(foo, 'not').andCallFake(function(value) {return value;});
- *
- * // mock example
- * foo.not(7 == 7);
- * expect(foo.not).toHaveBeenCalled();
- * expect(foo.not).toHaveBeenCalledWith(true);
- *
- * @constructor
- * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
- * @param {String} name
- */
-jasmine.Spy = function(name) {
- /**
- * The name of the spy, if provided.
- */
- this.identity = name || 'unknown';
- /**
- * Is this Object a spy?
- */
- this.isSpy = true;
- /**
- * The actual function this spy stubs.
- */
- this.plan = function() {
- };
- /**
- * Tracking of the most recent call to the spy.
- * @example
- * var mySpy = jasmine.createSpy('foo');
- * mySpy(1, 2);
- * mySpy.mostRecentCall.args = [1, 2];
- */
- this.mostRecentCall = {};
-
- /**
- * Holds arguments for each call to the spy, indexed by call count
- * @example
- * var mySpy = jasmine.createSpy('foo');
- * mySpy(1, 2);
- * mySpy(7, 8);
- * mySpy.mostRecentCall.args = [7, 8];
- * mySpy.argsForCall[0] = [1, 2];
- * mySpy.argsForCall[1] = [7, 8];
- */
- this.argsForCall = [];
- this.calls = [];
-};
-
-/**
- * Tells a spy to call through to the actual implemenatation.
- *
- * @example
- * var foo = {
- * bar: function() { // do some stuff }
- * }
- *
- * // defining a spy on an existing property: foo.bar
- * spyOn(foo, 'bar').andCallThrough();
- */
-jasmine.Spy.prototype.andCallThrough = function() {
- this.plan = this.originalValue;
- return this;
-};
-
-/**
- * For setting the return value of a spy.
- *
- * @example
- * // defining a spy from scratch: foo() returns 'baz'
- * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
- *
- * // defining a spy on an existing property: foo.bar() returns 'baz'
- * spyOn(foo, 'bar').andReturn('baz');
- *
- * @param {Object} value
- */
-jasmine.Spy.prototype.andReturn = function(value) {
- this.plan = function() {
- return value;
- };
- return this;
-};
-
-/**
- * For throwing an exception when a spy is called.
- *
- * @example
- * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
- * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
- *
- * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
- * spyOn(foo, 'bar').andThrow('baz');
- *
- * @param {String} exceptionMsg
- */
-jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
- this.plan = function() {
- throw exceptionMsg;
- };
- return this;
-};
-
-/**
- * Calls an alternate implementation when a spy is called.
- *
- * @example
- * var baz = function() {
- * // do some stuff, return something
- * }
- * // defining a spy from scratch: foo() calls the function baz
- * var foo = jasmine.createSpy('spy on foo').andCall(baz);
- *
- * // defining a spy on an existing property: foo.bar() calls an anonymnous function
- * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
- *
- * @param {Function} fakeFunc
- */
-jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
- this.plan = fakeFunc;
- return this;
-};
-
-/**
- * Resets all of a spy's the tracking variables so that it can be used again.
- *
- * @example
- * spyOn(foo, 'bar');
- *
- * foo.bar();
- *
- * expect(foo.bar.callCount).toEqual(1);
- *
- * foo.bar.reset();
- *
- * expect(foo.bar.callCount).toEqual(0);
- */
-jasmine.Spy.prototype.reset = function() {
- this.wasCalled = false;
- this.callCount = 0;
- this.argsForCall = [];
- this.calls = [];
- this.mostRecentCall = {};
-};
-
-jasmine.createSpy = function(name) {
-
- var spyObj = function() {
- spyObj.wasCalled = true;
- spyObj.callCount++;
- var args = jasmine.util.argsToArray(arguments);
- spyObj.mostRecentCall.object = this;
- spyObj.mostRecentCall.args = args;
- spyObj.argsForCall.push(args);
- spyObj.calls.push({object: this, args: args});
- return spyObj.plan.apply(this, arguments);
- };
-
- var spy = new jasmine.Spy(name);
-
- for (var prop in spy) {
- spyObj[prop] = spy[prop];
- }
-
- spyObj.reset();
-
- return spyObj;
-};
-
-/**
- * Determines whether an object is a spy.
- *
- * @param {jasmine.Spy|Object} putativeSpy
- * @returns {Boolean}
- */
-jasmine.isSpy = function(putativeSpy) {
- return putativeSpy && putativeSpy.isSpy;
-};
-
-/**
- * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
- * large in one call.
- *
- * @param {String} baseName name of spy class
- * @param {Array} methodNames array of names of methods to make spies
- */
-jasmine.createSpyObj = function(baseName, methodNames) {
- if (!jasmine.isArray_(methodNames) || methodNames.length == 0) {
- throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
- }
- var obj = {};
- for (var i = 0; i < methodNames.length; i++) {
- obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
- }
- return obj;
-};
-
-/**
- * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
- *
- * Be careful not to leave calls to jasmine.log in production code.
- */
-jasmine.log = function() {
- var spec = jasmine.getEnv().currentSpec;
- spec.log.apply(spec, arguments);
-};
-
-/**
- * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
- *
- * @example
- * // spy example
- * var foo = {
- * not: function(bool) { return !bool; }
- * }
- * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
- *
- * @see jasmine.createSpy
- * @param obj
- * @param methodName
- * @returns a Jasmine spy that can be chained with all spy methods
- */
-var spyOn = function(obj, methodName) {
- return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
-};
-
-/**
- * Creates a Jasmine spec that will be added to the current suite.
- *
- * // TODO: pending tests
- *
- * @example
- * it('should be true', function() {
- * expect(true).toEqual(true);
- * });
- *
- * @param {String} desc description of this specification
- * @param {Function} func defines the preconditions and expectations of the spec
- */
-var it = function(desc, func) {
- return jasmine.getEnv().it(desc, func);
-};
-
-/**
- * Creates a disabled Jasmine spec.
- *
- * A convenience method that allows existing specs to be disabled temporarily during development.
- *
- * @param {String} desc description of this specification
- * @param {Function} func defines the preconditions and expectations of the spec
- */
-var xit = function(desc, func) {
- return jasmine.getEnv().xit(desc, func);
-};
-
-/**
- * Starts a chain for a Jasmine expectation.
- *
- * It is passed an Object that is the actual value and should chain to one of the many
- * jasmine.Matchers functions.
- *
- * @param {Object} actual Actual value to test against and expected value
- */
-var expect = function(actual) {
- return jasmine.getEnv().currentSpec.expect(actual);
-};
-
-/**
- * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
- *
- * @param {Function} func Function that defines part of a jasmine spec.
- */
-var runs = function(func) {
- jasmine.getEnv().currentSpec.runs(func);
-};
-
-/**
- * Waits a fixed time period before moving to the next block.
- *
- * @deprecated Use waitsFor() instead
- * @param {Number} timeout milliseconds to wait
- */
-var waits = function(timeout) {
- jasmine.getEnv().currentSpec.waits(timeout);
-};
-
-/**
- * Waits for the latchFunction to return true before proceeding to the next block.
- *
- * @param {Function} latchFunction
- * @param {String} optional_timeoutMessage
- * @param {Number} optional_timeout
- */
-var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
- jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
-};
-
-/**
- * A function that is called before each spec in a suite.
- *
- * Used for spec setup, including validating assumptions.
- *
- * @param {Function} beforeEachFunction
- */
-var beforeEach = function(beforeEachFunction) {
- jasmine.getEnv().beforeEach(beforeEachFunction);
-};
-
-/**
- * A function that is called after each spec in a suite.
- *
- * Used for restoring any state that is hijacked during spec execution.
- *
- * @param {Function} afterEachFunction
- */
-var afterEach = function(afterEachFunction) {
- jasmine.getEnv().afterEach(afterEachFunction);
-};
-
-/**
- * Defines a suite of specifications.
- *
- * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
- * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
- * of setup in some tests.
- *
- * @example
- * // TODO: a simple suite
- *
- * // TODO: a simple suite with a nested describe block
- *
- * @param {String} description A string, usually the class under test.
- * @param {Function} specDefinitions function that defines several specs.
- */
-var describe = function(description, specDefinitions) {
- return jasmine.getEnv().describe(description, specDefinitions);
-};
-
-/**
- * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
- *
- * @param {String} description A string, usually the class under test.
- * @param {Function} specDefinitions function that defines several specs.
- */
-var xdescribe = function(description, specDefinitions) {
- return jasmine.getEnv().xdescribe(description, specDefinitions);
-};
-
-
-// Provide the XMLHttpRequest class for IE 5.x-6.x:
-jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
- try {
- return new ActiveXObject("Msxml2.XMLHTTP.6.0");
- } catch(e) {
- }
- try {
- return new ActiveXObject("Msxml2.XMLHTTP.3.0");
- } catch(e) {
- }
- try {
- return new ActiveXObject("Msxml2.XMLHTTP");
- } catch(e) {
- }
- try {
- return new ActiveXObject("Microsoft.XMLHTTP");
- } catch(e) {
- }
- throw new Error("This browser does not support XMLHttpRequest.");
-} : XMLHttpRequest;
-/**
- * @namespace
- */
-jasmine.util = {};
-
-/**
- * Declare that a child class inherit it's prototype from the parent class.
- *
- * @private
- * @param {Function} childClass
- * @param {Function} parentClass
- */
-jasmine.util.inherit = function(childClass, parentClass) {
- /**
- * @private
- */
- var subclass = function() {
- };
- subclass.prototype = parentClass.prototype;
- childClass.prototype = new subclass;
-};
-
-jasmine.util.formatException = function(e) {
- var lineNumber;
- if (e.line) {
- lineNumber = e.line;
- }
- else if (e.lineNumber) {
- lineNumber = e.lineNumber;
- }
-
- var file;
-
- if (e.sourceURL) {
- file = e.sourceURL;
- }
- else if (e.fileName) {
- file = e.fileName;
- }
-
- var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
-
- if (file && lineNumber) {
- message += ' in ' + file + ' (line ' + lineNumber + ')';
- }
-
- return message;
-};
-
-jasmine.util.htmlEscape = function(str) {
- if (!str) return str;
- return str.replace(/&/g, '&')
- .replace(//g, '>');
-};
-
-jasmine.util.argsToArray = function(args) {
- var arrayOfArgs = [];
- for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
- return arrayOfArgs;
-};
-
-jasmine.util.extend = function(destination, source) {
- for (var property in source) destination[property] = source[property];
- return destination;
-};
-
-/**
- * Environment for Jasmine
- *
- * @constructor
- */
-jasmine.Env = function() {
- this.currentSpec = null;
- this.currentSuite = null;
- this.currentRunner_ = new jasmine.Runner(this);
-
- this.reporter = new jasmine.MultiReporter();
-
- this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
- this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
- this.lastUpdate = 0;
- this.specFilter = function() {
- return true;
- };
-
- this.nextSpecId_ = 0;
- this.nextSuiteId_ = 0;
- this.equalityTesters_ = [];
-
- // wrap matchers
- this.matchersClass = function() {
- jasmine.Matchers.apply(this, arguments);
- };
- jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
-
- jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
-};
-
-
-jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
-jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
-jasmine.Env.prototype.setInterval = jasmine.setInterval;
-jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
-
-/**
- * @returns an object containing jasmine version build info, if set.
- */
-jasmine.Env.prototype.version = function () {
- if (jasmine.version_) {
- return jasmine.version_;
- } else {
- throw new Error('Version not set');
- }
-};
-
-/**
- * @returns string containing jasmine version build info, if set.
- */
-jasmine.Env.prototype.versionString = function() {
- if (jasmine.version_) {
- var version = this.version();
- return version.major + "." + version.minor + "." + version.build + " revision " + version.revision;
- } else {
- return "version unknown";
- }
-};
-
-/**
- * @returns a sequential integer starting at 0
- */
-jasmine.Env.prototype.nextSpecId = function () {
- return this.nextSpecId_++;
-};
-
-/**
- * @returns a sequential integer starting at 0
- */
-jasmine.Env.prototype.nextSuiteId = function () {
- return this.nextSuiteId_++;
-};
-
-/**
- * Register a reporter to receive status updates from Jasmine.
- * @param {jasmine.Reporter} reporter An object which will receive status updates.
- */
-jasmine.Env.prototype.addReporter = function(reporter) {
- this.reporter.addReporter(reporter);
-};
-
-jasmine.Env.prototype.execute = function() {
- this.currentRunner_.execute();
-};
-
-jasmine.Env.prototype.describe = function(description, specDefinitions) {
- var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
-
- var parentSuite = this.currentSuite;
- if (parentSuite) {
- parentSuite.add(suite);
- } else {
- this.currentRunner_.add(suite);
- }
-
- this.currentSuite = suite;
-
- var declarationError = null;
- try {
- specDefinitions.call(suite);
- } catch(e) {
- declarationError = e;
- }
-
- this.currentSuite = parentSuite;
-
- if (declarationError) {
- this.it("encountered a declaration exception", function() {
- throw declarationError;
- });
- }
-
- return suite;
-};
-
-jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
- if (this.currentSuite) {
- this.currentSuite.beforeEach(beforeEachFunction);
- } else {
- this.currentRunner_.beforeEach(beforeEachFunction);
- }
-};
-
-jasmine.Env.prototype.currentRunner = function () {
- return this.currentRunner_;
-};
-
-jasmine.Env.prototype.afterEach = function(afterEachFunction) {
- if (this.currentSuite) {
- this.currentSuite.afterEach(afterEachFunction);
- } else {
- this.currentRunner_.afterEach(afterEachFunction);
- }
-
-};
-
-jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
- return {
- execute: function() {
- }
- };
-};
-
-jasmine.Env.prototype.it = function(description, func) {
- var spec = new jasmine.Spec(this, this.currentSuite, description);
- this.currentSuite.add(spec);
- this.currentSpec = spec;
-
- if (func) {
- spec.runs(func);
- }
-
- return spec;
-};
-
-jasmine.Env.prototype.xit = function(desc, func) {
- return {
- id: this.nextSpecId(),
- runs: function() {
- }
- };
-};
-
-jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
- if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
- return true;
- }
-
- a.__Jasmine_been_here_before__ = b;
- b.__Jasmine_been_here_before__ = a;
-
- var hasKey = function(obj, keyName) {
- return obj != null && obj[keyName] !== jasmine.undefined;
- };
-
- for (var property in b) {
- if (!hasKey(a, property) && hasKey(b, property)) {
- mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
- }
- }
- for (property in a) {
- if (!hasKey(b, property) && hasKey(a, property)) {
- mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
- }
- }
- for (property in b) {
- if (property == '__Jasmine_been_here_before__') continue;
- if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
- mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
- }
- }
-
- if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
- mismatchValues.push("arrays were not the same length");
- }
-
- delete a.__Jasmine_been_here_before__;
- delete b.__Jasmine_been_here_before__;
- return (mismatchKeys.length == 0 && mismatchValues.length == 0);
-};
-
-jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
- mismatchKeys = mismatchKeys || [];
- mismatchValues = mismatchValues || [];
-
- for (var i = 0; i < this.equalityTesters_.length; i++) {
- var equalityTester = this.equalityTesters_[i];
- var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
- if (result !== jasmine.undefined) return result;
- }
-
- if (a === b) return true;
-
- if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
- return (a == jasmine.undefined && b == jasmine.undefined);
- }
-
- if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
- return a === b;
- }
-
- if (a instanceof Date && b instanceof Date) {
- return a.getTime() == b.getTime();
- }
-
- if (a instanceof jasmine.Matchers.Any) {
- return a.matches(b);
- }
-
- if (b instanceof jasmine.Matchers.Any) {
- return b.matches(a);
- }
-
- if (jasmine.isString_(a) && jasmine.isString_(b)) {
- return (a == b);
- }
-
- if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
- return (a == b);
- }
-
- if (typeof a === "object" && typeof b === "object") {
- return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
- }
-
- //Straight check
- return (a === b);
-};
-
-jasmine.Env.prototype.contains_ = function(haystack, needle) {
- if (jasmine.isArray_(haystack)) {
- for (var i = 0; i < haystack.length; i++) {
- if (this.equals_(haystack[i], needle)) return true;
- }
- return false;
- }
- return haystack.indexOf(needle) >= 0;
-};
-
-jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
- this.equalityTesters_.push(equalityTester);
-};
-/** No-op base class for Jasmine reporters.
- *
- * @constructor
- */
-jasmine.Reporter = function() {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportSpecResults = function(spec) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.log = function(str) {
-};
-
-/**
- * Blocks are functions with executable code that make up a spec.
- *
- * @constructor
- * @param {jasmine.Env} env
- * @param {Function} func
- * @param {jasmine.Spec} spec
- */
-jasmine.Block = function(env, func, spec) {
- this.env = env;
- this.func = func;
- this.spec = spec;
-};
-
-jasmine.Block.prototype.execute = function(onComplete) {
- try {
- this.func.apply(this.spec);
- } catch (e) {
- this.spec.fail(e);
- }
- onComplete();
-};
-/** JavaScript API reporter.
- *
- * @constructor
- */
-jasmine.JsApiReporter = function() {
- this.started = false;
- this.finished = false;
- this.suites_ = [];
- this.results_ = {};
-};
-
-jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
- this.started = true;
- var suites = runner.topLevelSuites();
- for (var i = 0; i < suites.length; i++) {
- var suite = suites[i];
- this.suites_.push(this.summarize_(suite));
- }
-};
-
-jasmine.JsApiReporter.prototype.suites = function() {
- return this.suites_;
-};
-
-jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
- var isSuite = suiteOrSpec instanceof jasmine.Suite;
- var summary = {
- id: suiteOrSpec.id,
- name: suiteOrSpec.description,
- type: isSuite ? 'suite' : 'spec',
- children: []
- };
-
- if (isSuite) {
- var children = suiteOrSpec.children();
- for (var i = 0; i < children.length; i++) {
- summary.children.push(this.summarize_(children[i]));
- }
- }
- return summary;
-};
-
-jasmine.JsApiReporter.prototype.results = function() {
- return this.results_;
-};
-
-jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
- return this.results_[specId];
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
- this.finished = true;
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
- this.results_[spec.id] = {
- messages: spec.results().getItems(),
- result: spec.results().failedCount > 0 ? "failed" : "passed"
- };
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.log = function(str) {
-};
-
-jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
- var results = {};
- for (var i = 0; i < specIds.length; i++) {
- var specId = specIds[i];
- results[specId] = this.summarizeResult_(this.results_[specId]);
- }
- return results;
-};
-
-jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
- var summaryMessages = [];
- var messagesLength = result.messages.length;
- for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
- var resultMessage = result.messages[messageIndex];
- summaryMessages.push({
- text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
- passed: resultMessage.passed ? resultMessage.passed() : true,
- type: resultMessage.type,
- message: resultMessage.message,
- trace: {
- stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
- }
- });
- }
-
- return {
- result : result.result,
- messages : summaryMessages
- };
-};
-
-/**
- * @constructor
- * @param {jasmine.Env} env
- * @param actual
- * @param {jasmine.Spec} spec
- */
-jasmine.Matchers = function(env, actual, spec, opt_isNot) {
- this.env = env;
- this.actual = actual;
- this.spec = spec;
- this.isNot = opt_isNot || false;
- this.reportWasCalled_ = false;
-};
-
-// todo: @deprecated as of Jasmine 0.11, remove soon [xw]
-jasmine.Matchers.pp = function(str) {
- throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
-};
-
-// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
-jasmine.Matchers.prototype.report = function(result, failing_message, details) {
- throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
-};
-
-jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
- for (var methodName in prototype) {
- if (methodName == 'report') continue;
- var orig = prototype[methodName];
- matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
- }
-};
-
-jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
- return function() {
- var matcherArgs = jasmine.util.argsToArray(arguments);
- var result = matcherFunction.apply(this, arguments);
-
- if (this.isNot) {
- result = !result;
- }
-
- if (this.reportWasCalled_) return result;
-
- var message;
- if (!result) {
- if (this.message) {
- message = this.message.apply(this, arguments);
- if (jasmine.isArray_(message)) {
- message = message[this.isNot ? 1 : 0];
- }
- } else {
- var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
- message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
- if (matcherArgs.length > 0) {
- for (var i = 0; i < matcherArgs.length; i++) {
- if (i > 0) message += ",";
- message += " " + jasmine.pp(matcherArgs[i]);
- }
- }
- message += ".";
- }
- }
- var expectationResult = new jasmine.ExpectationResult({
- matcherName: matcherName,
- passed: result,
- expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
- actual: this.actual,
- message: message
- });
- this.spec.addMatcherResult(expectationResult);
- return jasmine.undefined;
- };
-};
-
-
-
-
-/**
- * toBe: compares the actual to the expected using ===
- * @param expected
- */
-jasmine.Matchers.prototype.toBe = function(expected) {
- return this.actual === expected;
-};
-
-/**
- * toNotBe: compares the actual to the expected using !==
- * @param expected
- * @deprecated as of 1.0. Use not.toBe() instead.
- */
-jasmine.Matchers.prototype.toNotBe = function(expected) {
- return this.actual !== expected;
-};
-
-/**
- * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
- *
- * @param expected
- */
-jasmine.Matchers.prototype.toEqual = function(expected) {
- return this.env.equals_(this.actual, expected);
-};
-
-/**
- * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
- * @param expected
- * @deprecated as of 1.0. Use not.toNotEqual() instead.
- */
-jasmine.Matchers.prototype.toNotEqual = function(expected) {
- return !this.env.equals_(this.actual, expected);
-};
-
-/**
- * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes
- * a pattern or a String.
- *
- * @param expected
- */
-jasmine.Matchers.prototype.toMatch = function(expected) {
- return new RegExp(expected).test(this.actual);
-};
-
-/**
- * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
- * @param expected
- * @deprecated as of 1.0. Use not.toMatch() instead.
- */
-jasmine.Matchers.prototype.toNotMatch = function(expected) {
- return !(new RegExp(expected).test(this.actual));
-};
-
-/**
- * Matcher that compares the actual to jasmine.undefined.
- */
-jasmine.Matchers.prototype.toBeDefined = function() {
- return (this.actual !== jasmine.undefined);
-};
-
-/**
- * Matcher that compares the actual to jasmine.undefined.
- */
-jasmine.Matchers.prototype.toBeUndefined = function() {
- return (this.actual === jasmine.undefined);
-};
-
-/**
- * Matcher that compares the actual to null.
- */
-jasmine.Matchers.prototype.toBeNull = function() {
- return (this.actual === null);
-};
-
-/**
- * Matcher that boolean not-nots the actual.
- */
-jasmine.Matchers.prototype.toBeTruthy = function() {
- return !!this.actual;
-};
-
-
-/**
- * Matcher that boolean nots the actual.
- */
-jasmine.Matchers.prototype.toBeFalsy = function() {
- return !this.actual;
-};
-
-
-/**
- * Matcher that checks to see if the actual, a Jasmine spy, was called.
- */
-jasmine.Matchers.prototype.toHaveBeenCalled = function() {
- if (arguments.length > 0) {
- throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
- }
-
- if (!jasmine.isSpy(this.actual)) {
- throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
- }
-
- this.message = function() {
- return [
- "Expected spy " + this.actual.identity + " to have been called.",
- "Expected spy " + this.actual.identity + " not to have been called."
- ];
- };
-
- return this.actual.wasCalled;
-};
-
-/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
-jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
-
-/**
- * Matcher that checks to see if the actual, a Jasmine spy, was not called.
- *
- * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
- */
-jasmine.Matchers.prototype.wasNotCalled = function() {
- if (arguments.length > 0) {
- throw new Error('wasNotCalled does not take arguments');
- }
-
- if (!jasmine.isSpy(this.actual)) {
- throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
- }
-
- this.message = function() {
- return [
- "Expected spy " + this.actual.identity + " to not have been called.",
- "Expected spy " + this.actual.identity + " to have been called."
- ];
- };
-
- return !this.actual.wasCalled;
-};
-
-/**
- * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
- *
- * @example
- *
- */
-jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
- var expectedArgs = jasmine.util.argsToArray(arguments);
- if (!jasmine.isSpy(this.actual)) {
- throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
- }
- this.message = function() {
- if (this.actual.callCount == 0) {
- // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw]
- return [
- "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.",
- "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was."
- ];
- } else {
- return [
- "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall),
- "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall)
- ];
- }
- };
-
- return this.env.contains_(this.actual.argsForCall, expectedArgs);
-};
-
-/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
-jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
-
-/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
-jasmine.Matchers.prototype.wasNotCalledWith = function() {
- var expectedArgs = jasmine.util.argsToArray(arguments);
- if (!jasmine.isSpy(this.actual)) {
- throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
- }
-
- this.message = function() {
- return [
- "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
- "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
- ]
- };
-
- return !this.env.contains_(this.actual.argsForCall, expectedArgs);
-};
-
-/**
- * Matcher that checks that the expected item is an element in the actual Array.
- *
- * @param {Object} expected
- */
-jasmine.Matchers.prototype.toContain = function(expected) {
- return this.env.contains_(this.actual, expected);
-};
-
-/**
- * Matcher that checks that the expected item is NOT an element in the actual Array.
- *
- * @param {Object} expected
- * @deprecated as of 1.0. Use not.toNotContain() instead.
- */
-jasmine.Matchers.prototype.toNotContain = function(expected) {
- return !this.env.contains_(this.actual, expected);
-};
-
-jasmine.Matchers.prototype.toBeLessThan = function(expected) {
- return this.actual < expected;
-};
-
-jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
- return this.actual > expected;
-};
-
-/**
- * Matcher that checks that the expected exception was thrown by the actual.
- *
- * @param {String} expected
- */
-jasmine.Matchers.prototype.toThrow = function(expected) {
- var result = false;
- var exception;
- if (typeof this.actual != 'function') {
- throw new Error('Actual is not a function');
- }
- try {
- this.actual();
- } catch (e) {
- exception = e;
- }
- if (exception) {
- result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
- }
-
- var not = this.isNot ? "not " : "";
-
- this.message = function() {
- if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
- return ["Expected function " + not + "to throw", expected ? expected.message || expected : " an exception", ", but it threw", exception.message || exception].join(' ');
- } else {
- return "Expected function to throw an exception.";
- }
- };
-
- return result;
-};
-
-jasmine.Matchers.Any = function(expectedClass) {
- this.expectedClass = expectedClass;
-};
-
-jasmine.Matchers.Any.prototype.matches = function(other) {
- if (this.expectedClass == String) {
- return typeof other == 'string' || other instanceof String;
- }
-
- if (this.expectedClass == Number) {
- return typeof other == 'number' || other instanceof Number;
- }
-
- if (this.expectedClass == Function) {
- return typeof other == 'function' || other instanceof Function;
- }
-
- if (this.expectedClass == Object) {
- return typeof other == 'object';
- }
-
- return other instanceof this.expectedClass;
-};
-
-jasmine.Matchers.Any.prototype.toString = function() {
- return '';
-};
-
-/**
- * @constructor
- */
-jasmine.MultiReporter = function() {
- this.subReporters_ = [];
-};
-jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
-
-jasmine.MultiReporter.prototype.addReporter = function(reporter) {
- this.subReporters_.push(reporter);
-};
-
-(function() {
- var functionNames = [
- "reportRunnerStarting",
- "reportRunnerResults",
- "reportSuiteResults",
- "reportSpecStarting",
- "reportSpecResults",
- "log"
- ];
- for (var i = 0; i < functionNames.length; i++) {
- var functionName = functionNames[i];
- jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
- return function() {
- for (var j = 0; j < this.subReporters_.length; j++) {
- var subReporter = this.subReporters_[j];
- if (subReporter[functionName]) {
- subReporter[functionName].apply(subReporter, arguments);
- }
- }
- };
- })(functionName);
- }
-})();
-/**
- * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
- *
- * @constructor
- */
-jasmine.NestedResults = function() {
- /**
- * The total count of results
- */
- this.totalCount = 0;
- /**
- * Number of passed results
- */
- this.passedCount = 0;
- /**
- * Number of failed results
- */
- this.failedCount = 0;
- /**
- * Was this suite/spec skipped?
- */
- this.skipped = false;
- /**
- * @ignore
- */
- this.items_ = [];
-};
-
-/**
- * Roll up the result counts.
- *
- * @param result
- */
-jasmine.NestedResults.prototype.rollupCounts = function(result) {
- this.totalCount += result.totalCount;
- this.passedCount += result.passedCount;
- this.failedCount += result.failedCount;
-};
-
-/**
- * Adds a log message.
- * @param values Array of message parts which will be concatenated later.
- */
-jasmine.NestedResults.prototype.log = function(values) {
- this.items_.push(new jasmine.MessageResult(values));
-};
-
-/**
- * Getter for the results: message & results.
- */
-jasmine.NestedResults.prototype.getItems = function() {
- return this.items_;
-};
-
-/**
- * Adds a result, tracking counts (total, passed, & failed)
- * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
- */
-jasmine.NestedResults.prototype.addResult = function(result) {
- if (result.type != 'log') {
- if (result.items_) {
- this.rollupCounts(result);
- } else {
- this.totalCount++;
- if (result.passed()) {
- this.passedCount++;
- } else {
- this.failedCount++;
- }
- }
- }
- this.items_.push(result);
-};
-
-/**
- * @returns {Boolean} True if everything below passed
- */
-jasmine.NestedResults.prototype.passed = function() {
- return this.passedCount === this.totalCount;
-};
-/**
- * Base class for pretty printing for expectation results.
- */
-jasmine.PrettyPrinter = function() {
- this.ppNestLevel_ = 0;
-};
-
-/**
- * Formats a value in a nice, human-readable string.
- *
- * @param value
- */
-jasmine.PrettyPrinter.prototype.format = function(value) {
- if (this.ppNestLevel_ > 40) {
- throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
- }
-
- this.ppNestLevel_++;
- try {
- if (value === jasmine.undefined) {
- this.emitScalar('undefined');
- } else if (value === null) {
- this.emitScalar('null');
- } else if (value === jasmine.getGlobal()) {
- this.emitScalar('');
- } else if (value instanceof jasmine.Matchers.Any) {
- this.emitScalar(value.toString());
- } else if (typeof value === 'string') {
- this.emitString(value);
- } else if (jasmine.isSpy(value)) {
- this.emitScalar("spy on " + value.identity);
- } else if (value instanceof RegExp) {
- this.emitScalar(value.toString());
- } else if (typeof value === 'function') {
- this.emitScalar('Function');
- } else if (typeof value.nodeType === 'number') {
- this.emitScalar('HTMLNode');
- } else if (value instanceof Date) {
- this.emitScalar('Date(' + value + ')');
- } else if (value.__Jasmine_been_here_before__) {
- this.emitScalar('');
- } else if (jasmine.isArray_(value) || typeof value == 'object') {
- value.__Jasmine_been_here_before__ = true;
- if (jasmine.isArray_(value)) {
- this.emitArray(value);
- } else {
- this.emitObject(value);
- }
- delete value.__Jasmine_been_here_before__;
- } else {
- this.emitScalar(value.toString());
- }
- } finally {
- this.ppNestLevel_--;
- }
-};
-
-jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
- for (var property in obj) {
- if (property == '__Jasmine_been_here_before__') continue;
- fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) != null) : false);
- }
-};
-
-jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
-jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
-jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
-jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
-
-jasmine.StringPrettyPrinter = function() {
- jasmine.PrettyPrinter.call(this);
-
- this.string = '';
-};
-jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
-
-jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
- this.append(value);
-};
-
-jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
- this.append("'" + value + "'");
-};
-
-jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
- this.append('[ ');
- for (var i = 0; i < array.length; i++) {
- if (i > 0) {
- this.append(', ');
- }
- this.format(array[i]);
- }
- this.append(' ]');
-};
-
-jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
- var self = this;
- this.append('{ ');
- var first = true;
-
- this.iterateObject(obj, function(property, isGetter) {
- if (first) {
- first = false;
- } else {
- self.append(', ');
- }
-
- self.append(property);
- self.append(' : ');
- if (isGetter) {
- self.append('');
- } else {
- self.format(obj[property]);
- }
- });
-
- this.append(' }');
-};
-
-jasmine.StringPrettyPrinter.prototype.append = function(value) {
- this.string += value;
-};
-jasmine.Queue = function(env) {
- this.env = env;
- this.blocks = [];
- this.running = false;
- this.index = 0;
- this.offset = 0;
- this.abort = false;
-};
-
-jasmine.Queue.prototype.addBefore = function(block) {
- this.blocks.unshift(block);
-};
-
-jasmine.Queue.prototype.add = function(block) {
- this.blocks.push(block);
-};
-
-jasmine.Queue.prototype.insertNext = function(block) {
- this.blocks.splice((this.index + this.offset + 1), 0, block);
- this.offset++;
-};
-
-jasmine.Queue.prototype.start = function(onComplete) {
- this.running = true;
- this.onComplete = onComplete;
- this.next_();
-};
-
-jasmine.Queue.prototype.isRunning = function() {
- return this.running;
-};
-
-jasmine.Queue.LOOP_DONT_RECURSE = true;
-
-jasmine.Queue.prototype.next_ = function() {
- var self = this;
- var goAgain = true;
-
- while (goAgain) {
- goAgain = false;
-
- if (self.index < self.blocks.length && !this.abort) {
- var calledSynchronously = true;
- var completedSynchronously = false;
-
- var onComplete = function () {
- if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
- completedSynchronously = true;
- return;
- }
-
- if (self.blocks[self.index].abort) {
- self.abort = true;
- }
-
- self.offset = 0;
- self.index++;
-
- var now = new Date().getTime();
- if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
- self.env.lastUpdate = now;
- self.env.setTimeout(function() {
- self.next_();
- }, 0);
- } else {
- if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
- goAgain = true;
- } else {
- self.next_();
- }
- }
- };
- self.blocks[self.index].execute(onComplete);
-
- calledSynchronously = false;
- if (completedSynchronously) {
- onComplete();
- }
-
- } else {
- self.running = false;
- if (self.onComplete) {
- self.onComplete();
- }
- }
- }
-};
-
-jasmine.Queue.prototype.results = function() {
- var results = new jasmine.NestedResults();
- for (var i = 0; i < this.blocks.length; i++) {
- if (this.blocks[i].results) {
- results.addResult(this.blocks[i].results());
- }
- }
- return results;
-};
-
-
-/**
- * Runner
- *
- * @constructor
- * @param {jasmine.Env} env
- */
-jasmine.Runner = function(env) {
- var self = this;
- self.env = env;
- self.queue = new jasmine.Queue(env);
- self.before_ = [];
- self.after_ = [];
- self.suites_ = [];
-};
-
-jasmine.Runner.prototype.execute = function() {
- var self = this;
- if (self.env.reporter.reportRunnerStarting) {
- self.env.reporter.reportRunnerStarting(this);
- }
- self.queue.start(function () {
- self.finishCallback();
- });
-};
-
-jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
- beforeEachFunction.typeName = 'beforeEach';
- this.before_.splice(0,0,beforeEachFunction);
-};
-
-jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
- afterEachFunction.typeName = 'afterEach';
- this.after_.splice(0,0,afterEachFunction);
-};
-
-
-jasmine.Runner.prototype.finishCallback = function() {
- this.env.reporter.reportRunnerResults(this);
-};
-
-jasmine.Runner.prototype.addSuite = function(suite) {
- this.suites_.push(suite);
-};
-
-jasmine.Runner.prototype.add = function(block) {
- if (block instanceof jasmine.Suite) {
- this.addSuite(block);
- }
- this.queue.add(block);
-};
-
-jasmine.Runner.prototype.specs = function () {
- var suites = this.suites();
- var specs = [];
- for (var i = 0; i < suites.length; i++) {
- specs = specs.concat(suites[i].specs());
- }
- return specs;
-};
-
-jasmine.Runner.prototype.suites = function() {
- return this.suites_;
-};
-
-jasmine.Runner.prototype.topLevelSuites = function() {
- var topLevelSuites = [];
- for (var i = 0; i < this.suites_.length; i++) {
- if (!this.suites_[i].parentSuite) {
- topLevelSuites.push(this.suites_[i]);
- }
- }
- return topLevelSuites;
-};
-
-jasmine.Runner.prototype.results = function() {
- return this.queue.results();
-};
-/**
- * Internal representation of a Jasmine specification, or test.
- *
- * @constructor
- * @param {jasmine.Env} env
- * @param {jasmine.Suite} suite
- * @param {String} description
- */
-jasmine.Spec = function(env, suite, description) {
- if (!env) {
- throw new Error('jasmine.Env() required');
- }
- if (!suite) {
- throw new Error('jasmine.Suite() required');
- }
- var spec = this;
- spec.id = env.nextSpecId ? env.nextSpecId() : null;
- spec.env = env;
- spec.suite = suite;
- spec.description = description;
- spec.queue = new jasmine.Queue(env);
-
- spec.afterCallbacks = [];
- spec.spies_ = [];
-
- spec.results_ = new jasmine.NestedResults();
- spec.results_.description = description;
- spec.matchersClass = null;
-};
-
-jasmine.Spec.prototype.getFullName = function() {
- return this.suite.getFullName() + ' ' + this.description + '.';
-};
-
-
-jasmine.Spec.prototype.results = function() {
- return this.results_;
-};
-
-/**
- * All parameters are pretty-printed and concatenated together, then written to the spec's output.
- *
- * Be careful not to leave calls to jasmine.log in production code.
- */
-jasmine.Spec.prototype.log = function() {
- return this.results_.log(arguments);
-};
-
-jasmine.Spec.prototype.runs = function (func) {
- var block = new jasmine.Block(this.env, func, this);
- this.addToQueue(block);
- return this;
-};
-
-jasmine.Spec.prototype.addToQueue = function (block) {
- if (this.queue.isRunning()) {
- this.queue.insertNext(block);
- } else {
- this.queue.add(block);
- }
-};
-
-/**
- * @param {jasmine.ExpectationResult} result
- */
-jasmine.Spec.prototype.addMatcherResult = function(result) {
- this.results_.addResult(result);
-};
-
-jasmine.Spec.prototype.expect = function(actual) {
- var positive = new (this.getMatchersClass_())(this.env, actual, this);
- positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
- return positive;
-};
-
-/**
- * Waits a fixed time period before moving to the next block.
- *
- * @deprecated Use waitsFor() instead
- * @param {Number} timeout milliseconds to wait
- */
-jasmine.Spec.prototype.waits = function(timeout) {
- var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
- this.addToQueue(waitsFunc);
- return this;
-};
-
-/**
- * Waits for the latchFunction to return true before proceeding to the next block.
- *
- * @param {Function} latchFunction
- * @param {String} optional_timeoutMessage
- * @param {Number} optional_timeout
- */
-jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
- var latchFunction_ = null;
- var optional_timeoutMessage_ = null;
- var optional_timeout_ = null;
-
- for (var i = 0; i < arguments.length; i++) {
- var arg = arguments[i];
- switch (typeof arg) {
- case 'function':
- latchFunction_ = arg;
- break;
- case 'string':
- optional_timeoutMessage_ = arg;
- break;
- case 'number':
- optional_timeout_ = arg;
- break;
- }
- }
-
- var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
- this.addToQueue(waitsForFunc);
- return this;
-};
-
-jasmine.Spec.prototype.fail = function (e) {
- var expectationResult = new jasmine.ExpectationResult({
- passed: false,
- message: e ? jasmine.util.formatException(e) : 'Exception'
- });
- this.results_.addResult(expectationResult);
-};
-
-jasmine.Spec.prototype.getMatchersClass_ = function() {
- return this.matchersClass || this.env.matchersClass;
-};
-
-jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
- var parent = this.getMatchersClass_();
- var newMatchersClass = function() {
- parent.apply(this, arguments);
- };
- jasmine.util.inherit(newMatchersClass, parent);
- jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
- this.matchersClass = newMatchersClass;
-};
-
-jasmine.Spec.prototype.finishCallback = function() {
- this.env.reporter.reportSpecResults(this);
-};
-
-jasmine.Spec.prototype.finish = function(onComplete) {
- this.removeAllSpies();
- this.finishCallback();
- if (onComplete) {
- onComplete();
- }
-};
-
-jasmine.Spec.prototype.after = function(doAfter) {
- if (this.queue.isRunning()) {
- this.queue.add(new jasmine.Block(this.env, doAfter, this));
- } else {
- this.afterCallbacks.unshift(doAfter);
- }
-};
-
-jasmine.Spec.prototype.execute = function(onComplete) {
- var spec = this;
- if (!spec.env.specFilter(spec)) {
- spec.results_.skipped = true;
- spec.finish(onComplete);
- return;
- }
-
- this.env.reporter.reportSpecStarting(this);
-
- spec.env.currentSpec = spec;
-
- spec.addBeforesAndAftersToQueue();
-
- spec.queue.start(function () {
- spec.finish(onComplete);
- });
-};
-
-jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
- var runner = this.env.currentRunner();
- var i;
-
- for (var suite = this.suite; suite; suite = suite.parentSuite) {
- for (i = 0; i < suite.before_.length; i++) {
- this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
- }
- }
- for (i = 0; i < runner.before_.length; i++) {
- this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
- }
- for (i = 0; i < this.afterCallbacks.length; i++) {
- this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
- }
- for (suite = this.suite; suite; suite = suite.parentSuite) {
- for (i = 0; i < suite.after_.length; i++) {
- this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
- }
- }
- for (i = 0; i < runner.after_.length; i++) {
- this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
- }
-};
-
-jasmine.Spec.prototype.explodes = function() {
- throw 'explodes function should not have been called';
-};
-
-jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
- if (obj == jasmine.undefined) {
- throw "spyOn could not find an object to spy upon for " + methodName + "()";
- }
-
- if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
- throw methodName + '() method does not exist';
- }
-
- if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
- throw new Error(methodName + ' has already been spied upon');
- }
-
- var spyObj = jasmine.createSpy(methodName);
-
- this.spies_.push(spyObj);
- spyObj.baseObj = obj;
- spyObj.methodName = methodName;
- spyObj.originalValue = obj[methodName];
-
- obj[methodName] = spyObj;
-
- return spyObj;
-};
-
-jasmine.Spec.prototype.removeAllSpies = function() {
- for (var i = 0; i < this.spies_.length; i++) {
- var spy = this.spies_[i];
- spy.baseObj[spy.methodName] = spy.originalValue;
- }
- this.spies_ = [];
-};
-
-/**
- * Internal representation of a Jasmine suite.
- *
- * @constructor
- * @param {jasmine.Env} env
- * @param {String} description
- * @param {Function} specDefinitions
- * @param {jasmine.Suite} parentSuite
- */
-jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
- var self = this;
- self.id = env.nextSuiteId ? env.nextSuiteId() : null;
- self.description = description;
- self.queue = new jasmine.Queue(env);
- self.parentSuite = parentSuite;
- self.env = env;
- self.before_ = [];
- self.after_ = [];
- self.children_ = [];
- self.suites_ = [];
- self.specs_ = [];
-};
-
-jasmine.Suite.prototype.getFullName = function() {
- var fullName = this.description;
- for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
- fullName = parentSuite.description + ' ' + fullName;
- }
- return fullName;
-};
-
-jasmine.Suite.prototype.finish = function(onComplete) {
- this.env.reporter.reportSuiteResults(this);
- this.finished = true;
- if (typeof(onComplete) == 'function') {
- onComplete();
- }
-};
-
-jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
- beforeEachFunction.typeName = 'beforeEach';
- this.before_.unshift(beforeEachFunction);
-};
-
-jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
- afterEachFunction.typeName = 'afterEach';
- this.after_.unshift(afterEachFunction);
-};
-
-jasmine.Suite.prototype.results = function() {
- return this.queue.results();
-};
-
-jasmine.Suite.prototype.add = function(suiteOrSpec) {
- this.children_.push(suiteOrSpec);
- if (suiteOrSpec instanceof jasmine.Suite) {
- this.suites_.push(suiteOrSpec);
- this.env.currentRunner().addSuite(suiteOrSpec);
- } else {
- this.specs_.push(suiteOrSpec);
- }
- this.queue.add(suiteOrSpec);
-};
-
-jasmine.Suite.prototype.specs = function() {
- return this.specs_;
-};
-
-jasmine.Suite.prototype.suites = function() {
- return this.suites_;
-};
-
-jasmine.Suite.prototype.children = function() {
- return this.children_;
-};
-
-jasmine.Suite.prototype.execute = function(onComplete) {
- var self = this;
- this.queue.start(function () {
- self.finish(onComplete);
- });
-};
-jasmine.WaitsBlock = function(env, timeout, spec) {
- this.timeout = timeout;
- jasmine.Block.call(this, env, null, spec);
-};
-
-jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
-
-jasmine.WaitsBlock.prototype.execute = function (onComplete) {
- this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
- this.env.setTimeout(function () {
- onComplete();
- }, this.timeout);
-};
-/**
- * A block which waits for some condition to become true, with timeout.
- *
- * @constructor
- * @extends jasmine.Block
- * @param {jasmine.Env} env The Jasmine environment.
- * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
- * @param {Function} latchFunction A function which returns true when the desired condition has been met.
- * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
- * @param {jasmine.Spec} spec The Jasmine spec.
- */
-jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
- this.timeout = timeout || env.defaultTimeoutInterval;
- this.latchFunction = latchFunction;
- this.message = message;
- this.totalTimeSpentWaitingForLatch = 0;
- jasmine.Block.call(this, env, null, spec);
-};
-jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
-
-jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
-
-jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
- this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
- var latchFunctionResult;
- try {
- latchFunctionResult = this.latchFunction.apply(this.spec);
- } catch (e) {
- this.spec.fail(e);
- onComplete();
- return;
- }
-
- if (latchFunctionResult) {
- onComplete();
- } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
- var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
- this.spec.fail({
- name: 'timeout',
- message: message
- });
-
- this.abort = true;
- onComplete();
- } else {
- this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
- var self = this;
- this.env.setTimeout(function() {
- self.execute(onComplete);
- }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
- }
-};
-// Mock setTimeout, clearTimeout
-// Contributed by Pivotal Computer Systems, www.pivotalsf.com
-
-jasmine.FakeTimer = function() {
- this.reset();
-
- var self = this;
- self.setTimeout = function(funcToCall, millis) {
- self.timeoutsMade++;
- self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
- return self.timeoutsMade;
- };
-
- self.setInterval = function(funcToCall, millis) {
- self.timeoutsMade++;
- self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
- return self.timeoutsMade;
- };
-
- self.clearTimeout = function(timeoutKey) {
- self.scheduledFunctions[timeoutKey] = jasmine.undefined;
- };
-
- self.clearInterval = function(timeoutKey) {
- self.scheduledFunctions[timeoutKey] = jasmine.undefined;
- };
-
-};
-
-jasmine.FakeTimer.prototype.reset = function() {
- this.timeoutsMade = 0;
- this.scheduledFunctions = {};
- this.nowMillis = 0;
-};
-
-jasmine.FakeTimer.prototype.tick = function(millis) {
- var oldMillis = this.nowMillis;
- var newMillis = oldMillis + millis;
- this.runFunctionsWithinRange(oldMillis, newMillis);
- this.nowMillis = newMillis;
-};
-
-jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
- var scheduledFunc;
- var funcsToRun = [];
- for (var timeoutKey in this.scheduledFunctions) {
- scheduledFunc = this.scheduledFunctions[timeoutKey];
- if (scheduledFunc != jasmine.undefined &&
- scheduledFunc.runAtMillis >= oldMillis &&
- scheduledFunc.runAtMillis <= nowMillis) {
- funcsToRun.push(scheduledFunc);
- this.scheduledFunctions[timeoutKey] = jasmine.undefined;
- }
- }
-
- if (funcsToRun.length > 0) {
- funcsToRun.sort(function(a, b) {
- return a.runAtMillis - b.runAtMillis;
- });
- for (var i = 0; i < funcsToRun.length; ++i) {
- try {
- var funcToRun = funcsToRun[i];
- this.nowMillis = funcToRun.runAtMillis;
- funcToRun.funcToCall();
- if (funcToRun.recurring) {
- this.scheduleFunction(funcToRun.timeoutKey,
- funcToRun.funcToCall,
- funcToRun.millis,
- true);
- }
- } catch(e) {
- }
- }
- this.runFunctionsWithinRange(oldMillis, nowMillis);
- }
-};
-
-jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
- this.scheduledFunctions[timeoutKey] = {
- runAtMillis: this.nowMillis + millis,
- funcToCall: funcToCall,
- recurring: recurring,
- timeoutKey: timeoutKey,
- millis: millis
- };
-};
-
-/**
- * @namespace
- */
-jasmine.Clock = {
- defaultFakeTimer: new jasmine.FakeTimer(),
-
- reset: function() {
- jasmine.Clock.assertInstalled();
- jasmine.Clock.defaultFakeTimer.reset();
- },
-
- tick: function(millis) {
- jasmine.Clock.assertInstalled();
- jasmine.Clock.defaultFakeTimer.tick(millis);
- },
-
- runFunctionsWithinRange: function(oldMillis, nowMillis) {
- jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
- },
-
- scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
- jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
- },
-
- useMock: function() {
- if (!jasmine.Clock.isInstalled()) {
- var spec = jasmine.getEnv().currentSpec;
- spec.after(jasmine.Clock.uninstallMock);
-
- jasmine.Clock.installMock();
- }
- },
-
- installMock: function() {
- jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
- },
-
- uninstallMock: function() {
- jasmine.Clock.assertInstalled();
- jasmine.Clock.installed = jasmine.Clock.real;
- },
-
- real: {
- setTimeout: jasmine.getGlobal().setTimeout,
- clearTimeout: jasmine.getGlobal().clearTimeout,
- setInterval: jasmine.getGlobal().setInterval,
- clearInterval: jasmine.getGlobal().clearInterval
- },
-
- assertInstalled: function() {
- if (!jasmine.Clock.isInstalled()) {
- throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
- }
- },
-
- isInstalled: function() {
- return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
- },
-
- installed: null
-};
-jasmine.Clock.installed = jasmine.Clock.real;
-
-//else for IE support
-jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
- if (jasmine.Clock.installed.setTimeout.apply) {
- return jasmine.Clock.installed.setTimeout.apply(this, arguments);
- } else {
- return jasmine.Clock.installed.setTimeout(funcToCall, millis);
- }
-};
-
-jasmine.getGlobal().setInterval = function(funcToCall, millis) {
- if (jasmine.Clock.installed.setInterval.apply) {
- return jasmine.Clock.installed.setInterval.apply(this, arguments);
- } else {
- return jasmine.Clock.installed.setInterval(funcToCall, millis);
- }
-};
-
-jasmine.getGlobal().clearTimeout = function(timeoutKey) {
- if (jasmine.Clock.installed.clearTimeout.apply) {
- return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
- } else {
- return jasmine.Clock.installed.clearTimeout(timeoutKey);
- }
-};
-
-jasmine.getGlobal().clearInterval = function(timeoutKey) {
- if (jasmine.Clock.installed.clearTimeout.apply) {
- return jasmine.Clock.installed.clearInterval.apply(this, arguments);
- } else {
- return jasmine.Clock.installed.clearInterval(timeoutKey);
- }
-};
-
-
-jasmine.version_= {
- "major": 1,
- "minor": 0,
- "build": "0.rc1",
- "revision": 1282853377
-};
diff --git a/test/vendor/jasmine/spec/PlayerSpec.js b/test/vendor/jasmine/spec/PlayerSpec.js
deleted file mode 100644
index 79f1022..0000000
--- a/test/vendor/jasmine/spec/PlayerSpec.js
+++ /dev/null
@@ -1,58 +0,0 @@
-describe("Player", function() {
- var player;
- var song;
-
- beforeEach(function() {
- player = new Player();
- song = new Song();
- });
-
- it("should be able to play a Song", function() {
- player.play(song);
- expect(player.currentlyPlayingSong).toEqual(song);
-
- //demonstrates use of custom matcher
- expect(player).toBePlaying(song);
- });
-
- describe("when song has been paused", function() {
- beforeEach(function() {
- player.play(song);
- player.pause();
- });
-
- it("should indicate that the song is currently paused", function() {
- expect(player.isPlaying).toBeFalsy();
-
- // demonstrates use of 'not' with a custom matcher
- expect(player).not.toBePlaying(song);
- });
-
- it("should be possible to resume", function() {
- player.resume();
- expect(player.isPlaying).toBeTruthy();
- expect(player.currentlyPlayingSong).toEqual(song);
- });
- });
-
- // demonstrates use of spies to intercept and test method calls
- it("tells the current song if the user has made it a favorite", function() {
- spyOn(song, 'persistFavoriteStatus');
-
- player.play(song);
- player.makeFavorite();
-
- expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);
- });
-
- //demonstrates use of expected exceptions
- describe("#resume", function() {
- it("should throw an exception if song is already playing", function() {
- player.play(song);
-
- expect(function() {
- player.resume();
- }).toThrow("song is already playing");
- });
- });
-});
\ No newline at end of file
diff --git a/test/vendor/jasmine/spec/SpecHelper.js b/test/vendor/jasmine/spec/SpecHelper.js
deleted file mode 100644
index 4919c87..0000000
--- a/test/vendor/jasmine/spec/SpecHelper.js
+++ /dev/null
@@ -1,9 +0,0 @@
-beforeEach(function() {
- this.addMatchers({
- toBePlaying: function(expectedSong) {
- var player = this.actual;
- return player.currentlyPlayingSong === expectedSong
- && player.isPlaying;
- }
- })
-});
diff --git a/test/vendor/jasmine/src/Player.js b/test/vendor/jasmine/src/Player.js
deleted file mode 100644
index fcce826..0000000
--- a/test/vendor/jasmine/src/Player.js
+++ /dev/null
@@ -1,22 +0,0 @@
-function Player() {
-}
-Player.prototype.play = function(song) {
- this.currentlyPlayingSong = song;
- this.isPlaying = true;
-};
-
-Player.prototype.pause = function() {
- this.isPlaying = false;
-};
-
-Player.prototype.resume = function() {
- if (this.isPlaying) {
- throw new Error("song is already playing");
- }
-
- this.isPlaying = true;
-};
-
-Player.prototype.makeFavorite = function() {
- this.currentlyPlayingSong.persistFavoriteStatus(true);
-};
\ No newline at end of file
diff --git a/test/vendor/jasmine/src/Song.js b/test/vendor/jasmine/src/Song.js
deleted file mode 100644
index a8a3f2d..0000000
--- a/test/vendor/jasmine/src/Song.js
+++ /dev/null
@@ -1,7 +0,0 @@
-function Song() {
-}
-
-Song.prototype.persistFavoriteStatus = function(value) {
- // something complicated
- throw new Error("not yet implemented");
-};
\ No newline at end of file
diff --git a/wiki.md b/wiki.md
new file mode 100644
index 0000000..8997aec
--- /dev/null
+++ b/wiki.md
@@ -0,0 +1,218 @@
+Welcome to the arboreal.js wiki!
+
+## Usage
+
+Add script to you webpage
+
+```html
+
+
+```
+
+
+Arboreal,js provides a set of methods for parsing, manipulating, and
+traversing tree like data structures. A tree can be created from scratch and then extended with child elements.
+
+ var tree = new Arboreal()
+
+ tree
+ .appendChild()
+ .appendChild()
+ .children[0]
+ .appendChild()
+ .appendChild();
+
+For each child node, Arboreal.js will automatically assign an id string representing the depth and the index
+the position of the node within the tree structure.
+
+ tree.children[0].children[1].id
+
+ // => 0/0/1
+
+### Parsing
+
+Alternatively, Arboreal.js can also parse an existing object into a tree (though it will need to
+know the name of the 'children' attribute).
+
+ var wikipediaJsCategory = {
+ category: 'JavaScript',
+ subcategories: [
+ {category: 'Ajax (programming)'},
+ {category: 'JavaScript engines'},
+ {category: 'JavaScript programming languages family',
+ subcategories: [{
+ category: 'JavaScript dialect engines'
+ }]
+ },
+ {category: 'JavaScript based calendar components'},
+ {category: 'JavaScript based HTML editors'}
+ ]
+ };
+
+ var tree = Arboreal.parse(wikipediaJsCategory, 'subcategories');
+
+Also several children (or even the whole tree) can be added at the same time (syntax is similar as parse)
+
+ tree.children[1].appendChildren({category:'C#', subitems:[{category:'WPF'}]}, "subitems" );
+
+### Traversal
+
+An Arboreal.js object can be traversed either upwards or downwards.
+
+ function iterator (node) {
+ var depth = "", i;
+ for (i = 1; i <= node.depth; i++) depth += ">>";
+ console.info([depth, node.data.category].join(" "));
+ }
+
+#### Traverse down
+
+ tree.traverseDown(iterator);
+
+ // => JavaScript
+ // >> Ajax (programming)
+ // >> JavaScript engines
+ // >> JavaScript produgramming languages family
+ // >>>> JavaScript dialect engines
+ // >> JavaScript based calendar components
+ // >> JavaScript based HTML editors
+
+#### Traverse up
+
+ tree.children[2].traverseUp(iterator);
+
+ // => >> JavaScript produgramming languages family
+ // >>>> JavaScript dialect engines
+ // JavaScript
+ // >> Ajax (programming)
+ // >> JavaScript engines
+ // >> JavaScript based calendar components
+ // >> JavaScript based HTML editors
+
+Note that in both the `traverseDown` and the `traverseUp` methods, the
+value of `this` in the iterator is bound to the value of the
+currently traversed `node`. Our iterator function can in fact be
+rewritten as:
+
+ function iterator () {
+ var depth = "", i;
+ for (i = 1; i <= this.depth; i++) depth += ">>";
+ console.info([depth, this.data.category].join(" "));
+ }
+
+#### Bubble Up
+
+Arboreal object can be iterated up to the root using method 'bubbleUp':
+
+ tree.children[2].children[0].bubbleUp(iterator)
+
+ // => >>>> JavaScript dialect engines
+ // >> JavaScript programming languages family
+ // JavaScript
+
+### Advanced Traversal Down
+
+What if we want to build HTML list from our tree? It not big deal, but there a
+catch - we need a way to close tags. So here solution for it:
+
+ var rowStr = "";
+
+ var wikipediaJsCategory = {
+ category: 'JavaScript',
+ subcategories: [
+ {category: 'Ajax (programming)'},
+ {category: 'JavaScript engines'},
+ {category: 'JavaScript programming languages family',
+ subcategories: [{
+ category: 'JavaScript dialect engines'
+ }]
+ },
+ {category: 'JavaScript based calendar components'},
+ {category: 'JavaScript based HTML editors'}
+ ]
+ };
+
+
+ var tree = Arboreal.parse(wikipediaJsCategory, 'subcategories');
+
+
+ function iterator(node) {
+ if (node.children.length > 0)
+ {
+ rowStr += '';
+ rowStr += '' + node.data.category + '';
+ } else
+ {
+ rowStr += '' + node.data.category + ' ';
+ }
+ }
+
+ function iteratorAfter(node) {
+ if (node.children.length > 0)
+ {
+
+ rowStr += ' ';
+ }
+ }
+
+ tree.traverseDown(iterator, iteratorAfter);
+
+ rowStr += " ";
+
+Check [this demo]() to check how it works.
+
+
+### Search
+
+#### Find
+
+In order to search for a single node into an arboreal object, one can use the `find`
+method.
+
+ tree.find(function (node) {
+ return (/calendar/).test(node.data.category)
+ }).data.category;
+
+ // => JavaScript based calendar components
+
+The find method will also accept a string as an argument. In that case,
+it will try to find a node by id.
+
+ tree.find("0/2/0").data.category
+
+ // => JavaScript dialect engines
+
+#### Path
+
+You can use node id to get actual Arboreal.js object. To do so use
+`.path()` function
+
+ tree.path("/2/0").data.category
+
+ // => JavaScript dialect engines
+
+If you use different separator for Id's then you must pass it as second parameter
+
+ tree.path("_2_0","_").data.category
+
+ // => JavaScript dialect engines
+
+### Manipulation
+
+While traversing a tree, nodes can be deleted by calling the `remove`
+method on the node object bound to the iterator function.
+
+ tree.length;
+
+ // => 7
+
+ tree.traverseDown(function (item) {
+ var toDelete = 'JavaScript programming languages family';
+ if (item.data.category === toDelete) {
+ this.remove();
+ }
+ });
+
+ tree.length;
+
+ // 5