Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[lib] rewrite

  • Loading branch information...
commit a44d75c4f10c1846da00cc5fdb0fcd31d17ff3b9 1 parent 92c4362
Paolo Fragomeni authored
Showing with 197 additions and 99 deletions.
  1. +197 −99 lib/plates.js
View
296 lib/plates.js
@@ -6,8 +6,7 @@
tag: new RegExp(
[
- '\\s*',
- '<',
+ '(<',
'(/?)', // 1 - is closing
'([-:\\w]+)', // 2 - name
'((?:\\s+[-\\w]+(?:', '=', '(?:' +
@@ -15,142 +14,241 @@
'"[^"]*"|' +
'\'[^\']*\'))?)*)', // 3 - attributes
'(/?)', // 4 - is self-closing
- '>'
- ].join('\\s*'),
- 'gi'
+ '>)(.*)'
+ ].join('\\s*')
),
attr: new RegExp(
- '([-\\w]+)=(?:["\']([-\.\\w\\s]+)["\'])', // '([-\\w]+)=(?:([-\\w]+)|["\']([-\\w\\s]+)["\'])'
+ '([-\\w]*)=(?:["\']([-\.\\w\\s]*)["\'])',
'gi'
),
- bind: function bind(html, data, map) {
+ hasClass: function(str, className) {
+ return str.indexOf(className) > -1;
+ },
- while (matchedTag = this.tag.exec(html)) {
-
- //
- // must not be a closing tag, must have attributes.
- //
- if (matchedTag[1] !== '/' && matchedTag[3] !== '') {
+ bind: function bind(html, data, map) {
- if (!map) {
-
- while (matchedAttr = this.attr.exec(matchedTag[3])) {
+ var that = this;
+ var out = '';
+ var pos = 0;
+ var openers = 0;
+ var updates = '';
+ var insertion = null;
+ var matchmode = false;
+ var insert = true;
+
+ if (map) {
+ map = map.mappings;
+ }
- //
- // has an attribute who's value is a match.
- //
- if (matchedAttr[1] === 'id' && data[matchedAttr[2]]) {
- var idx = matchedTag.index + matchedTag[0].length;
- html = html.slice(0, idx) + data[matchedAttr[2]] + html.slice(idx);
+ while (html) {
+ html.replace(that.tag, function (body, match, closing, tag, attributes, selfclosing, remainder, index) {
+
+ //
+ // search for the next tag, so we know
+ // where and how to slice and dice the html.
+ //
+ var next = remainder.search(that.tag);
+
+ //
+ // its a closing tag
+ //
+ if (matchmode) {
+ if (!!closing) {
+ if(openers === 0) {
+ insert = true;
+ matchmode = false;
+ }
+ else {
+ --openers;
}
}
+ //
+ // its not a closing tag!
+ //
+ else {
+ ++openers;
+ insert = false;
+ }
+ }
+ //
+ // we're at the end of the line.
+ //
+ if (next === -1) {
+ out += match + remainder;
+ html = null;
}
+ //
+ // we have a section, let's copy it over to
+ // the output buffer and then disgard the rest.
+ //
else {
+ pos = next + (index + match.length);
+
//
- // if there is a map, the user wants an explicit
- // data-key to tag-attribute match.
+ // find out if there is a match with either the map
+ // or the data that was provided to the bind method.
//
- var match = false, idx = 0;
- for (var key in map) {
- if (data[key]) {
- while (matchedAttr = this.attr.exec(matchedTag[3])) {
+ !matchmode && match.replace(that.attr, function (attr, key, value, idx) {
+
+ if (map) {
+
+ //
+ // 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--) {
+
+ if (map[i].replace === key && data[map[i].dataKey]) {
+ insertion = {
+ start: idx,
+ end: attr.length
+ };
+ updates = data[map[i].dataKey];
+ }
//
- // an attribute who's value is a match.
+ // find out if this tag is the target by looking at an
+ // arbitrary attribute that the user has provided.
//
-
-
- if (matchedAttr[1] === map[key] ||
- matchedAttr[1] === map[key][0]) {
-
- match = false, idx = 0;
-
- //
- // if the preferred attribute is `class`, split the class and
- // make sure that the actual key is a match with the data.
- //
- if (map[key] === 'class' || (map[key][0] && map[key][0] === 'class')) {
- var classNames = matchedAttr[2].split(/\s+/);
- for (var i = 0, l = classNames.length; i < l; i++) {
- if (classNames[i] === key) {
- match = true; break;
- }
- }
- }
-
- //
- // if there is a match from the class, or there is a match of
- // the key, then splice the data into the string where it belongs.
- //
-
- if (matchedAttr[2] === key || match) {
-
- var d = match ? key : matchedAttr[2];
-
- if (typeof map[key] === 'object') {
-
- //
- // find the attribute key/value pair in the tag.
- //
- var att = matchedTag[0].match(
- new RegExp(map[key][1] + '\\s*=\\s*["|\'](.*)["|\']')
- );
-
- if (map[key][1]) {
- //
- // determine the start and end positions.
- //
- var start = matchedTag.index + att.index + (att[0].length - att[1].length);
- var end = start + att[1].length;
-
- html = html.slice(0, start-1) + data[d] + html.slice(end-1);
- }
- else if (matchedTag[4] !== '/') {
- //
- // must not be a self closing tag
- //
- idx = matchedTag.index + matchedTag[0].length;
- html = html.slice(0, idx) + data[d] + html.slice(idx);
- }
- }
- else {
- //
- // insert the value as a `textnode` into the tag.
- //
- idx = matchedTag.index + matchedTag[0].length;
- html = html.slice(0, idx) + data[d] + html.slice(idx);
- }
- }
-
+ if (map[i].attribute === key && map[i].value === value) {
+ updates = data[map[i].dataKey];
+ matchmode = true;
}
}
}
+ 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 = data[value];
+ matchmode = true;
+ }
+ }
+ });
+
+ //
+ // should we write data?
+ //
+ if (updates) {
+
+ //
+ // position the new content
+ //
+ if (insertion) {
+
+ //
+ // in some cases the content goes inside the tag.
+ //
+ var start = ~(index + insertion.start + insertion.end + 1);
+ var value = html.slice(0, start);
+ value += updates + '"';
+ value += html.slice(insertion.start + insertion.end);
+ out += value;
+
+ insertion = null;
+ updates = null;
+ }
+ else {
+ //
+ // in other cases, it goes inside the tag body.
+ //
+ out += html.slice(0, index + match.length) + updates;
+ updates = null;
+ }
+ }
+ 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);
}
-
- }
+ });
}
- return html;
+ return out;
+ }
+ };
+
+ var Mapper = function Mapper(val) {
+ if (!(this instanceof Mapper)) { return new Mapper(val); }
+ this.mappings = [];
+ };
+
+ //
+ // almost, but not quite...
+ //
+ function last() {
+ if (this.mappings.length < 1) {
+ return this.mappings.push({});
+ }
+ else {
+ return this.mappings[this.mappings.length-1];
+ }
+ }
+
+ Mapper.prototype = {
+ use: function(val) {
+ last.call(this).dataKey = val;
+ return this;
+ },
+ is: function(val) {
+ last.call(this).value = val;
+ return this;
+ },
+ where: function(val) {
+
+ if (typeof val === 'string') {
+
+ this.mappings.push({
+ "attribute": val,
+ "replace": null
+ });
+ }
+ else {
+ //
+ // TODO: Accept an object literal of attribute `ids`
+ // to data `keys`. Lets iterate each one and build
+ // up the mappings hash. By default, matches will be
+ // made on a tag's IDs.
+ //
+ }
+ return this;
+ },
+ as: function(val) {
+ last.call(this).replace = val;
+ return this;
}
};
return ({
- bind: function(html, data, map) {
+ bind: function (html, data, map) {
var merge = new Merge();
return merge.bind(html, data, map);
- }
+ },
+ Map: Mapper
});
-
}();
if (typeof module !== 'undefined') {
exports.bind = function() {
return Plates.bind.apply(this, arguments);
};
+ exports.Map = function() {
+ return Paltes.Map.apply(this, arguments);
+ };
}
Please sign in to comment.
Something went wrong with that request. Please try again.