Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

live template binding

  • Loading branch information...
commit f78e0e86c1a189f39611d7e755f5b96039b80504 1 parent 3528bf9
@justinbmeyer justinbmeyer authored
View
5 lang/observe/observe.js
@@ -315,8 +315,9 @@ steal('jquery/class/class_core.js',function() {
var tAttr= typeof attr;
if(tAttr != 'string' && tAttr != 'number'){
return this._attrs(attr, val)
- }else if ( val === undefined ) {
- // if we are getting a value
+ }else if ( val === undefined ) {// if we are getting a value
+ // let people know we are reading (
+ $.Observe.__reading && $.Observe.__reading(this, attr)
return this._get(attr)
} else {
// otherwise we are setting
View
3  view/ejs/binding.ejs
@@ -0,0 +1,3 @@
+<div class='<%= task.attr('completed') ? "complete" %>'>
+ <%== task.attr('name') %>
+</div>
View
16 view/ejs/ejs.html
@@ -0,0 +1,16 @@
+<html>
+ <head>
+
+ </head>
+ <body>
+ <script type='text/javascript' src='../../../steal/steal.js'></script>
+ <script type='text/javascript'>
+ steal('jquery/view/ejs/ejs2.js', function(){
+ $.get('test_template.ejs',{},function(text){
+ console.log(EJS(text,{}))
+ })
+ })
+ </script>
+ </body>
+
+</html>
View
152 view/ejs/ejs.js
@@ -213,10 +213,23 @@ steal('jquery/view', 'jquery/lang/string/rsplit').then(function( $ ) {
var v = new EJS.Helpers(object, extraHelpers || {});
return this.template.fn.call(object, object, v);
};
+ var liveBind = function(observed, el, cb){
+ $.each(observed, function(i, ob){
+ ob.cb = function(){
+ console.log('prop changed!')
+ cb()
+ }
+ ob.obj.bind(ob.attr, ob.cb)
+ })
+ $(el).bind('destroyed', function(){
+ $.each(observed, function(i, ob){
+ ob.obj.unbind(ob.attr, ob.cb)
+ })
+ })
+ };
/**
* @Static
*/
-
extend(EJS, {
/**
* Used to convert what's in &lt;%= %> magic tags to a string
@@ -245,7 +258,64 @@ steal('jquery/view', 'jquery/lang/string/rsplit').then(function( $ ) {
* * a function - the attribute "data-view-id='XX'", where XX is a hookup number for jQuery.View
* * an array - the attribute "data-view-id='XX'", where XX is a hookup number for jQuery.View
*/
- text: function( input ) {
+ text: function(status, self, func ) {
+ console.log('holler')
+ // wire up observe listener
+ var observed = [];
+ $.Observe.__reading = function(obj, attr){
+ observed.push({
+ obj : obj,
+ attr : attr
+ })
+ }
+ var input = func.call(self);
+ delete $.Observe.__reading;
+ if(observed.length) {
+ if(status === 0){ // we are in some html ... (we can't know this!)
+ // return a span with a hookup function ...
+ return "<span "
+ + "data-view-id='" + $View.hookup(function(el){
+ // remove child, bind on parent
+ var parent = el.parentNode;
+ // get where el is ...
+ for(var i=0; i < parent.childNodes.length; i++){
+ if(parent.childNodes[i] === el){
+ break;
+ }
+ }
+ var node = document.createTextNode(input);
+ parent.insertBefore(node, el)
+ parent.removeChild(el);
+
+
+ // create textNode
+ liveBind(observed, parent, function(){
+ node.nodeValue = func.call(self)
+ })
+ }) + "'></span>"
+ } else if(status === 1){
+ return input;
+ // mark at end!
+ } else {
+
+
+ pendingHookups.push(function(el){
+ var attr = el.getAttribute(status);
+ var parts = attr.split("__!@#$%__")
+ parts.splice(1,0,input)
+ el.setAttribute(status, parts.join(""))
+
+ liveBind(observed, parent, function(){
+ parts[1] = func.call(self)
+ el.setAttribute(status, parts.join(""))
+ })
+ })
+ return "__!@#$%__";
+ return input;
+ }
+
+ }
+ console.log(observed)
// if it's a string, return
if ( typeof input == 'string' ) {
return input;
@@ -280,6 +350,19 @@ steal('jquery/view', 'jquery/lang/string/rsplit').then(function( $ ) {
// finally, if all else false, toString it
return input.toString ? input.toString() : "";
},
+ pending : function(){
+ if(pendingHookups.length){
+ var hooks = pendingHookups.slice(0);
+ pendingHookups = [];
+ return " data-view-id='" + $View.hookup(function(el){
+ $.each(hooks, function(i, fn){
+ fn(el);
+ })
+ }) + "'";
+ }else {
+ return "";
+ }
+ },
/**
* Escapes the text provided as html if it's a string.
* Otherwise, the value is passed to EJS.text(text).
@@ -327,6 +410,17 @@ steal('jquery/view', 'jquery/lang/string/rsplit').then(function( $ ) {
}
},
+ htmlTag =null,
+ quote = null,
+ beforeQuote = null,
+ status = function(){
+ // t - 1
+ // h - 0
+ // q - string beforeQuote
+
+ return quote ? "'"+beforeQuote.match(/([^\s]+)=$/)[1]+"'" : (htmlTag ? 1 : 0)
+ },
+ pendingHookups = [],
scanline = function( scanner, line, block ) {
scanner.lines++;
var line_split = rSplit(line, scanner.splitter),
@@ -355,7 +449,9 @@ steal('jquery/view', 'jquery/lang/string/rsplit').then(function( $ ) {
scan: scan,
lines: 0
});
- scanner.splitter = new RegExp("(" + [scanner.dLeft, scanner.dRight, scanner.eeLeft, scanner.eLeft, scanner.cmnt, scanner.left, scanner.right + '\n', scanner.right, '\n'].join(")|(").
+ scanner.splitter = new RegExp("(" + [scanner.dLeft, scanner.dRight, scanner.eeLeft,
+ scanner.eLeft, scanner.cmnt, scanner.left,
+ scanner.right + '\n', scanner.right, '\n',"<",">","'",'"'].join(")|(").
replace(/\[/g, "\\[").replace(/\]/g, "\\]") + ")");
return scanner;
},
@@ -387,11 +483,10 @@ steal('jquery/view', 'jquery/lang/string/rsplit').then(function( $ ) {
content = '',
// adds something to be inserted into the view template
// this comes out looking like __v1ew.push("CONENT")
- put = function( content ) {
- buff.push(put_cmd, '"', clean(content), '");');
+ put = function( content, bonus ) {
+ buff.push(put_cmd, '"', clean(content), '"'+(bonus||'')+');');
},
- // the starting magic tag
- startTag = null,
+
// cleans the running content
empty = function() {
content = ''
@@ -401,8 +496,13 @@ steal('jquery/view', 'jquery/lang/string/rsplit').then(function( $ ) {
// a stack used to keep track of how we should end a bracket }
// once we have a <%= %> with a leftBracket
// we store how the file should end here (either '))' or ';' )
- endStack =[];
+ endStack =[],
+ lastToken,
+ startTag = null;
+ // re-init the tag goodness
+ htmlTag = quote = beforeQuote = null;
+
// start going token to token
scan(makeScanner(left, left === '[' ? ']' : '>'), source || "", function( token, scanner ) {
// if we don't have a start pair
@@ -434,6 +534,36 @@ steal('jquery/view', 'jquery/lang/string/rsplit').then(function( $ ) {
// replace <%% with <%
content += scanner.left;
break;
+ case '<':
+ htmlTag = '<'
+ content += token;
+ console.log('<');
+
+ break;
+ case '>':
+ htmlTag = null;
+ console.log('>');
+ // add some code that checks for pending hookups?
+
+
+ put(content, ",jQuery.EJS.pending(),\">\"");
+ empty();
+
+
+ break;
+ case "'":
+ case '"':
+ console.log('q')
+ if(htmlTag){
+ if(quote && quote === token){
+ quote = null;
+ console.log('eq')
+ } else if(quote === null){
+ quote = token;
+ beforeQuote = lastToken;
+ console.log('Q')
+ }
+ }
default:
content += token;
break;
@@ -492,13 +622,13 @@ steal('jquery/view', 'jquery/lang/string/rsplit').then(function( $ ) {
endStack.push(doubleParen)
}
- buff.push(insert_cmd, "jQuery.EJS.text(", content,
+ buff.push(insert_cmd, "jQuery.EJS.text("+status()+",this,function(){ return ", content,
// if we have a block
bn ?
// start w/ startTxt "var _v1ew = [])"
startTxt :
// if not, add doubleParent to close push and text
- doubleParen
+ "}"+doubleParen
);
break;
}
@@ -512,7 +642,9 @@ steal('jquery/view', 'jquery/lang/string/rsplit').then(function( $ ) {
content += token;
break;
}
+
}
+ lastToken = token;
})
if ( content.length > 0 ) {
// Should be content.dump in Ruby
View
147 view/ejs/ejs2.js
@@ -0,0 +1,147 @@
+steal('jquery',function(){
+
+ EJS = function(template){
+ var split = template.split(tokens);
+ scan(split)
+ return split;
+ }
+ var returnReg = /\r\n/g,
+ retReg = /\r/g,
+ newReg = /\n/g,
+ nReg = /\n/,
+ slashReg = /\\/g,
+ quoteReg = /"/g,
+ singleQuoteReg = /'/g,
+ tabReg = /\t/g;
+ var clean = function( content ) {
+ return content.replace(slashReg, '\\\\').replace(newReg, '\\n').replace(quoteReg, '\\"').replace(tabReg, '\\t');
+ }
+ var put_cmd = "___v1ew.push(",
+ insert_cmd = put_cmd,
+ // the text that starts the view code (or block function)
+ startTxt = 'var ___v1ew = [];',
+ // the text that ends the view code (or block function)
+ finishTxt = "return ___v1ew.join('')",
+ setTokenAndSaveContent = function(state, token){
+ state.tag = token;
+ put(state)
+ },
+ put = function( state ) {
+ if(state.content.length > 0) {
+ state.buff.push(put_cmd, '"', clean(state.content.join('')), '");');
+ }
+
+ }
+ var rules = ["<%%","%%>","<%==","<%=","<%#","<%","%>","<",">",'"',"'"];
+ var tokens = new RegExp("(" +rules.join("|")+")")
+
+ var scan = function(tokens){
+ var content = [],
+ buff= [],
+ startTag =null,
+ htmlTag = null;
+
+ while(tokens.length){
+ var token = tokens.shift();
+
+ if ( startTag === null ) {
+ switch ( token ) {
+ case scanner.left:
+ case scanner.eLeft:
+ case scanner.eeLeft:
+ case scanner.cmnt:
+ // a new line, just add whatever content w/i a clean
+ // reset everything
+ startTag = token;
+ if ( content.length > 0 ) {
+ put(content);
+ }
+ empty();
+ break;
+
+ case scanner.dLeft:
+ // replace <%% with <%
+ content += scanner.left;
+ break;
+ default:
+ content += token;
+ break;
+ }
+ }
+ else {
+ //we have a start tag
+ switch ( token ) {
+ case scanner.right:
+ // %>
+ switch ( startTag ) {
+ case scanner.left:
+ // <%
+
+ // get the number of { minus }
+ bn = bracketNum(content);
+ // how are we ending this statement
+ var last =
+ // if the stack has value and we are ending a block
+ endStack.length && bn == -1 ?
+ // use the last item in the block stack
+ endStack.pop() :
+ // or use the default ending
+ ";";
+
+ // if we are ending a returning block
+ // add the finish text which returns the result of the
+ // block
+ if(last === doubleParen) {
+ buff.push(finishTxt)
+ }
+ // add the remaining content
+ buff.push(content, last);
+
+ // if we have a block, start counting
+ if(bn === 1 ){
+ endStack.push(";")
+ }
+ break;
+ case scanner.eLeft:
+ // <%= clean content
+ bn = bracketNum(content);
+ if( bn ) {
+ endStack.push(doubleParen)
+ }
+ buff.push(insert_cmd, "jQuery.EJS.clean(", content,bn ? startTxt : doubleParen);
+ break;
+ case scanner.eeLeft:
+ // <%== content
+
+ // get the number of { minus }
+ bn = bracketNum(content);
+ // if we have more {, it means there is a block
+ if( bn ){
+ // when we return to the same # of { vs } end wiht a doubleParen
+ endStack.push(doubleParen)
+ }
+
+ buff.push(insert_cmd, "jQuery.EJS.text(", content,
+ // if we have a block
+ bn ?
+ // start w/ startTxt "var _v1ew = [])"
+ startTxt :
+ // if not, add doubleParent to close push and text
+ doubleParen
+ );
+ break;
+ }
+ startTag = null;
+ empty();
+ break;
+ case scanner.dRight:
+ content += scanner.right;
+ break;
+ default:
+ content += token;
+ break;
+ }
+ }
+ }
+ }
+});
View
21 view/ejs/ejs_test.js
@@ -1,4 +1,5 @@
-steal('funcunit/qunit','jquery/view/ejs', function(){
+steal('funcunit/qunit','jquery/view/ejs',
+ 'jquery/lang/observe',function(){
module("jquery/view/ejs, rendering",{
setup : function(){
@@ -98,4 +99,22 @@ test("returning blocks", function(){
equals(res.match(/ItemsLength4/g).length, 4, "innerBlock and each")
});
+test("binding", function(){
+ var text = "<div class='<%== task.attr('completed') ? 'complete' : ''%>'><%== task.attr('name') %></div>";
+ var task = new $.Observe({
+ name : 'dishes'
+ })
+ var compiled = new $.EJS({text: text}).render({task: task}) ;
+ var div = $('<div/>').html(compiled)
+
+ console.log(div.html());
+ task.attr('name','lawn')
+ console.log(div.html());
+ task.attr('completed', true);
+ console.log(div.html());
+});
+
+
+
+
})

0 comments on commit f78e0e8

Please sign in to comment.
Something went wrong with that request. Please try again.