diff options
| -rw-r--r-- | angular-debug.js | 151 | ||||
| -rwxr-xr-x | server.sh | 2 | ||||
| -rw-r--r-- | src/Angular.js | 19 | ||||
| -rw-r--r-- | src/directives.js | 24 | ||||
| -rw-r--r-- | src/filters.js | 92 | ||||
| -rw-r--r-- | src/jqLite.js | 16 | ||||
| -rw-r--r-- | test/FiltersTest.js | 42 | ||||
| -rw-r--r-- | test/directivesSpec.js | 7 | ||||
| -rw-r--r-- | test/testabilityPatch.js | 16 |
9 files changed, 192 insertions, 177 deletions
diff --git a/angular-debug.js b/angular-debug.js index dd74428a..29bf63cc 100644 --- a/angular-debug.js +++ b/angular-debug.js @@ -110,9 +110,12 @@ function jqLiteWrap(element) { if (isString(element)) { var div = document.createElement('div'); div.innerHTML = element; - element = div.childNodes[0]; + element = new JQLite(div.childNodes); + } else if (element instanceof JQLite) { + } else if (isElement(element)) { + element = new JQLite(element); } - return element instanceof JQLite ? element : new JQLite(element); + return element; } function isUndefined(value){ return typeof value == 'undefined'; } function isDefined(value){ return typeof value != 'undefined'; } @@ -126,6 +129,10 @@ function lowercase(value){ return isString(value) ? value.toLowerCase() : value; function uppercase(value){ return isString(value) ? value.toUpperCase() : value; } function trim(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; } function nodeName(element) { return (element[0] || element).nodeName; } +function isElement(node) { + if (node && node[0]) node = node[0]; + return node && node.nodeName; +} function isVisible(element) { var rect = element[0].getBoundingClientRect(); @@ -209,14 +216,6 @@ function consoleLog(level, objs) { consoleNode.appendChild(log); } -function isNode(inp) { - return inp && - inp.tagName && - inp.nodeName && - inp.ownerDocument && - inp.removeAttribute; -} - function isLeafNode (node) { if (node) { switch (node.nodeName) { @@ -1843,7 +1842,15 @@ function jqClearData(element) { } function JQLite(element) { - this[0] = element; + if (element.length && element.item) { + for(var i=0; i < element.length; i++) { + this[i] = element[i]; + } + this.length = element.length; + } else { + this[0] = element; + this.length = 1; + } } JQLite.prototype = { @@ -1920,7 +1927,11 @@ JQLite.prototype = { }, append: function(node) { - this[0].appendChild(jqLite(node)[0]); + var self = this[0]; + node = jqLite(node); + foreach(node, function(child){ + self.appendChild(child); + }); }, remove: function() { @@ -2351,28 +2362,6 @@ defineApi('Date', [angularGlobal, angularDate], []); angular['Date']['toString'] = angularDate['toString']; defineApi('Function', [angularGlobal, angularCollection, angularFunction], ['bind', 'bindAll', 'delay', 'defer', 'wrap', 'compose']); -angularFilter.Meta = function(obj){ - if (obj) { - for ( var key in obj) { - this[key] = obj[key]; - } - } -}; -angularFilter.Meta.get = function(obj, attr){ - attr = attr || 'text'; - switch(typeof obj) { - case "string": - return attr == "text" ? obj : undefined; - case "object": - if (obj && typeof obj[attr] !== "undefined") { - return obj[attr]; - } - return undefined; - default: - return obj; - } -}; - var angularFilterGoogleChartApi; foreach({ @@ -2445,31 +2434,33 @@ foreach({ if (!returnValue && regexp.test(tNo)) { var text = carrier.name + ": " + trackingNo; var url = carrier.url + trackingNo; - returnValue = new angularFilter.Meta({ - text:text, - url:url, - html: '<a href="' + escapeAttr(url) + '">' + text + '</a>', - trackingNo:trackingNo}); + returnValue = jqLite('<a></a>'); + returnValue.text(text); + returnValue.attr('href', url); } }); }); if (returnValue) return returnValue; else if (trackingNo) - return noMatch || new angularFilter.Meta({text:trackingNo + " is not recognized"}); + return noMatch || trackingNo + " is not recognized"; else return null; };})(), 'link': function(obj, title) { - var text = title || angularFilter.Meta.get(obj); - var url = angularFilter.Meta.get(obj, "url") || angularFilter.Meta.get(obj); - if (url) { - if (angular.validator.email(url) === null) { - url = "mailto:" + url; + if (obj) { + var text = title || obj.text || obj; + var url = obj.url || obj; + if (url) { + if (angular.validator.email(url) === null) { + url = "mailto:" + url; + } + var a = jqLite('<a></a>'); + a.attr('href', url); + a.text(text); + return a; } - var html = '<a href="' + escapeHtml(url) + '">' + text + '</a>'; - return new angularFilter.Meta({text:text, url:url, html:html}); } return obj; }, @@ -2496,31 +2487,27 @@ foreach({ 'image': function(obj, width, height) { if (obj && obj.url) { - var style = ""; + var style = "", img = jqLite('<img>'); if (width) { - style = ' style="max-width: ' + width + - 'px; max-height: ' + (height || width) + 'px;"'; + img.css('max-width', width + 'px'); + img.css('max-height', (height || width) + 'px'); } - return new angularFilter.Meta({url:obj.url, text:obj.url, - html:'<img src="'+obj.url+'"' + style + '/>'}); + img.attr('src', obj.url); + return img; } return null; }, - 'lowercase': function (obj) { - var text = angularFilter.Meta.get(obj); - return text ? ("" + text).toLowerCase() : text; - }, + 'lowercase': lowercase, - 'uppercase': function (obj) { - var text = angularFilter.Meta.get(obj); - return text ? ("" + text).toUpperCase() : text; - }, + 'uppercase': uppercase, 'linecount': function (obj) { - var text = angularFilter.Meta.get(obj); - if (text==='' || !text) return 1; - return text.split(/\n|\f/).length; + if (isString(obj)) { + if (obj==='') return 1; + return obj.split(/\n|\f/).length; + } + return 1; }, 'if': function (result, expression) { @@ -2589,8 +2576,9 @@ foreach({ 'encode': function(params, width, height) { width = width || 200; height = height || width; - var url = "http://chart.apis.google.com/chart?"; - var urlParam = []; + var url = "http://chart.apis.google.com/chart?", + urlParam = [], + img = jqLite('<img>'); params['chs'] = width + "x" + height; foreach(params, function(value, key){ if (value) { @@ -2599,8 +2587,9 @@ foreach({ }); urlParam.sort(); url += urlParam.join("&"); - return new angularFilter.Meta({url:url, - html:'<img width="' + width + '" height="' + height + '" src="'+url+'"/>'}); + img.attr('src', url); + img.css({width: width + 'px', height: height + 'px'}); + return img; } } ), @@ -2644,7 +2633,7 @@ foreach({ }, 'html': function(html){ - return new angularFilter.Meta({html:html}); + return jqLite(html); }, 'linky': function(text){ @@ -2666,7 +2655,7 @@ foreach({ raw = raw.substring(i + url.length); } html.push(escapeHtml(raw)); - return new angularFilter.Meta({text:text, html:html.join('')}); + return jqLite(html.join('')); } }, function(v,k){angularFilter[k] = v;}); @@ -2832,14 +2821,23 @@ angularDirective("ng-eval", function(expression){ }); angularDirective("ng-bind", function(expression){ - var templateFn = compileBindTemplate("{{" + expression + "}}"); return function(element) { - var lastValue; + var lastValue, lastError; this.$onEval(function() { - var value = templateFn.call(this, element); - if (value != lastValue) { - element.text(value); + var error, value = this.$tryEval(expression, function(e){ + error = toJson(e); + }); + if (value != lastValue || error != lastError) { lastValue = value; + lastError = error; + elementError(element, NG_EXCEPTION, error); + if (error) value = error; + if (isElement(value)) { + element.html(''); + element.append(value); + } else { + element.text(value); + } } }, element); }; @@ -2866,7 +2864,10 @@ function compileBindTemplate(template){ var parts = [], self = this; foreach(bindings, function(fn){ var value = fn.call(self, element); - if (isObject(value)) value = toJson(value, true); + if (isElement(value)) + value = ''; + else if (isObject(value)) + value = toJson(value, true); parts.push(value); }); return parts.join(''); @@ -1 +1 @@ -java -jar lib/jstestdriver/JsTestDriver.jar --port 9876 --runnerMode DEBUG +java -jar lib/jstestdriver/JsTestDriver.jar --port 9876 diff --git a/src/Angular.js b/src/Angular.js index 11ebc4bc..d60e1b85 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -86,9 +86,12 @@ function jqLiteWrap(element) { if (isString(element)) { var div = document.createElement('div'); div.innerHTML = element; - element = div.childNodes[0]; + element = new JQLite(div.childNodes); + } else if (element instanceof JQLite) { + } else if (isElement(element)) { + element = new JQLite(element); } - return element instanceof JQLite ? element : new JQLite(element); + return element; } function isUndefined(value){ return typeof value == 'undefined'; } function isDefined(value){ return typeof value != 'undefined'; } @@ -102,6 +105,10 @@ function lowercase(value){ return isString(value) ? value.toLowerCase() : value; function uppercase(value){ return isString(value) ? value.toUpperCase() : value; } function trim(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; } function nodeName(element) { return (element[0] || element).nodeName; } +function isElement(node) { + if (node && node[0]) node = node[0]; + return node && node.nodeName; +} function isVisible(element) { var rect = element[0].getBoundingClientRect(); @@ -185,14 +192,6 @@ function consoleLog(level, objs) { consoleNode.appendChild(log); } -function isNode(inp) { - return inp && - inp.tagName && - inp.nodeName && - inp.ownerDocument && - inp.removeAttribute; -} - function isLeafNode (node) { if (node) { switch (node.nodeName) { diff --git a/src/directives.js b/src/directives.js index 22ff7544..2ead4979 100644 --- a/src/directives.js +++ b/src/directives.js @@ -23,14 +23,23 @@ angularDirective("ng-eval", function(expression){ }); angularDirective("ng-bind", function(expression){ - var templateFn = compileBindTemplate("{{" + expression + "}}"); return function(element) { - var lastValue; + var lastValue, lastError; this.$onEval(function() { - var value = templateFn.call(this, element); - if (value != lastValue) { - element.text(value); + var error, value = this.$tryEval(expression, function(e){ + error = toJson(e); + }); + if (value != lastValue || error != lastError) { lastValue = value; + lastError = error; + elementError(element, NG_EXCEPTION, error); + if (error) value = error; + if (isElement(value)) { + element.html(''); + element.append(value); + } else { + element.text(value); + } } }, element); }; @@ -57,7 +66,10 @@ function compileBindTemplate(template){ var parts = [], self = this; foreach(bindings, function(fn){ var value = fn.call(self, element); - if (isObject(value)) value = toJson(value, true); + if (isElement(value)) + value = ''; + else if (isObject(value)) + value = toJson(value, true); parts.push(value); }); return parts.join(''); diff --git a/src/filters.js b/src/filters.js index 0259f69c..d0fc2ae7 100644 --- a/src/filters.js +++ b/src/filters.js @@ -1,25 +1,3 @@ -angularFilter.Meta = function(obj){ - if (obj) { - for ( var key in obj) { - this[key] = obj[key]; - } - } -}; -angularFilter.Meta.get = function(obj, attr){ - attr = attr || 'text'; - switch(typeof obj) { - case "string": - return attr == "text" ? obj : undefined; - case "object": - if (obj && typeof obj[attr] !== "undefined") { - return obj[attr]; - } - return undefined; - default: - return obj; - } -}; - var angularFilterGoogleChartApi; foreach({ @@ -92,31 +70,33 @@ foreach({ if (!returnValue && regexp.test(tNo)) { var text = carrier.name + ": " + trackingNo; var url = carrier.url + trackingNo; - returnValue = new angularFilter.Meta({ - text:text, - url:url, - html: '<a href="' + escapeAttr(url) + '">' + text + '</a>', - trackingNo:trackingNo}); + returnValue = jqLite('<a></a>'); + returnValue.text(text); + returnValue.attr('href', url); } }); }); if (returnValue) return returnValue; else if (trackingNo) - return noMatch || new angularFilter.Meta({text:trackingNo + " is not recognized"}); + return noMatch || trackingNo + " is not recognized"; else return null; };})(), 'link': function(obj, title) { - var text = title || angularFilter.Meta.get(obj); - var url = angularFilter.Meta.get(obj, "url") || angularFilter.Meta.get(obj); - if (url) { - if (angular.validator.email(url) === null) { - url = "mailto:" + url; + if (obj) { + var text = title || obj.text || obj; + var url = obj.url || obj; + if (url) { + if (angular.validator.email(url) === null) { + url = "mailto:" + url; + } + var a = jqLite('<a></a>'); + a.attr('href', url); + a.text(text); + return a; } - var html = '<a href="' + escapeHtml(url) + '">' + text + '</a>'; - return new angularFilter.Meta({text:text, url:url, html:html}); } return obj; }, @@ -143,31 +123,27 @@ foreach({ 'image': function(obj, width, height) { if (obj && obj.url) { - var style = ""; + var style = "", img = jqLite('<img>'); if (width) { - style = ' style="max-width: ' + width + - 'px; max-height: ' + (height || width) + 'px;"'; + img.css('max-width', width + 'px'); + img.css('max-height', (height || width) + 'px'); } - return new angularFilter.Meta({url:obj.url, text:obj.url, - html:'<img src="'+obj.url+'"' + style + '/>'}); + img.attr('src', obj.url); + return img; } return null; }, - 'lowercase': function (obj) { - var text = angularFilter.Meta.get(obj); - return text ? ("" + text).toLowerCase() : text; - }, + 'lowercase': lowercase, - 'uppercase': function (obj) { - var text = angularFilter.Meta.get(obj); - return text ? ("" + text).toUpperCase() : text; - }, + 'uppercase': uppercase, 'linecount': function (obj) { - var text = angularFilter.Meta.get(obj); - if (text==='' || !text) return 1; - return text.split(/\n|\f/).length; + if (isString(obj)) { + if (obj==='') return 1; + return obj.split(/\n|\f/).length; + } + return 1; }, 'if': function (result, expression) { @@ -236,8 +212,9 @@ foreach({ 'encode': function(params, width, height) { width = width || 200; height = height || width; - var url = "http://chart.apis.google.com/chart?"; - var urlParam = []; + var url = "http://chart.apis.google.com/chart?", + urlParam = [], + img = jqLite('<img>'); params['chs'] = width + "x" + height; foreach(params, function(value, key){ if (value) { @@ -246,8 +223,9 @@ foreach({ }); urlParam.sort(); url += urlParam.join("&"); - return new angularFilter.Meta({url:url, - html:'<img width="' + width + '" height="' + height + '" src="'+url+'"/>'}); + img.attr('src', url); + img.css({width: width + 'px', height: height + 'px'}); + return img; } } ), @@ -291,7 +269,7 @@ foreach({ }, 'html': function(html){ - return new angularFilter.Meta({html:html}); + return jqLite(html); }, 'linky': function(text){ @@ -313,7 +291,7 @@ foreach({ raw = raw.substring(i + url.length); } html.push(escapeHtml(raw)); - return new angularFilter.Meta({text:text, html:html.join('')}); + return jqLite(html.join('')); } }, function(v,k){angularFilter[k] = v;}); diff --git a/src/jqLite.js b/src/jqLite.js index 6fc16e57..d4c5492c 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -34,7 +34,15 @@ function jqClearData(element) { } function JQLite(element) { - this[0] = element; + if (element.length && element.item) { + for(var i=0; i < element.length; i++) { + this[i] = element[i]; + } + this.length = element.length; + } else { + this[0] = element; + this.length = 1; + } } JQLite.prototype = { @@ -111,7 +119,11 @@ JQLite.prototype = { }, append: function(node) { - this[0].appendChild(jqLite(node)[0]); + var self = this[0]; + node = jqLite(node); + foreach(node, function(child){ + self.appendChild(child); + }); }, remove: function() { diff --git a/test/FiltersTest.js b/test/FiltersTest.js index 504dad02..ad38b94d 100644 --- a/test/FiltersTest.js +++ b/test/FiltersTest.js @@ -45,11 +45,8 @@ FiltersTest.prototype.testPackageTracking = function () { var assert = function(title, trackingNo) { var val = angular.filter.trackPackage(trackingNo, title); assertNotNull("Did Not Match: " + trackingNo, val); - assertEquals(angular.filter.Meta.TAG, val.TAG); - assertEquals(title + ": " + trim(trackingNo), val.text); - assertNotNull(val.url); - assertEquals(trim(trackingNo), val.trackingNo); - assertEquals('<a href="' + val.url + '">' + val.text + '</a>', val.html); + assertEquals(title + ": " + trim(trackingNo), val.text()); + assertNotNull(val.attr('href')); }; assert('UPS', ' 1Z 999 999 99 9999 999 9 '); assert('UPS', '1ZW5w5220379084747'); @@ -72,8 +69,7 @@ FiltersTest.prototype.testPackageTracking = function () { FiltersTest.prototype.testLink = function() { var assert = function(text, url, obj){ var val = angular.filter.link(obj); - assertEquals(angular.filter.Meta.TAG, val.TAG); - assertEquals('<a href="' + url + '">' + text + '</a>', val.html); + assertEquals('<a href="' + url + '">' + text + '</a>', sortedHtml(val)); }; assert("url", "url", "url"); assert("hello", "url", {text:"hello", url:"url"}); @@ -84,22 +80,22 @@ FiltersTest.prototype.testImage = function(){ assertEquals(null, angular.filter.image()); assertEquals(null, angular.filter.image({})); assertEquals(null, angular.filter.image("")); - assertEquals('<img src="abc"/>', angular.filter.image({url:"abc"}).html); + assertEquals('<img src="abc"></img>', sortedHtml(angular.filter.image({url:"abc"}))); assertEquals( - '<img src="abc" style="max-width: 10px; max-height: 10px;"/>', - angular.filter.image({url:"abc"}, 10).html); + '<img src="abc" style="max-height: 10px; max-width: 10px;"></img>', + sortedHtml(angular.filter.image({url:"abc"}, 10))); assertEquals( - '<img src="abc" style="max-width: 10px; max-height: 20px;"/>', - angular.filter.image({url:"abc"}, 10, 20).html); + '<img src="abc" style="max-height: 20px; max-width: 10px;"></img>', + sortedHtml(angular.filter.image({url:"abc"}, 10, 20))); }; FiltersTest.prototype.testQRcode = function() { assertEquals( - '<img width="200" height="200" src="http://chart.apis.google.com/chart?chl=Hello%20world&chs=200x200&cht=qr"/>', - angular.filter.qrcode('Hello world').html); + '<img src="http://chart.apis.google.com/chart?chl=Hello%20world&chs=200x200&cht=qr" style="height: 200px; width: 200px;"></img>', + sortedHtml(angular.filter.qrcode('Hello world'))); assertEquals( - '<img width="100" height="100" src="http://chart.apis.google.com/chart?chl=http%3A%2F%2Fserver%3Fa%26b%3Dc&chs=100x100&cht=qr"/>', - angular.filter.qrcode('http://server?a&b=c', 100).html); + '<img src="http://chart.apis.google.com/chart?chl=http%3A%2F%2Fserver%3Fa%26b%3Dc&chs=100x100&cht=qr" style="height: 100px; width: 100px;"></img>', + sortedHtml(angular.filter.qrcode('http://server?a&b=c', 100))); }; FiltersTest.prototype.testLowercase = function() { @@ -132,15 +128,14 @@ FiltersTest.prototype.testUnless = function() { FiltersTest.prototype.testGoogleChartApiEncode = function() { assertEquals( - '<img width="200" height="200" src="http://chart.apis.google.com/chart?chl=Hello world&chs=200x200&cht=qr"/>', - angular.filter.googleChartApi.encode({cht:"qr", chl:"Hello world"}).html); + '<img src="http://chart.apis.google.com/chart?chl=Hello world&chs=200x200&cht=qr" style="height: 200px; width: 200px;"></img>', + sortedHtml(angular.filter.googleChartApi.encode({cht:"qr", chl:"Hello world"}))); }; FiltersTest.prototype.testHtml = function() { - assertEquals( - "a<b>c</b>d", - angular.filter.html("a<b>c</b>d").html); - assertTrue(angular.filter.html("a<b>c</b>d") instanceof angular.filter.Meta); + var div = jqLite('<div></div>'); + div.append(angular.filter.html("a<b>c</b>d")); + assertEquals("a<b>c</b>d", div.html()); }; FiltersTest.prototype.testLinky = function() { @@ -150,8 +145,7 @@ FiltersTest.prototype.testLinky = function() { '(<a href="http://a">http://a</a>) ' + '<<a href="http://a">http://a</a>> \n ' + '<a href="http://1.2/v:~-123">http://1.2/v:~-123</a>. c', - linky("http://ab (http://a) <http://a> \n http://1.2/v:~-123. c").html); - assertTrue(linky("a") instanceof angular.filter.Meta); + sortedHtml(linky("http://ab (http://a) <http://a> \n http://1.2/v:~-123. c"))); assertEquals(undefined, linky(undefined)); }; diff --git a/test/directivesSpec.js b/test/directivesSpec.js index 1def9584..300602fe 100644 --- a/test/directivesSpec.js +++ b/test/directivesSpec.js @@ -37,6 +37,13 @@ describe("directives", function(){ expect(element.text()).toEqual('misko'); }); + it('should ng-bind html', function() { + var scope = compile('<div ng-bind="html|html"></div>'); + scope.html = '<div>hello</div>'; + scope.$eval(); + expect(element.html()).toEqual('<div>hello</div>'); + }); + it('should ng-bind-template', function() { var scope = compile('<div ng-bind-template="Hello {{name}}!"></div>'); scope.$set('name', 'Misko'); diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js index 5955e9a6..dfa14b97 100644 --- a/test/testabilityPatch.js +++ b/test/testabilityPatch.js @@ -35,7 +35,7 @@ function trigger(element, type) { function sortedHtml(element) { var html = ""; - (function toString(node) { + foreach(element, function toString(node) { if (node.nodeName == "#text") { html += escapeHtml(node.nodeValue); } else { @@ -56,6 +56,7 @@ function sortedHtml(element) { attr.name !='size' && attr.name !='start' && attr.name !='tabIndex' && + attr.name !='style' && attr.name.substr(0, 6) != 'jQuery') { // in IE we need to check for all of these. attrs.push(' ' + attr.name + '="' + attr.value + '"'); @@ -63,6 +64,17 @@ function sortedHtml(element) { } attrs.sort(); html += attrs.join(''); + var style = []; + for(var name in node.style) { + var value = node.style[name]; + if (value && isString(value) && (name != 1*name) && (name != 'cssText')) { + style.push(name + ': ' + value + ';'); + } + } + style.sort(); + if (style.length) { + html += ' style="' + style.join(' ') + '"'; + } html += '>'; var children = node.childNodes; for(var j=0; j<children.length; j++) { @@ -70,7 +82,7 @@ function sortedHtml(element) { } html += '</' + node.nodeName.toLowerCase() + '>'; } - })(element[0]); + }); return html; } |
