Skip to content

Commit

Permalink
Fixes #5, #33
Browse files Browse the repository at this point in the history
  • Loading branch information
rgthree committed Apr 24, 2014
1 parent 76da6d8 commit b131d61
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 100 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,10 @@ MooVeeStar uses grunt to automate linting, testing and compressing.
6. If all goes well, commit with a thought out message
7. If you have contributions, send a [pull request](https://help.github.com/articles/using-pull-requests)


## Breaking Changelog

**0.1.1+20140423**

- [Private] We will now use `<template>` tags and their shadowDom. This means we only store the fragment now, and do not _need_ to parse an HTML string. Now, templates may not strip comments & whitespace and only store a `DocumentFragment` under the template data's `fragment` key (removing the `markup` and `dom` keys respectively).
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "MooVeeStar",
"version": "0.1.0+20140330",
"version": "0.1.1+20140423",
"main": [
"mooveestar-min.js",
"src/mooveestar.js"
Expand Down
4 changes: 2 additions & 2 deletions mooveestar-min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "MooVeeStar",
"version": "0.1.0+20140330",
"version": "0.1.1+20140423",
"description": "MooTools MV* Framework",
"homepage": "https://rgthree.github.io/mooveestar/",
"author": {
Expand Down
178 changes: 107 additions & 71 deletions src/mooveestar.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// > MooVeeStar v0.1.0+20140330 - https://rgthree.github.io/mooveestar/
// > MooVeeStar v0.1.1+20140423 - https://rgthree.github.io/mooveestar/
// > by Regis Gaughan, III <[email protected]> http://regisgaughan.com
// > MooVeeStar may be freely distributed under the MIT license.

Expand Down Expand Up @@ -379,6 +379,8 @@

// The internal list of models
_models: [],
// A map of model's by their `id` (`getId()`), with a map of `count` and `model`
_modelsById: {},


// ### Collection Constructor
Expand Down Expand Up @@ -441,9 +443,15 @@
model = new self.modelClass(model);

// If we don't find the model in our list already, or we allowDuplicates
if(!self.findFirst(model.getId()) || self.options.allowDuplicates){
if(self.options.allowDuplicates || !self.findFirst(model.getId())){
model.addEvent('*', self._onModelEvent);
added.push(model);
// Add/or increase the count of the model's id
if(self._modelsById[model.getId()])
self._modelsById[model.getId()].count += 1;
else
self._modelsById[model.getId()] = { count:1, model:model};

if(options.at != null)
self._models.splice(options.at+addedCount, 0, model);
else
Expand Down Expand Up @@ -472,7 +480,7 @@
// Fires `Collection#change` and `Collection#remove`
//
remove: function(indexOrModels, options){
var self, modelsRemoved, model;
var self, modelsRemoved, model, toRemove, i, l;
self = this;
options = options || {};
modelsRemoved = [];
Expand All @@ -481,25 +489,29 @@
// even if that model exists elsewhere
if(typeof(indexOrModels) === 'number'){
model = self.at(indexOrModels);
if(self._models.contains(model)){
if(self._modelsById[model.getId()] && self._modelsById[model.getId()].count > 0){
modelsRemoved.include(model);
Array.splice(self._models, indexOrModels, 1);
self._modelsById[model.getId()].count -= 1;
// Only remove the event listener when there's no more instances of the model in the collection
if(!self._models.contains(model))
if(self._modelsById[model.getId()].count === 0){
model.removeEvent('*', self._onModelEvent);
delete self._modelsById[model.getId()];
}
}

// Otherwise, remove all occurrences of the model(s) passed in
}else{
indexOrModels = Array.from(indexOrModels);
// Loop over inversely so we do not mess with order if `indexOrModels === this._model` when `removeAll()`
for(var i = indexOrModels.length-1, l = 0; i >= 0; i--){
model = typeof(indexOrModels[i]) !== 'object' ? self.findFirst(indexOrModels[i]) : indexOrModels[i];
toRemove = [];
Array.from(indexOrModels).forEach(function(item){ toRemove.include(item); });
for(i = toRemove.length-1, l = 0; i >= 0; i--){
model = typeof(toRemove[i]) !== 'object' ? self.findFirst(toRemove[i]) : toRemove[i];
if(self._models.contains(model)){
model.removeEvent('*', self._onModelEvent);
modelsRemoved.include(model);
// Array.erase removes all instances of the model
self._models.erase(model);
delete self._modelsById[model.getId()];
}
}
}
Expand Down Expand Up @@ -620,10 +632,19 @@

// ### Collection#findFirst
//
// Returns the first model whos `idProperty` (or `keyToFind` value) matches the passed value
// Returns the first model whose `idProperty` (or `keyToFind` value) matches the passed value
//
findFirst: function(value, keyToFind){
var models = this.find(value, keyToFind);
findFirst: function(valueOrObj, keyToFind){
//if(valueOrObj == null)
//return null;

// If there's no keyToFind, then we can assume we're looking up by the model's id
if(!keyToFind){
var found = this._modelsById[valueOrObj] || (valueOrObj instanceof MooVeeStar.Model && this._modelsById(valueOrObj.getId())) || this._modelsById[valueOrObj.id] || this._modelsById[valueOrObj.cid] || null;
return found && found.model || null;
}

var models = this.find(valueOrObj, keyToFind);
return models.length ? models[0] : null;
},

Expand Down Expand Up @@ -1162,7 +1183,7 @@
// The templates registry
_templates: {},

// Test if the browser supports HTML5 <template> tags
// Test if the browser supports HTML5 template tags
supportsTemplate: ('content' in document.createElement('template')),


Expand All @@ -1182,15 +1203,14 @@
// // <li data-bind="name uuid:data-uuid accent:class privacy:(data-private class)" data-action="choose"></li>
// <li data-bind="name uuid accent privacy" data-bind-uuid="data-uuid" data-bind-accent="class" data-bind-privacy="data-private class" data-action="choose"></li>
//
_parseShorthand: function(element){
var elements;
elements = element.getElements('[data-bind]');
if(element.get('data-bind'))
elements.unshift(element);


elements.forEach(function(el){
var dataBind = el.get('data-bind');
_parseShorthand: function(elementOrFragment){
var elements, dataBind;
elements = elementOrFragment.querySelectorAll('[data-bind]');
if(elementOrFragment.getAttribute && elementOrFragment.getAttribute('data-bind'))
elements.unshift(elementOrFragment);

Array.from(elements).forEach(function(el){
var dataBind = el.getAttribute('data-bind');
if(dataBind && dataBind.contains(':')){
[
/\s*([^\s]+?)(?!\\):\(([^\)]+)\)/, // Capture bindings with parens
Expand All @@ -1199,50 +1219,60 @@
var match;
while((match = regex.exec(dataBind))){
dataBind = dataBind.replace(match[0], ' '+match[1]);
el.set('data-bind-'+match[1], match[2]);
el.setAttribute('data-bind-'+match[1], match[2]);
}
});
el.set('data-bind', dataBind.trim());
el.setAttribute('data-bind', dataBind.trim());
}
});
return element;
return elementOrFragment;
},


// ### templates::register
//
// Register a template to an html string and create a dom from it
// Register a template to it's key and a DocumentFragment to store the markup. Uses `<template>` shadow DOM if possible.
// Overloaded to accept a register a script as second param
//
register: function(key, htmlOrElementOrFunction){
var html, type;
var html, type, tag, parentTag, fragment;
key = mvstpl._cleanKey(key);
type = typeOf(htmlOrElementOrFunction);
tag = type === 'element' && htmlOrElementOrFunction.get('tag') || null;

if(type === 'function')
return mvstpl.registerScript(key, htmlOrElementOrFunction);

if(type !== 'element' && (type !== 'string' || !htmlOrElementOrFunction.length))
throw new Error('A registered script must pass an element or an html string. (key:"'+key+'")');

// If it's an element, use it's inner HTML (ior text content if a `<script>`)

if(type === 'element'){
if(htmlOrElementOrFunction.get('tag') === 'script')
html = htmlOrElementOrFunction.get('text');
else if(htmlOrElementOrFunction.get('tag') === 'template')
html = htmlOrElementOrFunction.get('html');
else
html = new Element('tpl').grab(htmlOrElementOrFunction).get('html');
}else if(type === 'string'){
html = htmlOrElementOrFunction;
}else{
throw new Error('A registered script must pass an element or a string. (key:"'+key+'")');
}
mvstpl._templates[key] = mvstpl._templates[key] || {};

html = html.replace(/<\!\-\-.*?\-\->/g, '').trim().replace(/\n/g,' ').replace(/\s+/g,' ');
// If we support HTML5 template tags, then use them wholesale
if(mvstpl.supportsTemplate && tag === 'template' && htmlOrElementOrFunction.content){
fragment = htmlOrElementOrFunction.content;
}else{
// If we don't support HTML5 templates, then scrape their html (or script tag text)
if(type === 'element'){
if(tag === 'script' || tag === 'template'){
html = htmlOrElementOrFunction.get(tag === 'script' ? 'text' : 'html');
}else{
parentTag = tag === 'tr' ? 'tbody' : (/^t(body|head|foot)/.test(tag) ? 'table' : (/^t(d|h)/.test(tag) ? 'tr' : 'tpl'));
html = new Element(parentTag).grab(htmlOrElementOrFunction).get('html');
}
}else{
html = htmlOrElementOrFunction;
}

mvstpl._templates[key] = mvstpl._templates[key] || {};
mvstpl._templates[key].tpl = mvstpl._parseShorthand(new Element('tpl[html="'+html+'"]'));
mvstpl._templates[key].markup = mvstpl._templates[key].tpl.innerHTML;
// Create our fragment and append each child to it.
fragment = document.createDocumentFragment();
html = html.replace(/<\!\-\-.*?\-\->/g, '').trim().replace(/\n/g,' ').replace(/\s+/g,' ');
parentTag = parentTag || (html.indexOf('<tr') === 0 && 'tbody') || (/^<t(body|head|foot)/.test(html) && 'table') || (/^<t(d|h)/i.test(html) && 'tr') || 'tpl';
new Element(parentTag+'[html="'+html+'"]').getChildren().forEach(function(el){
fragment.appendChild(el);
});
}
mvstpl._templates[key].fragment = mvstpl._parseShorthand(fragment);
},


Expand Down Expand Up @@ -1287,15 +1317,14 @@
var data, markupEl, els, childrenTemplates;
key = mvstpl._cleanKey(key);
data = mvstpl._templates[key];
if(data){
// If html5Shiv is installed, and we need to go around cloneNode for HTML5 elements
if(!mvstpl.supportsTemplate && window.html5 && window.html5.supportsUnknownElements === false){
var node = html5.createElement('template');
node.innerHTML = data.markup;
return $(node).set('data-templateid', key);
}else{
return data.tpl.clone().set('data-templateid', key);
}
if(data && data.fragment){
var node;
if(document.importNode)
node = document.importNode(data.fragment, true);
else
node = data.fragment.cloneNode(true);
node._templateid = key;
return node;
}else{
throw new Error('Ain\'t no template called '+key+' ('+typeOf(mvstpl._templates[key])+')');
}
Expand Down Expand Up @@ -1330,23 +1359,26 @@
}
if(dom){
var markups, children, script;
// Attach any markup templates
(dom.getElements('markup')).each(function(child){
var tpls, markupClass;
// If there's a class name specified on the <markup> tag, add it to the children
markupClass = child.get('class') || null;
// Check for nested templates by way of a [data-template] attribute
Array.from(dom.querySelectorAll('[data-template], [data-templateid], [template]')).each(function(child){
var tpls, className;
// If there's a class name specified on the template call, then we want to add it
className = child.className || null;
// If we passed in skipInit, use it, otherwise set to true assuming this pass is initializing final markup
tpls = mvstpl.inflate(child.get('template'), scriptData, skipInit != null ? skipInit : true);
tpls = mvstpl.inflate(child.get('data-template') || child.get('data-templateid') || child.get('template'), scriptData, skipInit != null ? skipInit : true);
tpls = typeOf(tpls) !== 'array' ? [tpls] : tpls;
tpls.reverse().each(function(tplChild){
tplChild.inject(child, 'after');
markupClass && tplChild.addClass(markupClass);
});
className && tplChild.addClass(className);
});
child.destroy();
});
children = dom.getChildren();
children.each(function(child){
child.set('data-tpl', (child.get('data-tpl') || '').split(' ').include(dom.get('data-templateid')).join(' ').clean());
children = [];
Array.from(dom.childNodes).each(function(child){
if(child.nodeType === 1){
child.setAttribute('data-tpl', (child.getAttribute('data-tpl') || '').split(' ').include(dom._templateid).join(' ').clean());
children.push(child);
}
});
children = children.length === 1 ? children[0] : children;
!skipInit && mvstpl.init(children, scriptData);
Expand Down Expand Up @@ -1479,11 +1511,15 @@
}else if(field === 'class' || (binding === 'class' && field === 'default')){
val && child.addClass(val);

}else if((field === 'html' || field === 'default') && /^element/.test(typeOf(val))) {
}else if((field === 'html' || field === 'default') && (/^element/.test(typeOf(val)) || val instanceof DocumentFragment)) {
child.empty();
Array.from(val).forEach(function(val){
child.grab(val);
});
if(val instanceof DocumentFragment){
child.appendChild(val);
}else{
Array.from(val).forEach(function(val){
child.grab(val);
});
}

}else if(field === 'default'){
field = /input|textarea|select/.test(child.get('tag')) ? 'value' : 'html';
Expand Down Expand Up @@ -1533,7 +1569,7 @@

// ### templates::scrape
//
// Scrape the dom and register any templates as `<template id="xxx">`, `<template data-id="xxx">` or `<script type="text/x-tpl" data-id="xxx">`
// Scrape the dom and register any templates as `template id="xxx"`, `template data-id="xxx"` or `script type="text/x-tpl" data-id="xxx"`
scrape: function(htmlOrEl){
var el = document.html;
if(typeOf(htmlOrEl) === 'string')
Expand Down Expand Up @@ -1572,4 +1608,4 @@
// Scrape the page on initialization
MooVeeStar.templates.scrape();

})(this);
})(this);
2 changes: 1 addition & 1 deletion test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</script>


<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/mootools/1.4.5/mootools-yui-compressed.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/mootools/1.4.5/mootools.js"></script>
<script src="../src/mooveestar.js"></script>

<script src="tests/test.js"></script>
Expand Down
Loading

0 comments on commit b131d61

Please sign in to comment.