diff options
67 files changed, 883 insertions, 4055 deletions
@@ -36,6 +36,7 @@ task :compile_scenario do lib/jquery/jquery-1.4.2.js \ src/scenario/angular.prefix \ src/Angular.js \ + src/jqLite.js \ src/JSON.js \ src/Scope.js \ src/Parser.js \ diff --git a/example/buzz/buzz.css b/example/buzz/buzz.css new file mode 100644 index 00000000..5fd5763d --- /dev/null +++ b/example/buzz/buzz.css @@ -0,0 +1,89 @@ +body { + background: -webkit-gradient(linear, left top, left 100, from(#bbb), to(#fff)); + background-repeat: no-repeat; + margin: 0px; + font-family: sans-serif; + font-size: 12px; +} + +body > div { + border-top: 1px solid white; + border-bottom: 1px solid black; + text-align: center; + background: -webkit-gradient(linear, left top, left bottom, from(#CCC), to(#888)); + -webkit-background-origin: padding; -webkit-background-clip: content; +} +body > div button { + margin: 5px; +} + +body > div span:FIRST-CHILD { + float: left; + font-family: monospace; + font-size: 1.5em; + color: black; + padding: 2px 5px; +} + +body > div span:last-child { + float: right; +} + +ul { + list-style: none; + padding: 10px; + margin: 0; +} + +body > ul > li { + border: 1px solid black; + margin: 15px 5px; + padding: 0; + -webkit-box-shadow: 5px 5px 5px #888; +} + +body > ul > li > h1 { + margin: 0; + background: -webkit-gradient(linear, left top, left bottom, from(#ddd), to(#999)); + font-size: 13px; + border-bottom: 1px solid black; +} + +h1 > img, +li > img { + max-height: 30px; + max-width: 30px; + vertical-align: middle; + padding: 3px; +} + +a > img { + margin-right: 5px; + margin-top: 5px; +} + +body > ul > li > h1 > a:last-child { + float: right; + margin: 10px; +} + +body > ul > li > div { + background-color: white; + background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#ddd)); + margin: 0; + padding: 10px; +} + +body > ul > li ul { + margin: 0; + padding: 0; + margin-left: 5px; + border-left: 5px solid lightgray; +} + +body > ul > li ul > li { + margin: 0; + padding: 10px; + background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#ddd)); +} + diff --git a/example/buzz/buzz.html b/example/buzz/buzz.html new file mode 100644 index 00000000..a6777ff8 --- /dev/null +++ b/example/buzz/buzz.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html xmlns:ng="http://angularjs.org"> + <head> + <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script> + <script type="text/javascript" src="http://angularjs.org/ng/js/angular-debug.js#autobind"></script> + <script type="text/javascript" src="buzz.js"></script> + <link rel="stylesheet" type="text/css" href="http://angularjs.org/ng/css/angular.css"/> + <link rel="stylesheet" type="text/css" href="buzz.css"/> + </head> + <body ng:init="$window.$root = this" ng:controller="BuzzController"> + <div> + <span><angular/> Buzz</span> + <span> + filter: + <input type="text" name="filterText"/> + </span> + <span> + user: + <input type="text" name="userId" ng:required/> + <button ng:click="$location.hashPath = userId">fetch</button> + </span> + </div> + <ul> + <li ng:repeat="item in activities.data.items.$filter(filterText)"> + <h1> + <img src="{{item.actor.thumbnailUrl}}"/> + <a href="{{item.actor.profileUrl}}">{{item.actor.name}}</a> + <a href="#" ng:click="expandReplies(item)">Replies: {{item.links.replies[0].count}}</a> + </h1> + <div> + {{item.object.content | html}} + <div> + <a href="{{attachment.links.enclosure[0].href}}" ng:repeat="attachment in item.object.attachments"> + <img src="{{attachment.links.preview[0].href}}"/> + </a> + </div> + </div> + <my:expand expand="item.replies.show"> + <ul> + <li ng:repeat="reply in item.replies.data.items"> + <img src="{{reply.actor.thumbnailUrl}}"/> + <a href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>: + {{reply.content | html}} + </li> + </ul> + </my:expand> + </li> + </ul> + </body> +</html> diff --git a/example/buzz/buzz.js b/example/buzz/buzz.js new file mode 100644 index 00000000..40813d16 --- /dev/null +++ b/example/buzz/buzz.js @@ -0,0 +1,46 @@ +angular.service('myApplication', function($resource){ + this.Activity = $resource( + 'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments', + {alt:'json', callback:'JSON_CALLBACK'}, + { + get: {method:'JSON', params:{visibility:'@self'}}, + replies: {method:'JSON', params:{visibility:'@self', comments:'@comments'}} + }); +}, {inject:['$resource']}); + +function BuzzController(){ + this.$watch('$location.hashPath', this.userChange); +} +BuzzController.prototype = { + userChange: function(){ + this.userId = this.$location.hashPath; + this.activities = this.Activity.get({userId:this.userId}); + }, + + expandReplies: function(activity) { + var self = this; + if (activity.replies) { + activity.replies.show = !activity.replies.show; + } else { + activity.replies = this.Activity.replies({userId:this.userId, activityId:activity.id}, function(){ + activity.replies.show = true; + }); + } + } +}; + +angular.widget('my:expand', function(element){ + element.css('display', 'block'); + this.descend(true); + return function(element) { + element.hide(); + var watch = element.attr('expand'); + this.$watch(watch, function(value){ + if (value) { + element.delay(0).slideDown('slow'); + } else { + element.slideUp('slow'); + } + }); + }; +}); diff --git a/example/memoryLeak.html b/example/memoryLeak.html index 9e5f512d..19b0d45d 100644 --- a/example/memoryLeak.html +++ b/example/memoryLeak.html @@ -48,8 +48,8 @@ <link rel="StyleSheet" type="text/css" href="../css/angular.css"/> </head> <body> - <input type="button" value="add" ng-click="add()"/> - <input type="button" value="remove" ng-click="remove()"/> + <input type="button" value="add" ng:click="add()"/> + <input type="button" value="remove" ng:click="remove()"/> <div id="partial"></div> </body> </html> diff --git a/example/temp.html b/example/temp.html index d07a6948..337f7fba 100644 --- a/example/temp.html +++ b/example/temp.html @@ -1,13 +1,10 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> <head> - <script type="text/javascript" src="../src/angular-bootstrap.js#autobind"></script> + <script type="text/javascript" + src="../src/angular-bootstrap.js#autobind"></script> </head> - <body> - {{$location.hashSearch.order}} <br/> - <input type="radio" name="$location.hashSearch.order" value="A"/> A <br/> - <input type="radio" name="$location.hashSearch.order" checked value="B"/> B <br/> - <input type="radio" name="$location.hashSearch.order" value="C"/> C <br/> - {{$location.hashSearch.order}} <br/> + <body ng:init="$window.$root = this"> + Hello {{'World'}}! </body> </html> diff --git a/example/tweeter/tweeter_addressbook.html b/example/tweeter/tweeter_addressbook.html index 4844c035..ba915cb1 100644 --- a/example/tweeter/tweeter_addressbook.html +++ b/example/tweeter/tweeter_addressbook.html @@ -8,25 +8,25 @@ <script type="text/javascript" src="../../src/angular-bootstrap.js"></script> <script type="text/javascript" src="tweeterclient.js"></script> </head> - <body ng-class="status" ng-init="mute={}" ng-watch="$anchor.user: tweets = fetchTweets($anchor.user)"> + <body ng:class="status" ng:init="mute={}" ng:watch="$anchor.user: tweets = fetchTweets($anchor.user)"> <div class="addressbook box"> <h1>Address Book</h1> [ Filter: <input type="text" name="userFilter"/>] <ul> - <li ng-repeat="user in users.$filter(userFilter).$orderBy('screen_name')" ng-class-even="'even'" ng-class-odd="'odd'"> - <a href="" ng-click="$anchor.user=user.screen_name"><img src="{{user.profile_image_url}}"/></a> - <a href="" ng-click="$anchor.user=user.screen_name">{{user.screen_name}}</a> + <li ng:repeat="user in users.$filter(userFilter).$orderBy('screen_name')" ng:class-even="'even'" ng:class-odd="'odd'"> + <a href="" ng:click="$anchor.user=user.screen_name"><img src="{{user.profile_image_url}}"/></a> + <a href="" ng:click="$anchor.user=user.screen_name">{{user.screen_name}}</a> as <span class="nickname">{{user.name}}</span> - [ <a href="#" ng-click="$anchor.edituser=user.screen_name">edit</a> - | <a href="#" ng-click="users.$remove(user)">X</a> - | <a href="#" ng-click="mute[user.screen_name] = ! mute[user.screen_name]">mute</a> + [ <a href="#" ng:click="$anchor.edituser=user.screen_name">edit</a> + | <a href="#" ng:click="users.$remove(user)">X</a> + | <a href="#" ng:click="mute[user.screen_name] = ! mute[user.screen_name]">mute</a> ] <div class="notes">{{user.notes|linky}}</div> <div class="clrleft"></div> </li> </ul> <hr/> - <div ng-show="$anchor.edituser" ng-eval="user = users.$find({:$.screen_name == $anchor.edituser})"> + <div ng:show="$anchor.edituser" ng:eval="user = users.$find({:$.screen_name == $anchor.edituser})"> <div class="editor"> <label>Username:</label> <input type="text" name="user.screen_name" disabled="disabled"/> @@ -37,7 +37,7 @@ <label>Notes:</label> <textarea type="text" name="user.notes"></textarea> - <input type="button" ng-click="$anchor.edituser=undefined" value="Close"/> + <input type="button" ng:click="$anchor.edituser=undefined" value="Close"/> </div> </div> <hr/> @@ -58,16 +58,16 @@ tweets={{tweets}} <div class="tweeter box"> <h1>Tweets: {{$anchor.user}}</h1> [ Filter: <input type="text" name="tweetFilter"/> - <span ng-show="$anchor.user">| <a href="#user="><< All</a></span> + <span ng:show="$anchor.user">| <a href="#user="><< All</a></span> ] <div class="loading">Loading...</div> <ul> - <li ng-repeat="tweet in tweets.$filter(tweetFilter).$filter({:!mute[$.user.screen_name]})" - ng-class-even="'even'" ng-class-odd="'odd'" - ng-eval="user = users.$find({: $.screen_name == tweet.user.screen_name}) || tweet.user"> + <li ng:repeat="tweet in tweets.$filter(tweetFilter).$filter({:!mute[$.user.screen_name]})" + ng:class-even="'even'" ng:class-odd="'odd'" + ng:eval="user = users.$find({: $.screen_name == tweet.user.screen_name}) || tweet.user"> <img src="{{user.profile_image_url}}"/> - [ <a href="" ng-click="$anchor.user=user.screen_name">{{user.nickname || user.name || user.screen_name }}</a> - | <a href="" ng-click="users.$includeIf(user, true)">+</a> + [ <a href="" ng:click="$anchor.user=user.screen_name">{{user.nickname || user.name || user.screen_name }}</a> + | <a href="" ng:click="users.$includeIf(user, true)">+</a> ]: {{tweet.text | linky}} <span class="notes">{{tweet.created_at}}</span> diff --git a/example/tweeter/tweeter_demo.html b/example/tweeter/tweeter_demo.html index 138d4e2b..a5ba95ba 100644 --- a/example/tweeter/tweeter_demo.html +++ b/example/tweeter/tweeter_demo.html @@ -8,19 +8,19 @@ <script type="text/javascript" src="../../src/angular-bootstrap.js"></script> <script type="text/javascript" src="tweeterclient.js"></script> </head> - <body ng-class="status" Xng-init="tweets = fetchTweets()"> + <body ng:class="status" Xng:init="tweets = fetchTweets()"> (TODO: I should fetch current tweets) <div class="tweeter box"> <h1>Tweets: {{$anchor.user}}</h1> [ Filter: <input type="text" name="tweetFilter"/> (TODO: this should act as search box) - <span ng-show="$anchor.user">| <a href="#user="><< All</a></span> + <span ng:show="$anchor.user">| <a href="#user="><< All</a></span> ] <div class="loading">Loading...</div> <ul> - <li Xng-repeat="tweet in tweets" - ng-class-even="'even'" ng-class-odd="'odd'"> + <li Xng:repeat="tweet in tweets" + ng:class-even="'even'" ng:class-odd="'odd'"> <img src="{{tweet.user.profile_image_url}}"/> - [ <a href="" Xng-click="$anchor.user=tweet.user.screen_name">{{tweet.user.nickname || tweet.user.name || tweet.user.screen_name }}</a> + [ <a href="" Xng:click="$anchor.user=tweet.user.screen_name">{{tweet.user.nickname || tweet.user.name || tweet.user.screen_name }}</a> ]: {{tweet.text}} (TODO: I want urls as links) <span class="notes">{{tweet.created_at}}</span> @@ -0,0 +1,2 @@ +#!/bin/sh +/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Commands/java $@ diff --git a/scenario/application-account.html b/scenario/application-account.html index a43deffc..81176df7 100644 --- a/scenario/application-account.html +++ b/scenario/application-account.html @@ -1,6 +1,6 @@ -<div ng-controller="AccountController"> +<div ng:controller="AccountController"> account page goes here! <input type="text" name="name" value="misko"/> - <button ng-click="hello()">hello</button> + <button ng:click="hello()">hello</button> </div> diff --git a/scenario/application.html b/scenario/application.html index 6b6ced69..5d5bb809 100644 --- a/scenario/application.html +++ b/scenario/application.html @@ -16,14 +16,14 @@ </script> </head> - <body ng-init="$window.$scope = this"> + <body ng:init="$window.$scope = this"> [ <a href="#login">login</a> | <a href="#account">account</a> ] <ng:switch on="$location.hashPath"> - <div ng-switch-when="login">login screen</div> - <ng:include ng-switch-when="account" src="'application-account.html'"></ng:include> + <div ng:switch-when="login">login screen</div> + <ng:include ng:switch-when="account" src="'application-account.html'"></ng:include> </ng:switch> diff --git a/scenario/cross-site-post/index.html b/scenario/cross-site-post/index.html index 3ff6af85..d7a87d3b 100644 --- a/scenario/cross-site-post/index.html +++ b/scenario/cross-site-post/index.html @@ -4,7 +4,7 @@ <script type="text/javascript" src="../../src/angular-bootstrap.js#autobind"></script> </head> <body ng:init="$window.$scope = this; People = $resource('People.json')"> - <button ng-click="people = People.query()">Load People</button> + <button ng:click="people = People.query()">Load People</button> <pre>people = {{people}}</pre> </body> </html> diff --git a/scenario/datastore.html b/scenario/datastore.html index 525d3636..1720b3bc 100644 --- a/scenario/datastore.html +++ b/scenario/datastore.html @@ -8,9 +8,9 @@ $(document).ready(function(){angular.compile(document).init();}); </script> </head> - <body ng-entity="book=Book" ng-init="books=Book.all()"> + <body ng-entity="book=Book" ng:init="books=Book.all()"> <p>{{book.$id}}</p> - <li ng-repeat="book in books.$orderBy('name')"> + <li ng:repeat="book in books.$orderBy('name')"> <li>{{book.name}}</li> </li> </body> diff --git a/scenario/perf.html b/scenario/perf.html index 50a8d28f..94af8b69 100644 --- a/scenario/perf.html +++ b/scenario/perf.html @@ -20,12 +20,12 @@ }; </script> </head> - <body ng:init="$window.$scope = this; items = createItems()" ng-controller="PerfCntl"> + <body ng:init="$window.$scope = this; items = createItems()" ng:controller="PerfCntl"> <input type="text" name="text"/> <hr/> <ul> - <li Xng-repeat="item in items.$filter('').$orderBy('name')" - ng-repeat="item in items"> + <li Xng:repeat="item in items.$filter('').$orderBy('name')" + ng:repeat="item in items"> {{item.name}} <a href="#{{item.name}}">{{item.parts.join(', ')}}</a> </li> </ul> diff --git a/scenario/widgets.html b/scenario/widgets.html index 86269e86..2626843d 100644 --- a/scenario/widgets.html +++ b/scenario/widgets.html @@ -72,7 +72,7 @@ </tr> <tr><th colspan="3">Buttons</th></tr> <tr> - <td>ng-change<br/>ng-click</td> + <td>ng-change<br/>ng:click</td> <td ng:init="button.count = 0"> <form> <input type="button" value="button" ng-change="button.count = button.count + 1"/> <br/> @@ -85,10 +85,10 @@ </tr> <tr><th colspan="3">Repeaters</th></tr> <tr> - <td>ng-repeat</td> + <td>ng:repeat</td> <td> <ul> - <li ng-repeat="name in ['misko', 'adam']">{{name}}</li> + <li ng:repeat="name in ['misko', 'adam']">{{name}}</li> </ul> </td> <td></td> diff --git a/src/Angular.js b/src/Angular.js index 2b26c88d..850fe34c 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -3,8 +3,6 @@ if (typeof document.getAttribute == 'undefined') document.getAttribute = function() {}; -if (!window['console']) window['console']={'log':noop, 'error':noop}; - var consoleNode, PRIORITY_FIRST = -99999, PRIORITY_WATCH = -1000, @@ -18,11 +16,12 @@ var consoleNode, msie = !!/(msie) ([\w.]+)/.exec(lowercase(navigator.userAgent)), jqLite = jQuery || jqLiteWrap, slice = Array.prototype.slice, + error = window['console'] ? bind(window['console'], window['console']['error'] || noop) : noop, angular = window['angular'] || (window['angular'] = {}), angularTextMarkup = extensionMap(angular, 'textMarkup'), angularAttrMarkup = extensionMap(angular, 'attrMarkup'), angularDirective = extensionMap(angular, 'directive'), - angularWidget = extensionMap(angular, 'widget'), + angularWidget = extensionMap(angular, 'widget', lowercase), angularValidator = extensionMap(angular, 'validator'), angularFilter = extensionMap(angular, 'filter'), angularFormatter = extensionMap(angular, 'formatter'), @@ -30,10 +29,6 @@ var consoleNode, angularCallbacks = extensionMap(angular, 'callbacks'), nodeName; -function angularAlert(){ - log(arguments); window.alert.apply(window, arguments); -} - function foreach(obj, iterator, context) { var key; if (obj) { @@ -78,11 +73,16 @@ function extend(dst) { return dst; } +function inherit(parent, extra) { + return extend(new (extend(function(){}, {prototype:parent}))(), extra); +}; + function noop() {} function identity($) {return $;} -function extensionMap(angular, name) { +function extensionMap(angular, name, transform) { var extPoint; return angular[name] || (extPoint = angular[name] = function (name, fn, prop){ + name = (transform || identity)(name); if (isDefined(fn)) { extPoint[name] = extend(fn, prop || {}); } @@ -173,50 +173,6 @@ function indexOf(array, obj) { return -1; } -function log(a, b, c){ - var console = window['console']; - switch(arguments.length) { - case 1: - console['log'](a); - break; - case 2: - console['log'](a, b); - break; - default: - console['log'](a, b, c); - break; - } -} - -function error(a, b, c){ - var console = window['console']; - switch(arguments.length) { - case 1: - console['error'](a); - break; - case 2: - console['error'](a, b); - break; - default: - console['error'](a, b, c); - break; - } -} - -function consoleLog(level, objs) { - var log = document.createElement("div"); - log.className = level; - var msg = ""; - var sep = ""; - for ( var i = 0; i < objs.length; i++) { - var obj = objs[i]; - msg += sep + (typeof obj == 'string' ? obj : toJson(obj)); - sep = " "; - } - log.appendChild(document.createTextNode(msg)); - consoleNode.appendChild(log); -} - function isLeafNode (node) { if (node) { switch (node.nodeName) { @@ -259,6 +215,32 @@ function copy(source, destination){ } } +function equals(o1, o2) { + if (o1 == o2) return true; + var t1 = typeof o1, t2 = typeof o2, length, key, keySet; + if (t1 == t2 && t1 == 'object') { + if (o1 instanceof Array) { + if ((length = o1.length) == o2.length) { + for(key=0; key<length; key++) { + if (!equals(o1[key], o2[key])) return false; + } + return true; + } + } else { + keySet = {}; + for(key in o1) { + if (key.charAt(0) !== '$' && !equals(o1[key], o2[key])) return false; + keySet[key] = true; + } + for(key in o2) { + if (key.charAt(0) !== '$' && keySet[key] !== true) return false; + } + return true; + } + } + return false; +} + function setHtml(node, html) { if (isLeafNode(node)) { if (msie) { @@ -310,12 +292,14 @@ function escapeAttr(html) { } function bind(_this, _function) { - if (!isFunction(_function)) - throw "Not a function!"; var curryArgs = slice.call(arguments, 2, arguments.length); - return function() { - return _function.apply(_this, curryArgs.concat(slice.call(arguments, 0, arguments.length))); - }; + return curryArgs.length == 0 ? + function() { + return _function.apply(_this, arguments); + } : + function() { + return _function.apply(_this, curryArgs.concat(slice.call(arguments, 0, arguments.length))); + }; } function outerHTML(node) { @@ -329,7 +313,7 @@ function outerHTML(node) { function toBoolean(value) { if (value && value.length !== 0) { var v = lowercase("" + value); - value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == '[]'); + value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]'); } else { value = false; } @@ -349,12 +333,12 @@ function merge(src, dst) { } } -function compile(element, parentScope, overrides) { +function compile(element, parentScope) { var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget), $element = jqLite(element), parent = extend({}, parentScope); parent.$element = $element; - return compiler.compile($element)($element, parent, overrides); + return compiler.compile($element)($element, parent); } ///////////////////////////////////////////////// @@ -389,7 +373,7 @@ function angularInit(config){ function angularJsConfig(document) { var filename = /(.*)\/angular(-(.*))?.js(#(.*))?/, - scripts = document.getElementsByTagName("SCRIPT"), + scripts = document.getElementsByTagName("script"), match; for(var j = 0; j < scripts.length; j++) { match = (scripts[j].src || "").match(filename); diff --git a/src/AngularPublic.js b/src/AngularPublic.js index 7230c3e5..7b093f88 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -1,7 +1,10 @@ var browserSingleton; angularService('$browser', function browserFactory(){ if (!browserSingleton) { - browserSingleton = new Browser(window.location, window.document); + browserSingleton = new Browser( + window.location, + jqLite(window.document), + jqLite(window.document.getElementsByTagName('head')[0])); browserSingleton.startUrlWatcher(); browserSingleton.bind(); } @@ -14,9 +17,12 @@ extend(angular, { 'scope': createScope, 'copy': copy, 'extend': extend, + 'equals': equals, 'foreach': foreach, 'noop':noop, 'bind':bind, + 'toJson': toJson, + 'fromJson': fromJson, 'identity':identity, 'isUndefined': isUndefined, 'isDefined': isDefined, diff --git a/src/Browser.js b/src/Browser.js index 0552b3ae..3287cf0e 100644 --- a/src/Browser.js +++ b/src/Browser.js @@ -2,7 +2,7 @@ // Browser ////////////////////////////// -function Browser(location, document) { +function Browser(location, document, head) { this.delay = 50; this.expectedUrl = location.href; this.urlListeners = []; @@ -21,8 +21,9 @@ function Browser(location, document) { }; this.location = location; - this.document = jqLite(document); - this.body = jqLite(document.body); + this.document = document; + this.head = head; + this.idCounter = 0; } Browser.prototype = { @@ -58,21 +59,34 @@ Browser.prototype = { callback = post; post = null; } - var xhr = new this.XHR(), - self = this; - xhr.open(method, url, true); - this.outstandingRequests.count ++; - xhr.onreadystatechange = function() { - if (xhr.readyState == 4) { - try { - callback(xhr.status || 200, xhr.responseText); - } finally { - self.outstandingRequests.count--; - self.processRequestCallbacks(); + if (lowercase(method) == 'json') { + var callbackId = "angular_" + Math.random() + '_' + (this.idCounter++); + callbackId = callbackId.replace(/\d\./, ''); + var script = this.document[0].createElement('script'); + script.type = 'text/javascript'; + script.src = url.replace('JSON_CALLBACK', callbackId); + this.head.append(script); + window[callbackId] = function(data){ + window[callbackId] = undefined; + callback(200, data); + }; + } else { + var xhr = new this.XHR(), + self = this; + xhr.open(method, url, true); + this.outstandingRequests.count ++; + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + try { + callback(xhr.status || 200, xhr.responseText); + } finally { + self.outstandingRequests.count--; + self.processRequestCallbacks(); + } } - } - }; - xhr.send(post || ''); + }; + xhr.send(post || ''); + } }, processRequestCallbacks: function(){ diff --git a/src/Compiler.js b/src/Compiler.js index c8910c27..bcf1f61a 100644 --- a/src/Compiler.js +++ b/src/Compiler.js @@ -30,7 +30,9 @@ Template.prototype = { element = jqLite(element); foreach(this.inits, function(fn) { queue.push(function(scope) { - scope.$tryEval(fn, element, element); + scope.$tryEval(function(){ + return fn.call(scope, element); + }, element); }); }); @@ -121,20 +123,25 @@ Compiler.prototype = { descend: function(value){ if(isDefined(value)) descend = value; return descend;}, directives: function(value){ if(isDefined(value)) directives = value; return directives;} }; - priority = element.attr('ng-eval-order') || priority || 0; + try { + priority = element.attr('ng:eval-order') || priority || 0; + } catch (e) { + // for some reason IE throws error under some weird circumstances. so just assume nothing + priority = priority || 0; + } if (isString(priority)) { priority = PRIORITY[uppercase(priority)] || 0; } template = new Template(priority); eachAttribute(element, function(value, name){ if (!widget) { - if (widget = self.widgets['@' + name]) { + if (widget = self.widgets('@' + name)) { widget = bind(selfApi, widget, value, element); } } }); if (!widget) { - if (widget = self.widgets[nodeName(element)]) { + if (widget = self.widgets(nodeName(element))) { widget = bind(selfApi, widget, element); } } @@ -200,7 +207,7 @@ function eachAttribute(element, fn){ var i, attrs = element[0].attributes || [], chld, attr, name, value, attrValue = {}; for (i = 0; i < attrs.length; i++) { attr = attrs[i]; - name = attr.name.replace(':', '-'); + name = attr.name; value = attr.value; if (msie && name == 'href') { value = decodeURIComponent(element[0].getAttribute(name, 2)); diff --git a/src/Parser.js b/src/Parser.js index df270792..5c2307e4 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -602,6 +602,8 @@ Parser.prototype = { var fnPtr = fn(self); if (typeof fnPtr === 'function') { return fnPtr.apply(self, args); + } else if (fnPtr === undefined) { + return fnPtr; } else { throw "Expression '" + fn.isAssignable + "' is not a function."; } diff --git a/src/Resource.js b/src/Resource.js index 6ee0b1cf..f4f26ebd 100644 --- a/src/Resource.js +++ b/src/Resource.js @@ -1,3 +1,5 @@ + + function Route(template, defaults) { this.template = template = template + '#'; this.defaults = defaults || {}; @@ -26,6 +28,7 @@ Route.prototype = { query.push(encodeURI(key) + '=' + encodeURI(value)); } }); + url = url.replace(/\/*$/, ''); return url + (query.length ? '?' + query.join('&') : ''); } }; @@ -91,11 +94,10 @@ ResourceFactory.prototype = { action.method, route.url(extend({}, action.params || {}, extractParams(data), params)), data, - function(status, response) { + function(status, response, clear) { if (status == 200) { if (action.isArray) { - if (action.cacheThenRetrieve) - value = []; + value.length = 0; foreach(response, function(item){ value.push(new Resource(item)); }); @@ -107,7 +109,7 @@ ResourceFactory.prototype = { throw {status: status, response:response, message: status + ": " + response}; } }, - action.cacheThenRetrieve + action.verifyCache ); return value; }; diff --git a/src/Scope.js b/src/Scope.js index e9b61ec4..4d2aa5c3 100644 --- a/src/Scope.js +++ b/src/Scope.js @@ -46,7 +46,15 @@ function setter(instance, path, value){ /////////////////////////////////// var getterFnCache = {}; -var JS_KEYWORDS = ["this", "throw", "for", "foreach", "var", "const"]; +var JS_KEYWORDS = {}; +foreach( + ["abstract", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", + "delete", "do", "double", "else", "enum", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", + "if", "implements", "import", "ininstanceof", "intinterface", "long", "native", "new", "null", "package", "private", + "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", + "transient", "true", "try", "typeof", "var", "volatile", "void", "while", "with"], + function(key){ JS_KEYWORDS[key] = true;} +); function getterFn(path){ var fn = getterFnCache[path]; if (fn) return fn; @@ -54,7 +62,7 @@ function getterFn(path){ var code = 'function (self){\n'; code += ' var last, fn, type;\n'; foreach(path.split('.'), function(key) { - key = (includes(JS_KEYWORDS, key)) ? '["' + key + '"]' : '.' + key; + key = (JS_KEYWORDS[key]) ? '["' + key + '"]' : '.' + key; code += ' if(!self) return self;\n'; code += ' last = self;\n'; code += ' self = self' + key + ';\n'; @@ -72,8 +80,8 @@ function getterFn(path){ } }); code += ' return self;\n}'; - fn = eval('(' + code + ')'); - fn.toString = function(){ return code; }; + fn = eval('fn = ' + code); + fn["toString"] = function(){ return code; }; return getterFnCache[path] = fn; } @@ -82,7 +90,7 @@ function getterFn(path){ var compileCache = {}; function expressionCompile(exp){ - if (isFunction(exp)) return exp; + if (typeof exp === 'function') return exp; var fn = compileCache[exp]; if (!fn) { var parser = new Parser(exp); @@ -122,24 +130,30 @@ function createScope(parent, services, existing) { $set: bind(instance, setter, instance), $eval: function $eval(exp) { - if (exp !== undefined) { - return expressionCompile(exp).apply(instance, slice.call(arguments, 1, arguments.length)); - } else { + if (exp === undefined) { for ( var i = 0, iSize = evalLists.sorted.length; i < iSize; i++) { for ( var queue = evalLists.sorted[i], - jSize = queue.length, - j= 0; j < jSize; j++) { + jSize = queue.length, + j= 0; j < jSize; j++) { instance.$tryEval(queue[j].fn, queue[j].handler); } } + } else if (typeof exp === 'function'){ + return exp.call(instance); + } else { + return expressionCompile(exp).call(instance); } }, $tryEval: function (expression, exceptionHandler) { try { - return expressionCompile(expression).apply(instance, slice.call(arguments, 2, arguments.length)); + if (typeof expression == 'function') { + return expression.call(instance); + } else { + return expressionCompile(expression).call(instance); + } } catch (e) { - error(e); + (instance.$log || {error:error}).error(e); if (isFunction(exceptionHandler)) { exceptionHandler(e); } else if (exceptionHandler) { @@ -153,12 +167,15 @@ function createScope(parent, services, existing) { $watch: function(watchExp, listener, exceptionHandler) { var watch = expressionCompile(watchExp), last; + listener = expressionCompile(listener); function watcher(){ var value = watch.call(instance), lastValue = last; if (last !== value) { last = value; - instance.$tryEval(listener, exceptionHandler, value, lastValue); + instance.$tryEval(function(){ + return listener.call(instance, value, lastValue); + }, exceptionHandler); } } instance.$onEval(PRIORITY_WATCH, watcher); diff --git a/src/apis.js b/src/apis.js index 306d0ce8..136473b6 100644 --- a/src/apis.js +++ b/src/apis.js @@ -12,7 +12,9 @@ var angularGlobal = { }; var angularCollection = { - 'size': size + 'copy': copy, + 'size': size, + 'equals': equals }; var angularObject = { 'extend': extend diff --git a/src/delete/Binder.js b/src/delete/Binder.js deleted file mode 100644 index 9fc32513..00000000 --- a/src/delete/Binder.js +++ /dev/null @@ -1,356 +0,0 @@ -function Binder(doc, widgetFactory, datastore, location, config) { - this.doc = doc; - this.location = location; - this.datastore = datastore; - this.anchor = {}; - this.widgetFactory = widgetFactory; - this.config = config || {}; - this.updateListeners = []; -} - -Binder.parseBindings = function(string) { - var results = []; - var lastIndex = 0; - var index; - while((index = string.indexOf('{{', lastIndex)) > -1) { - if (lastIndex < index) - results.push(string.substr(lastIndex, index - lastIndex)); - lastIndex = index; - - index = string.indexOf('}}', index); - index = index < 0 ? string.length : index + 2; - - results.push(string.substr(lastIndex, index - lastIndex)); - lastIndex = index; - } - if (lastIndex != string.length) - results.push(string.substr(lastIndex, string.length - lastIndex)); - return results.length === 0 ? [ string ] : results; -}; - -Binder.hasBinding = function(string) { - var bindings = Binder.parseBindings(string); - return bindings.length > 1 || Binder.binding(bindings[0]) !== null; -}; - -Binder.binding = function(string) { - var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/); - return binding ? binding[1] : null; -}; - - -Binder.prototype = { - parseQueryString: function(query) { - var params = {}; - query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, - function (match, left, right) { - if (left) params[decodeURIComponent(left)] = decodeURIComponent(right); - }); - return params; - }, - - parseAnchor: function() { - var self = this, url = this.location['get']() || ""; - - var anchorIndex = url.indexOf('#'); - if (anchorIndex < 0) return; - var anchor = url.substring(anchorIndex + 1); - - var anchorQuery = this.parseQueryString(anchor); - foreach(self.anchor, function(newValue, key) { - delete self.anchor[key]; - }); - foreach(anchorQuery, function(newValue, key) { - self.anchor[key] = newValue; - }); - }, - - onUrlChange: function() { - this.parseAnchor(); - this.updateView(); - }, - - updateAnchor: function() { - var url = this.location['get']() || ""; - var anchorIndex = url.indexOf('#'); - if (anchorIndex > -1) - url = url.substring(0, anchorIndex); - url += "#"; - var sep = ''; - for (var key in this.anchor) { - var value = this.anchor[key]; - if (typeof value === 'undefined' || value === null) { - delete this.anchor[key]; - } else { - url += sep + encodeURIComponent(key); - if (value !== true) - url += "=" + encodeURIComponent(value); - sep = '&'; - } - } - this.location['set'](url); - return url; - }, - - updateView: function() { - var start = new Date().getTime(); - var scope = jQuery(this.doc).scope(); - scope.clearInvalid(); - scope.updateView(); - var end = new Date().getTime(); - this.updateAnchor(); - foreach(this.updateListeners, function(fn) {fn();}); - }, - - docFindWithSelf: function(exp){ - var doc = jQuery(this.doc); - var selection = doc.find(exp); - if (doc.is(exp)){ - selection = selection.andSelf(); - } - return selection; - }, - - executeInit: function() { - this.docFindWithSelf("[ng-init]").each(function() { - var jThis = jQuery(this); - var scope = jThis.scope(); - try { - scope.eval(jThis.attr('ng-init')); - } catch (e) { - alert("EVAL ERROR:\n" + jThis.attr('ng-init') + '\n' + toJson(e, true)); - } - }); - }, - - entity: function (scope) { - var self = this; - this.docFindWithSelf("[ng-entity]").attr("ng-watch", function() { - try { - var jNode = jQuery(this); - var decl = scope.entity(jNode.attr("ng-entity"), self.datastore); - return decl + (jNode.attr('ng-watch') || ""); - } catch (e) { - log(e); - alert(e); - } - }); - }, - - compile: function() { - var jNode = jQuery(this.doc); - if (this.config['autoSubmit']) { - var submits = this.docFindWithSelf(":submit").not("[ng-action]"); - submits.attr("ng-action", "$save()"); - submits.not(":disabled").not("ng-bind-attr").attr("ng-bind-attr", '{disabled:"{{$invalidWidgets}}"}'); - } - this.precompile(this.doc)(this.doc, jNode.scope(), ""); - this.docFindWithSelf("a[ng-action]").live('click', function (event) { - var jNode = jQuery(this); - var scope = jNode.scope(); - try { - scope.eval(jNode.attr('ng-action')); - jNode.removeAttr('ng-error'); - jNode.removeClass("ng-exception"); - } catch (e) { - jNode.addClass("ng-exception"); - jNode.attr('ng-error', toJson(e, true)); - } - scope.get('$updateView')(); - return false; - }); - }, - - translateBinding: function(node, parentPath, factories) { - var path = parentPath.concat(); - var offset = path.pop(); - var parts = Binder.parseBindings(node.nodeValue); - if (parts.length > 1 || Binder.binding(parts[0])) { - var parent = node.parentNode; - if (isLeafNode(parent)) { - parent.setAttribute('ng-bind-template', node.nodeValue); - factories.push({path:path, fn:function(node, scope, prefix) { - return new BindUpdater(node, node.getAttribute('ng-bind-template')); - }}); - } else { - for (var i = 0; i < parts.length; i++) { - var part = parts[i]; - var binding = Binder.binding(part); - var newNode; - if (binding) { - newNode = document.createElement("span"); - var jNewNode = jQuery(newNode); - jNewNode.attr("ng-bind", binding); - if (i === 0) { - factories.push({path:path.concat(offset + i), fn:this.ng_bind}); - } - } else if (msie && part.charAt(0) == ' ') { - newNode = document.createElement("span"); - newNode.innerHTML = ' ' + part.substring(1); - } else { - newNode = document.createTextNode(part); - } - parent.insertBefore(newNode, node); - } - } - parent.removeChild(node); - } - }, - - precompile: function(root) { - var factories = []; - this.precompileNode(root, [], factories); - return function (template, scope, prefix) { - var len = factories.length; - for (var i = 0; i < len; i++) { - var factory = factories[i]; - var node = template; - var path = factory.path; - for (var j = 0; j < path.length; j++) { - node = node.childNodes[path[j]]; - } - try { - scope.addWidget(factory.fn(node, scope, prefix)); - } catch (e) { - alert(e); - } - } - }; - }, - - precompileNode: function(node, path, factories) { - var nodeType = node.nodeType; - if (nodeType == Node.TEXT_NODE) { - this.translateBinding(node, path, factories); - return; - } else if (nodeType != Node.ELEMENT_NODE && nodeType != Node.DOCUMENT_NODE) { - return; - } - - if (!node.getAttribute) return; - var nonBindable = node.getAttribute('ng-non-bindable'); - if (nonBindable || nonBindable === "") return; - - var attributes = node.attributes; - if (attributes) { - var bindings = node.getAttribute('ng-bind-attr'); - node.removeAttribute('ng-bind-attr'); - bindings = bindings ? fromJson(bindings) : {}; - var attrLen = attributes.length; - for (var i = 0; i < attrLen; i++) { - var attr = attributes[i]; - var attrName = attr.name; - // http://www.glennjones.net/Post/809/getAttributehrefbug.htm - var attrValue = msie && attrName == 'href' ? - decodeURI(node.getAttribute(attrName, 2)) : attr.value; - if (Binder.hasBinding(attrValue)) { - bindings[attrName] = attrValue; - } - } - var json = toJson(bindings); - if (json.length > 2) { - node.setAttribute("ng-bind-attr", json); - } - } - - if (!node.getAttribute) log(node); - var repeaterExpression = node.getAttribute('ng-repeat'); - if (repeaterExpression) { - node.removeAttribute('ng-repeat'); - var precompiled = this.precompile(node); - var view = document.createComment("ng-repeat: " + repeaterExpression); - var parentNode = node.parentNode; - parentNode.insertBefore(view, node); - parentNode.removeChild(node); - function template(childScope, prefix, i) { - var clone = jQuery(node).clone(); - clone.css('display', ''); - clone.attr('ng-repeat-index', "" + i); - clone.data('scope', childScope); - precompiled(clone[0], childScope, prefix + i + ":"); - return clone; - } - factories.push({path:path, fn:function(node, scope, prefix) { - return new RepeaterUpdater(jQuery(node), repeaterExpression, template, prefix); - }}); - return; - } - - if (node.getAttribute('ng-eval')) factories.push({path:path, fn:this.ng_eval}); - if (node.getAttribute('ng-bind')) factories.push({path:path, fn:this.ng_bind}); - if (node.getAttribute('ng-bind-attr')) factories.push({path:path, fn:this.ng_bind_attr}); - if (node.getAttribute('ng-hide')) factories.push({path:path, fn:this.ng_hide}); - if (node.getAttribute('ng-show')) factories.push({path:path, fn:this.ng_show}); - if (node.getAttribute('ng-class')) factories.push({path:path, fn:this.ng_class}); - if (node.getAttribute('ng-class-odd')) factories.push({path:path, fn:this.ng_class_odd}); - if (node.getAttribute('ng-class-even')) factories.push({path:path, fn:this.ng_class_even}); - if (node.getAttribute('ng-style')) factories.push({path:path, fn:this.ng_style}); - if (node.getAttribute('ng-watch')) factories.push({path:path, fn:this.ng_watch}); - var nodeName = node.nodeName; - if ((nodeName == 'INPUT' ) || - nodeName == 'TEXTAREA' || - nodeName == 'SELECT' || - nodeName == 'BUTTON') { - var self = this; - factories.push({path:path, fn:function(node, scope, prefix) { - node.name = prefix + node.name.split(":").pop(); - return self.widgetFactory.createController(jQuery(node), scope); - }}); - } - if (nodeName == 'OPTION') { - var html = jQuery('<select/>').append(jQuery(node).clone()).html(); - if (!html.match(/<option(\s.*\s|\s)value\s*=\s*.*>.*<\/\s*option\s*>/gi)) { - if (Binder.hasBinding(node.text)) { - jQuery(node).attr('ng-bind-attr', angular.toJson({'value':node.text})); - } else { - node.value = node.text; - } - } - } - - var children = node.childNodes; - for (var k = 0; k < children.length; k++) { - this.precompileNode(children[k], path.concat(k), factories); - } - }, - - ng_eval: function(node) { - return new EvalUpdater(node, node.getAttribute('ng-eval')); - }, - - ng_bind: function(node) { - return new BindUpdater(node, "{{" + node.getAttribute('ng-bind') + "}}"); - }, - - ng_bind_attr: function(node) { - return new BindAttrUpdater(node, fromJson(node.getAttribute('ng-bind-attr'))); - }, - - ng_hide: function(node) { - return new HideUpdater(node, node.getAttribute('ng-hide')); - }, - - ng_show: function(node) { - return new ShowUpdater(node, node.getAttribute('ng-show')); - }, - - ng_class: function(node) { - return new ClassUpdater(node, node.getAttribute('ng-class')); - }, - - ng_class_even: function(node) { - return new ClassEvenUpdater(node, node.getAttribute('ng-class-even')); - }, - - ng_class_odd: function(node) { - return new ClassOddUpdater(node, node.getAttribute('ng-class-odd')); - }, - - ng_style: function(node) { - return new StyleUpdater(node, node.getAttribute('ng-style')); - }, - - ng_watch: function(node, scope) { - scope.watch(node.getAttribute('ng-watch')); - } -}; diff --git a/src/delete/Model.js b/src/delete/Model.js deleted file mode 100644 index b09efd0e..00000000 --- a/src/delete/Model.js +++ /dev/null @@ -1,65 +0,0 @@ -// Single $ is special and does not get searched -// Double $$ is special an is client only (does not get sent to server) - -function Model(entity, initial) { - this['$$entity'] = entity; - this['$loadFrom'](initial||{}); - this['$entity'] = entity['title']; - this['$migrate'](); -}; - -Model.copyDirectFields = function(src, dst) { - if (src === dst || !src || !dst) return; - var isDataField = function(src, dst, field) { - return (field.substring(0,2) !== '$$') && - (typeof src[field] !== 'function') && - (typeof dst[field] !== 'function'); - }; - for (var field in dst) { - if (isDataField(src, dst, field)) - delete dst[field]; - } - for (field in src) { - if (isDataField(src, dst, field)) - dst[field] = src[field]; - } -}; - -extend(Model.prototype, { - '$migrate': function() { - merge(this['$$entity']['defaults'], this); - return this; - }, - - '$merge': function(other) { - merge(other, this); - return this; - }, - - '$save': function(callback) { - this['$$entity'].datastore.save(this, callback === true ? undefined : callback); - if (callback === true) this['$$entity'].datastore.flush(); - return this; - }, - - '$delete': function(callback) { - this['$$entity'].datastore.remove(this, callback === true ? undefined : callback); - if (callback === true) this['$$entity'].datastore.flush(); - return this; - }, - - '$loadById': function(id, callback) { - this['$$entity'].datastore.load(this, id, callback); - return this; - }, - - '$loadFrom': function(other) { - Model.copyDirectFields(other, this); - return this; - }, - - '$saveTo': function(other) { - Model.copyDirectFields(this, other); - return this; - } -});
\ No newline at end of file diff --git a/src/delete/Scope.js b/src/delete/Scope.js deleted file mode 100644 index ae3f9f11..00000000 --- a/src/delete/Scope.js +++ /dev/null @@ -1,407 +0,0 @@ -function Scope(initialState, name) { - var self = this; - self.widgets = []; - self.evals = []; - self.watchListeners = {}; - self.name = name; - initialState = initialState || {}; - var State = function(){}; - State.prototype = initialState; - self.state = new State(); - extend(self.state, { - '$parent': initialState, - '$watch': bind(self, self.addWatchListener), - '$eval': bind(self, self.eval), - '$bind': bind(self, bind, self), - // change name to autoEval? - '$addEval': bind(self, self.addEval), - '$updateView': bind(self, self.updateView) - }); - if (name == "ROOT") { - self.state['$root'] = self.state; - } -}; - -Scope.expressionCache = {}; -Scope.getter = function(instance, path) { - if (!path) return instance; - var element = path.split('.'); - var key; - var lastInstance = instance; - var len = element.length; - for ( var i = 0; i < len; i++) { - key = element[i]; - if (!key.match(/^[\$\w][\$\w\d]*$/)) - throw "Expression '" + path + "' is not a valid expression for accesing variables."; - if (instance) { - lastInstance = instance; - instance = instance[key]; - } - if (_.isUndefined(instance) && key.charAt(0) == '$') { - var type = angular['Global']['typeOf'](lastInstance); - type = angular[type.charAt(0).toUpperCase()+type.substring(1)]; - var fn = type ? type[[key.substring(1)]] : undefined; - if (fn) { - instance = _.bind(fn, lastInstance, lastInstance); - return instance; - } - } - } - if (typeof instance === 'function' && !instance['$$factory']) { - return bind(lastInstance, instance); - } - return instance; -}; - -Scope.setter = function(instance, path, value){ - var element = path.split('.'); - for ( var i = 0; element.length > 1; i++) { - var key = element.shift(); - var newInstance = instance[key]; - if (!newInstance) { - newInstance = {}; - instance[key] = newInstance; - } - instance = newInstance; - } - instance[element.shift()] = value; - return value; -}; - -Scope.prototype = { - // TODO: rename to update? or eval? - updateView: function() { - var self = this; - this.fireWatchers(); - foreach(this.widgets, function(widget){ - self.evalWidget(widget, "", {}, function(){ - this.updateView(self); - }); - }); - foreach(this.evals, bind(this, this.apply)); - }, - - addWidget: function(controller) { - if (controller) this.widgets.push(controller); - }, - - addEval: function(fn, listener) { - // todo: this should take a function/string and a listener - // todo: this is a hack, which will need to be cleaned up. - var self = this, - listenFn = listener || noop, - expr = self.compile(fn); - this.evals.push(function(){ - self.apply(listenFn, expr()); - }); - }, - - isProperty: function(exp) { - for ( var i = 0; i < exp.length; i++) { - var ch = exp.charAt(i); - if (ch!='.' && !Lexer.prototype.isIdent(ch)) { - return false; - } - } - return true; - }, - - get: function(path) { -// log('SCOPE.get', path, Scope.getter(this.state, path)); - return Scope.getter(this.state, path); - }, - - set: function(path, value) { -// log('SCOPE.set', path, value); - var instance = this.state; - return Scope.setter(instance, path, value); - }, - - setEval: function(expressionText, value) { - this.eval(expressionText + "=" + toJson(value)); - }, - - compile: function(exp) { - if (isFunction(exp)) return bind(this.state, exp); - var expFn = Scope.expressionCache[exp], self = this; - if (!expFn) { - var parser = new Parser(exp); - expFn = parser.statements(); - parser.assertAllConsumed(); - Scope.expressionCache[exp] = expFn; - } - return function(context){ - context = context || {}; - context.self = self.state; - context.scope = self; - return expFn.call(self, context); - }; - }, - - eval: function(exp, context) { -// log('Scope.eval', expressionText); - return this.compile(exp)(context); - }, - - //TODO: Refactor. This function needs to be an execution closure for widgets - // move to widgets - // remove expression, just have inner closure. - evalWidget: function(widget, expression, context, onSuccess, onFailure) { - try { - var value = this.eval(expression, context); - if (widget.hasError) { - widget.hasError = false; - jQuery(widget.view). - removeClass('ng-exception'). - removeAttr('ng-error'); - } - if (onSuccess) { - value = onSuccess.apply(widget, [value]); - } - return true; - } catch (e){ - var jsonError = toJson(e, true); - error('Eval Widget Error:', jsonError); - widget.hasError = true; - jQuery(widget.view). - addClass('ng-exception'). - attr('ng-error', jsonError); - if (onFailure) { - onFailure.apply(widget, [e, jsonError]); - } - return false; - } - }, - - validate: function(expressionText, value, element) { - var expression = Scope.expressionCache[expressionText]; - if (!expression) { - expression = new Parser(expressionText).validator(); - Scope.expressionCache[expressionText] = expression; - } - var self = {scope:this, self:this.state, '$element':element}; - return expression(self)(self, value); - }, - - entity: function(entityDeclaration, datastore) { - var expression = new Parser(entityDeclaration).entityDeclaration(); - return expression({scope:this, datastore:datastore}); - }, - - clearInvalid: function() { - var invalid = this.state['$invalidWidgets']; - while(invalid.length > 0) {invalid.pop();} - }, - - markInvalid: function(widget) { - this.state['$invalidWidgets'].push(widget); - }, - - watch: function(declaration) { - var self = this; - new Parser(declaration).watch()({ - scope:this, - addListener:function(watch, exp){ - self.addWatchListener(watch, function(n,o){ - try { - return exp({scope:self}, n, o); - } catch(e) { - alert(e); - } - }); - } - }); - }, - - addWatchListener: function(watchExpression, listener) { - // TODO: clean me up! - if (!isFunction(listener)) { - listener = this.compile(listener); - } - var watcher = this.watchListeners[watchExpression]; - if (!watcher) { - watcher = {listeners:[], expression:watchExpression}; - this.watchListeners[watchExpression] = watcher; - } - watcher.listeners.push(listener); - }, - - fireWatchers: function() { - var self = this, fired = false; - foreach(this.watchListeners, function(watcher) { - var value = self.eval(watcher.expression); - if (value !== watcher.lastValue) { - foreach(watcher.listeners, function(listener){ - listener(value, watcher.lastValue); - fired = true; - }); - watcher.lastValue = value; - } - }); - return fired; - }, - - apply: function(fn) { - fn.apply(this.state, slice.call(arguments, 1, arguments.length)); - } -}; - -////////////////////////////// - -function getter(instance, path) { - if (!path) return instance; - var element = path.split('.'); - var key; - var lastInstance = instance; - var len = element.length; - for ( var i = 0; i < len; i++) { - key = element[i]; - if (!key.match(/^[\$\w][\$\w\d]*$/)) - throw "Expression '" + path + "' is not a valid expression for accesing variables."; - if (instance) { - lastInstance = instance; - instance = instance[key]; - } - if (_.isUndefined(instance) && key.charAt(0) == '$') { - var type = angular['Global']['typeOf'](lastInstance); - type = angular[type.charAt(0).toUpperCase()+type.substring(1)]; - var fn = type ? type[[key.substring(1)]] : undefined; - if (fn) { - instance = _.bind(fn, lastInstance, lastInstance); - return instance; - } - } - } - if (typeof instance === 'function' && !instance['$$factory']) { - return bind(lastInstance, instance); - } - return instance; -}; - -function setter(instance, path, value){ - var element = path.split('.'); - for ( var i = 0; element.length > 1; i++) { - var key = element.shift(); - var newInstance = instance[key]; - if (!newInstance) { - newInstance = {}; - instance[key] = newInstance; - } - instance = newInstance; - } - instance[element.shift()] = value; - return value; -}; - -var compileCache = {}; -function expressionCompile(exp){ - if (isFunction(exp)) return exp; - var expFn = compileCache[exp]; - if (!expFn) { - var parser = new Parser(exp); - expFn = parser.statements(); - parser.assertAllConsumed(); - compileCache[exp] = expFn; - } - // return expFn - // TODO(remove this hack) - return function(){ - return expFn({ - scope: { - set: this.$set, - get: this.$get - } - }); - }; -}; - -var NON_RENDERABLE_ELEMENTS = { - '#text': 1, '#comment':1, 'TR':1, 'TH':1 -}; - -function isRenderableElement(element){ - return element && element[0] && !NON_RENDERABLE_ELEMENTS[element[0].nodeName]; -} - -function rethrow(e) { throw e; } -function errorHandlerFor(element) { - while (!isRenderableElement(element)) { - element = element.parent() || jqLite(document.body); - } - return function(error) { - element.attr('ng-error', angular.toJson(error)); - element.addClass('ng-exception'); - }; -} - -function createScope(parent, Class) { - function Parent(){} - function API(){} - function Behavior(){} - - var instance, behavior, api, watchList = [], evalList = []; - - Class = Class || noop; - parent = Parent.prototype = parent || {}; - api = API.prototype = new Parent(); - behavior = Behavior.prototype = extend(new API(), Class.prototype); - instance = new Behavior(); - - extend(api, { - $parent: parent, - $bind: bind(instance, bind, instance), - $get: bind(instance, getter, instance), - $set: bind(instance, setter, instance), - - $eval: function(exp) { - if (isDefined(exp)) { - return expressionCompile(exp).apply(instance, slice.call(arguments, 1, arguments.length)); - } else { - foreach(evalList, function(eval) { - instance.$tryEval(eval.fn, eval.handler); - }); - foreach(watchList, function(watch) { - var value = instance.$tryEval(watch.watch, watch.handler); - if (watch.last !== value) { - instance.$tryEval(watch.listener, watch.handler, value, watch.last); - watch.last = value; - } - }); - } - }, - - $tryEval: function (expression, exceptionHandler) { - try { - return expressionCompile(expression).apply(instance, slice.call(arguments, 2, arguments.length)); - } catch (e) { - error(e); - if (isFunction(exceptionHandler)) { - exceptionHandler(e); - } else if (exceptionHandler) { - errorHandlerFor(exceptionHandler)(e); - } - } - }, - - $watch: function(watchExp, listener, exceptionHandler) { - var watch = expressionCompile(watchExp); - watchList.push({ - watch: watch, - last: watch.call(instance), - handler: exceptionHandler, - listener:expressionCompile(listener) - }); - }, - - $onEval: function(expr, exceptionHandler){ - evalList.push({ - fn: expressionCompile(expr), - handler: exceptionHandler - }); - } - }); - - Class.apply(instance, slice.call(arguments, 2, arguments.length)); - - return instance; -} diff --git a/src/delete/Widgets.js b/src/delete/Widgets.js deleted file mode 100644 index 96b63793..00000000 --- a/src/delete/Widgets.js +++ /dev/null @@ -1,806 +0,0 @@ -function WidgetFactory(serverUrl, database) { - this.nextUploadId = 0; - this.serverUrl = serverUrl; - this.database = database; - if (window['swfobject']) { - this.createSWF = window['swfobject']['createSWF']; - } else { - this.createSWF = function(){ - alert("ERROR: swfobject not loaded!"); - }; - } -}; - -WidgetFactory.prototype = { - createController: function(input, scope) { - var controller; - var type = input.attr('type').toLowerCase(); - var exp = input.attr('name'); - if (exp) exp = exp.split(':').pop(); - var event = "change"; - var bubbleEvent = true; - var formatter = angularFormatter[input.attr('ng-format')] || angularFormatter['noop']; - if (type == 'button' || type == 'submit' || type == 'reset' || type == 'image') { - controller = new ButtonController(input[0], exp, formatter); - event = "click"; - bubbleEvent = false; - } else if (type == 'text' || type == 'textarea' || type == 'hidden' || type == 'password') { - controller = new TextController(input[0], exp, formatter); - event = "keyup change"; - } else if (type == 'checkbox') { - controller = new CheckboxController(input[0], exp, formatter); - event = "click"; - } else if (type == 'radio') { - controller = new RadioController(input[0], exp, formatter); - event="click"; - } else if (type == 'select-one') { - controller = new SelectController(input[0], exp, formatter); - } else if (type == 'select-multiple') { - controller = new MultiSelectController(input[0], exp, formatter); - } else if (type == 'file') { - controller = this.createFileController(input, exp, formatter); - } else { - throw 'Unknown type: ' + type; - } - input.data('controller', controller); - var updateView = scope.get('$updateView'); - var action = function() { - if (controller.updateModel(scope)) { - var action = jQuery(controller.view).attr('ng-action') || ""; - if (scope.evalWidget(controller, action)) { - updateView(scope); - } - } - return bubbleEvent; - }; - jQuery(controller.view, ":input"). - bind(event, action); - return controller; - }, - - createFileController: function(fileInput) { - var uploadId = '__uploadWidget_' + (this.nextUploadId++); - var view = FileController.template(uploadId); - fileInput.after(view); - var att = { - 'data':this.serverUrl + "/admin/ServerAPI.swf", - 'width':"95", 'height':"20", 'align':"top", - 'wmode':"transparent"}; - var par = { - 'flashvars':"uploadWidgetId=" + uploadId, - 'allowScriptAccess':"always"}; - var swfNode = this.createSWF(att, par, uploadId); - fileInput.remove(); - var cntl = new FileController(view, fileInput[0].name, swfNode, this.serverUrl + "/data/" + this.database); - jQuery(swfNode).parent().data('controller', cntl); - return cntl; - } -}; -///////////////////// -// FileController -/////////////////////// - -function FileController(view, scopeName, uploader, databaseUrl) { - this.view = view; - this.uploader = uploader; - this.scopeName = scopeName; - this.attachmentsPath = databaseUrl + '/_attachments'; - this.value = null; - this.lastValue = undefined; -}; - -angularCallbacks['flashEvent'] = function(id, event, args) { - var object = document.getElementById(id); - var jobject = jQuery(object); - var controller = jobject.parent().data("controller"); - FileController.prototype[event].apply(controller, args); - _.defer(jobject.scope().get('$updateView')); -}; - -FileController.template = function(id) { - return jQuery('<span class="ng-upload-widget">' + - '<input type="checkbox" ng-non-bindable="true"/>' + - '<object id="' + id + '" />' + - '<a></a>' + - '<span/>' + - '</span>'); -}; - -extend(FileController.prototype, { - 'cancel': noop, - 'complete': noop, - 'httpStatus': function(status) { - alert("httpStatus:" + this.scopeName + " status:" + status); - }, - 'ioError': function() { - alert("ioError:" + this.scopeName); - }, - 'open': function() { - alert("open:" + this.scopeName); - }, - 'progress':noop, - 'securityError': function() { - alert("securityError:" + this.scopeName); - }, - 'uploadCompleteData': function(data) { - var value = fromJson(data); - value.url = this.attachmentsPath + '/' + value.id + '/' + value.text; - this.view.find("input").attr('checked', true); - var scope = this.view.scope(); - this.value = value; - this.updateModel(scope); - this.value = null; - }, - 'select': function(name, size, type) { - this.name = name; - this.view.find("a").text(name).attr('href', name); - this.view.find("span").text(angular['filter']['bytes'](size)); - this.upload(); - }, - - updateModel: function(scope) { - var isChecked = this.view.find("input").attr('checked'); - var value = isChecked ? this.value : null; - if (this.lastValue === value) { - return false; - } else { - scope.set(this.scopeName, value); - return true; - } - }, - - updateView: function(scope) { - var modelValue = scope.get(this.scopeName); - if (modelValue && this.value !== modelValue) { - this.value = modelValue; - this.view.find("a"). - attr("href", this.value.url). - text(this.value.text); - this.view.find("span").text(angular['filter']['bytes'](this.value.size)); - } - this.view.find("input").attr('checked', !!modelValue); - }, - - upload: function() { - if (this.name) { - this.uploader['uploadFile'](this.attachmentsPath); - } - } -}); - -/////////////////////// -// NullController -/////////////////////// -function NullController(view) {this.view = view;}; -NullController.prototype = { - updateModel: function() { return true; }, - updateView: noop -}; -NullController.instance = new NullController(); - - -/////////////////////// -// ButtonController -/////////////////////// -var ButtonController = NullController; - -/////////////////////// -// TextController -/////////////////////// -function TextController(view, exp, formatter) { - this.view = view; - this.formatter = formatter; - this.exp = exp; - this.validator = view.getAttribute('ng-validate'); - this.required = typeof view.attributes['ng-required'] != "undefined"; - this.lastErrorText = null; - this.lastValue = undefined; - this.initialValue = this.formatter['parse'](view.value); - var widget = view.getAttribute('ng-widget'); - if (widget === 'datepicker') { - jQuery(view).datepicker(); - } -}; - -TextController.prototype = { - updateModel: function(scope) { - var value = this.formatter['parse'](this.view.value); - if (this.lastValue === value) { - return false; - } else { - scope.setEval(this.exp, value); - this.lastValue = value; - return true; - } - }, - - updateView: function(scope) { - var view = this.view; - var value = scope.get(this.exp); - if (typeof value === "undefined") { - value = this.initialValue; - scope.setEval(this.exp, value); - } - value = value ? value : ''; - if (!_(this.lastValue).isEqual(value)) { - view.value = this.formatter['format'](value); - this.lastValue = value; - } - - var isValidationError = false; - view.removeAttribute('ng-error'); - if (this.required) { - isValidationError = !(value && $.trim("" + value).length > 0); - } - var errorText = isValidationError ? "Required Value" : null; - if (!isValidationError && this.validator && value) { - errorText = scope.validate(this.validator, value, view); - isValidationError = !!errorText; - } - if (this.lastErrorText !== errorText) { - this.lastErrorText = isValidationError; - if (errorText && isVisible(view)) { - view.setAttribute('ng-error', errorText); - scope.markInvalid(this); - } - jQuery(view).toggleClass('ng-validation-error', isValidationError); - } - } -}; - -/////////////////////// -// CheckboxController -/////////////////////// -function CheckboxController(view, exp, formatter) { - this.view = view; - this.exp = exp; - this.lastValue = undefined; - this.formatter = formatter; - this.initialValue = this.formatter['parse'](view.checked ? view.value : ""); -}; - -CheckboxController.prototype = { - updateModel: function(scope) { - var input = this.view; - var value = input.checked ? input.value : ''; - value = this.formatter['parse'](value); - value = this.formatter['format'](value); - if (this.lastValue === value) { - return false; - } else { - scope.setEval(this.exp, this.formatter['parse'](value)); - this.lastValue = value; - return true; - } - }, - - updateView: function(scope) { - var input = this.view; - var value = scope.eval(this.exp); - if (typeof value === "undefined") { - value = this.initialValue; - scope.setEval(this.exp, value); - } - input.checked = this.formatter['parse'](input.value) == value; - } -}; - -/////////////////////// -// SelectController -/////////////////////// -function SelectController(view, exp) { - this.view = view; - this.exp = exp; - this.lastValue = undefined; - this.initialValue = view.value; -}; - -SelectController.prototype = { - updateModel: function(scope) { - var input = this.view; - if (input.selectedIndex < 0) { - scope.setEval(this.exp, null); - } else { - var value = this.view.value; - if (this.lastValue === value) { - return false; - } else { - scope.setEval(this.exp, value); - this.lastValue = value; - return true; - } - } - }, - - updateView: function(scope) { - var input = this.view; - var value = scope.get(this.exp); - if (typeof value === 'undefined') { - value = this.initialValue; - scope.setEval(this.exp, value); - } - if (value !== this.lastValue) { - input.value = value ? value : ""; - this.lastValue = value; - } - } -}; - -/////////////////////// -// MultiSelectController -/////////////////////// -function MultiSelectController(view, exp) { - this.view = view; - this.exp = exp; - this.lastValue = undefined; - this.initialValue = this.selected(); -}; - -MultiSelectController.prototype = { - selected: function () { - var value = []; - var options = this.view.options; - for ( var i = 0; i < options.length; i++) { - var option = options[i]; - if (option.selected) { - value.push(option.value); - } - } - return value; - }, - - updateModel: function(scope) { - var value = this.selected(); - // TODO: This is wrong! no caching going on here as we are always comparing arrays - if (this.lastValue === value) { - return false; - } else { - scope.setEval(this.exp, value); - this.lastValue = value; - return true; - } - }, - - updateView: function(scope) { - var input = this.view; - var selected = scope.get(this.exp); - if (typeof selected === "undefined") { - selected = this.initialValue; - scope.setEval(this.exp, selected); - } - if (selected !== this.lastValue) { - var options = input.options; - for ( var i = 0; i < options.length; i++) { - var option = options[i]; - option.selected = _.include(selected, option.value); - } - this.lastValue = selected; - } - } -}; - -/////////////////////// -// RadioController -/////////////////////// -function RadioController(view, exp) { - this.view = view; - this.exp = exp; - this.lastChecked = undefined; - this.lastValue = undefined; - this.inputValue = view.value; - this.initialValue = view.checked ? view.value : null; -}; - -RadioController.prototype = { - updateModel: function(scope) { - var input = this.view; - if (this.lastChecked) { - return false; - } else { - input.checked = true; - this.lastValue = scope.setEval(this.exp, this.inputValue); - this.lastChecked = true; - return true; - } - }, - - updateView: function(scope) { - var input = this.view; - var value = scope.get(this.exp); - if (this.initialValue && typeof value === "undefined") { - value = this.initialValue; - scope.setEval(this.exp, value); - } - if (this.lastValue != value) { - this.lastChecked = input.checked = this.inputValue == (''+value); - this.lastValue = value; - } - } -}; - -/////////////////////// -//ElementController -/////////////////////// -function BindUpdater(view, exp) { - this.view = view; - this.exp = Binder.parseBindings(exp); - this.hasError = false; -}; - -BindUpdater.toText = function(obj) { - var e = escapeHtml; - switch(typeof obj) { - case "string": - case "boolean": - case "number": - return e(obj); - case "function": - return BindUpdater.toText(obj()); - case "object": - if (isNode(obj)) { - return outerHTML(obj); - } else if (obj instanceof angular.filter.Meta) { - switch(typeof obj.html) { - case "string": - case "number": - return obj.html; - case "function": - return obj.html(); - case "object": - if (isNode(obj.html)) - return outerHTML(obj.html); - default: - break; - } - switch(typeof obj.text) { - case "string": - case "number": - return e(obj.text); - case "function": - return e(obj.text()); - default: - break; - } - } - if (obj === null) - return ""; - return e(toJson(obj, true)); - default: - return ""; - } -}; - -BindUpdater.prototype = { - updateModel: noop, - updateView: function(scope) { - var html = []; - var parts = this.exp; - var length = parts.length; - for(var i=0; i<length; i++) { - var part = parts[i]; - var binding = Binder.binding(part); - if (binding) { - scope.evalWidget(this, binding, {$element:this.view}, function(value){ - html.push(BindUpdater.toText(value)); - }, function(e, text){ - setHtml(this.view, text); - }); - if (this.hasError) { - return; - } - } else { - html.push(escapeHtml(part)); - } - } - setHtml(this.view, html.join('')); - } -}; - -function BindAttrUpdater(view, attrs) { - this.view = view; - this.attrs = attrs; -}; - -BindAttrUpdater.prototype = { - updateModel: noop, - updateView: function(scope) { - var jNode = jQuery(this.view); - var attributeTemplates = this.attrs; - if (this.hasError) { - this.hasError = false; - jNode. - removeClass('ng-exception'). - removeAttr('ng-error'); - } - var isImage = jNode.is('img'); - for (var attrName in attributeTemplates) { - var attributeTemplate = Binder.parseBindings(attributeTemplates[attrName]); - var attrValues = []; - for ( var i = 0; i < attributeTemplate.length; i++) { - var binding = Binder.binding(attributeTemplate[i]); - if (binding) { - try { - var value = scope.eval(binding, {$element:jNode[0], attrName:attrName}); - if (value && (value.constructor !== array || value.length !== 0)) - attrValues.push(value); - } catch (e) { - this.hasError = true; - error('BindAttrUpdater', e); - var jsonError = toJson(e, true); - attrValues.push('[' + jsonError + ']'); - jNode. - addClass('ng-exception'). - attr('ng-error', jsonError); - } - } else { - attrValues.push(attributeTemplate[i]); - } - } - var attrValue = attrValues.length ? attrValues.join('') : null; - if(isImage && attrName == 'src' && !attrValue) - attrValue = scope.get('$config.blankImage'); - jNode.attr(attrName, attrValue); - } - } -}; - -function EvalUpdater(view, exp) { - this.view = view; - this.exp = exp; - this.hasError = false; -}; -EvalUpdater.prototype = { - updateModel: noop, - updateView: function(scope) { - scope.evalWidget(this, this.exp); - } -}; - -function HideUpdater(view, exp) { this.view = view; this.exp = exp; }; -HideUpdater.prototype = { - updateModel: noop, - updateView: function(scope) { - scope.evalWidget(this, this.exp, {}, function(hideValue){ - var view = jQuery(this.view); - if (toBoolean(hideValue)) { - view.hide(); - } else { - view.show(); - } - }); - } -}; - -function ShowUpdater(view, exp) { this.view = view; this.exp = exp; }; -ShowUpdater.prototype = { - updateModel: noop, - updateView: function(scope) { - scope.evalWidget(this, this.exp, {}, function(hideValue){ - var view = jQuery(this.view); - if (toBoolean(hideValue)) { - view.show(); - } else { - view.hide(); - } - }); - } -}; - -function ClassUpdater(view, exp) { this.view = view; this.exp = exp; }; -ClassUpdater.prototype = { - updateModel: noop, - updateView: function(scope) { - scope.evalWidget(this, this.exp, {}, function(classValue){ - if (classValue !== null && classValue !== undefined) { - this.view.className = classValue; - } - }); - } -}; - -function ClassEvenUpdater(view, exp) { this.view = view; this.exp = exp; }; -ClassEvenUpdater.prototype = { - updateModel: noop, - updateView: function(scope) { - scope.evalWidget(this, this.exp, {}, function(classValue){ - var index = scope.get('$index'); - jQuery(this.view).toggleClass(classValue, index % 2 === 1); - }); - } -}; - -function ClassOddUpdater(view, exp) { this.view = view; this.exp = exp; }; -ClassOddUpdater.prototype = { - updateModel: noop, - updateView: function(scope) { - scope.evalWidget(this, this.exp, {}, function(classValue){ - var index = scope.get('$index'); - jQuery(this.view).toggleClass(classValue, index % 2 === 0); - }); - } -}; - -function StyleUpdater(view, exp) { this.view = view; this.exp = exp; }; -StyleUpdater.prototype = { - updateModel: noop, - updateView: function(scope) { - scope.evalWidget(this, this.exp, {}, function(styleValue){ - jQuery(this.view).attr('style', "").css(styleValue); - }); - } -}; - -/////////////////////// -// RepeaterUpdater -/////////////////////// -function RepeaterUpdater(view, repeaterExpression, template, prefix) { - this.view = view; - this.template = template; - this.prefix = prefix; - this.children = []; - var match = repeaterExpression.match(/^\s*(.+)\s+in\s+(.*)\s*$/); - if (! match) { - throw "Expected ng-repeat in form of 'item in collection' but got '" + - repeaterExpression + "'."; - } - var keyValue = match[1]; - this.iteratorExp = match[2]; - match = keyValue.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/); - if (!match) { - throw "'item' in 'item in collection' should be identifier or (key, value) but get '" + - keyValue + "'."; - } - this.valueExp = match[3] || match[1]; - this.keyExp = match[2]; -}; - -RepeaterUpdater.prototype = { - updateModel: noop, - updateView: function(scope) { - scope.evalWidget(this, this.iteratorExp, {}, function(iterator){ - var self = this; - if (!iterator) { - iterator = []; - if (scope.isProperty(this.iteratorExp)) { - scope.set(this.iteratorExp, iterator); - } - } - var childrenLength = this.children.length; - var cursor = this.view; - var time = 0; - var child = null; - var keyExp = this.keyExp; - var valueExp = this.valueExp; - var iteratorCounter = 0; - foreach(iterator, function(value, key){ - if (iteratorCounter < childrenLength) { - // reuse children - child = self.children[iteratorCounter]; - child.scope.set(valueExp, value); - } else { - // grow children - var name = self.prefix + - valueExp + " in " + self.iteratorExp + "[" + iteratorCounter + "]"; - var childScope = new Scope(scope.state, name); - childScope.set('$index', iteratorCounter); - if (keyExp) - childScope.set(keyExp, key); - childScope.set(valueExp, value); - child = { scope:childScope, element:self.template(childScope, self.prefix, iteratorCounter) }; - cursor.after(child.element); - self.children.push(child); - } - cursor = child.element; - var s = new Date().getTime(); - child.scope.updateView(); - time += new Date().getTime() - s; - iteratorCounter++; - }); - // shrink children - for ( var r = childrenLength; r > iteratorCounter; --r) { - this.children.pop().element.remove(); - } - // Special case for option in select - if (child && child.element[0].nodeName === "OPTION") { - var select = jQuery(child.element[0].parentNode); - var cntl = select.data('controller'); - if (cntl) { - cntl.lastValue = undefined; - cntl.updateView(scope); - } - } - }); - } -}; - -////////////////////////////////// -// PopUp -////////////////////////////////// - -function PopUp(doc) { - this.doc = doc; -}; - -PopUp.OUT_EVENT = "mouseleave mouseout click dblclick keypress keyup"; - -PopUp.onOver = function(e) { - PopUp.onOut(); - var jNode = jQuery(this); - jNode.bind(PopUp.OUT_EVENT, PopUp.onOut); - var position = jNode.position(); - var de = document.documentElement; - var w = self.innerWidth || (de && de.clientWidth) || document.body.clientWidth; - var hasArea = w - position.left; - var width = 300; - var title = jNode.hasClass("ng-exception") ? "EXCEPTION:" : "Validation error..."; - var msg = jNode.attr("ng-error"); - - var x; - var arrowPos = hasArea>(width+75) ? "left" : "right"; - var tip = jQuery( - "<div id='ng-callout' style='width:"+width+"px'>" + - "<div class='ng-arrow-"+arrowPos+"'/>" + - "<div class='ng-title'>"+title+"</div>" + - "<div class='ng-content'>"+msg+"</div>" + - "</div>"); - jQuery("body").append(tip); - if(arrowPos === 'left'){ - x = position.left + this.offsetWidth + 11; - }else{ - x = position.left - (width + 15); - tip.find('.ng-arrow-right').css({left:width+1}); - } - - tip.css({left: x+"px", top: (position.top - 3)+"px"}); - return true; -}; - -PopUp.onOut = function() { - jQuery('#ng-callout'). - unbind(PopUp.OUT_EVENT, PopUp.onOut). - remove(); - return true; -}; - -PopUp.prototype = { - bind: function () { - var self = this; - this.doc.find('.ng-validation-error,.ng-exception'). - live("mouseover", PopUp.onOver); - } -}; - -////////////////////////////////// -// Status -////////////////////////////////// - -function NullStatus(body) { -}; - -NullStatus.prototype = { - beginRequest:function(){}, - endRequest:function(){} -}; - -function Status(body) { - this.requestCount = 0; - this.body = body; -}; - -Status.DOM ='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>'; - -Status.prototype = { - beginRequest: function () { - if (this.requestCount === 0) { - (this.loader = this.loader || this.body.append(Status.DOM).find("#ng-loading")).show(); - } - this.requestCount++; - }, - - endRequest: function () { - this.requestCount--; - if (this.requestCount === 0) { - this.loader.hide("fold"); - } - } -}; diff --git a/src/directives.js b/src/directives.js index cabf0c23..ffe37890 100644 --- a/src/directives.js +++ b/src/directives.js @@ -1,10 +1,10 @@ -angularDirective("ng-init", function(expression){ +angularDirective("ng:init", function(expression){ return function(element){ this.$tryEval(expression, element); }; }); -angularDirective("ng-controller", function(expression){ +angularDirective("ng:controller", function(expression){ return function(element){ var controller = getter(window, expression, true) || getter(this, expression, true); if (!controller) @@ -16,22 +16,23 @@ angularDirective("ng-controller", function(expression){ }; }); -angularDirective("ng-eval", function(expression){ +angularDirective("ng:eval", function(expression){ return function(element){ this.$onEval(expression, element); }; }); -angularDirective("ng-bind", function(expression){ +angularDirective("ng:bind", function(expression){ return function(element) { var lastValue = noop, lastError = noop; this.$onEval(function() { - var error, - value = this.$tryEval(expression, function(e){ - error = toJson(e); - }), - isHtml, - isDomElement; + var error, value, isHtml, isDomElement, + oldElement = this.hasOwnProperty('$element') ? this.$element : undefined; + this.$element = element; + value = this.$tryEval(expression, function(e){ + error = toJson(e); + }); + this.$element = oldElement; if (lastValue === value && lastError == error) return; isHtml = value instanceof HTML, isDomElement = isElement(value); @@ -74,7 +75,9 @@ function compileBindTemplate(template){ }); }); bindTemplateCache[template] = fn = function(element){ - var parts = [], self = this; + var parts = [], self = this, + oldElement = this.hasOwnProperty('$element') ? self.$element : undefined; + self.$element = element; for ( var i = 0; i < bindings.length; i++) { var value = bindings[i].call(self, element); if (isElement(value)) @@ -83,13 +86,14 @@ function compileBindTemplate(template){ value = toJson(value, true); parts.push(value); }; + self.$element = oldElement; return parts.join(''); }; } return fn; } -angularDirective("ng-bind-template", function(expression){ +angularDirective("ng:bind-template", function(expression){ var templateFn = compileBindTemplate(expression); return function(element) { var lastValue; @@ -108,7 +112,7 @@ var REMOVE_ATTRIBUTES = { 'readonly':'readOnly', 'checked':'checked' }; -angularDirective("ng-bind-attr", function(expression){ +angularDirective("ng:bind-attr", function(expression){ return function(element){ var lastValue = {}; this.$onEval(function(){ @@ -134,17 +138,17 @@ angularDirective("ng-bind-attr", function(expression){ }; }); -angularWidget("@ng-non-bindable", noop); +angularWidget("@ng:non-bindable", noop); -angularWidget("@ng-repeat", function(expression, element){ - element.removeAttr('ng-repeat'); - element.replaceWith(this.comment("ng-repeat: " + expression)); +angularWidget("@ng:repeat", function(expression, element){ + element.removeAttr('ng:repeat'); + element.replaceWith(this.comment("ng:repeat: " + expression)); var template = this.compile(element); return function(reference){ var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/), lhs, rhs, valueIdent, keyIdent; if (! match) { - throw "Expected ng-repeat in form of 'item in collection' but got '" + + throw "Expected ng:repeat in form of 'item in collection' but got '" + expression + "'."; } lhs = match[1]; @@ -157,32 +161,32 @@ angularWidget("@ng-repeat", function(expression, element){ valueIdent = match[3] || match[1]; keyIdent = match[2]; - if (isUndefined(this.$eval(rhs))) this.$set(rhs, []); - var children = [], currentScope = this; this.$onEval(function(){ var index = 0, childCount = children.length, childScope, lastElement = reference, - collection = this.$tryEval(rhs, reference); + collection = this.$tryEval(rhs, reference), is_array = isArray(collection); for ( var key in collection) { - if (index < childCount) { - // reuse existing child - childScope = children[index]; - childScope[valueIdent] = collection[key]; - if (keyIdent) childScope[keyIdent] = key; - } else { - // grow children - childScope = template(element.clone(), createScope(currentScope)); - childScope[valueIdent] = collection[key]; - if (keyIdent) childScope[keyIdent] = key; - lastElement.after(childScope.$element); - childScope.$index = index; - childScope.$element.attr('ng-repeat-index', index); - childScope.$init(); - children.push(childScope); + if (!is_array || collection.hasOwnProperty(key)) { + if (index < childCount) { + // reuse existing child + childScope = children[index]; + childScope[valueIdent] = collection[key]; + if (keyIdent) childScope[keyIdent] = key; + } else { + // grow children + childScope = template(element.clone(), createScope(currentScope)); + childScope[valueIdent] = collection[key]; + if (keyIdent) childScope[keyIdent] = key; + lastElement.after(childScope.$element); + childScope.$index = index; + childScope.$element.attr('ng:repeat-index', index); + childScope.$init(); + children.push(childScope); + } + childScope.$eval(); + lastElement = childScope.$element; + index ++; } - childScope.$eval(); - lastElement = childScope.$element; - index ++; }; // shrink children while(children.length > index) { @@ -192,7 +196,7 @@ angularWidget("@ng-repeat", function(expression, element){ }; }); -angularDirective("ng-click", function(expression, element){ +angularDirective("ng:click", function(expression, element){ return function(element){ var self = this; element.bind('click', function(){ @@ -203,7 +207,7 @@ angularDirective("ng-click", function(expression, element){ }; }); -angularDirective("ng-watch", function(expression, element){ +angularDirective("ng:watch", function(expression, element){ return function(element){ var self = this; new Parser(expression).watch()({ @@ -221,8 +225,8 @@ function ngClass(selector) { var existing = element[0].className + ' '; return function(element){ this.$onEval(function(){ - var value = this.$eval(expression); if (selector(this.$index)) { + var value = this.$eval(expression); if (isArray(value)) value = value.join(' '); element[0].className = trim(existing + value); } @@ -231,11 +235,11 @@ function ngClass(selector) { }; } -angularDirective("ng-class", ngClass(function(){return true;})); -angularDirective("ng-class-odd", ngClass(function(i){return i % 2 === 0;})); -angularDirective("ng-class-even", ngClass(function(i){return i % 2 === 1;})); +angularDirective("ng:class", ngClass(function(){return true;})); +angularDirective("ng:class-odd", ngClass(function(i){return i % 2 === 0;})); +angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;})); -angularDirective("ng-show", function(expression, element){ +angularDirective("ng:show", function(expression, element){ return function(element){ this.$onEval(function(){ element.css('display', toBoolean(this.$eval(expression)) ? '' : 'none'); @@ -243,7 +247,7 @@ angularDirective("ng-show", function(expression, element){ }; }); -angularDirective("ng-hide", function(expression, element){ +angularDirective("ng:hide", function(expression, element){ return function(element){ this.$onEval(function(){ element.css('display', toBoolean(this.$eval(expression)) ? 'none' : ''); @@ -251,10 +255,19 @@ angularDirective("ng-hide", function(expression, element){ }; }); -angularDirective("ng-style", function(expression, element){ +angularDirective("ng:style", function(expression, element){ return function(element){ + var resetStyle = getStyle(element); this.$onEval(function(){ - element.css(this.$eval(expression)); + var style = this.$eval(expression) || {}, key, mergedStyle = {}; + for(key in style) { + if (resetStyle[key] === undefined) resetStyle[key] = ''; + mergedStyle[key] = style[key]; + } + for(key in resetStyle) { + mergedStyle[key] = mergedStyle[key] || resetStyle[key]; + } + element.css(mergedStyle); }, element); }; }); diff --git a/src/filters.js b/src/filters.js index a911b935..99d17405 100644 --- a/src/filters.js +++ b/src/filters.js @@ -2,7 +2,7 @@ var angularFilterGoogleChartApi; foreach({ 'currency': function(amount){ - this.$element.toggleClass('ng-format-negative', amount < 0); + this.$element.toggleClass('ng:format-negative', amount < 0); return '$' + angularFilter['number'].apply(this, [amount, 2]); }, @@ -34,7 +34,11 @@ foreach({ return text; }, - 'date': function(amount) { + 'date': function(date) { + if (date instanceof Date) + return date.toLocaleDateString(); + else + return date; }, 'json': function(object) { diff --git a/src/jqLite.js b/src/jqLite.js index 68172fd8..cff9ae00 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -36,6 +36,23 @@ function jqClearData(element) { } } +function getStyle(element) { + var current = {}, style = element[0].style, value, name, i; + if (typeof style.length == 'number') { + for(i = 0; i < style.length; i++) { + name = style[i]; + current[name] = style[name]; + } + } else { + for (name in style) { + value = style[name]; + if (1*name != name && name != 'cssText' && value && typeof value == 'string' && value !='false') + current[name] = value; + } + } + return current; +} + function JQLite(element) { if (isElement(element)) { this[0] = element; diff --git a/src/markups.js b/src/markups.js index 74b293b8..ac2b5636 100644 --- a/src/markups.js +++ b/src/markups.js @@ -32,14 +32,14 @@ angularTextMarkup('{{}}', function(text, textNode, parentElement) { self = this; if (hasBindings(bindings)) { if (isLeafNode(parentElement[0])) { - parentElement.attr('ng-bind-template', text); + parentElement.attr('ng:bind-template', text); } else { var cursor = textNode, newElement; foreach(parseBindings(text), function(text){ var exp = binding(text); if (exp) { newElement = self.element('span'); - newElement.attr('ng-bind', exp); + newElement.attr('ng:bind', exp); } else { newElement = self.text(text); } @@ -68,18 +68,18 @@ angularTextMarkup('OPTION', function(text, textNode, parentElement){ } }); -var NG_BIND_ATTR = 'ng-bind-attr'; +var NG_BIND_ATTR = 'ng:bind-attr'; angularAttrMarkup('{{}}', function(value, name, element){ - if (name.substr(0, 3) != 'ng-') { - if (msie && name == 'src') - value = decodeURI(value); - var bindings = parseBindings(value), - bindAttr; - if (hasBindings(bindings)) { - element.removeAttr(name); - bindAttr = fromJson(element.attr(NG_BIND_ATTR) || "{}"); - bindAttr[name] = value; - element.attr(NG_BIND_ATTR, toJson(bindAttr)); - } + // don't process existing attribute markup + if (angularDirective(name) || angularDirective("@" + name)) return; + if (msie && name == 'src') + value = decodeURI(value); + var bindings = parseBindings(value), + bindAttr; + if (hasBindings(bindings)) { + element.removeAttr(name); + bindAttr = fromJson(element.attr(NG_BIND_ATTR) || "{}"); + bindAttr[name] = value; + element.attr(NG_BIND_ATTR, toJson(bindAttr)); } }); diff --git a/src/moveToAngularCom/ControlBar.js b/src/moveToAngularCom/ControlBar.js deleted file mode 100644 index 685beeb2..00000000 --- a/src/moveToAngularCom/ControlBar.js +++ /dev/null @@ -1,72 +0,0 @@ -function ControlBar(document, serverUrl, database) { - this._document = document; - this.serverUrl = serverUrl; - this.database = database; - this._window = window; - this.callbacks = []; -}; - -ControlBar.HTML = - '<div>' + - '<div class="ui-widget-overlay"></div>' + - '<div id="ng-login" ng-non-bindable="true">' + - '<div class="ng-login-container"></div>' + - '</div>' + - '</div>'; - - -ControlBar.FORBIDEN = - '<div ng-non-bindable="true" title="Permission Error:">' + - 'Sorry, you do not have permission for this!'+ - '</div>'; - -ControlBar.prototype = { - bind: function () { - }, - - login: function (loginSubmitFn) { - this.callbacks.push(loginSubmitFn); - if (this.callbacks.length == 1) { - this.doTemplate("/user_session/new.mini?database="+encodeURIComponent(this.database)+"&return_url=" + encodeURIComponent(this.urlWithoutAnchor())); - } - }, - - logout: function (loginSubmitFn) { - this.callbacks.push(loginSubmitFn); - if (this.callbacks.length == 1) { - this.doTemplate("/user_session/do_destroy.mini"); - } - }, - - urlWithoutAnchor: function (path) { - return this._window['location']['href'].split("#")[0]; - }, - - doTemplate: function (path) { - var self = this; - var id = new Date().getTime(); - var url = this.urlWithoutAnchor() + "#$iframe_notify=" + id; - var iframeHeight = 330; - var loginView = jQuery('<div style="overflow:hidden; padding:2px 0 0 0;"><iframe name="'+ url +'" src="'+this.serverUrl + path + '" width="500" height="'+ iframeHeight +'"/></div>'); - this._document.append(loginView); - loginView['dialog']({ - 'height':iframeHeight + 33, 'width':500, - 'resizable': false, 'modal':true, - 'title': 'Authentication: <a href="http://www.getangular.com"><tt><angular/></tt></a>' - }); - angularCallbacks["_iframe_notify_" + id] = function() { - loginView['dialog']("destroy"); - loginView['remove'](); - foreach(self.callbacks, function(callback){ - callback(); - }); - self.callbacks = []; - }; - }, - - notAuthorized: function () { - if (this.forbidenView) return; - this.forbidenView = jQuery(ControlBar.FORBIDEN); - this.forbidenView.dialog({bgiframe:true, height:70, modal:true}); - } -};
\ No newline at end of file diff --git a/src/moveToAngularCom/DataStore.js b/src/moveToAngularCom/DataStore.js deleted file mode 100644 index 70bcc623..00000000 --- a/src/moveToAngularCom/DataStore.js +++ /dev/null @@ -1,330 +0,0 @@ -function DataStore(post, users, anchor) { - this.post = post; - this.users = users; - this._cache_collections = []; - this._cache = {'$collections':this._cache_collections}; - this.anchor = anchor; - this.bulkRequest = []; -}; - -DataStore.NullEntity = extend(function(){}, { - 'all': function(){return [];}, - 'query': function(){return [];}, - 'load': function(){return {};}, - 'title': undefined -}); - -DataStore.prototype = { - cache: function(document) { - if (! document.datastore === this) { - throw "Parameter must be an instance of Entity! " + toJson(document); - } - var key = document['$entity'] + '/' + document['$id']; - var cachedDocument = this._cache[key]; - if (cachedDocument) { - Model.copyDirectFields(document, cachedDocument); - } else { - this._cache[key] = document; - cachedDocument = document; - } - return cachedDocument; - }, - - load: function(instance, id, callback, failure) { - if (id && id !== '*') { - var self = this; - this._jsonRequest(["GET", instance['$entity'] + "/" + id], function(response) { - instance['$loadFrom'](response); - instance['$migrate'](); - var clone = instance['$$entity'](instance); - self.cache(clone); - (callback||noop)(instance); - }, failure); - } - return instance; - }, - - loadMany: function(entity, ids, callback) { - var self=this; - var list = []; - var callbackCount = 0; - foreach(ids, function(id){ - list.push(self.load(entity(), id, function(){ - callbackCount++; - if (callbackCount == ids.length) { - (callback||noop)(list); - } - })); - }); - return list; - }, - - loadOrCreate: function(instance, id, callback) { - var self=this; - return this.load(instance, id, callback, function(response){ - if (response['$status_code'] == 404) { - instance['$id'] = id; - (callback||noop)(instance); - } else { - throw response; - } - }); - }, - - loadAll: function(entity, callback) { - var self = this; - var list = []; - list['$$accept'] = function(doc){ - return doc['$entity'] == entity['title']; - }; - this._cache_collections.push(list); - this._jsonRequest(["GET", entity['title']], function(response) { - var rows = response; - for ( var i = 0; i < rows.length; i++) { - var document = entity(); - document['$loadFrom'](rows[i]); - list.push(self.cache(document)); - } - (callback||noop)(list); - }); - return list; - }, - - save: function(document, callback) { - var self = this; - var data = {}; - document['$saveTo'](data); - this._jsonRequest(["POST", "", data], function(response) { - document['$loadFrom'](response); - var cachedDoc = self.cache(document); - _.each(self._cache_collections, function(collection){ - if (collection['$$accept'](document)) { - angularArray['includeIf'](collection, cachedDoc, true); - } - }); - if (document['$$anchor']) { - self.anchor[document['$$anchor']] = document['$id']; - } - if (callback) - callback(document); - }); - }, - - remove: function(document, callback) { - var self = this; - var data = {}; - document['$saveTo'](data); - this._jsonRequest(["DELETE", "", data], function(response) { - delete self._cache[document['$entity'] + '/' + document['$id']]; - _.each(self._cache_collections, function(collection){ - for ( var i = 0; i < collection.length; i++) { - var item = collection[i]; - if (item['$id'] == document['$id']) { - collection.splice(i, 1); - } - } - }); - (callback||noop)(response); - }); - }, - - _jsonRequest: function(request, callback, failure) { - request['$$callback'] = callback; - request['$$failure'] = failure||function(response){ - throw response; - }; - this.bulkRequest.push(request); - }, - - flush: function() { - if (this.bulkRequest.length === 0) return; - var self = this; - var bulkRequest = this.bulkRequest; - this.bulkRequest = []; - log('REQUEST:', bulkRequest); - function callback(code, bulkResponse){ - log('RESPONSE[' + code + ']: ', bulkResponse); - if(bulkResponse['$status_code'] == 401) { - self.users['login'](function(){ - self.post(bulkRequest, callback); - }); - } else if(bulkResponse['$status_code']) { - alert(toJson(bulkResponse)); - } else { - for ( var i = 0; i < bulkResponse.length; i++) { - var response = bulkResponse[i]; - var request = bulkRequest[i]; - var responseCode = response['$status_code']; - if(responseCode) { - if(responseCode == 403) { - self.users['notAuthorized'](); - } else { - request['$$failure'](response); - } - } else { - request['$$callback'](response); - } - } - } - } - this.post(bulkRequest, callback); - }, - - saveScope: function(scope, callback) { - var saveCounter = 1; - function onSaveDone() { - saveCounter--; - if (saveCounter === 0 && callback) - callback(); - } - for(var key in scope) { - var item = scope[key]; - if (item && item['$save'] == Model.prototype['$save']) { - saveCounter++; - item['$save'](onSaveDone); - } - } - onSaveDone(); - }, - - query: function(type, query, arg, callback){ - var self = this; - var queryList = []; - queryList['$$accept'] = function(doc){ - return false; - }; - this._cache_collections.push(queryList); - var request = type['title'] + '/' + query + '=' + arg; - this._jsonRequest(["GET", request], function(response){ - var list = response; - foreach(list, function(item){ - var document = type()['$loadFrom'](item); - queryList.push(self.cache(document)); - }); - (callback||noop)(queryList); - }); - return queryList; - }, - - entities: function(callback) { - var entities = []; - var self = this; - this._jsonRequest(["GET", "$entities"], function(response) { - foreach(response, function(value, entityName){ - entities.push(self.entity(entityName)); - }); - entities.sort(function(a,b){return a.title > b.title ? 1 : -1;}); - (callback||noop)(entities); - }); - return entities; - }, - - documentCountsByUser: function(){ - var counts = {}; - var self = this; - self.post([["GET", "$users"]], function(code, response){ - extend(counts, response[0]); - }); - return counts; - }, - - userDocumentIdsByEntity: function(user){ - var ids = {}; - var self = this; - self.post([["GET", "$users/" + user]], function(code, response){ - extend(ids, response[0]); - }); - return ids; - }, - - entity: function(name, defaults){ - if (!name) { - return DataStore.NullEntity; - } - var self = this; - var entity = extend(function(initialState){ - return new Model(entity, initialState); - }, { - // entity.name does not work as name seems to be reserved for functions - 'title': name, - '$$factory': true, - datastore: this, //private, obfuscate - 'defaults': defaults || {}, - 'load': function(id, callback){ - return self.load(entity(), id, callback); - }, - 'loadMany': function(ids, callback){ - return self.loadMany(entity, ids, callback); - }, - 'loadOrCreate': function(id, callback){ - return self.loadOrCreate(entity(), id, callback); - }, - 'all': function(callback){ - return self.loadAll(entity, callback); - }, - 'query': function(query, queryArgs, callback){ - return self.query(entity, query, queryArgs, callback); - }, - 'properties': function(callback) { - self._jsonRequest(["GET", name + "/$properties"], callback); - } - }); - return entity; - }, - - join: function(join){ - function fn(){ - throw "Joined entities can not be instantiated into a document."; - }; - function base(name){return name ? name.substring(0, name.indexOf('.')) : undefined;} - function next(name){return name.substring(name.indexOf('.') + 1);} - var joinOrder = _(join).chain(). - map(function($, name){ - return name;}). - sortBy(function(name){ - var path = []; - do { - if (_(path).include(name)) throw "Infinite loop in join: " + path.join(" -> "); - path.push(name); - if (!join[name]) throw _("Named entity '<%=name%>' is undefined.").template({name:name}); - name = base(join[name].on); - } while(name); - return path.length; - }). - value(); - if (_(joinOrder).select(function($){return join[$].on;}).length != joinOrder.length - 1) - throw "Exactly one entity needs to be primary."; - fn['query'] = function(exp, value) { - var joinedResult = []; - var baseName = base(exp); - if (baseName != joinOrder[0]) throw _("Named entity '<%=name%>' is not a primary entity.").template({name:baseName}); - var Entity = join[baseName].join; - var joinIndex = 1; - Entity['query'](next(exp), value, function(result){ - var nextJoinName = joinOrder[joinIndex++]; - var nextJoin = join[nextJoinName]; - var nextJoinOn = nextJoin.on; - var joinIds = {}; - _(result).each(function(doc){ - var row = {}; - joinedResult.push(row); - row[baseName] = doc; - var id = Scope.getter(row, nextJoinOn); - joinIds[id] = id; - }); - nextJoin.join.loadMany(_.toArray(joinIds), function(result){ - var byId = {}; - _(result).each(function(doc){ - byId[doc.$id] = doc; - }); - _(joinedResult).each(function(row){ - var id = Scope.getter(row, nextJoinOn); - row[nextJoinName] = byId[id]; - }); - }); - }); - return joinedResult; - }; - return fn; - } -}; diff --git a/src/moveToAngularCom/Server.js b/src/moveToAngularCom/Server.js deleted file mode 100644 index 5c4ec3c6..00000000 --- a/src/moveToAngularCom/Server.js +++ /dev/null @@ -1,68 +0,0 @@ -function Server(url, getScript) { - this.url = url; - this.nextId = 0; - this.getScript = getScript; - this.uuid = "_" + ("" + Math.random()).substr(2) + "_"; - this.maxSize = 1800; -}; - -Server.prototype = { - base64url: function(txt) { - return Base64.encode(txt); - }, - - request: function(method, url, request, callback) { - var requestId = this.uuid + (this.nextId++); - var payload = this.base64url(toJson({'u':url, 'm':method, 'p':request})); - var totalPockets = Math.ceil(payload.length / this.maxSize); - var baseUrl = this.url + "/$/" + requestId + "/" + totalPockets + "/"; - angularCallbacks[requestId] = function(response) { - delete angularCallbacks[requestId]; - callback(200, response); - }; - for ( var pocketNo = 0; pocketNo < totalPockets; pocketNo++) { - var pocket = payload.substr(pocketNo * this.maxSize, this.maxSize); - this.getScript(baseUrl + (pocketNo+1) + "?h=" + pocket, noop); - } - } -}; - -function FrameServer(frame) { - this.frame = frame; -}; -FrameServer.PREFIX = "$DATASET:"; - -FrameServer.prototype = { - read:function(){ - this.data = fromJson(this.frame.name.substr(FrameServer.PREFIX.length)); - }, - write:function(){ - this.frame.name = FrameServer.PREFIX + toJson(this.data); - }, - request: function(method, url, request, callback) { - //alert(method + " " + url + " " + toJson(request) + " " + toJson(callback)); - } -}; - - -function VisualServer(delegate, status, update) { - this.delegate = delegate; - this.update = update; - this.status = status; -}; - -VisualServer.prototype = { - request:function(method, url, request, callback) { - var self = this; - this.status.beginRequest(request); - this.delegate.request(method, url, request, function() { - self.status.endRequest(); - try { - callback.apply(this, arguments); - } catch (e) { - alert(toJson(e)); - } - self.update(); - }); - } -}; diff --git a/src/moveToAngularCom/Users.js b/src/moveToAngularCom/Users.js deleted file mode 100644 index fb5845d3..00000000 --- a/src/moveToAngularCom/Users.js +++ /dev/null @@ -1,35 +0,0 @@ -function Users(server, controlBar) { - this.server = server; - this.controlBar = controlBar; -}; - -extend(Users.prototype, { - 'fetchCurrentUser':function(callback) { - var self = this; - this.server.request("GET", "/account.json", {}, function(code, response){ - self['current'] = response['user']; - callback(response['user']); - }); - }, - - 'logout': function(callback) { - var self = this; - this.controlBar.logout(function(){ - delete self['current']; - (callback||noop)(); - }); - }, - - 'login': function(callback) { - var self = this; - this.controlBar.login(function(){ - self['fetchCurrentUser'](function(){ - (callback||noop)(); - }); - }); - }, - - 'notAuthorized': function(){ - this.controlBar.notAuthorized(); - } -}); diff --git a/src/moveToAngularCom/directivesAngularCom.js b/src/moveToAngularCom/directivesAngularCom.js deleted file mode 100644 index 84032bdd..00000000 --- a/src/moveToAngularCom/directivesAngularCom.js +++ /dev/null @@ -1,29 +0,0 @@ - -angular.directive("auth", function(expression, element){ - return function(){ - if(expression == "eager") { - this.$users.fetchCurrent(); - } - }; -}); - - -//expression = "book=Book:{year=2000}" -angular.directive("entity", function(expression, element){ - //parse expression, ignore element - var entityName; // "Book"; - var instanceName; // "book"; - var defaults; // {year: 2000}; - - parse(expression); - - return function(){ - this[entityName] = this.$datastore.entity(entityName, defaults); - this[instanceName] = this[entityName](); - this.$watch("$anchor."+instanceName, function(newAnchor){ - this[instanceName] = this[entityName].get(this.$anchor[instanceName]); - }); - }; -}); - - diff --git a/src/scenario/DSL.js b/src/scenario/DSL.js index bcf2b6c5..fcadd3ca 100644 --- a/src/scenario/DSL.js +++ b/src/scenario/DSL.js @@ -35,9 +35,8 @@ angular.scenario.dsl.input = function(selector) { value + "'", function(done){ var input = this.testDocument. find(':radio[name$=@' + selector + '][value=' + value + ']'); - var event = this.testWindow.document.createEvent('MouseEvent'); - event.initMouseEvent('click', true, true, this.testWindow, 0,0,0,0,0, false, false, false, false, 0, null); - input[0].dispatchEvent(event); + jqLiteWrap(input[0]).trigger('click'); + input[0].checked = true; done(); }); } diff --git a/src/services.js b/src/services.js index 64f2ea4f..106f8954 100644 --- a/src/services.js +++ b/src/services.js @@ -65,18 +65,24 @@ angularService("$location", function(browser){ }, {inject: ['$browser']}); angularService("$log", function($window){ - var console = $window.console, - log = console && console.log || noop; + var console = $window.console || {log: noop, warn: noop, info: noop, error: noop}, + log = console.log || noop; return { - log: log, - warn: console && console.warn || log, - info: console && console.info || log, - error: console && console.error || log + log: bind(console, log), + warn: bind(console, console.warn || log), + info: bind(console, console.info || log), + error: bind(console, console.error || log) }; }, {inject:['$window']}); -angularService("$hover", function(browser) { - var tooltip, self = this, error, width = 300, arrowWidth = 10; +angularService('$exceptionHandler', function($log){ + return function(e) { + $log.error(e); + }; +}, {inject:['$log']}); + +angularService("$hover", function(browser, document) { + var tooltip, self = this, error, width = 300, arrowWidth = 10, body = jqLite(document[0].body);; browser.hover(function(element, show){ if (show && (error = element.attr(NG_EXCEPTION) || element.attr(NG_VALIDATION_ERROR))) { if (!tooltip) { @@ -89,9 +95,9 @@ angularService("$hover", function(browser) { tooltip.callout.append(tooltip.arrow); tooltip.callout.append(tooltip.title); tooltip.callout.append(tooltip.content); - self.$browser.body.append(tooltip.callout); + body.append(tooltip.callout); } - var docRect = self.$browser.body[0].getBoundingClientRect(), + var docRect = body[0].getBoundingClientRect(), elementRect = element[0].getBoundingClientRect(), leftSpace = docRect.right - elementRect.right - arrowWidth; tooltip.title.text(element.hasClass("ng-exception") ? "EXCEPTION:" : "Validation error..."); @@ -119,7 +125,7 @@ angularService("$hover", function(browser) { tooltip = null; } }); -}, {inject:['$browser']}); +}, {inject:['$browser', '$document']}); angularService("$invalidWidgets", function(){ var invalidWidgets = []; @@ -313,7 +319,7 @@ angularService('$xhr.bulk', function($xhr, $error, $log){ angularService('$xhr.cache', function($xhr){ var inflight = {}, self = this;; - function cache(method, url, post, callback, cacheThenRetrieve){ + function cache(method, url, post, callback, verifyCache){ if (isFunction(post)) { callback = post; post = null; @@ -322,7 +328,7 @@ angularService('$xhr.cache', function($xhr){ var data; if (data = cache.data[url]) { callback(200, copy(data.value)); - if (!cacheThenRetrieve) + if (!verifyCache) return; } diff --git a/src/widgets.js b/src/widgets.js index efafa9c5..5f0fcf7c 100644 --- a/src/widgets.js +++ b/src/widgets.js @@ -15,7 +15,7 @@ function modelAccessor(scope, element) { function modelFormattedAccessor(scope, element) { var accessor = modelAccessor(scope, element), - formatterName = element.attr('ng-format') || NOOP, + formatterName = element.attr('ng:format') || NOOP, formatter = angularFormatter(formatterName); if (!formatter) throw "Formatter named '" + formatterName + "' not found."; return { @@ -33,10 +33,10 @@ function compileValidator(expr) { } function valueAccessor(scope, element) { - var validatorName = element.attr('ng-validate') || NOOP, + var validatorName = element.attr('ng:validate') || NOOP, validator = compileValidator(validatorName), - requiredExpr = element.attr('ng-required'), - formatterName = element.attr('ng-format') || NOOP, + requiredExpr = element.attr('ng:required'), + formatterName = element.attr('ng:format') || NOOP, formatter = angularFormatter(formatterName), format, parse, lastError, required; invalidWidgets = scope.$invalidWidgets || {markValid:noop, markInvalid:noop}; @@ -83,8 +83,7 @@ function valueAccessor(scope, element) { elementError(element, NG_VALIDATION_ERROR, null); invalidWidgets.markValid(element); } else { - var error, - validateScope = extend(new (extend(function(){}, {prototype:scope}))(), {$element:element}); + var error, validateScope = inherit(scope, {$element:element}); error = required && !value ? 'Required' : (value ? validator(validateScope, value) : null); @@ -193,10 +192,10 @@ function inputWidget(events, modelAccessor, viewAccessor, initFn) { var scope = this, model = modelAccessor(scope, element), view = viewAccessor(scope, element), - action = element.attr('ng-change') || '', + action = element.attr('ng:change') || '', lastValue; initFn.call(scope, model, view, element); - this.$eval(element.attr('ng-init')||''); + this.$eval(element.attr('ng:init')||''); // Don't register a handler if we are a button (noopAccessor) and there is no action if (action || modelAccessor !== noopAccessor) { element.bind(events, function(){ @@ -223,24 +222,24 @@ function inputWidgetSelector(element){ return INPUT_TYPE[lowercase(element[0].type)] || noop; } -angularWidget('INPUT', inputWidgetSelector); -angularWidget('TEXTAREA', inputWidgetSelector); -angularWidget('BUTTON', inputWidgetSelector); -angularWidget('SELECT', function(element){ +angularWidget('input', inputWidgetSelector); +angularWidget('textarea', inputWidgetSelector); +angularWidget('button', inputWidgetSelector); +angularWidget('select', function(element){ this.descend(true); return inputWidgetSelector.call(this, element); }); -angularWidget('NG:INCLUDE', function(element){ +angularWidget('ng:include', function(element){ var compiler = this, srcExp = element.attr("src"), scopeExp = element.attr("scope") || ''; - if (element[0]['ng-compiled']) { + if (element[0]['ng:compiled']) { this.descend(true); this.directives(true); } else { - element[0]['ng-compiled'] = true; + element[0]['ng:compiled'] = true; return function(element){ var scope = this, childScope; var changeCounter = 0; @@ -266,7 +265,7 @@ angularWidget('NG:INCLUDE', function(element){ } }); -var ngSwitch = angularWidget('NG:SWITCH', function (element){ +var ngSwitch = angularWidget('ng:switch', function (element){ var compiler = this, watchExpr = element.attr("on"), usingExpr = (element.attr("using") || 'equals'), @@ -276,7 +275,7 @@ var ngSwitch = angularWidget('NG:SWITCH', function (element){ cases = []; if (!usingFn) throw "Using expression '" + usingExpr + "' unknown."; eachNode(element, function(caseElement){ - var when = caseElement.attr('ng-switch-when'); + var when = caseElement.attr('ng:switch-when'); if (when) { cases.push({ when: function(scope, value){ @@ -1,2 +1,7 @@ -java -jar lib/jstestdriver/JsTestDriver.jar --tests all -# java -jar lib/jstestdriver/JsTestDriver.jar --tests all --config jsTestDriver-jquery.conf +tests=$1 +if [[ $tests = "" ]]; then + tests="all" +fi + +java -jar lib/jstestdriver/JsTestDriver.jar --tests "$tests" +#java -jar lib/jstestdriver/JsTestDriver.jar --tests "$tests" --config jsTestDriver-jquery.conf diff --git a/test/AngularSpec.js b/test/AngularSpec.js index de724f03..b4e90175 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -1,3 +1,7 @@ +beforeEach(function(){ + compileCache = {}; +}); + describe('Angular', function(){ xit('should fire on updateEvents', function(){ var onUpdateView = jasmine.createSpy(); @@ -50,3 +54,30 @@ describe("copy", function(){ }); }); + +describe('equals', function(){ + it('should return true if same object', function(){ + var o = {}; + expect(equals(o, o)).toEqual(true); + expect(equals(1, '1')).toEqual(true); + expect(equals(1, '2')).toEqual(false); + }); + + it('should recurse into object', function(){ + expect(equals({}, {})).toEqual(true); + expect(equals({name:'misko'}, {name:'misko'})).toEqual(true); + expect(equals({name:'misko', age:1}, {name:'misko'})).toEqual(false); + expect(equals({name:'misko'}, {name:'misko', age:1})).toEqual(false); + expect(equals({name:'misko'}, {name:'adam'})).toEqual(false); + expect(equals(['misko'], ['misko'])).toEqual(true); + expect(equals(['misko'], ['adam'])).toEqual(false); + expect(equals(['misko'], ['misko', 'adam'])).toEqual(false); + }); + + it('should ignore $ member variables', function(){ + expect(equals({name:'misko', $id:1}, {name:'misko', $id:2})).toEqual(true); + expect(equals({name:'misko'}, {name:'misko', $id:2})).toEqual(true); + expect(equals({name:'misko', $id:1}, {name:'misko'})).toEqual(true); + }); + +}); diff --git a/test/BinderTest.js b/test/BinderTest.js index ecdd506f..f38383ae 100644 --- a/test/BinderTest.js +++ b/test/BinderTest.js @@ -42,12 +42,12 @@ BinderTest.prototype.testChangingRadioUpdatesModel = function(){ }; BinderTest.prototype.testChangingCheckboxUpdatesModel = function(){ - var form = this.compile('<input type="checkbox" name="model.price" value="true" checked ng-format="boolean"/>'); + var form = this.compile('<input type="checkbox" name="model.price" value="true" checked ng:format="boolean"/>'); assertEquals(true, form.scope.model.price); }; BinderTest.prototype.testBindUpdate = function() { - var c = this.compile('<div ng-eval="a=123"/>'); + var c = this.compile('<div ng:eval="a=123"/>'); assertEquals(123, c.scope.$get('a')); }; @@ -71,26 +71,26 @@ BinderTest.prototype.testChangingSelectSelectedUpdatesModel = function(){ }; BinderTest.prototype.testExecuteInitialization = function() { - var c = this.compile('<div ng-init="a=123">'); + var c = this.compile('<div ng:init="a=123">'); assertEquals(c.scope.$get('a'), 123); }; BinderTest.prototype.testExecuteInitializationStatements = function() { - var c = this.compile('<div ng-init="a=123;b=345">'); + var c = this.compile('<div ng:init="a=123;b=345">'); assertEquals(c.scope.$get('a'), 123); assertEquals(c.scope.$get('b'), 345); }; BinderTest.prototype.testApplyTextBindings = function(){ - var form = this.compile('<div ng-bind="model.a">x</div>'); + var form = this.compile('<div ng:bind="model.a">x</div>'); form.scope.$set('model', {a:123}); form.scope.$eval(); assertEquals('123', form.node.text()); }; BinderTest.prototype.testReplaceBindingInTextWithSpan = function() { - assertEquals(this.compileToHtml("<b>a{{b}}c</b>"), '<b>a<span ng-bind="b"></span>c</b>'); - assertEquals(this.compileToHtml("<b>{{b}}</b>"), '<b><span ng-bind="b"></span></b>'); + assertEquals(this.compileToHtml("<b>a{{b}}c</b>"), '<b>a<span ng:bind="b"></span>c</b>'); + assertEquals(this.compileToHtml("<b>{{b}}</b>"), '<b><span ng:bind="b"></span></b>'); }; BinderTest.prototype.testBindingSpaceConfusesIE = function() { @@ -99,16 +99,16 @@ BinderTest.prototype.testBindingSpaceConfusesIE = function() { span.innerHTML = ' '; var nbsp = span.firstChild.nodeValue; assertEquals( - '<b><span ng-bind="a"></span><span>'+nbsp+'</span><span ng-bind="b"></span></b>', + '<b><span ng:bind="a"></span><span>'+nbsp+'</span><span ng:bind="b"></span></b>', this.compileToHtml("<b>{{a}} {{b}}</b>")); assertEquals( - '<b><span ng-bind="A"></span><span>'+nbsp+'x </span><span ng-bind="B"></span><span>'+nbsp+'(</span><span ng-bind="C"></span>)</b>', + '<b><span ng:bind="A"></span><span>'+nbsp+'x </span><span ng:bind="B"></span><span>'+nbsp+'(</span><span ng:bind="C"></span>)</b>', this.compileToHtml("<b>{{A}} x {{B}} ({{C}})</b>")); }; BinderTest.prototype.testBindingOfAttributes = function() { var c = this.compile("<a href='http://s/a{{b}}c' foo='x'></a>"); - var attrbinding = c.node.attr("ng-bind-attr"); + var attrbinding = c.node.attr("ng:bind-attr"); var bindings = fromJson(attrbinding); assertEquals("http://s/a{{b}}c", decodeURI(bindings.href)); assertTrue(!bindings.foo); @@ -116,7 +116,7 @@ BinderTest.prototype.testBindingOfAttributes = function() { BinderTest.prototype.testMarkMultipleAttributes = function() { var c = this.compile('<a href="http://s/a{{b}}c" foo="{{d}}"></a>'); - var attrbinding = c.node.attr("ng-bind-attr"); + var attrbinding = c.node.attr("ng:bind-attr"); var bindings = fromJson(attrbinding); assertEquals(bindings.foo, "{{d}}"); assertEquals(decodeURI(bindings.href), "http://s/a{{b}}c"); @@ -126,17 +126,17 @@ BinderTest.prototype.testAttributesNoneBound = function() { var c = this.compile("<a href='abc' foo='def'></a>"); var a = c.node; assertEquals(a[0].nodeName, "A"); - assertTrue(!a.attr("ng-bind-attr")); + assertTrue(!a.attr("ng:bind-attr")); }; BinderTest.prototype.testExistingAttrbindingIsAppended = function() { - var c = this.compile("<a href='http://s/{{abc}}' ng-bind-attr='{\"b\":\"{{def}}\"}'></a>"); + var c = this.compile("<a href='http://s/{{abc}}' ng:bind-attr='{\"b\":\"{{def}}\"}'></a>"); var a = c.node; - assertEquals('{"b":"{{def}}","href":"http://s/{{abc}}"}', a.attr('ng-bind-attr')); + assertEquals('{"b":"{{def}}","href":"http://s/{{abc}}"}', a.attr('ng:bind-attr')); }; BinderTest.prototype.testAttributesAreEvaluated = function(){ - var c = this.compile('<a ng-bind-attr=\'{"a":"a", "b":"a+b={{a+b}}"}\'></a>'); + var c = this.compile('<a ng:bind-attr=\'{"a":"a", "b":"a+b={{a+b}}"}\'></a>'); var binder = c.binder, form = c.node; c.scope.$eval('a=1;b=2'); c.scope.$eval(); @@ -147,7 +147,7 @@ BinderTest.prototype.testAttributesAreEvaluated = function(){ BinderTest.prototype.testInputTypeButtonActionExecutesInScope = function(){ var savedCalled = false; - var c = this.compile('<input type="button" ng-click="person.save()" value="Apply">'); + var c = this.compile('<input type="button" ng:click="person.save()" value="Apply">'); c.scope.$set("person.save", function(){ savedCalled = true; }); @@ -157,7 +157,7 @@ BinderTest.prototype.testInputTypeButtonActionExecutesInScope = function(){ BinderTest.prototype.testInputTypeButtonActionExecutesInScope2 = function(){ var log = ""; - var c = this.compile('<input type="image" ng-click="action()">'); + var c = this.compile('<input type="image" ng:click="action()">'); c.scope.$set("action", function(){ log += 'click;'; }); @@ -168,7 +168,7 @@ BinderTest.prototype.testInputTypeButtonActionExecutesInScope2 = function(){ BinderTest.prototype.testButtonElementActionExecutesInScope = function(){ var savedCalled = false; - var c = this.compile('<button ng-click="person.save()">Apply</button>'); + var c = this.compile('<button ng:click="person.save()">Apply</button>'); c.scope.$set("person.save", function(){ savedCalled = true; }); @@ -177,7 +177,7 @@ BinderTest.prototype.testButtonElementActionExecutesInScope = function(){ }; BinderTest.prototype.testRepeaterUpdateBindings = function(){ - var a = this.compile('<ul><LI ng-repeat="item in model.items" ng-bind="item.a"/></ul>'); + var a = this.compile('<ul><LI ng:repeat="item in model.items" ng:bind="item.a"/></ul>'); var form = a.node; var items = [{a:"A"}, {a:"B"}]; a.scope.$set('model', {items:items}); @@ -185,25 +185,25 @@ BinderTest.prototype.testRepeaterUpdateBindings = function(){ a.scope.$eval(); assertEquals('<ul>' + '<#comment></#comment>' + - '<li ng-bind="item.a" ng-repeat-index="0">A</li>' + - '<li ng-bind="item.a" ng-repeat-index="1">B</li>' + + '<li ng:bind="item.a" ng:repeat-index="0">A</li>' + + '<li ng:bind="item.a" ng:repeat-index="1">B</li>' + '</ul>', sortedHtml(form)); items.unshift({a:'C'}); a.scope.$eval(); assertEquals('<ul>' + '<#comment></#comment>' + - '<li ng-bind="item.a" ng-repeat-index="0">C</li>' + - '<li ng-bind="item.a" ng-repeat-index="1">A</li>' + - '<li ng-bind="item.a" ng-repeat-index="2">B</li>' + + '<li ng:bind="item.a" ng:repeat-index="0">C</li>' + + '<li ng:bind="item.a" ng:repeat-index="1">A</li>' + + '<li ng:bind="item.a" ng:repeat-index="2">B</li>' + '</ul>', sortedHtml(form)); items.shift(); a.scope.$eval(); assertEquals('<ul>' + '<#comment></#comment>' + - '<li ng-bind="item.a" ng-repeat-index="0">A</li>' + - '<li ng-bind="item.a" ng-repeat-index="1">B</li>' + + '<li ng:bind="item.a" ng:repeat-index="0">A</li>' + + '<li ng:bind="item.a" ng:repeat-index="1">B</li>' + '</ul>', sortedHtml(form)); items.shift(); @@ -212,19 +212,19 @@ BinderTest.prototype.testRepeaterUpdateBindings = function(){ }; BinderTest.prototype.testRepeaterContentDoesNotBind = function(){ - var a = this.compile('<ul><LI ng-repeat="item in model.items"><span ng-bind="item.a"></span></li></ul>'); + var a = this.compile('<ul><LI ng:repeat="item in model.items"><span ng:bind="item.a"></span></li></ul>'); a.scope.$set('model', {items:[{a:"A"}]}); a.scope.$eval(); assertEquals('<ul>' + '<#comment></#comment>' + - '<li ng-repeat-index="0"><span ng-bind="item.a">A</span></li>' + + '<li ng:repeat-index="0"><span ng:bind="item.a">A</span></li>' + '</ul>', sortedHtml(a.node)); }; BinderTest.prototype.testExpandEntityTag = function(){ assertEquals( - '<div ng-entity="Person" ng-watch="$anchor.a:1"></div>', - this.compileToHtml('<div ng-entity="Person" ng-watch="$anchor.a:1"/>')); + '<div ng-entity="Person" ng:watch="$anchor.a:1"></div>', + this.compileToHtml('<div ng-entity="Person" ng:watch="$anchor.a:1"/>')); }; BinderTest.prototype.testDoNotOverwriteCustomAction = function(){ @@ -233,7 +233,7 @@ BinderTest.prototype.testDoNotOverwriteCustomAction = function(){ }; BinderTest.prototype.testRepeaterAdd = function(){ - var c = this.compile('<div><input type="text" name="item.x" ng-repeat="item in items"></div>'); + var c = this.compile('<div><input type="text" name="item.x" ng:repeat="item in items"></div>'); var doc = c.node; c.scope.$set('items', [{x:'a'}, {x:'b'}]); c.scope.$eval(); @@ -248,7 +248,7 @@ BinderTest.prototype.testRepeaterAdd = function(){ }; BinderTest.prototype.testItShouldRemoveExtraChildrenWhenIteratingOverHash = function(){ - var c = this.compile('<div><div ng-repeat="i in items">{{i}}</div></div>'); + var c = this.compile('<div><div ng:repeat="i in items">{{i}}</div></div>'); var items = {}; c.scope.$set("items", items); @@ -308,8 +308,8 @@ BinderTest.prototype.testIfAttrBindingThrowsErrorDecorateTheAttribute = function }; BinderTest.prototype.testNestedRepeater = function() { - var a = this.compile('<div><div ng-repeat="m in model" name="{{m.name}}">' + - '<ul name="{{i}}" ng-repeat="i in m.item"></ul>' + + var a = this.compile('<div><div ng:repeat="m in model" name="{{m.name}}">' + + '<ul name="{{i}}" ng:repeat="i in m.item"></ul>' + '</div></div>'); a.scope.$set('model', [{name:'a', item:['a1', 'a2']}, {name:'b', item:['b1', 'b2']}]); @@ -317,20 +317,20 @@ BinderTest.prototype.testNestedRepeater = function() { assertEquals('<div>'+ '<#comment></#comment>'+ - '<div name="a" ng-bind-attr="{"name":"{{m.name}}"}" ng-repeat-index="0">'+ + '<div name="a" ng:bind-attr="{"name":"{{m.name}}"}" ng:repeat-index="0">'+ '<#comment></#comment>'+ - '<ul name="a1" ng-bind-attr="{"name":"{{i}}"}" ng-repeat-index="0"></ul>'+ - '<ul name="a2" ng-bind-attr="{"name":"{{i}}"}" ng-repeat-index="1"></ul>'+ + '<ul name="a1" ng:bind-attr="{"name":"{{i}}"}" ng:repeat-index="0"></ul>'+ + '<ul name="a2" ng:bind-attr="{"name":"{{i}}"}" ng:repeat-index="1"></ul>'+ '</div>'+ - '<div name="b" ng-bind-attr="{"name":"{{m.name}}"}" ng-repeat-index="1">'+ + '<div name="b" ng:bind-attr="{"name":"{{m.name}}"}" ng:repeat-index="1">'+ '<#comment></#comment>'+ - '<ul name="b1" ng-bind-attr="{"name":"{{i}}"}" ng-repeat-index="0"></ul>'+ - '<ul name="b2" ng-bind-attr="{"name":"{{i}}"}" ng-repeat-index="1"></ul>'+ + '<ul name="b1" ng:bind-attr="{"name":"{{i}}"}" ng:repeat-index="0"></ul>'+ + '<ul name="b2" ng:bind-attr="{"name":"{{i}}"}" ng:repeat-index="1"></ul>'+ '</div></div>', sortedHtml(a.node)); }; BinderTest.prototype.testHideBindingExpression = function() { - var a = this.compile('<div ng-hide="hidden == 3"/>'); + var a = this.compile('<div ng:hide="hidden == 3"/>'); a.scope.$set('hidden', 3); a.scope.$eval(); @@ -344,7 +344,7 @@ BinderTest.prototype.testHideBindingExpression = function() { }; BinderTest.prototype.testHideBinding = function() { - var c = this.compile('<div ng-hide="hidden"/>'); + var c = this.compile('<div ng:hide="hidden"/>'); c.scope.$set('hidden', 'true'); c.scope.$eval(); @@ -363,7 +363,7 @@ BinderTest.prototype.testHideBinding = function() { }; BinderTest.prototype.testShowBinding = function() { - var c = this.compile('<div ng-show="show"/>'); + var c = this.compile('<div ng:show="show"/>'); c.scope.$set('show', 'true'); c.scope.$eval(); @@ -382,42 +382,44 @@ BinderTest.prototype.testShowBinding = function() { }; BinderTest.prototype.testBindClassUndefined = function() { - var doc = this.compile('<div ng-class="undefined"/>'); + var doc = this.compile('<div ng:class="undefined"/>'); doc.scope.$eval(); assertEquals( - '<div class="undefined" ng-class="undefined"></div>', + '<div class="undefined" ng:class="undefined"></div>', sortedHtml(doc.node)); }; BinderTest.prototype.testBindClass = function() { - var c = this.compile('<div ng-class="class"/>'); + var c = this.compile('<div ng:class="class"/>'); c.scope.$set('class', 'testClass'); c.scope.$eval(); - assertEquals(sortedHtml(c.node), - '<div class="testClass" ng-class="class"></div>'); + assertEquals('<div class="testClass" ng:class="class"></div>', sortedHtml(c.node)); c.scope.$set('class', ['a', 'b']); c.scope.$eval(); - assertEquals(sortedHtml(c.node), - '<div class="a b" ng-class="class"></div>'); + assertEquals('<div class="a b" ng:class="class"></div>', sortedHtml(c.node)); }; BinderTest.prototype.testBindClassEvenOdd = function() { - var x = this.compile('<div><div ng-repeat="i in [0,1]" ng-class-even="\'e\'" ng-class-odd="\'o\'"/></div>'); + var x = this.compile('<div><div ng:repeat="i in [0,1]" ng:class-even="\'e\'" ng:class-odd="\'o\'"/></div>'); x.scope.$eval(); + var d1 = jqLite(x.node[0].childNodes[1]); + var d2 = jqLite(x.node[0].childNodes[2]); + expect(d1.hasClass('o')).toBeTruthy(); + expect(d2.hasClass('e')).toBeTruthy(); assertEquals( '<div><#comment></#comment>' + - '<div class="o" ng-class-even="\'e\'" ng-class-odd="\'o\'" ng-repeat-index="0"></div>' + - '<div class="e" ng-class-even="\'e\'" ng-class-odd="\'o\'" ng-repeat-index="1"></div></div>', + '<div class="o" ng:class-even="\'e\'" ng:class-odd="\'o\'" ng:repeat-index="0"></div>' + + '<div class="e" ng:class-even="\'e\'" ng:class-odd="\'o\'" ng:repeat-index="1"></div></div>', sortedHtml(x.node)); }; BinderTest.prototype.testBindStyle = function() { - var c = this.compile('<div ng-style="style"/>'); + var c = this.compile('<div ng:style="style"/>'); c.scope.$eval('style={color:"red"}'); c.scope.$eval(); @@ -430,7 +432,7 @@ BinderTest.prototype.testBindStyle = function() { BinderTest.prototype.testActionOnAHrefThrowsError = function(){ var model = {books:[]}; - var c = this.compile('<a ng-click="action()">Add Phone</a>', model); + var c = this.compile('<a ng:click="action()">Add Phone</a>', model); c.scope.action = function(){ throw {a:'abc', b:2}; }; @@ -450,23 +452,23 @@ BinderTest.prototype.testActionOnAHrefThrowsError = function(){ BinderTest.prototype.testShoulIgnoreVbNonBindable = function(){ var c = this.compile("<div>{{a}}" + - "<div ng-non-bindable>{{a}}</div>" + - "<div ng-non-bindable=''>{{b}}</div>" + - "<div ng-non-bindable='true'>{{c}}</div></div>"); + "<div ng:non-bindable>{{a}}</div>" + + "<div ng:non-bindable=''>{{b}}</div>" + + "<div ng:non-bindable='true'>{{c}}</div></div>"); c.scope.$set('a', 123); c.scope.$eval(); assertEquals('123{{a}}{{b}}{{c}}', c.node.text()); }; BinderTest.prototype.testOptionShouldUpdateParentToGetProperBinding = function() { - var c = this.compile('<select name="s"><option ng-repeat="i in [0,1]" value="{{i}}" ng-bind="i"></option></select>'); + var c = this.compile('<select name="s"><option ng:repeat="i in [0,1]" value="{{i}}" ng:bind="i"></option></select>'); c.scope.$set('s', 1); c.scope.$eval(); assertEquals(1, c.node[0].selectedIndex); }; BinderTest.prototype.testRepeaterShouldBindInputsDefaults = function () { - var c = this.compile('<div><input value="123" name="item.name" ng-repeat="item in items"></div>'); + var c = this.compile('<div><input value="123" name="item.name" ng:repeat="item in items"></div>'); c.scope.$set('items', [{}, {name:'misko'}]); c.scope.$eval(); @@ -474,19 +476,12 @@ BinderTest.prototype.testRepeaterShouldBindInputsDefaults = function () { assertEquals("misko", c.scope.$eval('items[1].name')); }; -BinderTest.prototype.testRepeaterShouldCreateArray = function () { - var c = this.compile('<input value="123" name="item.name" ng-repeat="item in items">'); - c.scope.$eval(); - - assertEquals(0, c.scope.$get('items').length); -}; - BinderTest.prototype.testShouldTemplateBindPreElements = function () { var c = this.compile('<pre>Hello {{name}}!</pre>'); c.scope.$set("name", "World"); c.scope.$eval(); - assertEquals('<pre ng-bind-template="Hello {{name}}!">Hello World!</pre>', sortedHtml(c.node)); + assertEquals('<pre ng:bind-template="Hello {{name}}!">Hello World!</pre>', sortedHtml(c.node)); }; BinderTest.prototype.testFillInOptionValueWhenMissing = function() { @@ -510,8 +505,8 @@ BinderTest.prototype.testFillInOptionValueWhenMissing = function() { }; BinderTest.prototype.testValidateForm = function() { - var c = this.compile('<div><input name="name" ng-required>' + - '<div ng-repeat="item in items"><input name="item.name" ng-required/></div></div>'); + var c = this.compile('<div><input name="name" ng:required>' + + '<div ng:repeat="item in items"><input name="item.name" ng:required/></div></div>'); var items = [{}, {}]; c.scope.$set("items", items); c.scope.$eval(); @@ -539,7 +534,7 @@ BinderTest.prototype.testValidateForm = function() { }; BinderTest.prototype.testValidateOnlyVisibleItems = function(){ - var c = this.compile('<div><input name="name" ng-required><input ng-show="show" name="name" ng-required></div>'); + var c = this.compile('<div><input name="name" ng:required><input ng:show="show" name="name" ng:required></div>'); jqLite(document.body).append(c.node); c.scope.$set("show", true); c.scope.$eval(); @@ -552,9 +547,9 @@ BinderTest.prototype.testValidateOnlyVisibleItems = function(){ BinderTest.prototype.testDeleteAttributeIfEvaluatesFalse = function() { var c = this.compile('<div>' + - '<input name="a0" ng-bind-attr="{disabled:\'{{true}}\'}"><input name="a1" ng-bind-attr="{disabled:\'{{false}}\'}">' + - '<input name="b0" ng-bind-attr="{disabled:\'{{1}}\'}"><input name="b1" ng-bind-attr="{disabled:\'{{0}}\'}">' + - '<input name="c0" ng-bind-attr="{disabled:\'{{[0]}}\'}"><input name="c1" ng-bind-attr="{disabled:\'{{[]}}\'}"></div>'); + '<input name="a0" ng:bind-attr="{disabled:\'{{true}}\'}"><input name="a1" ng:bind-attr="{disabled:\'{{false}}\'}">' + + '<input name="b0" ng:bind-attr="{disabled:\'{{1}}\'}"><input name="b1" ng:bind-attr="{disabled:\'{{0}}\'}">' + + '<input name="c0" ng:bind-attr="{disabled:\'{{[0]}}\'}"><input name="c1" ng:bind-attr="{disabled:\'{{[]}}\'}"></div>'); c.scope.$eval(); function assertChild(index, disabled) { var child = childNode(c.node, index); @@ -571,8 +566,8 @@ BinderTest.prototype.testDeleteAttributeIfEvaluatesFalse = function() { BinderTest.prototype.testItShouldDisplayErrorWhenActionIsSyntacticlyIncorect = function(){ var c = this.compile('<div>' + - '<input type="button" ng-click="greeting=\'ABC\'"/>' + - '<input type="button" ng-click=":garbage:"/></div>'); + '<input type="button" ng:click="greeting=\'ABC\'"/>' + + '<input type="button" ng:click=":garbage:"/></div>'); var first = jqLite(c.node[0].childNodes[0]); var second = jqLite(c.node[0].childNodes[1]); @@ -605,8 +600,8 @@ BinderTest.prototype.testItShouldSelectTheCorrectRadioBox = function() { BinderTest.prototype.testItShouldListenOnRightScope = function() { var c = this.compile( - '<ul ng-init="counter=0; gCounter=0" ng-watch="w:counter=counter+1">' + - '<li ng-repeat="n in [1,2,4]" ng-watch="w:counter=counter+1;w:$root.gCounter=$root.gCounter+n"/></ul>'); + '<ul ng:init="counter=0; gCounter=0" ng:watch="w:counter=counter+1">' + + '<li ng:repeat="n in [1,2,4]" ng:watch="w:counter=counter+1;w:$root.gCounter=$root.gCounter+n"/></ul>'); c.scope.$eval(); assertEquals(0, c.scope.$get("counter")); assertEquals(0, c.scope.$get("gCounter")); @@ -618,25 +613,25 @@ BinderTest.prototype.testItShouldListenOnRightScope = function() { }; BinderTest.prototype.testItShouldRepeatOnHashes = function() { - var x = this.compile('<ul><li ng-repeat="(k,v) in {a:0,b:1}" ng-bind=\"k + v\"></li></ul>'); + var x = this.compile('<ul><li ng:repeat="(k,v) in {a:0,b:1}" ng:bind=\"k + v\"></li></ul>'); x.scope.$eval(); assertEquals('<ul>' + '<#comment></#comment>' + - '<li ng-bind=\"k + v\" ng-repeat-index="0">a0</li>' + - '<li ng-bind=\"k + v\" ng-repeat-index="1">b1</li>' + + '<li ng:bind=\"k + v\" ng:repeat-index="0">a0</li>' + + '<li ng:bind=\"k + v\" ng:repeat-index="1">b1</li>' + '</ul>', sortedHtml(x.node)); }; BinderTest.prototype.testItShouldFireChangeListenersBeforeUpdate = function(){ - var x = this.compile('<div ng-bind="name"></div>'); + var x = this.compile('<div ng:bind="name"></div>'); x.scope.$set("name", ""); x.scope.$watch("watched", "name=123"); x.scope.$set("watched", "change"); x.scope.$eval(); assertEquals(123, x.scope.$get("name")); assertEquals( - '<div ng-bind="name">123</div>', + '<div ng:bind="name">123</div>', sortedHtml(x.node)); }; @@ -657,12 +652,12 @@ BinderTest.prototype.XtestItShouldRenderMultiRootHtmlInBinding = function() { x.scope.a = "a<b>c</b>d"; x.scope.$eval(); assertEquals( - '<div>before <span ng-bind="a|html">a<b>c</b>d</span>after</div>', + '<div>before <span ng:bind="a|html">a<b>c</b>d</span>after</div>', sortedHtml(x.node)); }; BinderTest.prototype.testItShouldUseFormaterForText = function() { - var x = this.compile('<input name="a" ng-format="list" value="a,b">'); + var x = this.compile('<input name="a" ng:format="list" value="a,b">'); x.scope.$eval(); assertEquals(['a','b'], x.scope.$get('a')); var input = x.node; diff --git a/test/BrowserSpecs.js b/test/BrowserSpecs.js index 3ce158b4..99632928 100644 --- a/test/BrowserSpecs.js +++ b/test/BrowserSpecs.js @@ -1,10 +1,14 @@ describe('browser', function(){ - var browser, location; + var browser, location, head; beforeEach(function(){ location = {href:"http://server", hash:""}; - browser = new Browser(location, {}); + head = { + scripts: [], + append: function(node){head.scripts.push(node);} + }; + browser = new Browser(location, jqLite(window.document), head); browser.setTimeout = noop; }); @@ -45,4 +49,22 @@ describe('browser', function(){ }); }); + describe('xhr', function(){ + describe('JSON', function(){ + it('should add script tag for request', function() { + var log = ""; + browser.xhr('JSON', 'http://example.org/path?cb=JSON_CALLBACK', function(code, data){ + log += code + ':' + data + ';'; + }); + expect(head.scripts.length).toEqual(1); + var url = head.scripts[0].src.split('?cb='); + expect(url[0]).toEqual('http://example.org/path'); + expect(typeof window[url[1]]).toEqual('function'); + window[url[1]]('data'); + expect(log).toEqual('200:data;'); + expect(typeof window[url[1]]).toEqual('undefined'); + }); + }); + }); + }); diff --git a/test/CompilerSpec.js b/test/CompilerSpec.js index da354ea5..3736ff4e 100644 --- a/test/CompilerSpec.js +++ b/test/CompilerSpec.js @@ -22,7 +22,7 @@ describe('compiler', function(){ }; textMarkup = []; attrMarkup = []; - widgets = {}; + widgets = extensionMap({}, 'widget'); compiler = new Compiler(textMarkup, attrMarkup, directives, widgets); compile = function(html){ var e = jqLite("<div>" + html + "</div>"); diff --git a/test/FiltersTest.js b/test/FiltersTest.js index f839bb51..903a7a2f 100644 --- a/test/FiltersTest.js +++ b/test/FiltersTest.js @@ -6,11 +6,11 @@ FiltersTest.prototype.testCurrency = function(){ var currency = bind(context, angular.filter.currency); assertEquals(currency(0), '$0.00'); - assertEquals(html.hasClass('ng-format-negative'), false); + assertEquals(html.hasClass('ng:format-negative'), false); assertEquals(currency(-999), '$-999.00'); - assertEquals(html.hasClass('ng-format-negative'), true); + assertEquals(html.hasClass('ng:format-negative'), true); assertEquals(currency(1234.5678), '$1,234.57'); - assertEquals(html.hasClass('ng-format-negative'), false); + assertEquals(html.hasClass('ng:format-negative'), false); }; FiltersTest.prototype.testFilterThisIsContext = function(){ diff --git a/test/ResourceSpec.js b/test/ResourceSpec.js index 2f285bcf..6e32ce18 100644 --- a/test/ResourceSpec.js +++ b/test/ResourceSpec.js @@ -28,13 +28,24 @@ describe("resource", function() { resource.route('URL').query(); }); + it('should ignore slashes of undefinend parameters', function(){ + var R = resource.route('/Path/:a/:b/:c'); + xhr.expectGET('/Path').respond({}); + xhr.expectGET('/Path/1').respond({}); + xhr.expectGET('/Path/2/3').respond({}); + xhr.expectGET('/Path/4/5/6').respond({}); + R.get({}); + R.get({a:1}); + R.get({a:2, b:3}); + R.get({a:4, b:5, c:6}); + }); + it("should build resource with default param", function(){ xhr.expectGET('/Order/123/Line/456.visa?minimum=0.05').respond({id:'abc'}); var LineItem = resource.route('/Order/:orderId/Line/:id:verb', {orderId: '123', id: '@id.key', verb:'.visa', minimum:0.05}); var item = LineItem.get({id:456}); xhr.flush(); nakedExpect(item).toEqual({id:'abc'}); - }); it("should create resource", function(){ @@ -66,8 +77,6 @@ describe("resource", function() { nakedExpect(cc).toEqual({id:{key:123}, name:'misko'}); expect(callback).wasNotCalled(); xhr.flush(); - nakedExpect(cc).toEqual({id:{key:123}, name:'rama'}); - expect(callback).wasCalledWith(cc); }); it("should query resource", function(){ @@ -138,6 +147,23 @@ describe("resource", function() { expect(person.name).toEqual('misko'); }); + it('should return the same object when verifying the cache', function(){ + var scope = angular.compile('<div></div>'); + var Person = scope.$resource('/Person/:id', null, {query: {method:'GET', isArray: true, verifyCache: true}}); + scope.$browser.xhr.expectGET('/Person/123').respond('[\n{\nname:\n"misko"\n}\n]'); + var person = Person.query({id:123}); + scope.$browser.xhr.flush(); + expect(person[0].name).toEqual('misko'); + + scope.$browser.xhr.expectGET('/Person/123').respond('[\n{\nname:\n"rob"\n}\n]'); + var person2 = Person.query({id:123}); + expect(person2[0].name).toEqual('misko'); + var person2Cache = person2; + scope.$browser.xhr.flush(); + expect(person2Cache).toEqual(person2); + expect(person2[0].name).toEqual('rob'); + }); + describe('failure mode', function(){ it('should report error when non 200', function(){ xhr.expectGET('/CreditCard/123').respond(500, "Server Error"); diff --git a/test/ScenarioSpec.js b/test/ScenarioSpec.js index 9afe8e95..7ea3192d 100644 --- a/test/ScenarioSpec.js +++ b/test/ScenarioSpec.js @@ -1,6 +1,6 @@ describe("ScenarioSpec: Compilation", function(){ it("should compile dom node and return scope", function(){ - var node = jqLite('<div ng-init="a=1">{{b=a+1}}</div>')[0]; + var node = jqLite('<div ng:init="a=1">{{b=a+1}}</div>')[0]; var scope = compile(node); scope.$init(); expect(scope.a).toEqual(1); diff --git a/test/ScopeSpec.js b/test/ScopeSpec.js index d93400e5..6f5485e7 100644 --- a/test/ScopeSpec.js +++ b/test/ScopeSpec.js @@ -15,12 +15,17 @@ describe('scope/model', function(){ expect(model.$root).toEqual(model); }); + it('should return noop function when LHS is undefined', function(){ + var model = createScope(); + expect(model.$eval('x.$filter()')).toEqual(undefined); + }); + describe('$eval', function(){ - it('should eval function with correct this and pass arguments', function(){ + it('should eval function with correct this', function(){ var model = createScope(); - model.$eval(function(name){ - this.name = name; - }, 'works'); + model.$eval(function(){ + this.name = 'works'; + }); expect(model.name).toEqual('works'); }); diff --git a/test/ValidatorsTest.js b/test/ValidatorsTest.js index 573c340d..2e156f84 100644 --- a/test/ValidatorsTest.js +++ b/test/ValidatorsTest.js @@ -7,7 +7,7 @@ ValidatorTest.prototype.testItShouldHaveThisSet = function() { validator.last = last; validator._this = this; }; - var scope = compile('<input name="name" ng-validate="myValidator:\'hevery\'"/>'); + var scope = compile('<input name="name" ng:validate="myValidator:\'hevery\'"/>'); scope.name = 'misko'; scope.$init(); assertEquals('misko', validator.first); @@ -109,7 +109,7 @@ describe('Validator:asynchronous', function(){ it('should make a request and show spinner', function(){ var value, fn; - var scope = compile('<input type="text" name="name" ng-validate="asynchronous:asyncFn"/>'); + var scope = compile('<input type="text" name="name" ng:validate="asynchronous:asyncFn"/>'); scope.$init(); var input = scope.$element; scope.asyncFn = function(v,f){ @@ -151,7 +151,7 @@ describe('Validator:asynchronous', function(){ }); it("should handle update function", function(){ - var scope = angular.compile('<input name="name" ng-validate="asynchronous:asyncFn:updateFn"/>'); + var scope = angular.compile('<input name="name" ng:validate="asynchronous:asyncFn:updateFn"/>'); scope.asyncFn = jasmine.createSpy(); scope.updateFn = jasmine.createSpy(); scope.name = 'misko'; diff --git a/test/angular-mocks.js b/test/angular-mocks.js index 8838b2cd..bac2e800 100644 --- a/test/angular-mocks.js +++ b/test/angular-mocks.js @@ -66,6 +66,7 @@ function MockBrowser() { self.xhr.expectPOST = angular.bind(self, self.xhr.expect, 'POST'); self.xhr.expectDELETE = angular.bind(self, self.xhr.expect, 'DELETE'); self.xhr.expectPUT = angular.bind(self, self.xhr.expect, 'PUT'); + self.xhr.expectJSON = angular.bind(self, self.xhr.expect, 'JSON'); self.xhr.flush = function() { while(requests.length) { requests.pop()(); diff --git a/test/delete/ScopeTest.js b/test/delete/ScopeTest.js deleted file mode 100644 index 24febf19..00000000 --- a/test/delete/ScopeTest.js +++ /dev/null @@ -1,145 +0,0 @@ -ScopeTest = TestCase('ScopeTest'); - -ScopeTest.prototype.testGetScopeRetrieval = function(){ - var scope = {}; - var form = jQuery("<a><b><c></c></b></a>"); - form.data('scope', scope); - var c = form.find('c'); - assertTrue(scope === c.scope()); -}; - -ScopeTest.prototype.testGetScopeRetrievalIntermediateNode = function(){ - var scope = {}; - var form = jQuery("<a><b><c></c></b></a>"); - form.find("b").data('scope', scope); - var b = form.find('b'); - assertTrue(scope === b.scope()); -}; - -ScopeTest.prototype.testNoScopeDoesNotCauseInfiniteRecursion = function(){ - var form = jQuery("<a><b><c></c></b></a>"); - var c = form.find('c'); - assertTrue(!c.scope()); -}; - -ScopeTest.prototype.testScopeEval = function(){ - var scope = new Scope({b:345}); - assertEquals(scope.eval('b = 123'), 123); - assertEquals(scope.get('b'), 123); -}; - -ScopeTest.prototype.testScopeFromPrototype = function(){ - var scope = new Scope({b:123}); - scope.eval('a = b'); - scope.eval('b = 456'); - assertEquals(scope.get('a'), 123); - assertEquals(scope.get('b'), 456); -}; - -ScopeTest.prototype.testSetScopeGet = function(){ - var scope = new Scope(); - assertEquals(987, scope.set('a', 987)); - assertEquals(scope.get('a'), 987); - assertEquals(scope.eval('a'), 987); -}; - -ScopeTest.prototype.testGetChain = function(){ - var scope = new Scope({a:{b:987}}); - assertEquals(scope.get('a.b'), 987); - assertEquals(scope.eval('a.b'), 987); -}; - -ScopeTest.prototype.testGetUndefinedChain = function(){ - var scope = new Scope(); - assertEquals(typeof scope.get('a.b'), 'undefined'); -}; - -ScopeTest.prototype.testSetChain = function(){ - var scope = new Scope({a:{}}); - scope.set('a.b', 987); - assertEquals(scope.get('a.b'), 987); - assertEquals(scope.eval('a.b'), 987); -}; - -ScopeTest.prototype.testSetGetOnChain = function(){ - var scope = new Scope(); - scope.set('a.b', 987); - assertEquals(scope.get('a.b'), 987); - assertEquals(scope.eval('a.b'), 987); -}; - -ScopeTest.prototype.testGlobalFunctionAccess =function(){ - window['scopeAddTest'] = function (a, b) {return a+b;}; - var scope = new Scope({window:window}); - assertEquals(scope.eval('window.scopeAddTest(1,2)'), 3); - - scope.set('add', function (a, b) {return a+b;}); - assertEquals(scope.eval('add(1,2)'), 3); - - scope.set('math.add', function (a, b) {return a+b;}); - assertEquals(scope.eval('math.add(1,2)'), 3); -}; - -ScopeTest.prototype.testValidationEval = function(){ - expectAsserts(4); - var scope = new Scope(); - scope.set("name", "misko"); - angular.validator.testValidator = function(value, expect){ - assertEquals("misko", this.name); - return value == expect ? null : "Error text"; - }; - - assertEquals("Error text", scope.validate("testValidator:'abc'", 'x')); - assertEquals(null, scope.validate("testValidator:'abc'", 'abc')); - - delete angular.validator['testValidator']; -}; - -ScopeTest.prototype.testCallingNonExistantMethodShouldProduceFriendlyException = function() { - expectAsserts(1); - var scope = new Scope({obj:{}}); - try { - scope.eval("obj.iDontExist()"); - fail(); - } catch (e) { - assertEquals("Expression 'obj.iDontExist' is not a function.", e); - } -}; - -ScopeTest.prototype.testAccessingWithInvalidPathShouldThrowError = function() { - var scope = new Scope(); - try { - scope.get('a.{{b}}'); - fail(); - } catch (e) { - assertEquals("Expression 'a.{{b}}' is not a valid expression for accesing variables.", e); - } -}; - -ScopeTest.prototype.testItShouldHave$parent = function() { - var parent = new Scope({}, "ROOT"); - var child = new Scope(parent.state); - assertSame("parent", child.state.$parent, parent.state); - assertSame("root", child.state.$root, parent.state); -}; - -ScopeTest.prototype.testItShouldHave$root = function() { - var scope = new Scope({}, "ROOT"); - assertSame(scope.state.$root, scope.state); -}; - -ScopeTest.prototype.testItShouldBuildPathOnUndefined = function(){ - var scope = new Scope({}, "ROOT"); - scope.setEval("a.$b.c", 1); - assertJsonEquals({$b:{c:1}}, scope.get("a")); -}; - -ScopeTest.prototype.testItShouldMapUnderscoreFunctions = function(){ - var scope = new Scope({}, "ROOT"); - scope.set("a", [1,2,3]); - assertEquals('function', typeof scope.get("a.$size")); - scope.eval("a.$includeIf(4,true)"); - assertEquals(4, scope.get("a.$size")()); - assertEquals(4, scope.eval("a.$size()")); - assertEquals('undefined', typeof scope.get("a.dontExist")); -}; diff --git a/test/delete/WidgetsTest.js b/test/delete/WidgetsTest.js deleted file mode 100644 index 313d7372..00000000 --- a/test/delete/WidgetsTest.js +++ /dev/null @@ -1,268 +0,0 @@ -WidgetTest = TestCase('WidgetTest'); - -WidgetTest.prototype.testRequired = function () { - var view = $('<input name="a" ng-required>'); - var scope = new Scope({$invalidWidgets:[]}); - var cntl = new TextController(view[0], 'a', angularFormatter.noop); - cntl.updateView(scope); - assertTrue(view.hasClass('ng-validation-error')); - assertEquals("Required Value", view.attr('ng-error')); - scope.set('a', 'A'); - cntl.updateView(scope); - assertFalse(view.hasClass('ng-validation-error')); - assertEquals("undefined", typeof view.attr('ng-error')); -}; - -WidgetTest.prototype.testValidator = function () { - var view = $('<input name="a" ng-validate="testValidator:\'ABC\'">'); - var scope = new Scope({$invalidWidgets:[]}); - var cntl = new TextController(view[0], 'a', angularFormatter.noop); - angular.validator.testValidator = function(value, expect){ - return value == expect ? false : "Error text"; - }; - - scope.set('a', ''); - cntl.updateView(scope); - assertEquals(view.hasClass('ng-validation-error'), false); - assertEquals(null, view.attr('ng-error')); - - scope.set('a', 'X'); - cntl.updateView(scope); - assertEquals(view.hasClass('ng-validation-error'), true); - assertEquals(view.attr('ng-error'), "Error text"); - assertEquals("Error text", view.attr('ng-error')); - - scope.set('a', 'ABC'); - cntl.updateView(scope); - assertEquals(view.hasClass('ng-validation-error'), false); - assertEquals(view.attr('ng-error'), null); - assertEquals(null, view.attr('ng-error')); - - delete angular.validator['testValidator']; -}; - -WidgetTest.prototype.testRequiredValidator = function () { - var view = $('<input name="a" ng-required ng-validate="testValidator:\'ABC\'">'); - var scope = new Scope({$invalidWidgets:[]}); - var cntl = new TextController(view[0], 'a', angularFormatter.noop); - angular.validator.testValidator = function(value, expect){ - return value == expect ? null : "Error text"; - }; - - scope.set('a', ''); - cntl.updateView(scope); - assertEquals(view.hasClass('ng-validation-error'), true); - assertEquals("Required Value", view.attr('ng-error')); - - scope.set('a', 'X'); - cntl.updateView(scope); - assertEquals(view.hasClass('ng-validation-error'), true); - assertEquals("Error text", view.attr('ng-error')); - - scope.set('a', 'ABC'); - cntl.updateView(scope); - assertEquals(view.hasClass('ng-validation-error'), false); - assertEquals(null, view.attr('ng-error')); - - delete angular.validator['testValidator']; -}; - -TextControllerTest = TestCase("TextControllerTest"); - -TextControllerTest.prototype.testDatePicker = function() { - var input = $('<input type="text" ng-widget="datepicker">'); - input.data('scope', new Scope()); - var body = $(document.body); - body.append(input); - var binder = new Binder(input[0], new WidgetFactory()); - assertTrue('before', input.data('datepicker') === undefined); - binder.compile(); - assertTrue('after', input.data('datepicker') !== null); - assertTrue(body.html(), input.hasClass('hasDatepicker')); -}; - -RepeaterUpdaterTest = TestCase("RepeaterUpdaterTest"); - -RepeaterUpdaterTest.prototype.testRemoveThenAdd = function() { - var view = $("<div><span/></div>"); - var template = function () { - return $("<li/>"); - }; - var repeater = new RepeaterUpdater(view.find("span"), "a in b", template, ""); - var scope = new Scope(); - scope.set('b', [1,2]); - - repeater.updateView(scope); - - scope.set('b', []); - repeater.updateView(scope); - - scope.set('b', [1]); - repeater.updateView(scope); - assertEquals(1, view.find("li").size()); -}; - -RepeaterUpdaterTest.prototype.testShouldBindWidgetOnRepeaterClone = function(){ - //fail(); -}; - -RepeaterUpdaterTest.prototype.testShouldThrowInformativeSyntaxError= function(){ - expectAsserts(1); - try { - var repeater = new RepeaterUpdater(null, "a=b"); - } catch (e) { - assertEquals("Expected ng-repeat in form of 'item in collection' but got 'a=b'.", e); - } -}; - -SelectControllerTest = TestCase("SelectControllerTest"); - -SelectControllerTest.prototype.testShouldUpdateModelNullOnNothingSelected = function(){ - var scope = new Scope(); - var view = {selectedIndex:-1, options:[]}; - var cntl = new SelectController(view, 'abc'); - cntl.updateModel(scope); - assertNull(scope.get('abc')); -}; - -SelectControllerTest.prototype.testShouldUpdateModelWhenNothingSelected = function(){ - var scope = new Scope(); - var view = {value:'123'}; - var cntl = new SelectController(view, 'abc'); - cntl.updateView(scope); - assertEquals("123", scope.get('abc')); -}; - -BindUpdaterTest = TestCase("BindUpdaterTest"); - -BindUpdaterTest.prototype.testShouldDisplayNothingForUndefined = function () { - var view = $('<span />'); - var controller = new BindUpdater(view[0], "{{a}}"); - var scope = new Scope(); - - scope.set('a', undefined); - controller.updateView(scope); - assertEquals("", view.text()); - - scope.set('a', null); - controller.updateView(scope); - assertEquals("", view.text()); -}; - -BindUpdaterTest.prototype.testShouldDisplayJsonForNonStrings = function () { - var view = $('<span />'); - var controller = new BindUpdater(view[0], "{{obj}}"); - - controller.updateView(new Scope({obj:[]})); - assertEquals("[]", view.text()); - - controller.updateView(new Scope({obj:{text:'abc'}})); - assertEquals('abc', fromJson(view.text()).text); -}; - - -BindUpdaterTest.prototype.testShouldInsertHtmlNode = function () { - var view = $('<span />'); - var controller = new BindUpdater(view[0], "<fake>&{{obj}}</fake>"); - var scope = new Scope(); - - scope.set("obj", $('<div>myDiv</div>')[0]); - controller.updateView(scope); - assertEquals("<fake>&myDiv</fake>", view.text()); -}; - - -BindUpdaterTest.prototype.testShouldDisplayTextMethod = function () { - var view = $('<div />'); - var controller = new BindUpdater(view[0], "{{obj}}"); - var scope = new Scope(); - - scope.set("obj", new angular.filter.Meta({text:function(){return "abc";}})); - controller.updateView(scope); - assertEquals("abc", view.text()); - - scope.set("obj", new angular.filter.Meta({text:"123"})); - controller.updateView(scope); - assertEquals("123", view.text()); - - scope.set("obj", {text:"123"}); - controller.updateView(scope); - assertEquals("123", fromJson(view.text()).text); -}; - -BindUpdaterTest.prototype.testShouldDisplayHtmlMethod = function () { - var view = $('<div />'); - var controller = new BindUpdater(view[0], "{{obj}}"); - var scope = new Scope(); - - scope.set("obj", new angular.filter.Meta({html:function(){return "a<div>b</div>c";}})); - controller.updateView(scope); - assertEquals("abc", view.text()); - - scope.set("obj", new angular.filter.Meta({html:"1<div>2</div>3"})); - controller.updateView(scope); - assertEquals("123", view.text()); - - scope.set("obj", {html:"123"}); - controller.updateView(scope); - assertEquals("123", fromJson(view.text()).html); -}; - -BindUpdaterTest.prototype.testUdateBoolean = function() { - var view = $('<div />'); - var controller = new BindUpdater(view[0], "{{true}}, {{false}}"); - controller.updateView(new Scope()); - assertEquals('true, false', view.text()); -}; - -BindAttrUpdaterTest = TestCase("BindAttrUpdaterTest"); - -BindAttrUpdaterTest.prototype.testShouldLoadBlankImageWhenBindingIsUndefined = function () { - var view = $('<img />'); - var controller = new BindAttrUpdater(view[0], {src: '{{imageUrl}}'}); - - var scope = new Scope(); - scope.set('imageUrl', undefined); - scope.set('$config.blankImage', 'http://server/blank.gif'); - - controller.updateView(scope); - assertEquals("http://server/blank.gif", view.attr('src')); -}; - -RepeaterUpdaterTest.prototype.testShouldNotDieWhenRepeatExpressionIsNull = function() { - var rep = new RepeaterUpdater(null, "$item in items", null, null); - var scope = new Scope(); - scope.set('items', undefined); - rep.updateView(scope); -}; - -RepeaterUpdaterTest.prototype.testShouldIterateOverKeys = function() { - var rep = new RepeaterUpdater(null, "($k,_v) in items", null, null); - assertEquals("items", rep.iteratorExp); - assertEquals("_v", rep.valueExp); - assertEquals("$k", rep.keyExp); -}; - -EvalUpdaterTest = TestCase("EvalUpdaterTest"); -EvalUpdaterTest.prototype.testEvalThrowsException = function(){ - var view = $('<div/>'); - var eval = new EvalUpdater(view[0], 'undefined()'); - - eval.updateView(new Scope()); - assertTrue(!!view.attr('ng-error')); - assertTrue(view.hasClass('ng-exception')); - - eval.exp = "1"; - eval.updateView(new Scope()); - assertFalse(!!view.attr('ng-error')); - assertFalse(view.hasClass('ng-exception')); -}; - -RadioControllerTest = TestCase("RadioController"); -RadioControllerTest.prototype.testItShouldTreatTrueStringAsBoolean = function () { - var view = $('<input type="radio" name="select" value="true"/>'); - var radio = new RadioController(view[0], 'select'); - var scope = new Scope({select:true}); - radio.updateView(scope); - assertTrue(view[0].checked); -}; diff --git a/test/directivesSpec.js b/test/directivesSpec.js index 42869a05..8a7da41d 100644 --- a/test/directivesSpec.js +++ b/test/directivesSpec.js @@ -17,57 +17,82 @@ describe("directives", function(){ expect(size(jqCache)).toEqual(0); }); - it("should ng-init", function() { - var scope = compile('<div ng-init="a=123"></div>'); + it("should ng:init", function() { + var scope = compile('<div ng:init="a=123"></div>'); expect(scope.a).toEqual(123); }); - it("should ng-eval", function() { - var scope = compile('<div ng-init="a=0" ng-eval="a = a + 1"></div>'); + it("should ng:eval", function() { + var scope = compile('<div ng:init="a=0" ng:eval="a = a + 1"></div>'); expect(scope.a).toEqual(1); scope.$eval(); expect(scope.a).toEqual(2); }); - it('should ng-bind', function() { - var scope = compile('<div ng-bind="a"></div>'); - expect(element.text()).toEqual(''); - scope.a = 'misko'; - scope.$eval(); - expect(element.text()).toEqual('misko'); - }); + describe('ng:bind', function(){ + it('should set text', function() { + var scope = compile('<div ng:bind="a"></div>'); + expect(element.text()).toEqual(''); + scope.a = 'misko'; + scope.$eval(); + 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(lowercase(element.html())).toEqual('<div>hello</div>'); - }); + it('should set html', function() { + var scope = compile('<div ng:bind="html|html"></div>'); + scope.html = '<div>hello</div>'; + scope.$eval(); + expect(lowercase(element.html())).toEqual('<div>hello</div>'); + }); + + it('should set element element', function() { + angularFilter.myElement = function() { + return jqLite('<a>hello</a>'); + }; + var scope = compile('<div ng:bind="0|myElement"></div>'); + scope.$eval(); + expect(lowercase(element.html())).toEqual('<a>hello</a>'); + }); + + it('should have $element set to current bind element', function(){ + angularFilter.myFilter = function(){ + this.$element.text('HELLO'); + }; + var scope = compile('<div>before<div ng:bind="0|myFilter"></div>after</div>'); + expect(scope.$element.text()).toEqual("beforeHELLOafter"); + }); - it('should ng-bind element', function() { - angularFilter.myElement = function() { - return jqLite('<a>hello</a>'); - }; - var scope = compile('<div ng-bind="0|myElement"></div>'); - scope.$eval(); - expect(lowercase(element.html())).toEqual('<a>hello</a>'); }); - it('should ng-bind-template', function() { - var scope = compile('<div ng-bind-template="Hello {{name}}!"></div>'); - scope.$set('name', 'Misko'); - scope.$eval(); - expect(element.text()).toEqual('Hello Misko!'); + describe('ng:bind-template', function(){ + it('should ng:bind-template', function() { + var scope = compile('<div ng:bind-template="Hello {{name}}!"></div>'); + scope.$set('name', 'Misko'); + scope.$eval(); + expect(element.text()).toEqual('Hello Misko!'); + }); + + it('should have $element set to current bind element', function(){ + var innerText = 'blank'; + angularFilter.myFilter = function(text){ + innerText = this.$element.text(); + return text; + }; + var scope = compile('<div>before<span ng:bind-template="{{\'HELLO\'|myFilter}}">INNER</span>after</div>'); + expect(scope.$element.text()).toEqual("beforeHELLOafter"); + expect(innerText).toEqual('INNER'); + }); + }); - it('should ng-bind-attr', function(){ - var scope = compile('<img ng-bind-attr="{src:\'http://localhost/mysrc\', alt:\'myalt\'}"/>'); + it('should ng:bind-attr', function(){ + var scope = compile('<img ng:bind-attr="{src:\'http://localhost/mysrc\', alt:\'myalt\'}"/>'); expect(element.attr('src')).toEqual('http://localhost/mysrc'); expect(element.attr('alt')).toEqual('myalt'); }); it('should remove special attributes on false', function(){ - var scope = compile('<input ng-bind-attr="{disabled:\'{{disabled}}\', readonly:\'{{readonly}}\', checked:\'{{checked}}\'}"/>'); + var scope = compile('<input ng:bind-attr="{disabled:\'{{disabled}}\', readonly:\'{{readonly}}\', checked:\'{{checked}}\'}"/>'); var input = scope.$element[0]; expect(input.disabled).toEqual(false); expect(input.readOnly).toEqual(false); @@ -83,51 +108,48 @@ describe("directives", function(){ expect(input.checked).toEqual(true); }); - it('should ng-non-bindable', function(){ - var scope = compile('<div ng-non-bindable><span ng-bind="name"></span></div>'); + it('should ng:non-bindable', function(){ + var scope = compile('<div ng:non-bindable><span ng:bind="name"></span></div>'); scope.$set('name', 'misko'); scope.$eval(); expect(element.text()).toEqual(''); }); - it('should ng-repeat over array', function(){ - var scope = compile('<ul><li ng-repeat="item in items" ng-init="suffix = \';\'" ng-bind="item + suffix"></li></ul>'); + it('should ng:repeat over array', function(){ + var scope = compile('<ul><li ng:repeat="item in items" ng:init="suffix = \';\'" ng:bind="item + suffix"></li></ul>'); - scope.$set('items', ['misko', 'shyam']); + Array.prototype.extraProperty = "should be ignored"; + scope.items = ['misko', 'shyam']; scope.$eval(); expect(element.text()).toEqual('misko;shyam;'); + delete Array.prototype.extraProperty; - scope.$set('items', ['adam', 'kai', 'brad']); + scope.items = ['adam', 'kai', 'brad']; scope.$eval(); expect(element.text()).toEqual('adam;kai;brad;'); - scope.$set('items', ['brad']); + scope.items = ['brad']; scope.$eval(); expect(element.text()).toEqual('brad;'); }); - it('should ng-repeat over object', function(){ - var scope = compile('<ul><li ng-repeat="(key, value) in items" ng-bind="key + \':\' + value + \';\' "></li></ul>'); + it('should ng:repeat over object', function(){ + var scope = compile('<ul><li ng:repeat="(key, value) in items" ng:bind="key + \':\' + value + \';\' "></li></ul>'); scope.$set('items', {misko:'swe', shyam:'set'}); scope.$eval(); expect(element.text()).toEqual('misko:swe;shyam:set;'); }); - it('should set ng-repeat to [] if undefinde', function(){ - var scope = compile('<ul><li ng-repeat="item in items"></li></ul>'); - expect(scope.items).toEqual([]); - }); - - it('should error on wrong parsing of ng-repeat', function(){ - var scope = compile('<ul><li ng-repeat="i dont parse"></li></ul>'); + it('should error on wrong parsing of ng:repeat', function(){ + var scope = compile('<ul><li ng:repeat="i dont parse"></li></ul>'); var log = ""; log += element.attr('ng-exception') + ';'; log += element.hasClass('ng-exception') + ';'; - expect(log).toEqual("\"Expected ng-repeat in form of 'item in collection' but got 'i dont parse'.\";true;"); + expect(log).toEqual("\"Expected ng:repeat in form of 'item in collection' but got 'i dont parse'.\";true;"); }); - it('should ng-watch', function(){ - var scope = compile('<div ng-watch="i: count = count + 1" ng-init="count = 0">'); + it('should ng:watch', function(){ + var scope = compile('<div ng:watch="i: count = count + 1" ng:init="count = 0">'); scope.$eval(); scope.$eval(); expect(scope.$get('count')).toEqual(0); @@ -138,8 +160,8 @@ describe("directives", function(){ expect(scope.$get('count')).toEqual(1); }); - it('should ng-click', function(){ - var scope = compile('<div ng-click="clicked = true"></div>'); + it('should ng:click', function(){ + var scope = compile('<div ng:click="clicked = true"></div>'); scope.$eval(); expect(scope.$get('clicked')).toBeFalsy(); @@ -147,16 +169,16 @@ describe("directives", function(){ expect(scope.$get('clicked')).toEqual(true); }); - it('should ng-class', function(){ - var scope = compile('<div class="existing" ng-class="[\'A\', \'B\']"></div>'); + it('should ng:class', function(){ + var scope = compile('<div class="existing" ng:class="[\'A\', \'B\']"></div>'); scope.$eval(); expect(element.hasClass('existing')).toBeTruthy(); expect(element.hasClass('A')).toBeTruthy(); expect(element.hasClass('B')).toBeTruthy(); }); - it('should ng-class odd/even', function(){ - var scope = compile('<ul><li ng-repeat="i in [0,1]" class="existing" ng-class-odd="\'odd\'" ng-class-even="\'even\'"></li><ul>'); + it('should ng:class odd/even', function(){ + var scope = compile('<ul><li ng:repeat="i in [0,1]" class="existing" ng:class-odd="\'odd\'" ng:class-even="\'even\'"></li><ul>'); scope.$eval(); var e1 = jqLite(element[0].childNodes[1]); var e2 = jqLite(element[0].childNodes[2]); @@ -166,14 +188,34 @@ describe("directives", function(){ expect(e2.hasClass('even')).toBeTruthy(); }); - it('should ng-style', function(){ - var scope = compile('<div ng-style="{color:\'red\'}"></div>'); - scope.$eval(); - expect(element.css('color')).toEqual('red'); + describe('ng:style', function(){ + it('should set', function(){ + var scope = compile('<div ng:style="{color:\'red\'}"></div>'); + scope.$eval(); + expect(element.css('color')).toEqual('red'); + }); + + it('should silently ignore undefined style', function() { + var scope = compile('<div ng:style="myStyle"></div>'); + scope.$eval(); + expect(element.hasClass('ng-exception')).toBeFalsy(); + }); + + it('should preserve and remove previous style', function(){ + var scope = compile('<div style="color:red;" ng:style="myStyle"></div>'); + scope.$eval(); + expect(getStyle(element)).toEqual({color:'red'}); + scope.myStyle = {color:'blue', width:'10px'}; + scope.$eval(); + expect(getStyle(element)).toEqual({color:'blue', width:'10px'}); + scope.myStyle = {}; + scope.$eval(); + expect(getStyle(element)).toEqual({color:'red'}); + }); }); - it('should ng-show', function(){ - var scope = compile('<div ng-hide="hide"></div>'); + it('should ng:show', function(){ + var scope = compile('<div ng:hide="hide"></div>'); scope.$eval(); expect(isCssVisible(scope.$element)).toEqual(true); scope.$set('hide', true); @@ -181,8 +223,8 @@ describe("directives", function(){ expect(isCssVisible(scope.$element)).toEqual(false); }); - it('should ng-hide', function(){ - var scope = compile('<div ng-show="show"></div>'); + it('should ng:hide', function(){ + var scope = compile('<div ng:show="show"></div>'); scope.$eval(); expect(isCssVisible(scope.$element)).toEqual(false); scope.$set('show', true); @@ -190,7 +232,7 @@ describe("directives", function(){ expect(isCssVisible(scope.$element)).toEqual(true); }); - describe('ng-controller', function(){ + describe('ng:controller', function(){ it('should bind', function(){ window.Greeter = function(){ this.greeting = 'hello'; @@ -203,18 +245,18 @@ describe("directives", function(){ return this.greeting + ' ' + name + this.suffix; } }; - var scope = compile('<div ng-controller="Greeter"></div>'); + var scope = compile('<div ng:controller="Greeter"></div>'); expect(scope.greeting).toEqual('hello'); expect(scope.greet('misko')).toEqual('hello misko!'); window.Greeter = undefined; }); }); - it('should eval things according to ng-eval-order', function(){ + it('should eval things according to ng:eval-order', function(){ var scope = compile( - '<div ng-init="log=\'\'">' + + '<div ng:init="log=\'\'">' + '{{log = log + \'e\'}}' + - '<span ng-eval-order="first" ng-eval="log = log + \'a\'">' + + '<span ng:eval-order="first" ng:eval="log = log + \'a\'">' + '{{log = log + \'b\'}}' + '<span src="{{log = log + \'c\'}}"></span>' + '<span bind-template="{{log = log + \'d\'}}"></span>' + diff --git a/test/markupSpec.js b/test/markupSpec.js index 8358b673..5fe5bba9 100644 --- a/test/markupSpec.js +++ b/test/markupSpec.js @@ -20,23 +20,23 @@ describe("markups", function(){ it('should translate {{}} in text', function(){ compile('<div>hello {{name}}!</div>'); - expect(sortedHtml(element)).toEqual('<div>hello <span ng-bind="name"></span>!</div>'); + expect(sortedHtml(element)).toEqual('<div>hello <span ng:bind="name"></span>!</div>'); scope.$set('name', 'Misko'); scope.$eval(); - expect(sortedHtml(element)).toEqual('<div>hello <span ng-bind="name">Misko</span>!</div>'); + expect(sortedHtml(element)).toEqual('<div>hello <span ng:bind="name">Misko</span>!</div>'); }); it('should translate {{}} in terminal nodes', function(){ compile('<select name="x"><option value="">Greet {{name}}!</option></select>'); - expect(sortedHtml(element).replace(' selected="true"', '')).toEqual('<select name="x"><option ng-bind-template="Greet {{name}}!">Greet !</option></select>'); + expect(sortedHtml(element).replace(' selected="true"', '')).toEqual('<select name="x"><option ng:bind-template="Greet {{name}}!">Greet !</option></select>'); scope.$set('name', 'Misko'); scope.$eval(); - expect(sortedHtml(element).replace(' selected="true"', '')).toEqual('<select name="x"><option ng-bind-template="Greet {{name}}!">Greet Misko!</option></select>'); + expect(sortedHtml(element).replace(' selected="true"', '')).toEqual('<select name="x"><option ng:bind-template="Greet {{name}}!">Greet Misko!</option></select>'); }); it('should translate {{}} in attributes', function(){ compile('<img src="http://server/{{path}}.png"/>'); - expect(element.attr('ng-bind-attr')).toEqual('{"src":"http://server/{{path}}.png"}'); + expect(element.attr('ng:bind-attr')).toEqual('{"src":"http://server/{{path}}.png"}'); scope.$set('path', 'a/b'); scope.$eval(); expect(element.attr('src')).toEqual("http://server/a/b.png"); diff --git a/test/moveToAngularCom/Base64Test.js b/test/moveToAngularCom/Base64Test.js deleted file mode 100644 index a9353186..00000000 --- a/test/moveToAngularCom/Base64Test.js +++ /dev/null @@ -1,5 +0,0 @@ -Base64Test = TestCase('Base64Test'); - -Base64Test.prototype.testEncodeDecode = function(){ - assertEquals(Base64.decode(Base64.encode('hello')), 'hello'); -}; diff --git a/test/moveToAngularCom/DataStoreTest.js b/test/moveToAngularCom/DataStoreTest.js deleted file mode 100644 index 87c5be2e..00000000 --- a/test/moveToAngularCom/DataStoreTest.js +++ /dev/null @@ -1,616 +0,0 @@ -DataStoreTest = TestCase('DataStoreTest'); - -DataStoreTest.prototype.testSavePostsToServer = function(){ - expectAsserts(10); - var response; - var post = function(data, callback){ - var method = data[0][0]; - var posted = data[0][2]; - assertEquals("POST", method); - assertEquals("abc", posted.$entity); - assertEquals("123", posted.$id); - assertEquals("1", posted.$version); - assertFalse('function' == typeof posted.save); - response = fromJson(toJson(posted)); - response.$entity = "abc"; - response.$id = "123"; - response.$version = "2"; - callback(200, [response]); - }; - var datastore = new DataStore(post); - var model = datastore.entity('abc', {name: "value"})(); - model.$id = "123"; - model.$version = "1"; - - datastore.save(model, function(obj){ - assertTrue(obj === model); - assertEquals(obj.$entity, "abc"); - assertEquals(obj.$id, "123"); - assertEquals(obj.$version, "2"); - assertEquals(obj.name, "value"); - obj.after = true; - }); - datastore.flush(); -}; - -DataStoreTest.prototype.testLoadGetsFromServer = function(){ - expectAsserts(12); - var post = function(data, callback){ - var method = data[0][0]; - var path = data[0][1]; - assertEquals("GET", method); - assertEquals("abc/1", path); - response = [{$entity:'abc', $id:'1', $version:'2', key:"value"}]; - callback(200, response); - }; - var datastore = new DataStore(post); - - var model = datastore.entity("abc", {merge:true})(); - assertEquals(datastore.load(model, '1', function(obj){ - assertEquals(obj.$entity, "abc"); - assertEquals(obj.$id, "1"); - assertEquals(obj.$version, "2"); - assertEquals(obj.key, "value"); - }), model); - datastore.flush(); - assertEquals(model.$entity, "abc"); - assertEquals(model.$id, "1"); - assertEquals(model.$version, "2"); - assertEquals(model.key, "value"); - assertEquals(model.merge, true); -}; - -DataStoreTest.prototype.testRemove = function(){ - expectAsserts(8); - var response; - var post = function(data, callback){ - var method = data[0][0]; - var posted = data[0][2]; - assertEquals("DELETE", method); - assertEquals("abc", posted.$entity); - assertEquals("123", posted.$id); - assertEquals("1", posted.$version); - assertFalse('function' == typeof posted.save); - response = fromJson(toJson(posted)); - response.$entity = "abc"; - response.$id = "123"; - response.$version = "2"; - callback(200, [response]); - }; - var model; - var datastore = new DataStore(post); - model = datastore.entity('abc', {name: "value"})(); - model.$id = "123"; - model.$version = "1"; - - datastore.remove(model, function(obj){ - assertEquals(obj.$id, "123"); - assertEquals(obj.$version, "2"); - assertEquals(obj.name, "value"); - obj.after = true; - }); - datastore.flush(); - -}; - - -DataStoreTest.prototype.test401ResponseDoesNotCallCallback = function(){ - expectAsserts(1); - var post = function(data, callback) { - callback(200, {$status_code: 401}); - }; - - var datastore = new DataStore(post, {login:function(){ - assertTrue(true); - }}); - - var onLoadAll = function(){ - assertTrue(false, "onLoadAll should not be called when response is status 401"); - }; - datastore.bulkRequest.push({}); - datastore.flush(); - datastore.loadAll({type: "A"}, onLoadAll); -}; - -DataStoreTest.prototype.test403ResponseDoesNotCallCallback = function(){ - expectAsserts(1); - var post = function(data, callback) { - callback(200, [{$status_code: 403}]); - }; - - var datastore = new DataStore(post, {notAuthorized:function(){ - assertTrue(true); - }}); - - var onLoadAll = function(){ - assertTrue(false, "onLoadAll should not be called when response is status 403"); - }; - datastore.bulkRequest.push({}); - datastore.flush(); - datastore.loadAll({type: "A"}, onLoadAll); -}; - -DataStoreTest.prototype.testLoadCalledWithoutIdShouldBeNoop = function(){ - expectAsserts(2); - var post = function(url, callback){ - assertTrue(false); - }; - var datastore = new DataStore(post); - var model = datastore.entity("abc")(); - assertEquals(datastore.load(model, undefined), model); - assertEquals(model.$entity, "abc"); -}; - -DataStoreTest.prototype.testEntityFactory = function(){ - var ds = new DataStore(); - var Recipe = ds.entity("Recipe", {a:1, b:2}); - assertEquals(Recipe.title, "Recipe"); - assertEquals(Recipe.defaults.a, 1); - assertEquals(Recipe.defaults.b, 2); - - var recipe = Recipe(); - assertEquals(recipe.$entity, "Recipe"); - assertEquals(recipe.a, 1); - assertEquals(recipe.b, 2); - - recipe = new Recipe(); - assertEquals(recipe.$entity, "Recipe"); - assertEquals(recipe.a, 1); - assertEquals(recipe.b, 2); -}; - -DataStoreTest.prototype.testEntityFactoryNoDefaults = function(){ - var ds = new DataStore(); - var Recipe = ds.entity("Recipe"); - assertEquals(Recipe.title, "Recipe"); - - recipe = new Recipe(); - assertEquals(recipe.$entity, "Recipe"); -}; - -DataStoreTest.prototype.testEntityFactoryWithInitialValues = function(){ - var ds = new DataStore(); - var Recipe = ds.entity("Recipe"); - - var recipe = Recipe({name: "name"}); - assertEquals("name", recipe.name); -}; - -DataStoreTest.prototype.testEntityLoad = function(){ - var ds = new DataStore(); - var Recipe = ds.entity("Recipe", {a:1, b:2}); - ds.load = function(instance, id, callback){ - callback.apply(instance); - return instance; - }; - var instance = null; - var recipe2 = Recipe.load("ID", function(){ - instance = this; - }); - assertTrue(recipe2 === instance); -}; - -DataStoreTest.prototype.testSaveScope = function(){ - var ds = new DataStore(); - var log = ""; - var Person = ds.entity("Person"); - var person1 = Person({name:"A", $entity:"Person", $id:"1", $version:"1"}, ds); - person1.$$anchor = "A"; - var person2 = Person({name:"B", $entity:"Person", $id:"2", $version:"2"}, ds); - person2.$$anchor = "B"; - var anchor = {}; - ds.anchor = anchor; - ds._jsonRequest = function(request, callback){ - log += "save(" + request[2].$id + ");"; - callback({$id:request[2].$id}); - }; - ds.saveScope({person1:person1, person2:person2, - ignoreMe:{name: "ignore", save:function(callback){callback();}}}, function(){ - log += "done();"; - }); - assertEquals("save(1);save(2);done();", log); - assertEquals(1, anchor.A); - assertEquals(2, anchor.B); -}; - -DataStoreTest.prototype.testEntityLoadAllRows = function(){ - var ds = new DataStore(); - var Recipe = ds.entity("Recipe"); - var list = []; - ds.loadAll = function(entity, callback){ - assertTrue(Recipe === entity); - callback.apply(list); - return list; - }; - var items = Recipe.all(function(){ - assertTrue(list === this); - }); - assertTrue(items === list); -}; - -DataStoreTest.prototype.testLoadAll = function(){ - expectAsserts(8); - var post = function(data, callback){ - assertEquals("GET", data[0][0]); - assertEquals("A", data[0][1]); - callback(200, [[{$entity:'A', $id:'1'},{$entity:'A', $id:'2'}]]); - }; - var datastore = new DataStore(post); - var list = datastore.entity("A").all(function(){ - assertTrue(true); - }); - datastore.flush(); - assertEquals(list.length, 2); - assertEquals(list[0].$entity, "A"); - assertEquals(list[0].$id, "1"); - assertEquals(list[1].$entity, "A"); - assertEquals(list[1].$id, "2"); -}; - -DataStoreTest.prototype.testQuery = function(){ - expectAsserts(5); - var post = function(data, callback) { - assertEquals("GET", data[0][0]); - assertEquals("Employee/managerId=123abc", data[0][1]); - callback(200, [[{$entity:"Employee", $id: "456", managerId: "123ABC"}]]); - - }; - var datastore = new DataStore(post); - var Employee = datastore.entity("Employee"); - var list = Employee.query('managerId', "123abc", function(){ - assertTrue(true); - }); - datastore.flush(); - assertJsonEquals([[{$entity:"Employee", $id: "456", managerId: "123ABC"}]], datastore._cache.$collections); - assertEquals(list[0].$id, "456"); -}; - -DataStoreTest.prototype.testLoadingDocumentRefreshesExistingArrays = function() { - expectAsserts(12); - var post; - var datastore = new DataStore(function(r, c){post(r,c);}); - var Book = datastore.entity('Book'); - post = function(req, callback) { - callback(200, [[{$id:1, $entity:"Book", name:"Moby"}, - {$id:2, $entity:"Book", name:"Dick"}]]); - }; - var allBooks = Book.all(); - datastore.flush(); - var queryBooks = Book.query("a", "b"); - datastore.flush(); - assertEquals("Moby", allBooks[0].name); - assertEquals("Dick", allBooks[1].name); - assertEquals("Moby", queryBooks[0].name); - assertEquals("Dick", queryBooks[1].name); - - post = function(req, callback) { - assertEquals('[["GET","Book/1"]]', toJson(req)); - callback(200, [{$id:1, $entity:"Book", name:"Moby Dick"}]); - }; - var book = Book.load(1); - datastore.flush(); - assertEquals("Moby Dick", book.name); - assertEquals("Moby Dick", allBooks[0].name); - assertEquals("Moby Dick", queryBooks[0].name); - - post = function(req, callback) { - assertEquals('POST', req[0][0]); - callback(200, [{$id:1, $entity:"Book", name:"The Big Fish"}]); - }; - book.$save(); - datastore.flush(); - assertEquals("The Big Fish", book.name); - assertEquals("The Big Fish", allBooks[0].name); - assertEquals("The Big Fish", queryBooks[0].name); -}; - -DataStoreTest.prototype.testEntityProperties = function() { - expectAsserts(2); - var datastore = new DataStore(); - var callback = {}; - - datastore._jsonRequest = function(request, callbackFn) { - assertJsonEquals(["GET", "Cheese/$properties"], request); - assertEquals(callback, callbackFn); - }; - - var Cheese = datastore.entity("Cheese"); - Cheese.properties(callback); - -}; - -DataStoreTest.prototype.testLoadInstanceIsNotFromCache = function() { - var post; - var datastore = new DataStore(function(r, c){post(r,c);}); - var Book = datastore.entity('Book'); - - post = function(req, callback) { - assertEquals('[["GET","Book/1"]]', toJson(req)); - callback(200, [{$id:1, $entity:"Book", name:"Moby Dick"}]); - }; - var book = Book.load(1); - datastore.flush(); - assertEquals("Moby Dick", book.name); - assertFalse(book === datastore._cache['Book/1']); -}; - -DataStoreTest.prototype.testLoadStarsIsNewDocument = function() { - var datastore = new DataStore(); - var Book = datastore.entity('Book'); - var book = Book.load('*'); - assertEquals('Book', book.$entity); -}; - -DataStoreTest.prototype.testUndefinedEntityReturnsNullValueObject = function() { - var datastore = new DataStore(); - var Entity = datastore.entity(undefined); - var all = Entity.all(); - assertEquals(0, all.length); -}; - -DataStoreTest.prototype.testFetchEntities = function(){ - expectAsserts(6); - var post = function(data, callback){ - assertJsonEquals(["GET", "$entities"], data[0]); - callback(200, [{A:0, B:0}]); - }; - var datastore = new DataStore(post); - var entities = datastore.entities(function(){ - assertTrue(true); - }); - datastore.flush(); - assertJsonEquals([], datastore.bulkRequest); - assertEquals(2, entities.length); - assertEquals("A", entities[0].title); - assertEquals("B", entities[1].title); -}; - -DataStoreTest.prototype.testItShouldMigrateSchema = function() { - var datastore = new DataStore(); - var Entity = datastore.entity("Entity", {a:[], user:{name:"Misko", email:""}}); - var doc = Entity().$loadFrom({b:'abc', user:{email:"misko@hevery.com"}}); - assertFalse( - toJson({a:[], b:'abc', user:{name:"Misko", email:"misko@hevery.com"}}) == - toJson(doc)); - doc.$migrate(); - assertEquals( - toJson({a:[], b:'abc', user:{name:"Misko", email:"misko@hevery.com"}}), - toJson(doc)); -}; - -DataStoreTest.prototype.testItShouldCollectRequestsForBulk = function() { - var ds = new DataStore(); - var Book = ds.entity("Book"); - var Library = ds.entity("Library"); - Book.all(); - Library.load("123"); - assertEquals(2, ds.bulkRequest.length); - assertJsonEquals(["GET", "Book"], ds.bulkRequest[0]); - assertJsonEquals(["GET", "Library/123"], ds.bulkRequest[1]); -}; - -DataStoreTest.prototype.testEmptyFlushShouldDoNothing = function () { - var ds = new DataStore(function(){ - fail("expecting noop"); - }); - ds.flush(); -}; - -DataStoreTest.prototype.testFlushShouldCallAllCallbacks = function() { - var log = ""; - function post(request, callback){ - log += 'BulkRequest:' + toJson(request) + ';'; - callback(200, [[{$id:'ABC'}], {$id:'XYZ'}]); - } - var ds = new DataStore(post); - var Book = ds.entity("Book"); - var Library = ds.entity("Library"); - Book.all(function(instance){ - log += toJson(instance) + ';'; - }); - Library.load("123", function(instance){ - log += toJson(instance) + ';'; - }); - assertEquals("", log); - ds.flush(); - assertJsonEquals([], ds.bulkRequest); - assertEquals('BulkRequest:[["GET","Book"],["GET","Library/123"]];[{"$id":"ABC"}];{"$id":"XYZ"};', log); -}; - -DataStoreTest.prototype.testSaveOnNotLoggedInRetriesAfterLoggin = function(){ - var log = ""; - var book; - var ds = new DataStore(null, {login:function(c){c();}}); - ds.post = function (request, callback){ - assertJsonEquals([["POST", "", book]], request); - ds.post = function(request, callback){ - assertJsonEquals([["POST", "", book]], request); - ds.post = function(){fail("too much recursion");}; - callback(200, [{saved:"ok"}]); - }; - callback(200, {$status_code:401}); - }; - book = ds.entity("Book")({name:"misko"}); - book.$save(); - ds.flush(); - assertJsonEquals({saved:"ok"}, book); -}; - -DataStoreTest.prototype.testItShouldRemoveItemFromCollectionWhenDeleted = function() { - expectAsserts(6); - var ds = new DataStore(); - ds.post = function(request, callback){ - assertJsonEquals([["GET", "Book"]], request); - callback(200, [[{name:"Moby Dick", $id:123, $entity:'Book'}]]); - }; - var Book = ds.entity("Book"); - var books = Book.all(); - ds.flush(); - assertJsonEquals([[{name:"Moby Dick", $id:123, $entity:'Book'}]], ds._cache.$collections); - assertDefined(ds._cache['Book/123']); - var book = Book({$id:123}); - ds.post = function(request, callback){ - assertJsonEquals([["DELETE", "", book]], request); - callback(200, [book]); - }; - ds.remove(book); - ds.flush(); - assertUndefined(ds._cache['Book/123']); - assertJsonEquals([[]],ds._cache.$collections); -}; - -DataStoreTest.prototype.testItShouldAddToAll = function() { - expectAsserts(8); - var ds = new DataStore(); - ds.post = function(request, callback){ - assertJsonEquals([["GET", "Book"]], request); - callback(200, [[]]); - }; - var Book = ds.entity("Book"); - var books = Book.all(); - assertEquals(0, books.length); - ds.flush(); - var moby = Book({name:'moby'}); - moby.$save(); - ds.post = function(request, callback){ - assertJsonEquals([["POST", "", moby]], request); - moby.$id = '123'; - callback(200, [moby]); - }; - ds.flush(); - assertEquals(1, books.length); - assertEquals(moby, books[0]); - - moby.$save(); - ds.flush(); - assertEquals(1, books.length); - assertEquals(moby, books[0]); -}; - -DataStoreTest.prototype.testItShouldReturnCreatedDocumentCountByUser = function(){ - expectAsserts(2); - var datastore = new DataStore( - function(request, callback){ - assertJsonEquals([["GET", "$users"]], request); - callback(200, [{misko:1, adam:1}]); - }); - var users = datastore.documentCountsByUser(); - assertJsonEquals({misko:1, adam:1}, users); -}; - - -DataStoreTest.prototype.testItShouldReturnDocumentIdsForUeserByEntity = function(){ - expectAsserts(2); - var datastore = new DataStore( - function(request, callback){ - assertJsonEquals([["GET", "$users/misko@hevery.com"]], request); - callback(200, [{Book:["1"], Library:["2"]}]); - }); - var users = datastore.userDocumentIdsByEntity("misko@hevery.com"); - assertJsonEquals({Book:["1"], Library:["2"]}, users); -}; - -DataStoreTest.prototype.testItShouldReturnNewInstanceOn404 = function(){ - expectAsserts(7); - var log = ""; - var datastore = new DataStore( - function(request, callback){ - assertJsonEquals([["GET", "User/misko"]], request); - callback(200, [{$status_code:404}]); - }); - var User = datastore.entity("User", {admin:false}); - var user = User.loadOrCreate('misko', function(i){log+="cb "+i.$id+";";}); - datastore.flush(); - assertEquals("misko", user.$id); - assertEquals("User", user.$entity); - assertEquals(false, user.admin); - assertEquals("undefined", typeof user.$secret); - assertEquals("undefined", typeof user.$version); - assertEquals("cb misko;", log); -}; - -DataStoreTest.prototype.testItShouldReturnNewInstanceOn404 = function(){ - var log = ""; - var datastore = new DataStore( - function(request, callback){ - assertJsonEquals([["GET", "User/misko"],["GET", "User/adam"]], request); - callback(200, [{$id:'misko'},{$id:'adam'}]); - }); - var User = datastore.entity("User"); - var users = User.loadMany(['misko', 'adam'], function(i){log+="cb "+toJson(i)+";";}); - datastore.flush(); - assertEquals("misko", users[0].$id); - assertEquals("adam", users[1].$id); - assertEquals('cb [{"$id":"misko"},{"$id":"adam"}];', log); -}; - -DataStoreTest.prototype.testItShouldCreateJoinAndQuery = function() { - var datastore = new DataStore(); - var Invoice = datastore.entity("Invoice"); - var Customer = datastore.entity("Customer"); - var InvoiceWithCustomer = datastore.join({ - invoice:{join:Invoice}, - customer:{join:Customer, on:"invoice.customer"} - }); - var invoiceWithCustomer = InvoiceWithCustomer.query("invoice.month", 1); - assertEquals([], invoiceWithCustomer); - assertJsonEquals([["GET", "Invoice/month=1"]], datastore.bulkRequest); - var request = datastore.bulkRequest.shift(); - request.$$callback([{$id:1, customer:1},{$id:2, customer:1},{$id:3, customer:3}]); - assertJsonEquals([["GET","Customer/1"],["GET","Customer/3"]], datastore.bulkRequest); - datastore.bulkRequest.shift().$$callback({$id:1}); - datastore.bulkRequest.shift().$$callback({$id:3}); - assertJsonEquals([ - {invoice:{$id:1,customer:1},customer:{$id:1}}, - {invoice:{$id:2,customer:1},customer:{$id:1}}, - {invoice:{$id:3,customer:3},customer:{$id:3}}], invoiceWithCustomer); -}; - -DataStoreTest.prototype.testItShouldThrowIfMoreThanOneEntityIsPrimary = function() { - var datastore = new DataStore(); - var Invoice = datastore.entity("Invoice"); - var Customer = datastore.entity("Customer"); - assertThrows("Exactly one entity needs to be primary.", function(){ - datastore.join({ - invoice:{join:Invoice}, - customer:{join:Customer} - }); - }); -}; - -DataStoreTest.prototype.testItShouldThrowIfLoopInReferences = function() { - var datastore = new DataStore(); - var Invoice = datastore.entity("Invoice"); - var Customer = datastore.entity("Customer"); - assertThrows("Infinite loop in join: invoice -> customer", function(){ - datastore.join({ - invoice:{join:Invoice, on:"customer.invoice"}, - customer:{join:Customer, on:"invoice.customer"} - }); - }); -}; - -DataStoreTest.prototype.testItShouldThrowIfReferenceToNonExistantJoin = function() { - var datastore = new DataStore(); - var Invoice = datastore.entity("Invoice"); - var Customer = datastore.entity("Customer"); - assertThrows("Named entity 'x' is undefined.", function(){ - datastore.join({ - invoice:{join:Invoice, on:"x.invoice"}, - customer:{join:Customer, on:"invoice.customer"} - }); - }); -}; - -DataStoreTest.prototype.testItShouldThrowIfQueryOnNonPrimary = function() { - var datastore = new DataStore(); - var Invoice = datastore.entity("Invoice"); - var Customer = datastore.entity("Customer"); - var InvoiceWithCustomer = datastore.join({ - invoice:{join:Invoice}, - customer:{join:Customer, on:"invoice.customer"} - }); - assertThrows("Named entity 'customer' is not a primary entity.", function(){ - InvoiceWithCustomer.query("customer.month", 1); - }); -}; diff --git a/test/moveToAngularCom/EntityDeclarationTest.js b/test/moveToAngularCom/EntityDeclarationTest.js deleted file mode 100644 index 28986ea8..00000000 --- a/test/moveToAngularCom/EntityDeclarationTest.js +++ /dev/null @@ -1,50 +0,0 @@ -EntityDeclarationTest = TestCase('EntityDeclarationTest'); - -EntityDeclarationTest.prototype.testEntityTypeOnly = function(){ - expectAsserts(2); - var datastore = {entity:function(name){ - assertEquals("Person", name); - }}; - var scope = new Scope(); - var init = scope.entity("Person", datastore); - assertEquals("", init); -}; - -EntityDeclarationTest.prototype.testWithDefaults = function(){ - expectAsserts(4); - var datastore = {entity:function(name, init){ - assertEquals("Person", name); - assertEquals("=a:", init.a); - assertEquals(0, init.b.length); - }}; - var scope = new Scope(); - var init = scope.entity('Person:{a:"=a:", b:[]}', datastore); - assertEquals("", init); -}; - -EntityDeclarationTest.prototype.testWithName = function(){ - expectAsserts(2); - var datastore = {entity:function(name, init){ - assertEquals("Person", name); - return function (){ return {}; }; - }}; - var scope = new Scope(); - var init = scope.entity('friend=Person', datastore); - assertEquals("$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};", init); -}; - -EntityDeclarationTest.prototype.testMultipleEntities = function(){ - expectAsserts(3); - var expect = ['Person', 'Book']; - var i=0; - var datastore = {entity:function(name, init){ - assertEquals(expect[i], name); - i++; - return function (){ return {}; }; - }}; - var scope = new Scope(); - var init = scope.entity('friend=Person;book=Book;', datastore); - assertEquals("$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};" + - "$anchor.book:{book=Book.load($anchor.book);book.$$anchor=\"book\";};", - init); -}; diff --git a/test/moveToAngularCom/FileControllerTest.js b/test/moveToAngularCom/FileControllerTest.js deleted file mode 100644 index 75c924e6..00000000 --- a/test/moveToAngularCom/FileControllerTest.js +++ /dev/null @@ -1,98 +0,0 @@ -FileControllerTest = TestCase('FileControllerTest'); - -FileControllerTest.prototype.XtestOnSelectUpdateView = function(){ - var view = jQuery('<span><a/><span/></span>'); - var swf = {}; - var controller = new FileController(view, null, swf); - swf.uploadFile = function(path){}; - controller.select('A', 9, '9 bytes'); - assertEquals(view.find('a').text(), "A"); - assertEquals(view.find('span').text(), "9 bytes"); -}; - -FileControllerTest.prototype.XtestUpdateModelView = function(){ - var view = FileController.template(''); - var input = $('<input name="value.input">'); - var controller; - var scope = new Scope({value:{}, $binder:{updateView:function(){ - controller.updateView(scope); - }}}); - view.data('scope', scope); - controller = new FileController(view, 'value.input', null, "http://server_base"); - var value = '{"text":"A", "size":123, "id":"890"}'; - controller.uploadCompleteData(value); - controller.updateView(scope); - assertEquals(scope.get('value.input.text'), 'A'); - assertEquals(scope.get('value.input.size'), 123); - assertEquals(scope.get('value.input.id'), '890'); - assertEquals(scope.get('value.input.url'), 'http://server_base/_attachments/890/A'); - assertEquals(view.find('a').text(), "A"); - assertEquals(view.find('a').attr('href'), "http://server_base/_attachments/890/A"); - assertEquals(view.find('span').text(), "123 bytes"); -}; - -FileControllerTest.prototype.XtestFileUpload = function(){ - expectAsserts(1); - var swf = {}; - var controller = new FileController(null, null, swf, "http://server_base"); - swf.uploadFile = function(path){ - assertEquals("http://server_base/_attachments", path); - }; - controller.name = "Name"; - controller.upload(); -}; - -FileControllerTest.prototype.XtestFileUploadNoFileIsNoop = function(){ - expectAsserts(0); - var swf = {uploadFile:function(path){ - fail(); - }}; - var controller = new FileController(null, swf); - controller.upload("basePath", null); -}; - -FileControllerTest.prototype.XtestRemoveAttachment = function(){ - var doc = FileController.template(); - var input = $('<input name="file">'); - var scope = new Scope(); - input.data('scope', scope); - var controller = new FileController(doc, 'file', null, null); - controller.updateView(scope); - assertEquals(false, doc.find('input').attr('checked')); - - scope.set('file', {url:'url', size:123}); - controller.updateView(scope); - assertEquals(true, doc.find('input').attr('checked')); - - doc.find('input').attr('checked', false); - controller.updateModel(scope); - assertNull(scope.get('file')); - - doc.find('input').attr('checked', true); - controller.updateModel(scope); - assertEquals('url', scope.get('file.url')); - assertEquals(123, scope.get('file.size')); -}; - -FileControllerTest.prototype.XtestShouldEmptyOutOnUndefined = function () { - var view = FileController.template('hello'); - var controller = new FileController(view, 'abc', null, null); - - var scope = new Scope(); - scope.set('abc', {text: 'myname', url: 'myurl', size: 1234}); - - controller.updateView(scope); - assertEquals("myurl", view.find('a').attr('href')); - assertEquals("myname", view.find('a').text()); - assertEquals(true, view.find('input').is(':checked')); - assertEquals("1.2 KB", view.find('span').text()); - - scope.set('abc', undefined); - controller.updateView(scope); - assertEquals("myurl", view.find('a').attr('href')); - assertEquals("myname", view.find('a').text()); - assertEquals(false, view.find('input').is(':checked')); - assertEquals("1.2 KB", view.find('span').text()); -}; - - diff --git a/test/moveToAngularCom/MiscTest.js b/test/moveToAngularCom/MiscTest.js deleted file mode 100644 index aa0e1186..00000000 --- a/test/moveToAngularCom/MiscTest.js +++ /dev/null @@ -1,35 +0,0 @@ -BinderTest.prototype.testExpandEntityTagWithName = function(){ - var c = this.compile('<div ng-entity="friend=Person"/>'); - assertEquals( - '<div ng-entity="friend=Person" ng-watch="$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};"></div>', - sortedHtml(c.node)); - assertEquals("Person", c.scope.$get("friend.$entity")); - assertEquals("friend", c.scope.$get("friend.$$anchor")); -}; - -BinderTest.prototype.testExpandSubmitButtonToAction = function(){ - var html = this.compileToHtml('<input type="submit" value="Save">'); - assertTrue(html, html.indexOf('ng-action="$save()"') > 0 ); - assertTrue(html, html.indexOf('ng-bind-attr="{"disabled":"{{$invalidWidgets}}"}"') > 0 ); -}; - -BinderTest.prototype.testReplaceFileUploadWithSwf = function(){ - expectAsserts(1); - var form = jQuery("body").append('<div id="testTag"><input type="file"></div>'); - form.data('scope', new Scope()); - var factory = {}; - var binder = new Binder(form.get(0), factory, new MockLocation()); - factory.createController = function(node){ - assertEquals(node.attr('type'), 'file'); - return {updateModel:function(){}}; - }; - binder.compile(); - jQuery("#testTag").remove(); -}; - -BinderTest.prototype.testExpandEntityTagWithDefaults = function(){ - assertEquals( - '<div ng-entity="Person:{a:\"a\"}" ng-watch=""></div>', - this.compileToHtml('<div ng-entity=\'Person:{a:"a"}\'/>')); -}; - diff --git a/test/moveToAngularCom/ModelTest.js b/test/moveToAngularCom/ModelTest.js deleted file mode 100644 index dbd97778..00000000 --- a/test/moveToAngularCom/ModelTest.js +++ /dev/null @@ -1,84 +0,0 @@ -ModelTest = TestCase('ModelTest'); - -ModelTest.prototype.testLoadSaveOperations = function(){ - var m1 = new DataStore().entity('A')(); - m1.a = 1; - - var m2 = {b:1}; - - m1.$loadFrom(m2); - - assertTrue(!m1.a); - assertEquals(m1.b, 1); -}; - -ModelTest.prototype.testLoadfromDoesNotClobberFunctions = function(){ - var m1 = new DataStore().entity('A')(); - m1.id = function(){return 'OK';}; - m1.$loadFrom({id:null}); - assertEquals(m1.id(), 'OK'); - - m1.b = 'OK'; - m1.$loadFrom({b:function(){}}); - assertEquals(m1.b, 'OK'); -}; - -ModelTest.prototype.testDataStoreDoesNotGetClobbered = function(){ - var ds = new DataStore(); - var m = ds.entity('A')(); - assertTrue(m.$$entity.datastore === ds); - m.$loadFrom({}); - assertTrue(m.$$entity.datastore === ds); -}; - -ModelTest.prototype.testManagedModelDelegatesMethodsToDataStore = function(){ - expectAsserts(7); - var datastore = new DataStore(); - var model = datastore.entity("A", {a:1})(); - var fn = {}; - datastore.save = function(instance, callback) { - assertTrue(model === instance); - assertTrue(callback === fn); - }; - datastore.remove = function(instance, callback) { - assertTrue(model === instance); - assertTrue(callback === fn); - }; - datastore.load = function(instance, id, callback) { - assertTrue(model === instance); - assertTrue(id === "123"); - assertTrue(callback === fn); - }; - model.$save(fn); - model.$delete(fn); - model.$loadById("123", fn); -}; - -ModelTest.prototype.testManagedModelCanBeForcedToFlush = function(){ - expectAsserts(6); - var datastore = new DataStore(); - var model = datastore.entity("A", {a:1})(); - - datastore.save = function(instance, callback) { - assertTrue(model === instance); - assertTrue(callback === undefined); - }; - datastore.remove = function(instance, callback) { - assertTrue(model === instance); - assertTrue(callback === undefined); - }; - datastore.flush = function(){ - assertTrue(true); - }; - model.$save(true); - model.$delete(true); -}; - - -ModelTest.prototype.testItShouldMakeDeepCopyOfInitialValues = function (){ - var initial = {a:[]}; - var entity = new DataStore().entity("A", initial); - var model = entity(); - model.a.push(1); - assertEquals(0, entity().a.length); -}; diff --git a/test/moveToAngularCom/ServerTest.js b/test/moveToAngularCom/ServerTest.js deleted file mode 100644 index 02fab84c..00000000 --- a/test/moveToAngularCom/ServerTest.js +++ /dev/null @@ -1,42 +0,0 @@ -ServerTest = TestCase("ServerTest"); -ServerTest.prototype.testBreakLargeRequestIntoPackets = function() { - var log = ""; - var server = new Server("http://server", function(url){ - log += "|" + url; - }); - server.maxSize = 30; - server.uuid = "uuid"; - server.request("POST", "/data/database", {}, function(code, r){ - assertEquals(200, code); - assertEquals("response", r); - }); - angularCallbacks.uuid0("response"); - assertEquals( - "|http://server/$/uuid0/2/1?h=eyJtIjoiUE9TVCIsInAiOnt9LCJ1Ij" + - "|http://server/$/uuid0/2/2?h=oiL2RhdGEvZGF0YWJhc2UifQ==", - log); -}; - -ServerTest.prototype.testItShouldEncodeUsingUrlRules = function() { - var server = new Server("http://server"); - assertEquals("fn5-fn5-", server.base64url("~~~~~~")); - assertEquals("fn5_fn5_", server.base64url("~~\u007f~~\u007f")); -}; - -FrameServerTest = TestCase("FrameServerTest"); - -FrameServerTest.prototype = { - testRead:function(){ - var window = {name:'$DATASET:"MyData"'}; - var server = new FrameServer(window); - server.read(); - assertEquals("MyData", server.data); - }, - testWrite:function(){ - var window = {}; - var server = new FrameServer(window); - server.data = "TestData"; - server.write(); - assertEquals('$DATASET:"TestData"', window.name); - } -}; diff --git a/test/moveToAngularCom/UsersTest.js b/test/moveToAngularCom/UsersTest.js deleted file mode 100644 index f0ff545a..00000000 --- a/test/moveToAngularCom/UsersTest.js +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2008,2009 BRAT Tech LLC - -UsersTest = TestCase("UsersTest"); - -UsersTest.prototype = { - setUp:function(){}, - - tearDown:function(){}, - - testItShouldFetchCurrentUser:function(){ - expectAsserts(5); - var user; - var users = new Users({request:function(method, url, request, callback){ - assertEquals("GET", method); - assertEquals("/account.json", url); - assertEquals("{}", toJson(request)); - callback(200, {$status_code:200, user:{name:'misko'}}); - }}); - users.fetchCurrentUser(function(u){ - user = u; - assertEquals("misko", u.name); - assertEquals("misko", users.current.name); - }); - } - -}; diff --git a/test/moveToAngularCom/miscTest.js b/test/moveToAngularCom/miscTest.js deleted file mode 100644 index aa0e1186..00000000 --- a/test/moveToAngularCom/miscTest.js +++ /dev/null @@ -1,35 +0,0 @@ -BinderTest.prototype.testExpandEntityTagWithName = function(){ - var c = this.compile('<div ng-entity="friend=Person"/>'); - assertEquals( - '<div ng-entity="friend=Person" ng-watch="$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};"></div>', - sortedHtml(c.node)); - assertEquals("Person", c.scope.$get("friend.$entity")); - assertEquals("friend", c.scope.$get("friend.$$anchor")); -}; - -BinderTest.prototype.testExpandSubmitButtonToAction = function(){ - var html = this.compileToHtml('<input type="submit" value="Save">'); - assertTrue(html, html.indexOf('ng-action="$save()"') > 0 ); - assertTrue(html, html.indexOf('ng-bind-attr="{"disabled":"{{$invalidWidgets}}"}"') > 0 ); -}; - -BinderTest.prototype.testReplaceFileUploadWithSwf = function(){ - expectAsserts(1); - var form = jQuery("body").append('<div id="testTag"><input type="file"></div>'); - form.data('scope', new Scope()); - var factory = {}; - var binder = new Binder(form.get(0), factory, new MockLocation()); - factory.createController = function(node){ - assertEquals(node.attr('type'), 'file'); - return {updateModel:function(){}}; - }; - binder.compile(); - jQuery("#testTag").remove(); -}; - -BinderTest.prototype.testExpandEntityTagWithDefaults = function(){ - assertEquals( - '<div ng-entity="Person:{a:\"a\"}" ng-watch=""></div>', - this.compileToHtml('<div ng-entity=\'Person:{a:"a"}\'/>')); -}; - diff --git a/test/scenario/RunnerSpec.js b/test/scenario/RunnerSpec.js index 689354df..98858747 100644 --- a/test/scenario/RunnerSpec.js +++ b/test/scenario/RunnerSpec.js @@ -164,7 +164,7 @@ describe('Runner', function(){ expect(log).toEqual('first;second;'); next(); expect(log).toEqual('first;second;done;'); - expect(spec).not.toEqual(window); + expect(spec === window).toEqual(false); expect(spec).toEqual(firstThis); expect(spec).toEqual(secondThis); expect(spec).toEqual(doneThis); diff --git a/test/servicesSpec.js b/test/servicesSpec.js index f679a39b..91538703 100644 --- a/test/servicesSpec.js +++ b/test/servicesSpec.js @@ -1,5 +1,5 @@ describe("service", function(){ - var scope, $xhrError, $log; + var scope, $xhrError, $log, mockServices; beforeEach(function(){ $xhrError = jasmine.createSpy('$xhr.error'); @@ -33,32 +33,45 @@ describe("service", function(){ describe("$log", function(){ it('should use console if present', function(){ - function log(){}; - function warn(){}; - function info(){}; - function error(){}; - var scope = createScope(null, angularService, {$window: {console:{log:log, warn:warn, info:info, error:error}}}); - expect(scope.$log.log).toEqual(log); - expect(scope.$log.warn).toEqual(warn); - expect(scope.$log.info).toEqual(info); - expect(scope.$log.error).toEqual(error); + var logger = ""; + function log(){ logger+= 'log;'; }; + function warn(){ logger+= 'warn;'; }; + function info(){ logger+= 'info;'; }; + function error(){ logger+= 'error;'; }; + var scope = createScope(null, angularService, {$window: {console:{log:log, warn:warn, info:info, error:error}}, $document:[{}]}); + scope.$log.log(); + scope.$log.warn(); + scope.$log.info(); + scope.$log.error(); + expect(logger).toEqual('log;warn;info;error;'); }); it('should use console.log if other not present', function(){ - function log(){}; - var scope = createScope(null, angularService, {$window: {console:{log:log}}}); - expect(scope.$log.log).toEqual(log); - expect(scope.$log.warn).toEqual(log); - expect(scope.$log.info).toEqual(log); - expect(scope.$log.error).toEqual(log); + var logger = ""; + function log(){ logger+= 'log;'; }; + var scope = createScope(null, angularService, {$window: {console:{log:log}}, $document:[{}]}); + scope.$log.log(); + scope.$log.warn(); + scope.$log.info(); + scope.$log.error(); + expect(logger).toEqual('log;log;log;log;'); }); it('should use noop if no console', function(){ - var scope = createScope(null, angularService, {$window: {}}); - expect(scope.$log.log).toEqual(noop); - expect(scope.$log.warn).toEqual(noop); - expect(scope.$log.info).toEqual(noop); - expect(scope.$log.error).toEqual(noop); + var scope = createScope(null, angularService, {$window: {}, $document:[{}]}); + scope.$log.log(); + scope.$log.warn(); + scope.$log.info(); + scope.$log.error(); + }); + }); + + describe("$exceptionHandler", function(){ + it('should log errors', function(){ + var error = ''; + $log.error = function(m) { error += m; }; + scope.$exceptionHandler('myError'); + expect(error).toEqual('myError'); }); }); @@ -136,7 +149,7 @@ describe("service", function(){ describe("$invalidWidgets", function(){ it("should count number of invalid widgets", function(){ - var scope = compile('<input name="price" ng-required ng-validate="number"></input>').$init(); + var scope = compile('<input name="price" ng:required ng:validate="number"></input>').$init(); expect(scope.$invalidWidgets.length).toEqual(1); scope.price = 123; scope.$eval(); diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js index d621b1f1..5d0df780 100644 --- a/test/testabilityPatch.js +++ b/test/testabilityPatch.js @@ -63,9 +63,11 @@ function sortedHtml(element) { html += '<' + node.nodeName.toLowerCase(); var attributes = node.attributes || []; var attrs = []; + if (node.className) + attrs.push(' class="' + node.className + '"'); for(var i=0; i<attributes.length; i++) { var attr = attributes[i]; - if(attr.name.match(/^ng-/) || + if(attr.name.match(/^ng:/) || attr.value && attr.value !='null' && attr.value !='auto' && @@ -76,6 +78,7 @@ function sortedHtml(element) { attr.name !='complete' && attr.name !='maxLength' && attr.name !='size' && + attr.name !='class' && attr.name !='start' && attr.name !='tabIndex' && attr.name !='style' && diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js index a053090e..ad98e482 100644 --- a/test/widgetsSpec.js +++ b/test/widgetsSpec.js @@ -22,7 +22,7 @@ describe("widget", function(){ describe("text", function(){ it('should input-text auto init and handle keyup/change events', function(){ - compile('<input type="Text" name="name" value="Misko" ng-change="count = count + 1" ng-init="count=0"/>'); + compile('<input type="Text" name="name" value="Misko" ng:change="count = count + 1" ng:init="count=0"/>'); expect(scope.$get('name')).toEqual("Misko"); expect(scope.$get('count')).toEqual(0); @@ -41,10 +41,10 @@ describe("widget", function(){ expect(scope.$get('count')).toEqual(2); }); - describe("ng-format", function(){ + describe("ng:format", function(){ it("should format text", function(){ - compile('<input type="Text" name="list" value="a,b,c" ng-format="list"/>'); + compile('<input type="Text" name="list" value="a,b,c" ng:format="list"/>'); expect(scope.$get('list')).toEqual(['a', 'b', 'c']); scope.$set('list', ['x', 'y', 'z']); @@ -57,7 +57,7 @@ describe("widget", function(){ }); it("should come up blank if null", function(){ - compile('<input type="text" name="age" ng-format="number"/>', function(){ + compile('<input type="text" name="age" ng:format="number"/>', function(){ scope.age = null; }); expect(scope.age).toBeNull(); @@ -65,7 +65,7 @@ describe("widget", function(){ }); it("should show incorect text while number does not parse", function(){ - compile('<input type="text" name="age" ng-format="number"/>'); + compile('<input type="text" name="age" ng:format="number"/>'); scope.age = 123; scope.$eval(); scope.$element.val('123X'); @@ -76,14 +76,14 @@ describe("widget", function(){ }); it("should clober incorect text if model changes", function(){ - compile('<input type="text" name="age" ng-format="number" value="123X"/>'); + compile('<input type="text" name="age" ng:format="number" value="123X"/>'); scope.age = 456; scope.$eval(); expect(scope.$element.val()).toEqual('456'); }); it("should not clober text if model changes doe to itself", function(){ - compile('<input type="text" name="list" ng-format="list" value="a"/>'); + compile('<input type="text" name="list" ng:format="list" value="a"/>'); scope.$element.val('a '); scope.$element.trigger('change'); @@ -107,7 +107,7 @@ describe("widget", function(){ }); it("should come up blank when no value specifiend", function(){ - compile('<input type="text" name="age" ng-format="number"/>'); + compile('<input type="text" name="age" ng:format="number"/>'); scope.$eval(); expect(scope.$element.val()).toEqual(''); expect(scope.age).toEqual(null); @@ -125,7 +125,7 @@ describe("widget", function(){ }); it('should support type="checkbox"', function(){ - compile('<input type="checkBox" name="checkbox" checked ng-change="action = true"/>'); + compile('<input type="checkBox" name="checkbox" checked ng:change="action = true"/>'); expect(scope.checkbox).toEqual(true); click(element); expect(scope.checkbox).toEqual(false); @@ -134,7 +134,7 @@ describe("widget", function(){ expect(scope.checkbox).toEqual(true); }); - it("should use ng-format", function(){ + it("should use ng:format", function(){ angularFormatter('testFormat', { parse: function(value){ return value ? "Worked" : "Failed"; @@ -146,7 +146,7 @@ describe("widget", function(){ } }); - compile('<input type="checkbox" name="state" ng-format="testFormat" checked/>'); + compile('<input type="checkbox" name="state" ng:format="testFormat" checked/>'); expect(scope.state).toEqual("Worked"); expect(scope.$element[0].checked).toEqual(true); @@ -161,9 +161,9 @@ describe("widget", function(){ }); }); - describe("ng-validate", function(){ - it("should process ng-validate", function(){ - compile('<input type="text" name="price" value="abc" ng-validate="number"/>'); + describe("ng:validate", function(){ + it("should process ng:validate", function(){ + compile('<input type="text" name="price" value="abc" ng:validate="number"/>'); expect(element.hasClass('ng-validation-error')).toBeTruthy(); expect(element.attr('ng-validation-error')).toEqual('Not a number'); @@ -179,7 +179,7 @@ describe("widget", function(){ }); it('should not blow up for validation with bound attributes', function() { - compile('<input type="text" name="price" boo="{{abc}}" ng-required/>'); + compile('<input type="text" name="price" boo="{{abc}}" ng:required/>'); expect(element.hasClass('ng-validation-error')).toBeTruthy(); expect(element.attr('ng-validation-error')).toEqual('Required'); @@ -192,7 +192,7 @@ describe("widget", function(){ it("should not call validator if undefined/empty", function(){ var lastValue = "NOT_CALLED"; angularValidator.myValidator = function(value){lastValue = value;}; - compile('<input type="text" name="url" ng-validate="myValidator"/>'); + compile('<input type="text" name="url" ng:validate="myValidator"/>'); expect(lastValue).toEqual("NOT_CALLED"); scope.url = 'http://server'; @@ -205,19 +205,19 @@ describe("widget", function(){ }); it("should ignore disabled widgets", function(){ - compile('<input type="text" name="price" ng-required disabled/>'); + compile('<input type="text" name="price" ng:required disabled/>'); expect(element.hasClass('ng-validation-error')).toBeFalsy(); expect(element.attr('ng-validation-error')).toBeFalsy(); }); it("should ignore readonly widgets", function(){ - compile('<input type="text" name="price" ng-required readonly/>'); + compile('<input type="text" name="price" ng:required readonly/>'); expect(element.hasClass('ng-validation-error')).toBeFalsy(); expect(element.attr('ng-validation-error')).toBeFalsy(); }); - it("should process ng-required", function(){ - compile('<input type="text" name="price" ng-required/>'); + it("should process ng:required", function(){ + compile('<input type="text" name="price" ng:required/>'); expect(element.hasClass('ng-validation-error')).toBeTruthy(); expect(element.attr('ng-validation-error')).toEqual('Required'); @@ -232,8 +232,8 @@ describe("widget", function(){ expect(element.attr('ng-validation-error')).toEqual('Required'); }); - it('should allow conditions on ng-required', function() { - compile('<input type="text" name="price" ng-required="ineedz"/>'); + it('should allow conditions on ng:required', function() { + compile('<input type="text" name="price" ng:required="ineedz"/>'); scope.$set('ineedz', false); scope.$eval(); expect(element.hasClass('ng-validation-error')).toBeFalsy(); @@ -256,7 +256,7 @@ describe("widget", function(){ expect(element.attr('ng-validation-error')).toBeFalsy(); }); - it("should process ng-required2", function() { + it("should process ng:required2", function() { compile('<textarea name="name">Misko</textarea>'); expect(scope.$get('name')).toEqual("Misko"); @@ -273,14 +273,14 @@ describe("widget", function(){ expect(scope.$get('name')).toEqual('Kai'); }); - it('should call ng-change on button click', function(){ - compile('<input type="button" value="Click Me" ng-change="clicked = true"/>'); + it('should call ng:change on button click', function(){ + compile('<input type="button" value="Click Me" ng:change="clicked = true"/>'); click(element); expect(scope.$get('clicked')).toEqual(true); }); it('should support button alias', function(){ - compile('<button ng-change="clicked = true">Click Me</button>'); + compile('<button ng:change="clicked = true">Click Me</button>'); click(element); expect(scope.$get('clicked')).toEqual(true); }); @@ -289,9 +289,9 @@ describe("widget", function(){ it('should support type="radio"', function(){ compile('<div>' + - '<input type="radio" name="chose" value="A" ng-change="clicked = 1"/>' + - '<input type="radio" name="chose" value="B" checked ng-change="clicked = 2"/>' + - '<input type="radio" name="chose" value="C" ng-change="clicked = 3"/>' + + '<input type="radio" name="chose" value="A" ng:change="clicked = 1"/>' + + '<input type="radio" name="chose" value="B" checked ng:change="clicked = 2"/>' + + '<input type="radio" name="chose" value="C" ng:change="clicked = 3"/>' + '</div>'); var a = element[0].childNodes[0]; var b = element[0].childNodes[1]; @@ -373,8 +373,8 @@ describe("widget", function(){ expect(element.hasClass('ng-exception')).toBeTruthy(); }); - it('should report error on ng-change exception', function(){ - compile('<button ng-change="a-2=x">click</button>'); + it('should report error on ng:change exception', function(){ + compile('<button ng:change="a-2=x">click</button>'); click(element); expect(element.hasClass('ng-exception')).toBeTruthy(); }); @@ -382,7 +382,7 @@ describe("widget", function(){ describe('ng:switch', function(){ it('should switch on value change', function(){ - compile('<ng:switch on="select"><div ng-switch-when="1">first:{{name}}</div><div ng-switch-when="2">second:{{name}}</div></ng:switch>'); + compile('<ng:switch on="select"><div ng:switch-when="1">first:{{name}}</div><div ng:switch-when="2">second:{{name}}</div></ng:switch>'); expect(element.html()).toEqual(''); scope.select = 1; scope.$eval(); @@ -399,7 +399,7 @@ describe("widget", function(){ }); it("should match urls", function(){ - var scope = angular.compile('<ng:switch on="url" using="route:params"><div ng-switch-when="/Book/:name">{{params.name}}</div></ng:switch>'); + var scope = angular.compile('<ng:switch on="url" using="route:params"><div ng:switch-when="/Book/:name">{{params.name}}</div></ng:switch>'); scope.url = '/Book/Moby'; scope.$init(); expect(scope.$element.text()).toEqual('Moby'); @@ -407,12 +407,12 @@ describe("widget", function(){ it("should match sandwich ids", function(){ var scope = {}; - var match = angular.widget['NG:SWITCH'].route.call(scope, '/a/123/b', '/a/:id'); + var match = angular.widget('NG:SWITCH').route.call(scope, '/a/123/b', '/a/:id'); expect(match).toBeFalsy(); }); it('should call init on switch', function(){ - var scope = angular.compile('<ng:switch on="url" change="name=\'works\'"><div ng-switch-when="a">{{name}}</div></ng:switch>'); + var scope = angular.compile('<ng:switch on="url" change="name=\'works\'"><div ng:switch-when="a">{{name}}</div></ng:switch>'); var cleared = false; scope.url = 'a'; scope.$invalidWidgets = {clearOrphans: function(){ |
