Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[lib/dist] second pass

  • Loading branch information...
commit 90abd5ab0c791a6a246ebb4ae7ef4aeaf99b4ad8 1 parent b430a41
Paolo Fragomeni authored
View
4 README.md
@@ -2,7 +2,7 @@
## Synopsys
-Plates (short for templates) binds data to markup. There's NO special syntax. It works in the browser and in node.js! The right way to do this is with a DOM. Unfortunately, at the moment, the DOM is slow. On the server, it is quite slow. So Plates implements a very loose HTML parser.
+Plates (short for templates) binds data to markup. There's NO special syntax. It works in the browser and in `Node.js`. The right way to do this is with a DOM. Unfortunately, at the moment, the DOM is slow. On the server, it is quite slow.
## Motivation
@@ -18,7 +18,7 @@ Plates (short for templates) binds data to markup. There's NO special syntax. It
Install the library using npm or add it to your `package.json` file as a dependancy.
```bash
- $npm install plates
+ npm install plates
```
Take some markup, some data, bind them, done.
View
BIN  lib/.plates.js.swp
Binary file not shown
View
264 lib/plates.js
@@ -1,5 +1,5 @@
-;var Plates = function main(undefined) {
+;var Plates = function Plates(undefined) {
var Merge = function Merge() {};
@@ -8,13 +8,13 @@
tag: new RegExp(
[
'(<',
- '(/?)', // 1 - is closing
- '([-:\\w]+)', // 2 - name
+ '(/?)', // 2 - is closing
+ '([-:\\w]+)', // 3 - name
'((?:\\s+[-\\w]+(?:', '=', '(?:' +
'\\w+|' +
'"[^"]*"|' +
- '\'[^\']*\'))?)*)', // 3 - attributes
- '(/?)', // 4 - is self-closing
+ '\'[^\']*\'))?)*)', // 4 - attributes
+ '(/?)', // 5 - is self-closing
'>)(.*)'
].join('\\s*')
),
@@ -27,35 +27,45 @@
bind: function bind(html, data, map) {
- var that = this,
- out = '',
- pos = 0,
- openers = 0,
- updates = [],
- matchmode = false,
- createmode = false,
- insert = true;
+ data = data || {};
if (map) {
map = map.mappings;
}
- while (html) {
- html.replace(that.tag, function (body, match, closing, tag, attributes, selfclosing, remainder, index) {
+ var that = this;
+ var str = html;
+ var buffer = '';
- //
- // search for the next tag, so we know
- // where and how to slice and dice the html.
- //
- var next = remainder.search(that.tag);
+ var matchmode = false;
+ var openers = 0;
+
+ var right = 0, left = 0;
+ var match, tag, text;
+
+ do {
+
+ tag = '', text = '';
+ left = right;
+ match = this.tag.exec(str); // test
+ str = match && match[6]; // next
+
+ if (match) {
+
+ right += match.index + match[1].length;
//
- // its a closing tag
+ // get the prior text to the left of the tag that was found.
+ // if it is a self closing tag, it will never have text.
//
+
if (matchmode) {
- if (!!closing) {
- if(openers === 0) {
- insert = true;
+
+ //
+ // and its a closing.
+ //
+ if (!!match[2]) {
+ if(openers <= 0) {
matchmode = false;
}
else {
@@ -63,146 +73,98 @@
}
}
//
- // its not a closing tag!
+ // and its not a closing tag!
//
- else if (!selfclosing) {
+ else if (!match[5]) {
++openers;
- insert = false;
}
+
+ }
+ else if (!match[5]) {
+
+ text = html.slice(left, right - match[1].length);
}
//
- // we're at the end of the line.
+ // get the next tag from the stream.
//
- if (next === -1) {
- out += match + remainder;
- html = null;
- }
+ tag = html.slice(left + match.index, right);
+
//
- // we have a section, let's copy it over to
- // the output buffer and then disgard the rest.
+ // if there is a map, we may want to replace the value
+ // in an attribute or replace the body of the tag based
+ // on a specified attribute value. Closing tags are ignored.
//
- else {
+ if (map && !match[2]) {
- pos = next + (index + match.length);
+ for (var i = map.length - 1; i >= 0; i--) {
- //
- // find out if there is a match with either the map
- // or the data that was provided to the bind method.
- //
- !matchmode && match.replace(that.attr, function (attr, key, value, idx) {
-
- if (map) {
-
- //
- // if the map specifies a replacement to be made,
- // but there is no matching key found in the attributes
- // of the tag, we should create the attribute and then
- // popuate it.
- //
- createmode = false;
-
- //
- // look at all of the items in the map and try to apply
- // the mappings for all of the values that have been spec'd.
- //
-
- for (var i = map.length - 1; i >= 0; i--) {
-
- //
- // There may be an exact match for the key
- // that the map wants to replace.
- //
- if (map[i].replace === key && data[map[i].dataKey]) {
- updates.push({
- data: data[map[i].dataKey],
- insertion: {
- start: idx,
- end: attr.length
- }
- });
- continue;
- }
+ tag = tag.replace(
+ this.attr,
+ function (attr, key, value, idx) {
+
+ console.log(key, !map[i].replace, map[i].attribute === key, map[i].value, value)
+
+ if (map[i].replace === key) {
- //
- // find out if this tag is the target by looking at an
- // arbitrary attribute that the user has provided.
- //
- if (map[i].attribute === key && !map[i].replace &&
- (map[i].value === value || key === 'class' && that.hasClass(value))) {
- updates.push({ data: data[map[i].dataKey] });
+ //
+ // if there is data intended to replace the attribute, use that
+ // otherwise look at the data structure and try to use that.
+ //
+ var newdata = map[i].dataKey ? data[map[i].dataKey] : data[key];
+ return key + '="' + (newdata || '') + '"';
+ }
+ else if (!map[i].replace && map[i].attribute === key && map[i].value === value) {
+
+ buffer += text + tag + data[map[i].dataKey];
matchmode = true;
+ return attr;
}
- }
- }
- else {
+ else {
- //
- // find out if this tag is the target by checking the data
- // object and looking for this attribute value as an object key.
- //
- if (key === 'id' && data[value]) {
- updates.push({ data: data[value] });
- matchmode = true;
+ return attr;
+ }
}
- }
- });
+ );
+
+ console.log(tag);
+ }
+ }
+ else if (!match[5]) { // cant be self closing to have text in the body.
//
- // should we write data?
+ // if there is no map, we are just looking to match
+ // the specified id to a data key in the data object.
//
- if (updates.length > 0) {
-
- //
- // there may be multiple updates from the map
- //
-
- var update = null;
- while(update = updates.pop()) {
-
- //
- // position the new content
- //
-
- if (update.insertion) {
-
- //
- // in some cases the content goes inside the tag.
- //
- var i = update.insertion;
- var start = ~(index + i.start + i.end + 1);
- var value = html.slice(0, start);
-
- value += update.data + '"';
- value += html.slice(i.start + i.end);
- out += value;
- }
- else {
- //
- // in other cases, it goes inside the tag body.
- //
- out += html.slice(0, index + match.length) + update.data;
+ tag.replace(
+ this.attr,
+ function (attr, key, value, idx) {
+ if (key === 'id' && data[value]) {
+ buffer += text + tag + data[value];
+ matchmode = true;
}
}
- }
- else if (insert) {
- //
- // if there are no updates and we are not in the middle of a
- // match, just grab the next slice and add it to our buffer.
- //
- out += html.slice(0, pos);
- }
+ );
+ }
- //
- // reduce the initial html from existence.
- //
- html = html.substr(pos);
+ if (!matchmode) {
+ buffer += (text || '') + (tag || '');
}
- });
+ }
+ }
+ while (str);
+
+ //
+ // write anything that is leftover, as it will
+ // be arbitrary bites leftover from the stream.
+ //
+ if (html) {
+ buffer += html.slice(right);
}
- return out;
+ return buffer;
+
}
};
@@ -237,21 +199,15 @@
}
};
- return ({
- bind: function (html, data, map) {
- var merge = new Merge();
- return merge.bind(html, data, map);
- },
- Map: Mapper
- });
+ var host = (typeof process !== 'undefined' && process.title === 'node' ? exports : Plates);
-}();
-
-if (typeof module !== 'undefined') {
- exports.bind = function() {
- return Plates.bind.apply(this, arguments);
- };
- exports.Map = function() {
- return Paltes.Map.apply(this, arguments);
+ host.bind = function (html, data, map) {
+ var merge = new Merge();
+ return merge.bind(html, data, map);
};
-}
+
+ host.Map = Mapper;
+
+ return host;
+
+}();
View
41 test/api-test.js
@@ -1,10 +1,47 @@
var vows = require('vows'),
assert = require('assert'),
+ Plates = require('../lib/plates');
common = require('./fixtures/common');
vows.describe('merge data into markup').addBatch({
'when providing both data and markup': {
- 'for simple template': common.createTest('simple'),
- 'for one level template': common.createTest('one-level'),
+
+ '(1) a single tag with an `id` that corresponds to a `data-key`.': (
+
+ function() {
+ return common.createTest('test-1');
+ }()
+
+ ),
+ '(2) a deeply nested tag with an `id` that corresponds to a `data-key`.': (
+
+ function() {
+ return common.createTest('test-2');
+ }()
+
+ ),
+ '(3) a tag with an `id` that corresponds to a `data-key`, having a body of nested tags.': (
+
+ function() {
+ return common.createTest('test-3');
+ }()
+
+ ),
+ '(4) a tag with an arbitrary attribute that corresponds to a `data-key`.': (
+
+ function() {
+
+ var map = new Plates.Map();
+ map.where('src').is('google.com').use('key1');
+ map.where('src').is('yahoo.com').use('key2');
+
+ return common.createTest('test-4', map);
+
+ }()
+
+ ),
+ '(5) a tag that matches a specified attribute in the map': {
+
+ }
}
}).export(module);
View
17 test/fixtures/common.js
@@ -1,30 +1,39 @@
+
var common = exports,
assert = require('assert'),
fs = require('fs'),
Plates = require('../../lib/plates');
function get(name, extension) {
+
try {
- return fs.readFileSync(__dirname + '/../templates/' +
- name + '.' + extension).toString();
+ return fs.readFileSync(
+ __dirname +
+ '/../templates/' +
+ name + '.' +
+ extension
+ ).toString();
} catch(e) {
return null;
}
};
common.render = function(name, data, map) {
- var html = get(name, 'html');
+ var html = get(name, 'html');
return Plates.bind(html, data, map);
};
common.createTest = function(name, map) {
+
return {
topic: function() {
+
this.out = get(name, 'out');
this.data = JSON.parse(get(name, 'json') || "{}");
return {
+
render: common.render(name, this.data, map),
};
},
@@ -32,4 +41,6 @@ common.createTest = function(name, map) {
assert.equal(result.render, this.out);
}
};
+
};
+
View
59 test/simple-test.js
@@ -1,59 +0,0 @@
-
-
- // map.where(<attribute>).is(<value>).use(<data-key>).as(<attribute>);
-
-
- var html = '111<div id="outer">222<div id="inner">333</div><img class="test" src=""/>444</div>555';
-
- !function() {
- var data = { "foo": "New Value" };
- var map = Plates.Map();
-
- map.where('class').is('test').use('foo').as('src');
- map.where('id').is('inner').use('foo');
-
- console.log(Plates.bind(html, data, map));
- }();
-
-
-
- // Able to match an `id` with the value of `foo`, but could not find a data
- // key with with the name `fodo`. Should fail to populate, shouldn't affect
- // the inner values of the match.
-
-
- !function() {
-
- var data = { "foo": "New Value" };
- var map = Plates.Map();
-
- map.where("id").is('foo').use("fodo");
- console.log(Plates.bind(html, data, map));
- }();
-
-
-
-
- // Able to match an `id` with the value of `foo` as well as find a data
- // key with the value `foo`. Should replace all inner content of the tag.
-
-
- !function() {
- var data = { "foo": "New Value" };
- var map = Plates.Map();
-
- map.where("id").is('inner').use("foo");
- console.log(Plates.bind(html, data, map));
- }();
-
-
- // without using a map, there should be a match between
- // the data-keys and the IDs in the HTML fragment.
-
-
- !function() {
- var data = { "outer": "New Value" };
- console.log(Plates.bind(html, data));
- }();
-
-
View
1  test/templates/data-bind.html
@@ -1 +0,0 @@
-<div data-bind=name></div>
View
1  test/templates/data-bind.json
@@ -1 +0,0 @@
-{ "name": "frank" }
View
1  test/templates/data-bind.out
@@ -1 +0,0 @@
-<div data-bind=name>frank</div>
View
3  test/templates/one-level.html
@@ -1,3 +0,0 @@
-<div id="a"></div>
-<div id="b"></div>
-<div id="c"></div>
View
1  test/templates/one-level.json
@@ -1 +0,0 @@
-{ "b": "ok" }
View
3  test/templates/one-level.out
@@ -1,3 +0,0 @@
-<div id="a"></div>
-<div id="b">ok</div>
-<div id="c"></div>
View
1  test/templates/simple.html
@@ -1 +0,0 @@
-<div id='a.b'></div>
View
3  test/templates/simple.json
@@ -1,3 +0,0 @@
-{
- "a.b": "ok"
-}
View
1  test/templates/simple.out
@@ -1 +0,0 @@
-<div id='a.b'>ok</div>
View
1  test/templates/test-1.html
@@ -0,0 +1 @@
+<div id='key'></div>
View
3  test/templates/test-1.json
@@ -0,0 +1,3 @@
+{
+ "key": "value"
+}
View
1  test/templates/test-1.out
@@ -0,0 +1 @@
+<div id='key'>value</div>
View
1  test/templates/test-2.html
@@ -0,0 +1 @@
+<!-- foo --><div id="key1"><div id="key2"></div><img src="/"></div>
View
3  test/templates/test-2.json
@@ -0,0 +1,3 @@
+{
+ "key2": "value2"
+}
View
1  test/templates/test-2.out
@@ -0,0 +1 @@
+<!-- foo --><div id="key1"><div id="key2">value2</div><img src="/"></div>
View
1  test/templates/test-3.html
@@ -0,0 +1 @@
+<div id="key1"><!-- foo --><div id="key2"></div><img src="/"/></div>
View
3  test/templates/test-3.json
@@ -0,0 +1,3 @@
+{
+ "key1": "value1"
+}
View
1  test/templates/test-3.out
@@ -0,0 +1 @@
+<div id="key1">value1</div>
View
2  test/templates/test-4.html
@@ -0,0 +1,2 @@
+<a id="someid" src="google.com"></a>
+<a id="someotherid" src="yahoo.com"></a>
View
4 test/templates/test-4.json
@@ -0,0 +1,4 @@
+{
+ "key1": "Google",
+ "key2": "Yahoo!"
+}
View
2  test/templates/test-4.out
@@ -0,0 +1,2 @@
+<a id="someid" src="google.com">Google</a>
+<a id="someotherid" src="yahoo.com">Yahoo!</a>
Please sign in to comment.
Something went wrong with that request. Please try again.