Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #79 from flatiron/manipulation

Manipulation [reviewed]
  • Loading branch information...
commit 5450c28f3c4e066365fc60c730f39be004124751 2 parents bee4f18 + ad8eefb
paolo fragomeni hij1nx authored
Showing with 1,427 additions and 147 deletions.
  1. +68 −11 README.md
  2. +353 −134 lib/plates.js
  3. +112 −2 test/api-test.js
  4. +809 −0 test/browser.html
  5. +46 −0 test/browser.js
  6. +1 −0  test/fixtures/partial-1.html
  7. +1 −0  test/fixtures/test-21.html
  8. +1 −0  test/fixtures/test-21.json
  9. +1 −0  test/fixtures/test-21.out
  10. +1 −0  test/fixtures/test-22.html
  11. +1 −0  test/fixtures/test-22.json
  12. +1 −0  test/fixtures/test-22.out
  13. +1 −0  test/fixtures/test-23.html
  14. +1 −0  test/fixtures/test-23.json
  15. +1 −0  test/fixtures/test-23.out
  16. +1 −0  test/fixtures/test-24.html
  17. +1 −0  test/fixtures/test-24.json
  18. +1 −0  test/fixtures/test-24.out
  19. +1 −0  test/fixtures/test-25.html
  20. +1 −0  test/fixtures/test-25.json
  21. +1 −0  test/fixtures/test-25.out
  22. +1 −0  test/fixtures/test-26.html
  23. +5 −0 test/fixtures/test-26.json
  24. +1 −0  test/fixtures/test-26.out
  25. +1 −0  test/fixtures/test-27.html
  26. +1 −0  test/fixtures/test-27.json
  27. +2 −0  test/fixtures/test-27.out
  28. +1 −0  test/fixtures/test-28.html
  29. +1 −0  test/fixtures/test-28.json
  30. +4 −0 test/fixtures/test-28.out
  31. +1 −0  test/fixtures/test-29.html
  32. +1 −0  test/fixtures/test-29.json
  33. +1 −0  test/fixtures/test-29.out
  34. +1 −0  test/fixtures/test-30.html
  35. +1 −0  test/fixtures/test-30.json
  36. +1 −0  test/fixtures/test-30.out
79 README.md
View
@@ -1,4 +1,4 @@
-<img src="https://github.com/flatiron/plates/raw/master/plates.png" />
+![plates](https://github.com/flatiron/plates/raw/master/plates.png)
# Synopsis
Plates (short for templates) binds data to markup. Plates has NO special syntax. It works in the browser and in [Node.js](http://nodejs.org/).
@@ -21,12 +21,15 @@ Plates (short for templates) binds data to markup. Plates has NO special syntax.
- TODO: Specify option to create attribute if it does not exist.
# Installation
-There are a few ways to use `plates`. Install the library using npm. You can add it to your `package.json` file as a dependancy, or include the script in your HTML page.
+There are a few ways to use `plates`. Install the library using npm. You can add
+it to your `package.json` file as a dependancy, or include the script in your
+HTML page.
# Usage
## Simple case
-By default, `plates` will try to match the key in the data to an `id` in the tag, since both should be unique.
+By default, `plates` will try to match the key in the data to an `id` in the
+tag, since both should be unique.
```js
var Plates = require('plates');
@@ -38,7 +41,8 @@ var output = Plates.bind(html, data);
```
## Explicit instructions
-A common use case is to apply the new value to each tag's body based on the `class` attribute.
+A common use case is to apply the new value to each tag's body based on the
+`class` attribute.
```js
var html = '<span class="name">User</span>...<span class="name">User</span>';
@@ -78,7 +82,9 @@ map.where('href').has(/bar/).insert('newurl'); // `has` can take a regular expre
console.log(Plates.bind(html, data, map));
```
-In even more complex cases, an arbitrary attribute can be specified. If a value is matched, a specific value can be used and then used as another attribute's value.
+In even more complex cases, an arbitrary attribute can be specified. If a value
+is matched, a specific value can be used and then used as another attribute's
+value.
```js
var html = '<img data-foo="bar" src=""></img>';
@@ -106,6 +112,21 @@ var collection = [
console.log(Plates.bind(html, collection));
```
+## Partials
+
+Plates also supports partials:
+
+```js
+var partial = '<li class="partial"></li>';
+var base = '<div><h1 class="foo"></h1><ul class="menu"></ul></div>';
+
+var baseData = { foo: 'bar' };
+var mapping = Plates.Map();
+
+mapping.class('menu').append(partial);
+console.log(Plates.bind(base, baseData, mapping));
+```
+
# API
## Plates Static Methods
@@ -141,7 +162,7 @@ This method will initiate a clause. Once a clause has been established,
other member methods may be chained to each other in any order.
```
-### class()
+### class(), className()
```
function Map#class(attribute)
@@ -196,14 +217,50 @@ If there is no attribute by that name found, one may be created depending on the
that were passed to the `Map` constructor.
```
+### remove()
+
+```
+function Map#remove()
+
+Removes the matching elements from the template.
+```
+
+### append(), partial()
+
+```
+function Map#append(html, data, map)
+@param html {String} A string that represents the new template that needs to be
+added.
+@param data {Mixed} data for the partial, if it's a string it's a reference to a
+key in the data structure that was supplied to the main template.
+@param map {Plates.Map} data mapping for the partial.
+
+If the supplied HTML string doesn't contain any HTML markup we assume that we
+the given string is the location of the template. When you are using Plates on
+the browser is assumes that you supplied it with an id selector and will fetch
+the innerHTML from the element. If you are using Plates in Node.js it assumes
+that you gave it a file path that is relative to the current working directory.
+```
+
# License
(The MIT License)
Copyright (c) 2011 Nodejitsu Inc. http://www.twitter.com/nodejitsu
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the 'Software'), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
487 lib/plates.js
View
@@ -1,17 +1,69 @@
var Plates = (typeof module !== 'undefined' && typeof module.exports !== 'undefined' && typeof exports !== 'undefined') ? exports : {};
-!function(exports) {
+!function(exports, env, undefined) {
"use strict";
- var _toString = Object.prototype.toString,
- isArray = Array.isArray || function (o) {
- return _toString.call(o) === "[object Array]";
- };
+ //
+ // Cache variables to increase lookup speed.
+ //
+ var _toString = Object.prototype.toString;
+
+ //
+ // Polyfill the Array#indexOf method for cross browser compatibility.
+ //
+ [].indexOf || (Array.prototype.indexOf = function indexOf(a, b ,c){
+ for (
+ c = this.length , b = (c+ ~~b) % c;
+ b < c && (!(b in this) || this[b] !==a );
+ b++
+ );
+
+ return b^c ? b : -1;
+ });
+
+ //
+ // Polyfill Array.isArray for cross browser compatibility.
+ //
+ Array.isArray || (Array.isArray = function isArray(a) {
+ return _toString.call(a) === '[object Array]';
+ });
+
+ //
+ // ### function fetch(data, mapping, value, key)
+ // #### @data {Object} the data that we need to fetch a value from
+ // #### @mapping {Object} The iterated mapping step
+ // #### @tagbody {String} the tagbody we operated against
+ // #### @key {String} optional key if the mapping doesn't have a dataKey
+ // Fetches the correct piece of data
+ //
+ function fetch(data, mapping, value, tagbody, key) {
+ key = mapping.dataKey || key;
+
+ //
+ // Check if we have data manipulation or filtering function.
+ //
+ if (mapping.dataKey && typeof mapping.dataKey === 'function') {
+ return mapping.dataKey(data, value || '', tagbody || '', key);
+ }
- var Merge = function Merge() {};
+ //
+ // See if we are using dot notation style
+ //
+ if (!~key.indexOf('.')) return data[key];
- Merge.prototype = {
+ var result = key
+ , structure = data;
+
+ for (var paths = key.split('.'), i = 0, length = paths.length; i < length && structure; i++) {
+ result = structure[+paths[i] || paths[i]];
+ structure = result;
+ }
+
+ return result || data[key];
+ }
+ var Merge = function Merge() {};
+ Merge.prototype = {
nest: [],
tag: new RegExp([
@@ -24,28 +76,50 @@ var Plates = (typeof module !== 'undefined' && typeof module.exports !== 'undefi
'>'
].join('\\s*')),
+ //
+ // HTML attribute parser.
+ //
attr: /([\-\w]*)=(?:["\']([\-\.\w\s\/:;&#]*)["\'])/gi,
- hasClass: function(str, className) {
- return str.split(' ').indexOf(className) > -1;
+ //
+ // In HTML5 it's allowed to have to use self closing tags without closing
+ // separators. So we need to detect these elements based on the tag name.
+ //
+ selfClosing: /area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr/,
+
+ //
+ // ### function hasClass(str, className)
+ // #### @str {String} the class attribute
+ // #### @className {String} the className that the classAttribute should contain
+ //
+ // Helper function for detecting if a class attribute contains the className
+ //
+ hasClass: function hasClass(str, className) {
+ return ~str.split(' ').indexOf(className);
},
- iterate: function(html, value, components, tagname, key) {
-
+ //
+ // ### function iterate(html, value, components, tagname, key)
+ // #### @html {String} peice of HTML
+ // #### @value {Mixed} iterateable object with data
+ // #### @components {Array} result of the this.tag regexp execution
+ // #### @tagname {String} the name of the tag that we iterate on
+ // #### @key {String} the key of the data that we need to extract from the value
+ //
+ // Iterate over over the supplied HTML.
+ //
+ iterate: function iterate(html, value, components, tagname, key) {
var output = '',
segment = html.slice(
html.search(components.input),
html.lastIndexOf(tagname) + tagname.length + 1
),
-
data = {};
// Is it an array?
- if (isArray(value)) {
-
+ if (Array.isArray(value)) {
// Yes: set the output to the result of iterating through the array
for (var i = 0, l = value.length; i < l; i++) {
-
// If there is a key, then we have a simple object and
// must construct a simple object to use as the data
if (key) {
@@ -55,34 +129,34 @@ var Plates = (typeof module !== 'undefined' && typeof module.exports !== 'undefi
}
output += this.bind(segment, data);
-
}
return output;
-
- }
-
- // Is it an object?
- else if (typeof value === 'object') {
-
+ } else if (typeof value === 'object') {
// We need to refine the selection now that we know we're dealing with a
// nested object
segment = segment.slice(components.input.length, -(tagname.length + 3));
return output += this.bind(segment, value);
-
}
return value;
-
},
+ //
+ // ### function bind(html, data, map)
+ // #### @html {String} the template that we need to modify
+ // #### @data {Object} data for the template
+ // #### @map {Mapper} instructions for the data placement in the template
+ // Process the actual template
+ //
bind: function bind(html, data, map) {
-
- if (isArray(data)) {
+ if (Array.isArray(data)) {
var output = '';
+
for (var i = 0, l = data.length; i<l; i++) {
output += this.bind(html, data[i], map);
}
+
return output;
}
@@ -92,6 +166,7 @@ var Plates = (typeof module !== 'undefined' && typeof module.exports !== 'undefi
var that = this;
var openers = 0,
+ remove = 0,
components,
attributes,
mappings = map && map.mappings,
@@ -99,6 +174,7 @@ var Plates = (typeof module !== 'undefined' && typeof module.exports !== 'undefi
tagname = '',
isClosing = false,
isSelfClosing = false,
+ selfClosing = false,
matchmode = false,
createAttribute = map && map.conf && map.conf.create,
closing,
@@ -109,21 +185,22 @@ var Plates = (typeof module !== 'undefined' && typeof module.exports !== 'undefi
left;
for (var i = 0, l = html.length; i < l; i++) {
- c = html[i];
+ c = html.charAt(i);
+ //
+ // Figure out which part of the HTML we are currently processing. And if
+ // we have queued up enough HTML to process it's data.
+ //
if (c === '!' && intag && !matchmode) {
intag = false;
- buffer += html.slice(left, i+1);
- }
- else if (c === '<' && !intag) {
+ buffer += html.slice(left, i + 1);
+ } else if (c === '<' && !intag) {
closing = true;
intag = true;
left = i;
- }
- else if (c === '>' && intag) {
-
+ } else if (c === '>' && intag) {
intag = false;
- tagbody = html.slice(left, i+1);
+ tagbody = html.slice(left, i + 1);
components = this.tag.exec(tagbody);
if(!components) {
@@ -134,135 +211,148 @@ var Plates = (typeof module !== 'undefined' && typeof module.exports !== 'undefi
isClosing = components[1];
tagname = components[2];
attributes = components[3];
- isSelfClosing = components[4];
+ selfClosing = components[4];
+ isSelfClosing = this.selfClosing.test(tagname);
if (matchmode) {
-
//
// and its a closing.
//
if (!!isClosing) {
-
if (openers <= 0) {
matchmode = false;
- }
- else {
+ } else {
--openers;
}
- }
- //
- // and its not a self-closing tag
- //
- else if (!isSelfClosing) {
+ } else if (!isSelfClosing) {
+ //
+ // and its not a self-closing tag
+ //
++openers;
}
}
if (!isClosing && !matchmode) {
-
//
// if there is a match in progress and
//
if (mappings && mappings.length > 0) {
-
for (var ii = mappings.length - 1; ii >= 0; ii--) {
-
var setAttribute = false
- , shouldSetAttribute = mappings[ii].re && attributes.match(mappings[ii].re);
+ , mapping = mappings[ii]
+ , shouldSetAttribute = mapping.re && attributes.match(mapping.re);
+
+ //
+ // check if we are targetting a element only or attributes
+ //
+ if ('tag' in mapping && !this.attr.test(tagbody) && mapping.tag === tagname) {
+ tagbody = tagbody + fetch(data, mapping, '', tagbody);
+ continue;
+ }
tagbody = tagbody.replace(this.attr, function(str, key, value, a) {
+ var newdata;
- var newdata = mappings[ii].dataKey ? data[mappings[ii].dataKey] : data[key];
-
- if (shouldSetAttribute && mappings[ii].replace !== key) {
-
+ if (shouldSetAttribute && mapping.replace !== key || remove) {
return str;
- }
- else if (shouldSetAttribute || typeof mappings[ii].replacePartial1 !== 'undefined') {
-
+ } else if (shouldSetAttribute || typeof mapping.replacePartial1 !== 'undefined') {
setAttribute = true;
//
// determine if we should use the replace argument or some value from the data object.
//
- if (typeof mappings[ii].replacePartial2 !== 'undefined') {
- newdata = value.replace(mappings[ii].replacePartial1, mappings[ii].replacePartial2);
- }
- else if (typeof mappings[ii].replacePartial1 !== 'undefined' && mappings[ii].dataKey) {
-
- newdata = value.replace(mappings[ii].replacePartial1, data[mappings[ii].dataKey]);
+ if (typeof mapping.replacePartial2 !== 'undefined') {
+ newdata = value.replace(mapping.replacePartial1, mapping.replacePartial2);
+ } else if (typeof mapping.replacePartial1 !== 'undefined' && mapping.dataKey) {
+ newdata = value.replace(mapping.replacePartial1, fetch(data, mapping, value, tagbody, key));
+ } else {
+ newdata = fetch(data, mapping, value, tagbody, key);
}
return key + '="' + (newdata || '') + '"';
- }
- else if (!mappings[ii].replace && mappings[ii].attribute === key) {
-
+ } else if (!mapping.replace && mapping.attribute === key) {
if (
- mappings[ii].value === value ||
- that.hasClass(value, mappings[ii].value ||
+ mapping.value === value ||
+ that.hasClass(value, mapping.value ||
mappings.conf.where === key) ||
- ( ({}).toString.call(mappings[ii].value) === '[object RegExp]' &&
- mappings[ii].value.exec(value) !== null) ) {
-
- var v = data[mappings[ii].dataKey];
-
- newdata = tagbody + newdata;
-
- if (isArray(v)) {
-
- newdata = that.iterate(html, v, components, tagname, value);
- // If the item is an array, then we need to tell
- // Plates that we're dealing with nests
- that.nest.push(tagname);
- }
- else if (typeof v === 'object') {
-
- newdata = tagbody + that.iterate(html, v, components, tagname, value);
+ (_toString.call(mapping.value) === '[object RegExp]' &&
+ mapping.value.exec(value) !== null)
+ ) {
+ if (mapping.remove) {
+ //
+ // only increase the remove counter if it's not a self
+ // closing element. As matchmode is suffectient to
+ // remove tose
+ //
+ if (!isSelfClosing) remove++;
+ matchmode = true;
+ } else if (mapping.plates) {
+ var partial = that.bind(
+ mapping.plates
+ , typeof mapping.data === 'string' ? data[mapping.data] : mapping.data || data
+ , mapping.mapper
+ );
+
+ buffer += tagbody + that.iterate(html, partial, components, tagname);
+ matchmode = true;
+ } else {
+ var v = newdata = fetch(data, mapping, value, tagbody, key);
+ newdata = tagbody + newdata;
+
+ if (Array.isArray(v)) {
+ newdata = that.iterate(html, v, components, tagname, value);
+ // If the item is an array, then we need to tell
+ // Plates that we're dealing with nests
+ that.nest.push(tagname);
+ } else if (typeof v === 'object') {
+ newdata = tagbody + that.iterate(html, v, components, tagname, value);
+ }
+
+ buffer += newdata || '';
+ matchmode = true;
}
-
- buffer += newdata || '';
- matchmode = true;
}
}
+
return str;
});
+ //
+ // Do we need to create the attributes if they don't exist.
+ //
if (createAttribute && shouldSetAttribute && !setAttribute) {
- var spliced = isSelfClosing? 2 : 1;
- var close = isSelfClosing? '/>': '>';
- var left = tagbody.substr(0, tagbody.length - spliced);
- if (left[left.length - 1] == ' ') {
+ var spliced = selfClosing ? 2 : 1
+ , close = selfClosing ? '/>': '>'
+ , left = tagbody.substr(0, tagbody.length - spliced);
+
+ if (left[left.length - 1] === ' ') {
left = left.substr(0, left.length - 1);
- if (isSelfClosing) {
+
+ if (selfClosing) {
close = ' ' + close;
}
}
+
tagbody = [
left,
' ',
- mappings[ii].replace,
+ mapping.replace,
'="',
- data[mappings[ii].dataKey],
+ fetch(data, mapping),
'"',
close
].join('');
}
-
}
- }
- else {
-
+ } else {
//
// if there is no map, we are just looking to match
// the specified id to a data key in the data object.
//
- tagbody.replace(
- this.attr,
- function (attr, key, value, idx) {
+ tagbody.replace(this.attr, function (attr, key, value, idx) {
if (key === map && map.conf.where || 'id' && data[value]) {
-
- var v = data[value],
- nest = isArray(v),
+ var v = data[value],
+ nest = Array.isArray(v),
output = (nest || typeof v === 'object') ? that.iterate(html, v, components, tagname, value) : v;
// If the item is an array, then we need to tell
@@ -282,106 +372,235 @@ var Plates = (typeof module !== 'undefined' && typeof module.exports !== 'undefi
// just write the tagbody to the buffer.
//
if (!matchmode && that.nest.length === 0) {
- buffer += tagbody;
+ if (!remove) buffer += tagbody;
+
+ if (remove && !!isClosing) --remove;
} else if (!matchmode && that.nest.length) {
this.nest.pop();
}
-
- }
- else if (!intag && !matchmode) {
-
+ } else if (!intag && !matchmode) {
//
// currently not inside a tag and there is no
// match in progress, we can write the char to
// the buffer.
//
- buffer += c;
+ if (!remove) buffer += c;
}
-
}
return buffer;
}
-
};
- var Mapper = function Mapper(conf) {
+ //
+ // ### function Mapper(conf)
+ // #### @conf {Object} configuration object
+ // Constructor function for the Mapper instance that is responsible for
+ // providing the mapping for the data structure
+ //
+ function Mapper(conf) {
if (!(this instanceof Mapper)) { return new Mapper(conf); }
+
this.mappings = [];
this.conf = conf || {};
- };
+ }
+ //
+ // ### function last(newitem)
+ // #### @newitem {Boolean} do we need to add a new item to the mapping
+ // Helper function for adding new attribute maps to a Mapper instance
+ //
function last(newitem) {
-
if (newitem) {
-
this.mappings.push({});
}
- var m = this.mappings[this.mappings.length-1];
+
+ var m = this.mappings[this.mappings.length - 1];
if (m && m.attribute && m.value && m.dataKey && m.replace) {
m.re = new RegExp(m.attribute + '=([\'"]?)' + m.value + '\\1');
-
} else if (m) {
delete m.re;
}
+
return m;
}
+ //
+ // Create the actual chainable methods: where('class').is('foo').insert('bla')
+ //
Mapper.prototype = {
- replace: function(val1, val2) {
+ //
+ // ### function replace(val1, val2)
+ // #### @val1 {String|RegExp} The part of the attribute that needs to be replaced
+ // #### @val2 {String} The value it should be replaced with
+ //
+ replace: function replace(val1, val2) {
var l = last.call(this);
l.replacePartial1 = val1;
l.replacePartial2 = val2;
return this;
},
- use: function(val) {
+
+ //
+ // ### function use(val)
+ // #### @val {String} A string that represents a key.
+ // Data will be inserted into the attribute that was specified in the
+ // `where` clause.
+ //
+ use: function use(val) {
last.call(this).dataKey = val;
return last.call(this) && this;
},
- to: function(val) {
- return this.use(val);
- },
- where: function(val) {
+
+ //
+ // ### function where(val)
+ // #### @val {String} an attribute that may be found in a tag
+ // This method will initiate a clause. Once a clause has been established
+ // other member methods will be chained to each other in any order.
+ //
+ where: function where(val) {
last.call(this, true).attribute = val;
return last.call(this) && this;
},
- "class": function(val) {
+ //
+ // ### function class(val)
+ // #### @val {String} a value that may be found in the `class` attribute of a tag
+ // the method name should be wrapped in quotes or it will throw errors in IE.
+ //
+ 'class': function className(val) {
return this.where('class').is(val);
},
- tag: function(val) {
+
+ //
+ // ### function tag(val)
+ // #### @val {String} the name of the tag should be found
+ //
+ tag: function tag(val) {
last.call(this, true).tag = val;
return this;
},
- is: function(val) {
+ //
+ // ### function is(val)
+ // #### @val {string} The value of the attribute that was specified in the
+ // `where` clause.
+ //
+ is: function is(val) {
last.call(this).value = val;
return last.call(this) && this;
},
- has: function(val) {
+
+ //
+ // ### function has(val)
+ // #### @val {String|RegExp} The value of the attribute that was specified
+ // in the `where` clause.
+ //
+ has: function has(val) {
last.call(this).value = val;
this.replace(val);
return last.call(this) && this;
},
- insert: function(val) {
+
+ //
+ // ### function insert(val)
+ // #### @val {String} A string that represents a key. Data will be inserted
+ // in to the attribute that was specified in the `where` clause.
+ //
+ insert: function insert(val) {
var l = last.call(this);
l.replace = l.attribute;
l.dataKey = val;
return last.call(this) && this;
},
- as: function(val) {
+
+ //
+ // ### function as(val)
+ // #### @val {String} A string that represents an attribute in the tag.
+ // If there is no attribute by that name name found, one may be created
+ // depending on the options that where passed in the `Plates.Map`
+ // constructor.
+ //
+ as: function as(val) {
last.call(this).replace = val;
return last.call(this) && this;
+ },
+
+ //
+ // ### function remove()
+ // This will remove the element that was specified in the `where` clause
+ // from the template.
+ //
+ remove: function remove() {
+ last.call(this).remove = true;
+ return last.call(this, true);
+ },
+
+ //
+ // ### function append(plates, data, map)
+ // #### @plates {String} Template or path/id of the template
+ // #### @data {Object|String} data for the appended template
+ // #### @map {Plates.Map} mapping for the data
+ //
+ append: function append(plates, data, map) {
+ var l = last.call(this);
+
+ if (data instanceof Mapper) {
+ map = data;
+ data = undefined;
+ }
+
+ // If the supplied plates template doesn't contain any HTML it's most
+ // likely that we need to import it. To improve performance we will cache
+ // the result of the file system.
+ if (!/<[^<]+?>/.test(plates) && !exports.cache[plates]) {
+ // figure out if we are running in Node.js or a browser
+ if ('document' in env && 'getElementById' in env.document) {
+ exports.cache[plates] = document.getElementById(plates).innerHTML;
+ } else {
+ exports.cache[plates] = require('fs').readFileSync(
+ require('path').join(process.cwd(), plates),
+ 'utf8'
+ );
+ }
+ }
+
+ l.plates = exports.cache[plates] || plates;
+ l.data = data;
+ l.mapper = map;
+
+ return last.call(this, true);
}
};
- // where('class').is('foo').insert('bla')
-
- exports.bind = function (html, data, map) {
+ //
+ // Provide helpful aliases that well help with increased compatibility as not
+ // all browsers allow the Mapper#class prototype (IE).
+ //
+ Mapper.prototype.className = Mapper.prototype['class'];
+
+ //
+ // Aliases of different methods.
+ //
+ Mapper.prototype.partial = Mapper.prototype.append;
+ Mapper.prototype.to = Mapper.prototype.use;
+
+ //
+ // Expose a simple cache object so people can clear the cached partials if
+ // they want to.
+ //
+ exports.cache = {};
+
+ //
+ // Expose the Plates#bind interface.
+ //
+ exports.bind = function bind(html, data, map) {
var merge = new Merge();
return merge.bind(html, data, map);
};
+ //
+ // Expose the Mapper.
+ //
exports.Map = Mapper;
-
-}(Plates);
+}(Plates, this);
114 test/api-test.js
View
@@ -1,5 +1,4 @@
var vows = require('vows'),
- assert = require('assert'),
Plates = require('../lib/plates');
common = require('./common');
@@ -215,8 +214,119 @@ vows.describe('merge data into markup').addBatch({
return common.createTest('test-20', map);
}()
- )
+ ),
+
+ '(21) Remove should remove the whole element': (
+
+ function() {
+ var map = Plates.Map();
+ map.class('removed').remove();
+
+ return common.createTest('test-21', map);
+ }()
+ ),
+
+ '(22) Remove should remove self closing elements': (
+
+ function() {
+ var map = Plates.Map();
+ map.where('type').is('email').remove();
+
+ return common.createTest('test-22', map);
+ }()
+ ),
+
+ '(23) Remove should remove self closing elements without optional ending slash': (
+
+ function() {
+ var map = Plates.Map();
+ map.where('type').is('email').remove();
+
+ return common.createTest('test-23', map);
+ }()
+ ),
+
+ '(24) Should append new templates': (
+
+ function() {
+ var map = Plates.Map();
+ map.class('insert').append('<div class="trolling"></div>');
+
+ return common.createTest('test-24', map);
+ }()
+ ),
+
+ '(25) New templates should also process map and custom data': (
+
+ function() {
+ var map = Plates.Map();
+ var partial = Plates.Map();
+
+ partial.class('trolling').to('boink');
+ map.class('insert').append('<div class="trolling"></div>', { boink: 'moo' }, partial);
+
+ return common.createTest('test-25', map);
+ }()
+ ),
+ '(26) When the data for the partial was provided as a string, get it from the parent data structure': (
+
+ function() {
+ var map = Plates.Map();
+ var partial = Plates.Map();
+
+ partial.class('trolling').to('boink');
+ map.class('insert').append('<div class="trolling"></div>', 'partial', partial);
+
+ return common.createTest('test-26', map);
+ }()
+ ),
+
+ '(27) append should read from file system if no template has been provided': (
+
+ function() {
+ var map = Plates.Map();
+ var partial = Plates.Map();
+
+ map.class('insert').append('./test/fixtures/partial-1.html');
+
+ return common.createTest('test-27', map);
+ }()
+ ),
+
+ '(28) it should repeat the partial for each item in the array': (
+
+ function() {
+ var map = Plates.Map();
+ var partial = Plates.Map();
+
+ map.class('insert').append('./test/fixtures/partial-1.html', [{}, {}, {}]);
+
+ return common.createTest('test-28', map);
+ }()
+ ),
+
+ '(29) a tag match without attributes should replace the contents': (
+
+ function() {
+ var map = Plates.Map();
+ map.tag('div').use('foo');
+
+ return common.createTest('test-29', map);
+ }()
+ ),
+
+ '(30) a tag match without attributes should replace the contents': (
+
+ function() {
+ var map = Plates.Map();
+ map.class('transformation').use(function (data, key) {
+ return data.uppercase.toLowerCase();
+ });
+
+ return common.createTest('test-30', map);
+ }()
+ )
}
}).export(module);
809 test/browser.html
View
@@ -0,0 +1,809 @@
+<!doctype html>
+<head>
+ <title>Plates browser tests</title>
+ <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-git.css" />
+ <script src="http://code.jquery.com/qunit/qunit-git.js"></script>
+ <script src="http://bestiejs.github.com/json3/lib/json3.js"></script>
+ <script src="../lib/plates.js"></script>
+</head>
+<body>
+<div id="qunit"></div>
+
+<script type="test/fixture" id="partial-1.html">
+<strong>strong</strong> plates
+
+</script>
+<script type="test/fixture" id="test-1.html">
+<div id='key'></div>
+</script>
+<script type="test/fixture" id="test-1.json">
+{
+ "key": "value"
+}
+
+</script>
+<script type="test/fixture" id="test-1.out">
+<div id='key'>value</div>
+</script>
+<script type="test/fixture" id="test-10.html">
+<br><img class="logo"><input name="first_name"/><input name="last_name" />
+</script>
+<script type="test/fixture" id="test-10.json">
+{
+ "url": "/some/image.png",
+ "fname": "Raul",
+ "lname": "Julia"
+}
+</script>
+<script type="test/fixture" id="test-10.out">
+<br><img class="logo" src="/some/image.png"><input name="first_name" value="Raul"/><input name="last_name" value="Julia" />
+</script>
+<script type="test/fixture" id="test-11.html">
+<input type="hidden" name="method" value="" /><input type="hidden" name="id" value="" />
+
+</script>
+<script type="test/fixture" id="test-11.json">
+{
+ "method": "DELETE",
+ "id": 7
+}
+
+</script>
+<script type="test/fixture" id="test-11.out">
+<input type="hidden" name="method" value="DELETE" /><input type="hidden" name="id" value="7" />
+
+</script>
+<script type="test/fixture" id="test-12.html">
+<ul><li class="name"><span class="first"></span>:<span class="middle"></span><span class="last"></span></li></ul>
+
+</script>
+<script type="test/fixture" id="test-12.json">
+{
+ "name": [
+ {
+ "first" : "John",
+ "middle" : "C",
+ "last" : "Smith"
+ },
+ {
+ "first" : "Jane",
+ "middle" : "A",
+ "last" : "Doe"
+ }
+ ]
+}
+
+</script>
+<script type="test/fixture" id="test-12.out">
+<ul><li class="name"><span class="first">John</span>:<span class="middle">C</span><span class="last">Smith</span></li><li class="name"><span class="first">Jane</span>:<span class="middle">A</span><span class="last">Doe</span></li></ul>
+
+</script>
+<script type="test/fixture" id="test-13.html">
+<a href="aA1-_:/&#1235; "></a>
+</script>
+<script type="test/fixture" id="test-13.json">
+{
+ "test": "test"
+}
+</script>
+<script type="test/fixture" id="test-13.out">
+<a href="test"></a>
+</script>
+<script type="test/fixture" id="test-14.html">
+<table>
+ <tr class="names">
+ <td class="first"></td>
+ <td class="last"></td>
+ </tr>
+</table>
+
+</script>
+<script type="test/fixture" id="test-14.json">
+{
+ "names" : [
+ {
+ "first" : "John",
+
+ "last" : "Smith"
+ },
+ {
+ "first" : "Jane",
+ "last" : "Doe"
+ }
+ ]
+}
+
+</script>
+<script type="test/fixture" id="test-14.out">
+<table>
+ <tr class="names">
+ <td class="first">John</td>
+ <td class="last">Smith</td>
+ </tr><tr class="names">
+ <td class="first">Jane</td>
+ <td class="last">Doe</td>
+ </tr>
+</table>
+
+</script>
+<script type="test/fixture" id="test-15.html">
+<a href="placeholder">Link</a>
+<ul>
+ <li class="names">
+ <span class="first"></span>
+ <span class="middle"></span>
+ <span class="last"></span>
+ </li>
+</ul>
+
+</script>
+<script type="test/fixture" id="test-15.json">
+{
+ "link" : "/foobar",
+ "names" : [
+ {
+ "first" : "John",
+ "middle" : "C",
+ "last" : "Smith"
+ },
+ {
+ "first" : "Jane",
+ "middle" : "A",
+ "last" : "Doe"
+ }
+ ]
+}
+
+</script>
+<script type="test/fixture" id="test-15.out">
+<a href="/foobar">Link</a>
+<ul>
+ <li class="names">
+ <span class="first">John</span>
+ <span class="middle">C</span>
+ <span class="last">Smith</span>
+ </li><li class="names">
+ <span class="first">Jane</span>
+ <span class="middle">A</span>
+ <span class="last">Doe</span>
+ </li>
+</ul>
+
+</script>
+<script type="test/fixture" id="test-16.html">
+<b>Flatiron Components</b>
+<br/>
+<ul>
+ <li class='components'></li>
+</ul>
+
+</script>
+<script type="test/fixture" id="test-16.json">
+{
+ "components": [
+ "Union",
+ "Director",
+ "Broadway",
+ "Plates",
+ "Resourceful"
+ ]
+}
+
+</script>
+<script type="test/fixture" id="test-16.out">
+<b>Flatiron Components</b>
+<br/>
+<ul>
+ <li class='components'>Union</li><li class='components'>Director</li><li class='components'>Broadway</li><li class='components'>Plates</li><li class='components'>Resourceful</li>
+</ul>
+
+</script>
+<script type="test/fixture" id="test-17.html">
+<ul>
+ <li class="organizations">
+ <h3 class="name"></h3>
+ <ul>
+ <li class="staff">
+ <table>
+ <tr class="name">
+ <td class="first"></td>
+ <td class="last"></td>
+ </tr>
+ </table>
+ </li>
+ </ul>
+ </li>
+</ul>
+
+</script>
+<script type="test/fixture" id="test-17.json">
+{
+ "organizations" : [
+ {
+ "name": "DotOrg",
+ "staff": [
+ {
+ "name": {
+ "first": "Dot",
+ "last" : "Matrix"
+ }
+ },
+ {
+ "name": {
+ "first": "Serious",
+ "last" : "Business"
+ }
+ }
+ ]
+ },
+ {
+ "name": "FooBar",
+ "staff": [
+ {
+ "name": {
+ "first": "Foo",
+ "last" : "Bar"
+ }
+ },
+ {
+ "name": {
+ "first": "Baz",
+ "last" : "Bitsom"
+ }
+ }
+ ]
+ }
+ ]
+}
+
+</script>
+<script type="test/fixture" id="test-17.out">
+<ul>
+ <li class="organizations">
+ <h3 class="name">DotOrg</h3>
+ <ul>
+ <li class="staff">
+ <table>
+ <tr class="name">
+ <td class="first">Dot</td>
+ <td class="last">Matrix</td>
+ </tr>
+ </table>
+ </li><li class="staff">
+ <table>
+ <tr class="name">
+ <td class="first">Serious</td>
+ <td class="last">Business</td>
+ </tr>
+ </table>
+ </li>
+ </ul>
+ </li><li class="organizations">
+ <h3 class="name">FooBar</h3>
+ <ul>
+ <li class="staff">
+ <table>
+ <tr class="name">
+ <td class="first">Foo</td>
+ <td class="last">Bar</td>
+ </tr>
+ </table>
+ </li><li class="staff">
+ <table>
+ <tr class="name">
+ <td class="first">Baz</td>
+ <td class="last">Bitsom</td>
+ </tr>
+ </table>
+ </li>
+ </ul>
+ </li>
+</ul>
+
+</script>
+<script type="test/fixture" id="test-18.html">
+<span class="username"></span>
+<span class="login name"></span>
+
+</script>
+<script type="test/fixture" id="test-18.json">
+{
+ "username": "lord thingy",
+ "name": "Tom Marvolo Riddle"
+}
+
+</script>
+<script type="test/fixture" id="test-18.out">
+<span class="username">lord thingy</span>
+<span class="login name">Tom Marvolo Riddle</span>
+
+</script>
+<script type="test/fixture" id="test-19.html">
+<a href="bar/bla">test</a>
+</script>
+<script type="test/fixture" id="test-19.json">
+{}
+</script>
+<script type="test/fixture" id="test-19.out">
+<a href="bazz/bla">test</a>
+</script>
+<script type="test/fixture" id="test-2.html">
+<!-- foo --><div id="key1"><div id="key2"></div><img src="/"/></div>
+</script>
+<script type="test/fixture" id="test-2.json">
+{
+ "key2": "value2"
+}
+</script>
+<script type="test/fixture" id="test-2.out">
+<!-- foo --><div id="key1"><div id="key2">value2</div><img src="/"/></div>
+</script>
+<script type="test/fixture" id="test-20.html">
+<a href="bar/bla"></a>
+</script>
+<script type="test/fixture" id="test-20.json">
+{ "test": "test" }
+</script>
+<script type="test/fixture" id="test-20.out">
+<a href="test/bla"></a>
+</script>
+<script type="test/fixture" id="test-21.html">
+<div id="key"><div class="removed">Hello world</div></div>
+
+</script>
+<script type="test/fixture" id="test-21.json">
+{}
+
+</script>
+<script type="test/fixture" id="test-21.out">
+<div id="key"></div>
+
+</script>
+<script type="test/fixture" id="test-22.html">
+<div id="key"><input type="email" value="testing" /></div>
+
+</script>
+<script type="test/fixture" id="test-22.json">
+{}
+
+</script>
+<script type="test/fixture" id="test-22.out">
+<div id="key"></div>
+
+</script>
+<script type="test/fixture" id="test-23.html">
+<div id="key"><input type="email" value="testing"></div>
+
+</script>
+<script type="test/fixture" id="test-23.json">
+{}
+
+</script>
+<script type="test/fixture" id="test-23.out">
+<div id="key"></div>
+
+</script>
+<script type="test/fixture" id="test-24.html">
+<div class="insert"></div>
+
+</script>
+<script type="test/fixture" id="test-24.json">
+{}
+
+</script>
+<script type="test/fixture" id="test-24.out">
+<div class="insert"><div class="trolling"></div></div>
+
+</script>
+<script type="test/fixture" id="test-25.html">
+<div class="insert"></div>
+
+</script>
+<script type="test/fixture" id="test-25.json">
+{}
+
+</script>
+<script type="test/fixture" id="test-25.out">
+<div class="insert"><div class="trolling">moo</div></div>
+
+</script>
+<script type="test/fixture" id="test-26.html">
+<div class="insert"></div>
+
+</script>
+<script type="test/fixture" id="test-26.json">
+{
+ "partial": {
+ "boink": "moo"
+ }
+}
+
+</script>
+<script type="test/fixture" id="test-26.out">
+<div class="insert"><div class="trolling">moo</div></div>
+
+</script>
+<script type="test/fixture" id="test-27.html">
+<div class="insert"></div>
+
+</script>
+<script type="test/fixture" id="test-27.json">
+{}
+
+</script>
+<script type="test/fixture" id="test-27.out">
+<div class="insert">
+<strong>strong</strong> plates
+
+</div>
+
+</script>
+<script type="test/fixture" id="test-28.html">
+<div class="insert"></div>
+
+</script>
+<script type="test/fixture" id="test-28.json">
+{}
+
+</script>
+<script type="test/fixture" id="test-28.out">
+<div class="insert">
+<strong>strong</strong> plates
+
+
+<strong>strong</strong> plates
+
+
+<strong>strong</strong> plates
+
+</div>
+
+</script>
+<script type="test/fixture" id="test-29.html">
+<div></div>
+
+</script>
+<script type="test/fixture" id="test-29.json">
+{ "foo": "bar" }
+
+</script>
+<script type="test/fixture" id="test-29.out">
+<div>bar</div>
+
+</script>
+<script type="test/fixture" id="test-3.html">
+<div id="key1" style="visible: foobar > 1"><!-- foo --><div id="key2"></div><img src="/"/></div>
+
+</script>
+<script type="test/fixture" id="test-3.json">
+{
+ "key1": "value1"
+}
+</script>
+<script type="test/fixture" id="test-3.out">
+<div id="key1" style="visible: foobar > 1">value1</div>
+
+</script>
+<script type="test/fixture" id="test-4.html">
+<a id="someid" src="google.com"></a>
+<a id="someotherid" src="yahoo.com"></a>
+</script>
+<script type="test/fixture" id="test-4.json">
+{
+ "key1": "Google",
+ "key2": "Yahoo!"
+}
+</script>
+<script type="test/fixture" id="test-4.out">
+<a id="someid" src="google.com">Google</a>
+<a id="someotherid" src="yahoo.com">Yahoo!</a>
+</script>
+<script type="test/fixture" id="test-5.html">
+<a id="someid" class="red hidden large" href="/foo">Old Value</a>
+</script>
+<script type="test/fixture" id="test-5.json">
+{
+ "key1": "New Value"
+}
+</script>
+<script type="test/fixture" id="test-5.out">
+<a id="someid" class="red hidden large" href="/foo">New Value</a>
+</script>
+<script type="test/fixture" id="test-6.html">
+<a id="someid" class="red hidden large" href="/foo">Old Value</a>
+</script>
+<script type="test/fixture" id="test-6.json">
+{
+ "key1": "New Value",
+ "key2": "/bar"
+}
+</script>
+<script type="test/fixture" id="test-6.out">
+<a id="someid" class="red hidden large" href="/bar">New Value</a>
+</script>
+<script type="test/fixture" id="test-7.html">
+<a id="someid1" class="name" href="/foo"></a>
+<a id="someid2" class="name" href="/foo"></a>
+</script>
+<script type="test/fixture" id="test-7.json">
+{
+ "name": "Alpha"
+}
+</script>
+<script type="test/fixture" id="test-7.out">
+<a id="someid1" class="name" href="/foo">Alpha</a>
+<a id="someid2" class="name" href="/foo">Alpha</a>
+</script>
+<script type="test/fixture" id="test-8.html">
+<a href="/"></a>
+</script>
+<script type="test/fixture" id="test-8.json">
+{
+ "newurl": "/test"
+}
+</script>
+<script type="test/fixture" id="test-8.out">
+<a href="/test"></a>
+</script>
+<script type="test/fixture" id="test-9.html">
+<a href="/"></a>
+
+</script>
+<script type="test/fixture" id="test-9.json">
+[
+ { "url": "/a" },
+ { "url": "/b" },
+ { "url": "/c" }
+]
+
+</script>
+<script type="test/fixture" id="test-9.out">
+<a href="/a"></a>
+
+
+<a href="/b"></a>
+
+
+<a href="/c"></a>
+
+</script>
+<script>
+var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003" +
+ "\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" +
+ "\u2029\uFEFF";
+if (!String.prototype.trim || ws.trim()) {
+ // http://blog.stevenlevithan.com/archives/faster-trim-javascript
+ // http://perfectionkills.com/whitespace-deviations/
+ ws = "[" + ws + "]";
+ var trimBeginRegexp = new RegExp("^" + ws + ws + "*"),
+ trimEndRegexp = new RegExp(ws + ws + "*$");
+ String.prototype.trim = function trim() {
+ if (this === undefined || this === null) {
+ throw new TypeError("can't convert "+this+" to object");
+ }
+ return String(this).replace(trimBeginRegexp, "").replace(trimEndRegexp, "");
+ };
+}
+
+var common = {
+ createTest: function (name, map) {
+ var html = document.getElementById(name + '.html').innerHTML
+ , out = document.getElementById(name + '.out').innerHTML
+ , data = JSON.parse(document.getElementById(name + '.json').innerHTML);
+
+ var result = Plates.bind(html, data, map);
+
+ assert.equal(
+ result.trim().replace(/[\n|\r]/g, '')
+ , out.trim().replace(/[\n|\r]/g, '')
+ , 'Expecting `' + html + '` to equal `' + out + '`'
+ );
+ }
+}
+module("merge data into markup, when providing both data and markup");
+
+test("(1) a single tag with an `id` that corresponds to a `data-key`.", function () {
+ return common.createTest('test-1');
+});
+
+test("(2) a deeply nested tag with an `id` that corresponds to a `data-key`.", function () {
+ return common.createTest('test-2');
+});
+
+test("(3) a tag with an `id` that corresponds to a `data-key`, having a body of nested tags.", function () {
+ return common.createTest('test-3');
+});
+
+test("(4) a tag with an arbitrary attribute that corresponds to a `data-key`.", function () {
+ var map = Plates.Map();
+ map.where('src').is('google.com').use('key1');
+ map.where('src').is('yahoo.com').use('key2');
+
+ return common.createTest('test-4', map);
+});
+
+test("(5) a tag with a class attribute whith a value that corresponds to a `data-key`.", function () {
+ var map = Plates.Map();
+ map.where('class').is('hidden').use('key1');
+
+ return common.createTest('test-5', map);
+});
+
+test("(6) a tag with a class attribute whith a value that corresponds to a `data-key`, a second map item.", function () {
+ var map = Plates.Map();
+ map.where('class').is('hidden').use('key1');
+ map.where('href').is('/foo').use('key2').as('href');
+
+ return common.createTest('test-6', map);
+});
+
+test("(7) a map that redefines the default matching attribute of `id`.", function () {
+ var map = Plates.Map({
+ where: 'class'
+ });
+
+ return common.createTest('test-7', map);
+});
+
+test("(8) using the insert shortcut.", function () {
+ var map = Plates.Map();
+ map.where('href').is('/').insert('newurl');
+
+ return common.createTest('test-8', map);
+});
+
+test("(9) iterate a collection.", function () {
+ var map = Plates.Map();
+ map.where('href').is('/').insert('url');
+
+ return common.createTest('test-9', map);
+});
+
+test("(10) a map that defines creating missing attributes.", function () {
+ var map = Plates.Map({
+ create: true
+ });
+
+ map.className('logo').use('url').as('src');
+ map.where('name').is('first_name').use('fname').as('value');
+ map.where('name').is('last_name').use('lname').as('value');
+
+ return common.createTest('test-10', map);
+});
+
+test("(11) differing on \"is\" parameter only.", function () {
+ var map = Plates.Map();
+ map.where('name').is('method').use('method').as('value');
+ map.where('name').is('id').use('id').as('value');
+
+ return common.createTest('test-11', map);
+});
+
+test("(12) iterate a collection of over an element with children.", function () {
+ return common.createTest('test-12');
+});
+
+test("(13) attributes can contain valid characters", function () {
+ var map = Plates.Map();
+ map.where('href').is('aA1-_:/&#1235; ').insert('test');
+
+ return common.createTest('test-13', map);
+});
+
+test("(14) It should be able to iterate over collections with maps", function () {
+ var map = Plates.Map();
+ map.className("names").use("names");
+
+ return common.createTest('test-14', map);
+});
+
+test("(15) It should be able to iterate over collections with multiple maps", function () {
+ var map = Plates.Map();
+ map.where("href").is("placeholder").insert("link");
+ map.className("names").use("names");
+
+ return common.createTest('test-15', map);
+});
+
+test("(16) It should be able to iterate over simple arrays", function () {
+ return common.createTest('test-16');
+});
+
+test("(17) It should be able to iterate over deeply nested objects", function () {
+ return common.createTest('test-17');
+});
+
+test("(18) It should be able to see the classnames properly", function () {
+ var map = Plates.Map();
+ map.className("username").use("username");
+ map.className("name").use("name");
+
+ return common.createTest('test-18', map);
+});
+
+test("(19) Replace partial value with a new value", function () {
+ var map = Plates.Map();
+ map.where("href").is(/^bar/).replace(/bar/, 'bazz');
+
+ return common.createTest('test-19', map);
+});
+
+test("(20) Replace partial value with a new value from the data object", function () {
+ var map = Plates.Map();
+ map.where("href").has(/^bar/).insert('test');
+
+ return common.createTest('test-20', map);
+});
+
+test("(21) Remove should remove the whole element", function () {
+ var map = Plates.Map();
+ map.className('removed').remove();
+
+ return common.createTest('test-21', map);
+});
+
+test("(22) Remove should remove self closing elements", function () {
+ var map = Plates.Map();
+ map.where('type').is('email').remove();
+
+ return common.createTest('test-22', map);
+});
+
+test("(23) Remove should remove self closing elements without optional ending slash", function () {
+ var map = Plates.Map();
+ map.where('type').is('email').remove();
+
+ return common.createTest('test-23', map);
+});
+
+test("(24) Should append new templates", function () {
+ var map = Plates.Map();
+ map.className('insert').append('<div class="trolling"></div>');
+
+ return common.createTest('test-24', map);
+});
+
+test("(25) New templates should also process map and custom data", function () {
+ var map = Plates.Map();
+ var partial = Plates.Map();
+
+ partial.className('trolling').to('boink');
+ map.className('insert').append('<div class="trolling"></div>', { boink: 'moo' }, partial);
+
+ return common.createTest('test-25', map);
+});
+
+test("(26) When the data for the partial was provided as a string, get it from the parent data structure", function () {
+ var map = Plates.Map();
+ var partial = Plates.Map();
+
+ partial.className('trolling').to('boink');
+ map.className('insert').append('<div class="trolling"></div>', 'partial', partial);
+
+ return common.createTest('test-26', map);
+});
+
+test("(27) append should read from file system if no template has been provided", function () {
+ var map = Plates.Map();
+ var partial = Plates.Map();
+
+ map.className('insert').append('partial-1.html');
+
+ return common.createTest('test-27', map);
+});
+
+test("(28) it should repeat the partial for each item in the array", function () {
+ var map = Plates.Map();
+ var partial = Plates.Map();
+
+ map.className('insert').append('partial-1.html', [{}, {}, {}]);
+
+ return common.createTest('test-28', map);
+});
+test("(29) a tag match without attributes should replace the contents", function () {
+ var map = Plates.Map();
+ map.tag('div').use('foo');
+
+ return common.createTest('test-29', map);
+});
+</script>
46 test/browser.js
View
@@ -0,0 +1,46 @@
+"use strict";
+
+/**
+ * Browser.js: A simple conversion script that changes the existing vows test
+ * suite in to a qunit compatible test suite.
+ */
+
+var fs = require('fs')
+ , fixtures = './test/fixtures';
+
+// generate the fixtures for the browser
+fs.readdirSync(fixtures).forEach(function (file) {
+ var content = fs.readFileSync(fixtures + '/' + file, 'utf8');
+
+ console.log([
+ '<script type="test/fixture" id="'+ file +'">'
+ , content
+ , '</script>'
+ ].join('\n'));
+});
+
+var test = require('./api-test');
+Object.keys(test).forEach(function (name) {
+ var suite = test[name]
+ , body = ['<script>'];
+
+ suite.batches.forEach(function (test) {
+ var wut = Object.keys(test)[0]
+ , when = Object.keys(test[wut])[0];
+
+ body.push('module("'+name + ', ' + when +'");');
+ body.push('');
+
+ Object.keys(test[wut][when]).forEach(function (expecting) {
+ body.push('test("'+ expecting +'", function () {');
+ body.push('');
+ body.push('});');
+ body.push('');
+ });
+ });
+
+ body.push('</script>');
+
+ // output the generated test suite
+ console.log(body.join('\n'));
+});
1  test/fixtures/partial-1.html
View
@@ -0,0 +1 @@
+<strong>strong</strong> plates
1  test/fixtures/test-21.html
View
@@ -0,0 +1 @@
+<div id="key"><div class="removed">Hello world</div></div>
1  test/fixtures/test-21.json
View
@@ -0,0 +1 @@
+{}
1  test/fixtures/test-21.out
View
@@ -0,0 +1 @@
+<div id="key"></div>
1  test/fixtures/test-22.html
View
@@ -0,0 +1 @@
+<div id="key"><input type="email" value="testing" /></div>
1  test/fixtures/test-22.json
View
@@ -0,0 +1 @@
+{}
1  test/fixtures/test-22.out
View
@@ -0,0 +1 @@
+<div id="key"></div>
1  test/fixtures/test-23.html
View
@@ -0,0 +1 @@
+<div id="key"><input type="email" value="testing"></div>
1  test/fixtures/test-23.json
View
@@ -0,0 +1 @@
+{}
1  test/fixtures/test-23.out
View
@@ -0,0 +1 @@
+<div id="key"></div>
1  test/fixtures/test-24.html
View
@@ -0,0 +1 @@
+<div class="insert"></div>
1  test/fixtures/test-24.json
View
@@ -0,0 +1 @@
+{}
1  test/fixtures/test-24.out
View
@@ -0,0 +1 @@
+<div class="insert"><div class="trolling"></div></div>
1  test/fixtures/test-25.html
View
@@ -0,0 +1 @@
+<div class="insert"></div>
1  test/fixtures/test-25.json
View
@@ -0,0 +1 @@
+{}
1  test/fixtures/test-25.out
View
@@ -0,0 +1 @@
+<div class="insert"><div class="trolling">moo</div></div>
1  test/fixtures/test-26.html
View
@@ -0,0 +1 @@
+<div class="insert"></div>
5 test/fixtures/test-26.json
View
@@ -0,0 +1,5 @@
+{
+ "partial": {
+ "boink": "moo"
+ }
+}
1  test/fixtures/test-26.out
View
@@ -0,0 +1 @@
+<div class="insert"><div class="trolling">moo</div></div>
1  test/fixtures/test-27.html
View
@@ -0,0 +1 @@
+<div class="insert"></div>
1  test/fixtures/test-27.json
View
@@ -0,0 +1 @@
+{}
2  test/fixtures/test-27.out
View
@@ -0,0 +1,2 @@
+<div class="insert"><strong>strong</strong> plates
+</div>
1  test/fixtures/test-28.html
View
@@ -0,0 +1 @@
+<div class="insert"></div>
1  test/fixtures/test-28.json
View
@@ -0,0 +1 @@
+{}
4 test/fixtures/test-28.out
View
@@ -0,0 +1,4 @@
+<div class="insert"><strong>strong</strong> plates
+<strong>strong</strong> plates
+<strong>strong</strong> plates
+</div>
1  test/fixtures/test-29.html
View
@@ -0,0 +1 @@
+<div></div>
1  test/fixtures/test-29.json
View
@@ -0,0 +1 @@
+{ "foo": "bar" }
1  test/fixtures/test-29.out
View
@@ -0,0 +1 @@
+<div>bar</div>
1  test/fixtures/test-30.html
View
@@ -0,0 +1 @@
+<div class="transformation"></div>
1  test/fixtures/test-30.json
View
@@ -0,0 +1 @@
+{ "uppercase": "UPPERCASED" }
1  test/fixtures/test-30.out
View
@@ -0,0 +1 @@
+<div class="transformation">uppercased</div>
Please sign in to comment.
Something went wrong with that request. Please try again.