mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-21 00:40:24 +00:00
Merge branch 'master' of github.com:angular/angular.js into future
This commit is contained in:
commit
b42072733c
67 changed files with 885 additions and 4057 deletions
1
Rakefile
1
Rakefile
|
|
@ -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 \
|
||||
|
|
|
|||
89
example/buzz/buzz.css
Normal file
89
example/buzz/buzz.css
Normal file
|
|
@ -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));
|
||||
}
|
||||
|
||||
50
example/buzz/buzz.html
Normal file
50
example/buzz/buzz.html
Normal file
|
|
@ -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>
|
||||
46
example/buzz/buzz.js
Normal file
46
example/buzz/buzz.js
Normal file
|
|
@ -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');
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
2
java
Executable file
2
java
Executable file
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Commands/java $@
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
106
src/Angular.js
106
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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(){
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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.";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
43
src/Scope.js
43
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);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ var angularGlobal = {
|
|||
};
|
||||
|
||||
var angularCollection = {
|
||||
'size': size
|
||||
'copy': copy,
|
||||
'size': size,
|
||||
'equals': equals
|
||||
};
|
||||
var angularObject = {
|
||||
'extend': extend
|
||||
|
|
|
|||
|
|
@ -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'));
|
||||
}
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -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);
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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});
|
||||
}
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
|
@ -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]);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
9
test.sh
9
test.sh
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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>");
|
||||
|
|
|
|||
|
|
@ -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(){
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
1
test/angular-mocks.js
vendored
1
test/angular-mocks.js
vendored
|
|
@ -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()();
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
};
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
|
@ -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 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 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>');
|
||||
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 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!');
|
||||
});
|
||||
|
||||
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>' +
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
Base64Test = TestCase('Base64Test');
|
||||
|
||||
Base64Test.prototype.testEncodeDecode = function(){
|
||||
assertEquals(Base64.decode(Base64.encode('hello')), 'hello');
|
||||
};
|
||||
|
|
@ -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);
|
||||
});
|
||||
};
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
|
@ -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());
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -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"}\'/>'));
|
||||
};
|
||||
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -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"}\'/>'));
|
||||
};
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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' &&
|
||||
|
|
|
|||
|
|
@ -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(){
|
||||
|
|
|
|||
Loading…
Reference in a new issue