Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Browser tests, fix greedy matching, default data-bind, convention and configuration #105

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 120 additions & 20 deletions lib/plates.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ var Plates = (typeof module !== 'undefined' && 'id' in module && typeof exports
if (!map1.attribute) return 1;
if (!map2.attribute) return -1;

// Attribute setters should be before tag content setters
if (('replace' in map1) && ! ('replace' in map2)) {
return 1;
}
if (! ('replace' in map1) && ('replace' in map2)) {
return -1;
}

if (map1.attribute !== map2.attribute) {
return map1.attribute < map2.attribute ? -1 : 1;
}
Expand Down Expand Up @@ -142,6 +150,15 @@ function matchClosing(input, tagname, html) {
//
attr: /([\-\w]*)\s*=\s*(?:["\']([\-\.\w\s\/:;&#]*)["\'])/gi,

//
// For using 'data-bind' and 'data-bind-(attr)' to automatically map data.
//
defaults: {
where: 'data-bind',
reGlobal: /data-bind-([\-\w]*?)=(["\'])(.{0,}?)\2/gi,
re: /data-bind-([\-\w]*?)=(["\'])(.{0,}?)\2/i
},

//
// 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.
Expand Down Expand Up @@ -296,14 +313,95 @@ function matchClosing(input, tagname, html) {
//
// if there is a match in progress and
//

//
// General mapping that matches 'data-bind-(attr)' attributes.
// (Should be applied before the explicit mappings as 'tag content' matches there push content to the buffer.)
//
var defaults = this.defaults;
var dataBinds = [];
var matches = tagbody.match(defaults.reGlobal);
if (matches) {
for (var ii = 0; ii < matches.length; ii++) {
var match = matches[ii].match(defaults.re);
var replace = match[1];
var dataKey = match[3];
var attribute = match[0].slice(0, match[0].indexOf('='));
var hasMapping = false;

// Check if data exists
if (data[dataKey] !== undefined) {
// Don't change values that are mapped explicitly
if (mappings && mappings.length > 0) {
for (var iii = mappings.length - 1; iii >= 0; iii--) {
if (mappings[iii].attribute === attribute && mappings[iii].value === dataKey) {
hasMapping = true;
continue;
}
}
}

// Only change attributes without a mapping
if (!hasMapping) {
var regex = new RegExp('^'+replace+'=(?:\\w+|["|\'](?:.*)["|\'])', 'i');
dataBinds.push({
replace: replace,
dataKey: dataKey,
re: regex,
setAttribute: false
});
}
}
}
}

if (dataBinds) {
for (var ii = 0; ii < dataBinds.length; ii++) {
var bind = dataBinds[ii];

tagbody = tagbody.replace(this.attr, function(str, key, value, a) {

if(str.match(bind.re)) {
bind.setAttribute=true;
return key+'="'+data[bind.dataKey]+'"';
}
return str;
});

if(!bind.setAttribute) {
var spliced = isSelfClosing? 2 : 1;
var close = isSelfClosing? '/>': '>';
var left = tagbody.substr(0, tagbody.length - spliced);
if (left[left.length - 1] == ' ') {
left = left.substr(0, left.length - 1);
if (isSelfClosing) {
close = ' ' + close;
}
}
tagbody = [
left,
' ',
bind.replace,
'="',
data[bind.dataKey],
'"',
close
].join('');
}
}
}

//
// Explicit mappings
//
if (mappings && mappings.length > 0) {
for (var ii = mappings.length - 1; ii >= 0; ii--) {
var setAttribute = false
, mapping = mappings[ii]
, shouldSetAttribute = mapping.re && attributes.match(mapping.re);
, shouldSetAttribute = mapping.re && attributes && attributes.match(mapping.re);

//
// check if we are targetting a element only or attributes
// check if we are targeting a element only or attributes
//
if ('tag' in mapping && !this.attr.test(tagbody) && mapping.tag === tagname) {
tagbody = tagbody + fetch(data, mapping, '', tagbody);
Expand Down Expand Up @@ -341,8 +439,8 @@ function matchClosing(input, tagname, html) {
if (mapping.remove) {
//
// only increase the remove counter if it's not a self
// closing element. As matchmode is suffectient to
// remove tose
// closing element. As matchmode is sufficient to
// remove those
//
if (!isSelfClosing) remove++;
matchmode = true;
Expand Down Expand Up @@ -404,26 +502,28 @@ function matchClosing(input, tagname, html) {
].join('');
}
}
} else {
//
// if there is no map, we are just looking to match
// the specified id to a data key in the data object.
//
}

//
// If there is no match (in mappings), try to match
// the specified 'id', 'data-bind' or map.conf.where
// to a data key in the data object.
//
if (!matchmode) {
tagbody.replace(this.attr, function (attr, key, value, idx) {
if (key === map && map.conf.where || 'id' && data[value]) {
var v = data[value],
nest = Array.isArray(v),
output = (nest || typeof v === 'object') ? that.iterate(html, v, components, tagname, value, map) : v;
if ((data[value] !== undefined) && (key === (map && map.conf.where) || key === defaults.where || key === 'id')) {
var v = data[value],
nest = Array.isArray(v),
output = (nest || typeof v === 'object') ? that.iterate(html, v, components, tagname, value, map) : v;

// If the item is an array, then we need to tell
// Plates that we're dealing with nests
if (nest) { that.nest.push(tagname); }
// If the item is an array, then we need to tell
// Plates that we're dealing with nests
if (nest) { that.nest.push(tagname); }

buffer += nest ? output : tagbody + output;
matchmode = true;
}
buffer += nest ? output : tagbody + output;
matchmode = true;
}
);
});
}
}

Expand Down
Loading