mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-21 12:51:51 +00:00
moved all uneeded files out, widgets.html works, tests horribly broken
This commit is contained in:
parent
1990cbbf28
commit
258ca5f165
32 changed files with 1629 additions and 1514 deletions
|
|
@ -1,13 +1,8 @@
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
<!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">
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
<head>
|
<head>
|
||||||
<script type="text/javascript" src="../lib/underscore/underscore.js"></script>
|
|
||||||
<script type="text/javascript" src="../lib/jquery/jquery-1.4.2.js"></script>
|
|
||||||
<script type="text/javascript" src="../src/angular-bootstrap.js"></script>
|
<script type="text/javascript" src="../src/angular-bootstrap.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function(){
|
|
||||||
angular.compile(document).init();
|
|
||||||
});
|
|
||||||
function asyncValidate(value, callback){
|
function asyncValidate(value, callback){
|
||||||
var x = value.length % 2 ? null: "even";
|
var x = value.length % 2 ? null: "even";
|
||||||
//callback(x);
|
//callback(x);
|
||||||
|
|
@ -16,7 +11,8 @@
|
||||||
</script>
|
</script>
|
||||||
<link rel="StyleSheet" type="text/css" href="../css/angular.css"/>
|
<link rel="StyleSheet" type="text/css" href="../css/angular.css"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body onload="angular.compile(document).$init()">
|
||||||
|
|
||||||
<input type="checkbox" name="form.checked" ng-format="boolean" value="true" checked="checked" />
|
<input type="checkbox" name="form.checked" ng-format="boolean" value="true" checked="checked" />
|
||||||
<input ng-show="form.checked" name="form.required" ng-required/>
|
<input ng-show="form.checked" name="form.required" ng-required/>
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
@ -26,8 +22,6 @@
|
||||||
<input type="checkbox" name="form.boolean" ng-format="boolean" value="true" checked="checked" />
|
<input type="checkbox" name="form.boolean" ng-format="boolean" value="true" checked="checked" />
|
||||||
<input type="checkbox" name="form.boolean" ng-format="boolean" value="true" />
|
<input type="checkbox" name="form.boolean" ng-format="boolean" value="true" />
|
||||||
<hr/>
|
<hr/>
|
||||||
<input type="text" name="form.async" ng-validate="asynchronous:$window.asyncValidate" />
|
|
||||||
<hr/>
|
|
||||||
<select name="select">
|
<select name="select">
|
||||||
<option>A</option>
|
<option>A</option>
|
||||||
<option selected>B</option>
|
<option selected>B</option>
|
||||||
|
|
|
||||||
7
scenario/style.css
Normal file
7
scenario/style.css
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
|
@ -1,58 +1,84 @@
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
<!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">
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
<head>
|
<head>
|
||||||
<script type="text/javascript" src="../lib/underscore/underscore.js"></script>
|
<link rel="stylesheet" type="text/css" href="style.css"></link>
|
||||||
<script type="text/javascript" src="../lib/jquery/jquery-1.3.2.js"></script>
|
<script type="text/javascript" src="../src/angular-bootstrap.js#autobind&rootScope=$view"></script>
|
||||||
<script type="text/javascript" src="../src/angular-bootstrap.js"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
$(document).ready(function(){angular.compile(document).init();});
|
|
||||||
</script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p>
|
<table>
|
||||||
name: <input type="text" name="name" /> name={{name}} <br/>
|
<tr>
|
||||||
</p>
|
<th>Description</th>
|
||||||
<p>
|
<th>Test</th>
|
||||||
<input type="radio" name="gender" value="female"/> Female
|
<th>Result</th>
|
||||||
<input type="radio" name="gender" value="male"/> Male
|
</tr>
|
||||||
gender={{gender}}
|
<tr><th colspan="3">Input text field</th></tr>
|
||||||
</p>
|
<tr>
|
||||||
<p>
|
<td>basic</td>
|
||||||
<input type="checkbox" name="tea" checked value="on"/> tea={{tea}} <br/>
|
<td><input type="text" name="text.basic" /></td>
|
||||||
<input type="checkbox" name="coffee" value="on"/> coffee={{coffee}} <br/>
|
<td>text.basic={{text.basic}}</td>
|
||||||
</p>
|
</tr>
|
||||||
<p ng-init="count = 0">
|
<tr>
|
||||||
<form>
|
<td>password</td>
|
||||||
<input type="button" value="button" ng-action="count = count + 1"/>
|
<td><input type="password" name="text.password" /></td>
|
||||||
<input type="submit" value="submit" ng-action="count = count + 1"/>
|
<td>text.password={{text.password}}</td>
|
||||||
<input type="image" src="" ng-action="count = count + 1"/>
|
</tr>
|
||||||
<a href="#ERROR" ng-action="count=count+1">action</a>
|
<tr>
|
||||||
count={{count}}
|
<td>hidden</td>
|
||||||
</form>
|
<td><input type="hidden" name="hidden" value="hiddenValue" /></td>
|
||||||
</p>
|
<td>hidden={{hidden}}</td>
|
||||||
<p>
|
</tr>
|
||||||
<select name="select">
|
<tr><th colspan="3">Input selection field</th></tr>
|
||||||
<option>A</option>
|
<tr>
|
||||||
<option>B</option>
|
<td>radio</td>
|
||||||
<option>C</option>
|
<td>
|
||||||
</select>
|
<input type="radio" name="gender" value="female"/> Female <br/>
|
||||||
select={{select}}
|
<input type="radio" name="gender" value="male"/> Male
|
||||||
</p>
|
</td>
|
||||||
<p>
|
<td>gender={{gender}}</td>
|
||||||
<select name="multiple" multiple>
|
</tr>
|
||||||
<option>A</option>
|
<tr>
|
||||||
<option>B</option>
|
<td>checkbox</td>
|
||||||
<option>C</option>
|
<td>
|
||||||
</select>
|
<input type="checkbox" name="checkbox.tea" checked value="on"/> Tea<br/>
|
||||||
multiple={{multiple}}
|
<input type="checkbox" name="checkbox.coffee" value="on"/> Coffe
|
||||||
</p>
|
</td>
|
||||||
<p>
|
<td>checkbox={{checkbox}}</td>
|
||||||
<input type="hidden" name="hidden" value="hiddenValue" />
|
</tr>
|
||||||
Hidden field = {{hidden}}
|
<tr>
|
||||||
</p>
|
<td>select</td>
|
||||||
<p>
|
<td>
|
||||||
<input type="password" name="password" value="passwordValue" />
|
<select name="select">
|
||||||
Password field = {{password}}
|
<option>A</option>
|
||||||
</p>
|
<option>B</option>
|
||||||
|
<option>C</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td>select={{select}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>multiselect</td>
|
||||||
|
<td>
|
||||||
|
<select name="multiselect" multiple>
|
||||||
|
<option>A</option>
|
||||||
|
<option>B</option>
|
||||||
|
<option>C</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td>multiselect={{multiselect}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr><th colspan="3">Buttons</th></tr>
|
||||||
|
<tr>
|
||||||
|
<td>ng-action</td>
|
||||||
|
<td>
|
||||||
|
<form ng-init="button.count = 0">
|
||||||
|
<input type="button" value="button" ng-action="button.count = button.count + 1"/> <br/>
|
||||||
|
<input type="submit" value="submit" ng-action="button.count = button.count + 1"/><br/>
|
||||||
|
<input type="image" src="" ng-action="button.count = button.count + 1"/><br/>
|
||||||
|
<a href="" ng-action="button.count = button.count + 1">action</a>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>button={{button}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
232
src/Angular.js
232
src/Angular.js
|
|
@ -1,22 +1,5 @@
|
||||||
if (typeof document.getAttribute == 'undefined')
|
if (typeof document.getAttribute == 'undefined')
|
||||||
document.getAttribute = function() {};
|
document.getAttribute = function() {};
|
||||||
if (typeof Node == 'undefined') {
|
|
||||||
//TODO: can we get rid of this?
|
|
||||||
Node = {
|
|
||||||
ELEMENT_NODE : 1,
|
|
||||||
ATTRIBUTE_NODE : 2,
|
|
||||||
TEXT_NODE : 3,
|
|
||||||
CDATA_SECTION_NODE : 4,
|
|
||||||
ENTITY_REFERENCE_NODE : 5,
|
|
||||||
ENTITY_NODE : 6,
|
|
||||||
PROCESSING_INSTRUCTION_NODE : 7,
|
|
||||||
COMMENT_NODE : 8,
|
|
||||||
DOCUMENT_NODE : 9,
|
|
||||||
DOCUMENT_TYPE_NODE : 10,
|
|
||||||
DOCUMENT_FRAGMENT_NODE : 11,
|
|
||||||
NOTATION_NODE : 12
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function noop() {}
|
function noop() {}
|
||||||
function identity($) {return $;}
|
function identity($) {return $;}
|
||||||
|
|
@ -32,9 +15,11 @@ function extensionMap(angular, name) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var consoleNode, msie,
|
var consoleNode,
|
||||||
NOOP = 'noop',
|
NOOP = 'noop',
|
||||||
jQuery = window['jQuery'] || window['$'], // weirdness to make IE happy
|
jQuery = window['jQuery'] || window['$'], // weirdness to make IE happy
|
||||||
|
_ = window['_'],
|
||||||
|
jqLite = jQuery,
|
||||||
slice = Array.prototype.slice,
|
slice = Array.prototype.slice,
|
||||||
angular = window['angular'] || (window['angular'] = {}),
|
angular = window['angular'] || (window['angular'] = {}),
|
||||||
angularTextMarkup = extensionMap(angular, 'textMarkup'),
|
angularTextMarkup = extensionMap(angular, 'textMarkup'),
|
||||||
|
|
@ -77,6 +62,7 @@ function extend(dst, obj) {
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isUndefined(value){ return typeof value == 'undefined'; }
|
||||||
function isDefined(value){ return typeof value != 'undefined'; }
|
function isDefined(value){ return typeof value != 'undefined'; }
|
||||||
function isObject(value){ return typeof value == 'object';}
|
function isObject(value){ return typeof value == 'object';}
|
||||||
function isString(value){ return typeof value == 'string';}
|
function isString(value){ return typeof value == 'string';}
|
||||||
|
|
@ -85,6 +71,12 @@ function isFunction(value){ return typeof value == 'function';}
|
||||||
function lowercase(value){ return isString(value) ? value.toLowerCase() : value; }
|
function lowercase(value){ return isString(value) ? value.toLowerCase() : value; }
|
||||||
function uppercase(value){ return isString(value) ? value.toUpperCase() : value; }
|
function uppercase(value){ return isString(value) ? value.toUpperCase() : value; }
|
||||||
function trim(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; };
|
function trim(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; };
|
||||||
|
function includes(array, obj) {
|
||||||
|
for ( var i = 0; i < array.length; i++) {
|
||||||
|
if (obj === array[i]) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function log(a, b, c){
|
function log(a, b, c){
|
||||||
var console = window['console'];
|
var console = window['console'];
|
||||||
|
|
@ -154,18 +146,18 @@ function copy(source, destination){
|
||||||
if (!destination) {
|
if (!destination) {
|
||||||
if (!source) {
|
if (!source) {
|
||||||
return source;
|
return source;
|
||||||
} else if (_.isArray(source)) {
|
} else if (isArray(source)) {
|
||||||
return copy(source, []);
|
return copy(source, []);
|
||||||
} else {
|
} else {
|
||||||
return copy(source, {});
|
return copy(source, {});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (_.isArray(source)) {
|
if (isArray(source)) {
|
||||||
while(destination.length) {
|
while(destination.length) {
|
||||||
destination.pop();
|
destination.pop();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_(destination).each(function(value, key){
|
foreach(function(value, key){
|
||||||
delete destination[key];
|
delete destination[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -236,201 +228,19 @@ function merge(src, dst) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ////////////////////////////
|
|
||||||
// UrlWatcher
|
|
||||||
// ////////////////////////////
|
|
||||||
|
|
||||||
function UrlWatcher(location) {
|
|
||||||
this.location = location;
|
|
||||||
this.delay = 25;
|
|
||||||
this.setTimeout = function(fn, delay) {
|
|
||||||
window.setTimeout(fn, delay);
|
|
||||||
};
|
|
||||||
this.listener = function(url) {
|
|
||||||
return url;
|
|
||||||
};
|
|
||||||
this.expectedUrl = location.href;
|
|
||||||
}
|
|
||||||
|
|
||||||
UrlWatcher.prototype = {
|
|
||||||
listen: function(fn){
|
|
||||||
this.listener = fn;
|
|
||||||
},
|
|
||||||
watch: function() {
|
|
||||||
var self = this;
|
|
||||||
var pull = function() {
|
|
||||||
if (self.expectedUrl !== self.location.href) {
|
|
||||||
var notify = self.location.hash.match(/^#\$iframe_notify=(.*)$/);
|
|
||||||
if (notify) {
|
|
||||||
if (!self.expectedUrl.match(/#/)) {
|
|
||||||
self.expectedUrl += "#";
|
|
||||||
}
|
|
||||||
self.location.href = self.expectedUrl;
|
|
||||||
var id = '_iframe_notify_' + notify[1];
|
|
||||||
var notifyFn = angularCallbacks[id];
|
|
||||||
delete angularCallbacks[id];
|
|
||||||
try {
|
|
||||||
(notifyFn||noop)();
|
|
||||||
} catch (e) {
|
|
||||||
alert(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.listener(self.location.href);
|
|
||||||
self.expectedUrl = self.location.href;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.setTimeout(pull, self.delay);
|
|
||||||
};
|
|
||||||
pull();
|
|
||||||
},
|
|
||||||
|
|
||||||
set: function(url) {
|
|
||||||
var existingURL = this.location.href;
|
|
||||||
if (!existingURL.match(/#/))
|
|
||||||
existingURL += '#';
|
|
||||||
if (existingURL != url)
|
|
||||||
this.location.href = url;
|
|
||||||
this.existingURL = url;
|
|
||||||
},
|
|
||||||
|
|
||||||
get: function() {
|
|
||||||
return window.location.href;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
function configureJQueryPlugins() {
|
|
||||||
var fn = jQuery['fn'];
|
|
||||||
fn['scope'] = function() {
|
|
||||||
var element = this;
|
|
||||||
while (element && element.get(0)) {
|
|
||||||
var scope = element.data("scope");
|
|
||||||
if (scope)
|
|
||||||
return scope;
|
|
||||||
element = element.parent();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
fn['controller'] = function() {
|
|
||||||
return this.data('controller') || NullController.instance;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function configureLogging(config) {
|
|
||||||
if (config.debug == 'console' && !consoleNode) {
|
|
||||||
consoleNode = document.createElement("div");
|
|
||||||
consoleNode.id = 'ng-console';
|
|
||||||
document.getElementsByTagName('body')[0].appendChild(consoleNode);
|
|
||||||
log = function() {
|
|
||||||
consoleLog('ng-console-info', arguments);
|
|
||||||
};
|
|
||||||
console.error = function() {
|
|
||||||
consoleLog('ng-console-error', arguments);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function exposeMethods(obj, methods){
|
|
||||||
var bound = {};
|
|
||||||
foreach(methods, function(fn, name){
|
|
||||||
bound[name] = _(fn).bind(obj);
|
|
||||||
});
|
|
||||||
return bound;
|
|
||||||
}
|
|
||||||
|
|
||||||
function wireAngular(element, config) {
|
|
||||||
var widgetFactory = new WidgetFactory(config['server'], config['database']);
|
|
||||||
var binder = new Binder(element[0], widgetFactory, datastore, config['location'], config);
|
|
||||||
binder.updateListeners.push(config.onUpdateView);
|
|
||||||
var controlBar = new ControlBar(element.find('body'), config['server'], config['database']);
|
|
||||||
var onUpdate = function(){binder.updateView();};
|
|
||||||
var server = config['database'] =="$MEMORY" ?
|
|
||||||
new FrameServer(window) :
|
|
||||||
new Server(config['server'], jQuery['getScript']);
|
|
||||||
server = new VisualServer(server, new NullStatus(element.find('body')), onUpdate);
|
|
||||||
var users = new Users(server, controlBar);
|
|
||||||
var databasePath = '/data/' + config['database'];
|
|
||||||
var post = function(request, callback){
|
|
||||||
server.request("POST", databasePath, request, callback);
|
|
||||||
};
|
|
||||||
var datastore = new DataStore(post, users, binder.anchor);
|
|
||||||
binder.datastore = datastore;
|
|
||||||
binder.updateListeners.push(function(){datastore.flush();});
|
|
||||||
var scope = new Scope({
|
|
||||||
'$anchor' : binder.anchor,
|
|
||||||
'$updateView': _(binder.updateView).bind(binder),
|
|
||||||
'$config' : config,
|
|
||||||
'$invalidWidgets': [],
|
|
||||||
'$console' : window.console,
|
|
||||||
'$datastore' : exposeMethods(datastore, {
|
|
||||||
'load': datastore.load,
|
|
||||||
'loadMany': datastore.loadMany,
|
|
||||||
'loadOrCreate': datastore.loadOrCreate,
|
|
||||||
'loadAll': datastore.loadAll,
|
|
||||||
'save': datastore.save,
|
|
||||||
'remove': datastore.remove,
|
|
||||||
'flush': datastore.flush,
|
|
||||||
'query': datastore.query,
|
|
||||||
'entity': datastore.entity,
|
|
||||||
'entities': datastore.entities,
|
|
||||||
'documentCountsByUser': datastore.documentCountsByUser,
|
|
||||||
'userDocumentIdsByEntity': datastore.userDocumentIdsByEntity,
|
|
||||||
'join': datastore.join
|
|
||||||
}),
|
|
||||||
'$save' : function(callback) {
|
|
||||||
datastore.saveScope(scope.state, callback, binder.anchor);
|
|
||||||
},
|
|
||||||
'$window' : window,
|
|
||||||
'$uid' : function() {
|
|
||||||
return "" + new Date().getTime();
|
|
||||||
},
|
|
||||||
'$users' : users
|
|
||||||
}, "ROOT");
|
|
||||||
|
|
||||||
element.data('scope', scope);
|
|
||||||
binder.entity(scope);
|
|
||||||
binder.compile();
|
|
||||||
controlBar.bind();
|
|
||||||
|
|
||||||
//TODO: remove this code
|
|
||||||
new PopUp(element).bind();
|
|
||||||
|
|
||||||
var self = _(exposeMethods(scope, {
|
|
||||||
'set': scope.set,
|
|
||||||
'get': scope.get,
|
|
||||||
'eval': scope.eval
|
|
||||||
})).extend({
|
|
||||||
'init':function(){
|
|
||||||
config['location']['listen'](_(binder.onUrlChange).bind(binder));
|
|
||||||
binder.parseAnchor();
|
|
||||||
binder.executeInit();
|
|
||||||
binder.updateView();
|
|
||||||
return self;
|
|
||||||
},
|
|
||||||
'element':element[0],
|
|
||||||
'updateView': _(binder.updateView).bind(binder),
|
|
||||||
'config':config
|
|
||||||
});
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
angular['startUrlWatcher'] = function(){
|
|
||||||
var watcher = new UrlWatcher(window['location']);
|
|
||||||
watcher.watch();
|
|
||||||
return exposeMethods(watcher, {'listen':watcher.listen, 'set':watcher.set, 'get':watcher.get});
|
|
||||||
};
|
|
||||||
|
|
||||||
angular['compile'] = function(element, config) {
|
angular['compile'] = function(element, config) {
|
||||||
jQuery = window['jQuery'];
|
config = extend({
|
||||||
msie = jQuery['browser']['msie'];
|
|
||||||
config = _({
|
|
||||||
'onUpdateView': noop,
|
'onUpdateView': noop,
|
||||||
'server': "",
|
'server': "",
|
||||||
'location': {'get':noop, 'set':noop, 'listen':noop}
|
'location': {'get':noop, 'set':noop, 'listen':noop}
|
||||||
}).extend(config||{});
|
}, config||{});
|
||||||
|
|
||||||
configureLogging(config);
|
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget);
|
||||||
configureJQueryPlugins();
|
$element = jqLite(element),
|
||||||
|
rootScope = {
|
||||||
return wireAngular(jQuery(element), config);
|
'$window': window
|
||||||
|
};
|
||||||
|
return rootScope['$root'] = compiler.compile($element)($element, rootScope);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ Template.prototype = {
|
||||||
//Compiler
|
//Compiler
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
function isTextNode(node) {
|
function isTextNode(node) {
|
||||||
return node.nodeType == Node.TEXT_NODE;
|
return node.nodeName == '#text';
|
||||||
}
|
}
|
||||||
|
|
||||||
function eachTextNode(element, fn){
|
function eachTextNode(element, fn){
|
||||||
|
|
@ -92,10 +92,13 @@ Compiler.prototype = {
|
||||||
rawElement = jqLite(rawElement);
|
rawElement = jqLite(rawElement);
|
||||||
var template = this.templatize(rawElement) || new Template();
|
var template = this.templatize(rawElement) || new Template();
|
||||||
return function(element, parentScope){
|
return function(element, parentScope){
|
||||||
var model = scope(parentScope);
|
var scope = createScope(parentScope);
|
||||||
return extend(model, {
|
return extend(scope, {
|
||||||
$element:element,
|
$element:element,
|
||||||
$init: bind(template, template.init, element, model)
|
$init: function() {
|
||||||
|
template.init(element, scope);
|
||||||
|
scope.$eval();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,17 @@ extend(angularFormatter, {
|
||||||
|
|
||||||
'list':formater(
|
'list':formater(
|
||||||
function(obj) { return obj ? obj.join(", ") : obj; },
|
function(obj) { return obj ? obj.join(", ") : obj; },
|
||||||
function(value) {
|
function(value) {
|
||||||
return value ? _(_(value.split(',')).map(jQuery.trim)).select(_.identity) : [];
|
var list = [];
|
||||||
|
foreach(value.split(','), function(item){
|
||||||
|
item = trim(item);
|
||||||
|
if (item) list.push(item);
|
||||||
|
});
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
||||||
'trim':formater(
|
'trim':formater(
|
||||||
function(obj) { return obj ? $.trim("" + obj) : ""; }
|
function(obj) { return obj ? $.trim("" + obj) : ""; }
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ array = [].constructor;
|
||||||
|
|
||||||
function toJson(obj, pretty){
|
function toJson(obj, pretty){
|
||||||
var buf = [];
|
var buf = [];
|
||||||
toJsonArray(buf, obj, pretty ? "\n " : null, _([]));
|
toJsonArray(buf, obj, pretty ? "\n " : null, []);
|
||||||
return buf.join('');
|
return buf.join('');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -27,7 +27,7 @@ angular['fromJson'] = fromJson;
|
||||||
|
|
||||||
function toJsonArray(buf, obj, pretty, stack){
|
function toJsonArray(buf, obj, pretty, stack){
|
||||||
if (typeof obj == "object") {
|
if (typeof obj == "object") {
|
||||||
if (stack.include(obj)) {
|
if (includes(stack, obj)) {
|
||||||
buf.push("RECURSION");
|
buf.push("RECURSION");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
278
src/Scope.js
278
src/Scope.js
|
|
@ -1,253 +1,3 @@
|
||||||
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) {
|
function getter(instance, path) {
|
||||||
if (!path) return instance;
|
if (!path) return instance;
|
||||||
var element = path.split('.');
|
var element = path.split('.');
|
||||||
|
|
@ -262,12 +12,12 @@ function getter(instance, path) {
|
||||||
lastInstance = instance;
|
lastInstance = instance;
|
||||||
instance = instance[key];
|
instance = instance[key];
|
||||||
}
|
}
|
||||||
if (_.isUndefined(instance) && key.charAt(0) == '$') {
|
if (isUndefined(instance) && key.charAt(0) == '$') {
|
||||||
var type = angular['Global']['typeOf'](lastInstance);
|
var type = angular['Global']['typeOf'](lastInstance);
|
||||||
type = angular[type.charAt(0).toUpperCase()+type.substring(1)];
|
type = angular[type.charAt(0).toUpperCase()+type.substring(1)];
|
||||||
var fn = type ? type[[key.substring(1)]] : undefined;
|
var fn = type ? type[[key.substring(1)]] : undefined;
|
||||||
if (fn) {
|
if (fn) {
|
||||||
instance = _.bind(fn, lastInstance, lastInstance);
|
instance = bind(fn, lastInstance, lastInstance);
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -303,24 +53,26 @@ function expressionCompile(exp){
|
||||||
parser.assertAllConsumed();
|
parser.assertAllConsumed();
|
||||||
compileCache[exp] = expFn;
|
compileCache[exp] = expFn;
|
||||||
}
|
}
|
||||||
// return expFn
|
return parserNewScopeAdapter(expFn);
|
||||||
// TODO(remove this hack)
|
};
|
||||||
|
|
||||||
|
// return expFn
|
||||||
|
// TODO(remove this hack)
|
||||||
|
function parserNewScopeAdapter(fn) {
|
||||||
return function(){
|
return function(){
|
||||||
return expFn({
|
return fn({
|
||||||
scope: {
|
scope: {
|
||||||
set: this.$set,
|
set: this.$set,
|
||||||
get: this.$get
|
get: this.$get
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
var NON_RENDERABLE_ELEMENTS = {
|
function isRenderableElement(element) {
|
||||||
'#text': 1, '#comment':1, 'TR':1, 'TH':1
|
var name = element && element[0] && element[0].nodeName;
|
||||||
};
|
return name && name.charAt(0) != '#' &&
|
||||||
|
!includes(['TR', 'COL', 'COLGROUP', 'TBODY', 'THEAD', 'TFOOT'], name);
|
||||||
function isRenderableElement(element){
|
|
||||||
return element && element[0] && !NON_RENDERABLE_ELEMENTS[element[0].nodeName];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function rethrow(e) { throw e; }
|
function rethrow(e) { throw e; }
|
||||||
|
|
@ -334,7 +86,7 @@ function errorHandlerFor(element) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function scope(parent, Class) {
|
function createScope(parent, Class) {
|
||||||
function Parent(){}
|
function Parent(){}
|
||||||
function API(){}
|
function API(){}
|
||||||
function Behavior(){}
|
function Behavior(){}
|
||||||
|
|
|
||||||
62
src/UrlWatcher.js
Normal file
62
src/UrlWatcher.js
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
|
||||||
|
// ////////////////////////////
|
||||||
|
// UrlWatcher
|
||||||
|
// ////////////////////////////
|
||||||
|
|
||||||
|
function UrlWatcher(location) {
|
||||||
|
this.location = location;
|
||||||
|
this.delay = 25;
|
||||||
|
this.setTimeout = function(fn, delay) {
|
||||||
|
window.setTimeout(fn, delay);
|
||||||
|
};
|
||||||
|
this.listener = function(url) {
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
this.expectedUrl = location.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
UrlWatcher.prototype = {
|
||||||
|
listen: function(fn){
|
||||||
|
this.listener = fn;
|
||||||
|
},
|
||||||
|
watch: function() {
|
||||||
|
var self = this;
|
||||||
|
var pull = function() {
|
||||||
|
if (self.expectedUrl !== self.location.href) {
|
||||||
|
var notify = self.location.hash.match(/^#\$iframe_notify=(.*)$/);
|
||||||
|
if (notify) {
|
||||||
|
if (!self.expectedUrl.match(/#/)) {
|
||||||
|
self.expectedUrl += "#";
|
||||||
|
}
|
||||||
|
self.location.href = self.expectedUrl;
|
||||||
|
var id = '_iframe_notify_' + notify[1];
|
||||||
|
var notifyFn = angularCallbacks[id];
|
||||||
|
delete angularCallbacks[id];
|
||||||
|
try {
|
||||||
|
(notifyFn||noop)();
|
||||||
|
} catch (e) {
|
||||||
|
alert(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.listener(self.location.href);
|
||||||
|
self.expectedUrl = self.location.href;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.setTimeout(pull, self.delay);
|
||||||
|
};
|
||||||
|
pull();
|
||||||
|
},
|
||||||
|
|
||||||
|
set: function(url) {
|
||||||
|
var existingURL = this.location.href;
|
||||||
|
if (!existingURL.match(/#/))
|
||||||
|
existingURL += '#';
|
||||||
|
if (existingURL != url)
|
||||||
|
this.location.href = url;
|
||||||
|
this.existingURL = url;
|
||||||
|
},
|
||||||
|
|
||||||
|
get: function() {
|
||||||
|
return window.location.href;
|
||||||
|
}
|
||||||
|
};
|
||||||
913
src/Widgets.js
913
src/Widgets.js
|
|
@ -1,806 +1,137 @@
|
||||||
function WidgetFactory(serverUrl, database) {
|
function modelAccessor(scope, element) {
|
||||||
this.nextUploadId = 0;
|
var expr = element.attr('name'),
|
||||||
this.serverUrl = serverUrl;
|
farmatterName = element.attr('ng-format') || NOOP,
|
||||||
this.database = database;
|
formatter = angularFormatter(farmatterName);
|
||||||
if (window['swfobject']) {
|
if (!expr) throw "Required field 'name' not found.";
|
||||||
this.createSWF = window['swfobject']['createSWF'];
|
if (!formatter) throw "Formatter named '" + farmatterName + "' not found.";
|
||||||
} else {
|
return {
|
||||||
this.createSWF = function(){
|
get: function() {
|
||||||
alert("ERROR: swfobject not loaded!");
|
return formatter['format'](scope.$eval(expr));
|
||||||
};
|
},
|
||||||
}
|
set: function(value) {
|
||||||
};
|
scope.$eval(expr + '=' + toJson(formatter['parse'](value)));
|
||||||
|
|
||||||
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) {
|
function compileValidator(expr) {
|
||||||
var uploadId = '__uploadWidget_' + (this.nextUploadId++);
|
return new Parser(expr).validator()();
|
||||||
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) {
|
function valueAccessor(element) {
|
||||||
this.view = view;
|
var validatorName = element.attr('ng-validate') || NOOP,
|
||||||
this.uploader = uploader;
|
validator = compileValidator(validatorName),
|
||||||
this.scopeName = scopeName;
|
required = element.attr('ng-required'),
|
||||||
this.attachmentsPath = databaseUrl + '/_attachments';
|
lastError;
|
||||||
this.value = null;
|
required = required || required == '';
|
||||||
this.lastValue = undefined;
|
if (!validator) throw "Validator named '" + validatorName + "' not found.";
|
||||||
};
|
function validate(value) {
|
||||||
|
var error = required && !trim(value) ? "Required" : validator.call(this, value);
|
||||||
angularCallbacks['flashEvent'] = function(id, event, args) {
|
if (error !== lastError) {
|
||||||
var object = document.getElementById(id);
|
if (error) {
|
||||||
var jobject = jQuery(object);
|
element.addClass(NG_VALIDATION_ERROR);
|
||||||
var controller = jobject.parent().data("controller");
|
element.attr(NG_ERROR, error);
|
||||||
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 {
|
} else {
|
||||||
scope.setEval(this.exp, value);
|
element.removeClass(NG_VALIDATION_ERROR);
|
||||||
this.lastValue = value;
|
element.removeAttr(NG_ERROR);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
lastError = error;
|
||||||
}
|
}
|
||||||
return 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
return {
|
||||||
|
get: function(){ return validate(element.val()); },
|
||||||
|
set: function(value){ element.val(validate(value)); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////
|
function checkedAccessor(element) {
|
||||||
// RadioController
|
var domElement = element[0];
|
||||||
///////////////////////
|
return {
|
||||||
function RadioController(view, exp) {
|
get: function(){ return !!domElement.checked; },
|
||||||
this.view = view;
|
set: function(value){ domElement.checked = !!value; }
|
||||||
this.exp = exp;
|
};
|
||||||
this.lastChecked = undefined;
|
}
|
||||||
this.lastValue = undefined;
|
|
||||||
this.inputValue = view.value;
|
|
||||||
this.initialValue = view.checked ? view.value : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
RadioController.prototype = {
|
function radioAccessor(element) {
|
||||||
updateModel: function(scope) {
|
var domElement = element[0];
|
||||||
var input = this.view;
|
return {
|
||||||
if (this.lastChecked) {
|
get: function(){ return domElement.checked ? domElement.value : null; },
|
||||||
return false;
|
set: function(value){ domElement.checked = value == domElement.value; }
|
||||||
} else {
|
};
|
||||||
input.checked = true;
|
}
|
||||||
this.lastValue = scope.setEval(this.exp, this.inputValue);
|
|
||||||
this.lastChecked = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateView: function(scope) {
|
function optionsAccessor(element) {
|
||||||
var input = this.view;
|
var options = element[0].options;
|
||||||
var value = scope.get(this.exp);
|
return {
|
||||||
if (this.initialValue && typeof value === "undefined") {
|
get: function(){
|
||||||
value = this.initialValue;
|
var values = [];
|
||||||
scope.setEval(this.exp, value);
|
foreach(options, function(option){
|
||||||
}
|
if (option.selected) values.push(option.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
|
return values;
|
||||||
for ( var r = childrenLength; r > iteratorCounter; --r) {
|
},
|
||||||
this.children.pop().element.remove();
|
set: function(values){
|
||||||
}
|
var keys = {};
|
||||||
// Special case for option in select
|
foreach(values, function(value){ keys[value] = true; });
|
||||||
if (child && child.element[0].nodeName === "OPTION") {
|
foreach(options, function(option){
|
||||||
var select = jQuery(child.element[0].parentNode);
|
option.selected = keys[option.value];
|
||||||
var cntl = select.data('controller');
|
});
|
||||||
if (cntl) {
|
}
|
||||||
cntl.lastValue = undefined;
|
};
|
||||||
cntl.updateView(scope);
|
}
|
||||||
}
|
|
||||||
}
|
function noopAccessor() { return { get: noop, set: noop }; }
|
||||||
|
|
||||||
|
var NG_ERROR = 'ng-error',
|
||||||
|
NG_VALIDATION_ERROR = 'ng-validation-error',
|
||||||
|
textWidget = inputWidget('keyup change', modelAccessor, valueAccessor, ''),
|
||||||
|
buttonWidget = inputWidget('click', noopAccessor, noopAccessor, undefined),
|
||||||
|
INPUT_TYPE = {
|
||||||
|
'text': textWidget,
|
||||||
|
'textarea': textWidget,
|
||||||
|
'hidden': textWidget,
|
||||||
|
'password': textWidget,
|
||||||
|
'button': buttonWidget,
|
||||||
|
'submit': buttonWidget,
|
||||||
|
'reset': buttonWidget,
|
||||||
|
'image': buttonWidget,
|
||||||
|
'checkbox': inputWidget('click', modelAccessor, checkedAccessor, false),
|
||||||
|
'radio': inputWidget('click', modelAccessor, radioAccessor, undefined),
|
||||||
|
'select-one': inputWidget('click', modelAccessor, valueAccessor, null),
|
||||||
|
'select-multiple': inputWidget('click', modelAccessor, optionsAccessor, [])
|
||||||
|
// 'file': fileWidget???
|
||||||
|
};
|
||||||
|
|
||||||
|
function inputWidget(events, modelAccessor, viewAccessor, initValue) {
|
||||||
|
return function(element) {
|
||||||
|
var scope = this,
|
||||||
|
model = modelAccessor(scope, element),
|
||||||
|
view = viewAccessor(element),
|
||||||
|
action = element.attr('ng-action') || '',
|
||||||
|
value = view.get() || copy(initValue);
|
||||||
|
if (isDefined(value)) model.set(value);
|
||||||
|
this.$eval(element.attr('ng-init')||'');
|
||||||
|
element.bind(events, function(){
|
||||||
|
model.set(view.get());
|
||||||
|
scope.$tryEval(action, element);
|
||||||
|
scope.$root.$eval();
|
||||||
|
// if we have no initValue than we are just a button,
|
||||||
|
// therefore we want to prevent default action
|
||||||
|
return isDefined(initValue);
|
||||||
});
|
});
|
||||||
}
|
scope.$watch(model.get, view.set);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////
|
function inputWidgetSelector(element){
|
||||||
// PopUp
|
return INPUT_TYPE[lowercase(element[0].type)] || noop;
|
||||||
//////////////////////////////////
|
}
|
||||||
|
|
||||||
function PopUp(doc) {
|
angularWidget('INPUT', inputWidgetSelector);
|
||||||
this.doc = doc;
|
angularWidget('TEXTAREA', inputWidgetSelector);
|
||||||
};
|
angularWidget('BUTTON', inputWidgetSelector);
|
||||||
|
angularWidget('SELECT', function(element){
|
||||||
PopUp.OUT_EVENT = "mouseleave mouseout click dblclick keypress keyup";
|
this.descend(true);
|
||||||
|
return inputWidgetSelector.call(this, element);
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
||||||
61
src/angular-bootstrap.js
vendored
61
src/angular-bootstrap.js
vendored
|
|
@ -1,18 +1,18 @@
|
||||||
/**
|
/**
|
||||||
* The MIT License
|
* The MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
|
* Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
* in the Software without restriction, including without limitation the rights
|
* in the Software without restriction, including without limitation the rights
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
* furnished to do so, subject to the following conditions:
|
* furnished to do so, subject to the following conditions:
|
||||||
*
|
*
|
||||||
* The above copyright notice and this permission notice shall be included in
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* all copies or substantial portions of the Software.
|
* all copies or substantial portions of the Software.
|
||||||
*
|
*
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
|
@ -22,35 +22,58 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
(function(previousOnLoad){
|
(function(previousOnLoad){
|
||||||
var filename = /(.*)\/angular-(.*).js/;
|
var filename = /(.*)\/angular-(.*).js(#(.*))?/;
|
||||||
var scripts = document.getElementsByTagName("script");
|
var scripts = document.getElementsByTagName("SCRIPT");
|
||||||
var serverPath;
|
var serverPath;
|
||||||
|
var config = {};
|
||||||
for(var j = 0; j < scripts.length; j++) {
|
for(var j = 0; j < scripts.length; j++) {
|
||||||
var match = (scripts[j].src || "").match(filename);
|
var match = (scripts[j].src || "").match(filename);
|
||||||
if (match) {
|
if (match) {
|
||||||
serverPath = match[1];
|
serverPath = match[1];
|
||||||
|
parseConfig(match[4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseConfig(args) {
|
||||||
|
var keyValues = args.split('&'), keyValue, i = 0;
|
||||||
|
for (; i < keyValues.length; i++) {
|
||||||
|
keyValue = keyValues[i].split('=');
|
||||||
|
config[keyValue[0]] = keyValue[1] || true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addScript(file){
|
function addScript(file){
|
||||||
document.write('<script type="text/javascript" src="' + serverPath + file +'"></script>');
|
document.write('<script type="text/javascript" src="' + serverPath + file +'"></script>');
|
||||||
};
|
}
|
||||||
|
|
||||||
addScript("/Angular.js");
|
addScript("/Angular.js");
|
||||||
addScript("/API.js");
|
|
||||||
addScript("/Binder.js");
|
|
||||||
addScript("/ControlBar.js");
|
|
||||||
addScript("/DataStore.js");
|
|
||||||
addScript("/Filters.js");
|
|
||||||
addScript("/Formatters.js");
|
|
||||||
addScript("/JSON.js");
|
addScript("/JSON.js");
|
||||||
addScript("/Model.js");
|
addScript("/Compiler.js");
|
||||||
|
addScript("/Scope.js");
|
||||||
|
addScript("/jqlite.js");
|
||||||
addScript("/Parser.js");
|
addScript("/Parser.js");
|
||||||
addScript("/Resource.js");
|
addScript("/Resource.js");
|
||||||
addScript("/Scope.js");
|
addScript("/URLWatcher.js");
|
||||||
addScript("/Server.js");
|
|
||||||
addScript("/Users.js");
|
// Extension points
|
||||||
addScript("/Validators.js");
|
addScript("/apis.js");
|
||||||
addScript("/Widgets.js");
|
addScript("/filters.js");
|
||||||
|
addScript("/formatters.js");
|
||||||
|
addScript("/validators.js");
|
||||||
|
addScript("/directives.js");
|
||||||
|
addScript("/markups.js");
|
||||||
|
addScript("/widgets.js");
|
||||||
|
|
||||||
|
if (config.autobind) {
|
||||||
|
window.onload = function(){
|
||||||
|
try {
|
||||||
|
if (previousOnLoad) previousOnLoad();
|
||||||
|
} catch(e) {}
|
||||||
|
var scope = angular.compile(window.document, config);
|
||||||
|
if (config.rootScope) window[config.rootScope] = scope;
|
||||||
|
scope.$init();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
})(window.onload);
|
})(window.onload);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ var angularArray = {
|
||||||
if (fn($)){
|
if (fn($)){
|
||||||
defaultValue = $;
|
defaultValue = $;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
},
|
},
|
||||||
|
|
@ -146,7 +146,7 @@ var angularArray = {
|
||||||
},
|
},
|
||||||
'orderBy':function(array, expression, descend) {
|
'orderBy':function(array, expression, descend) {
|
||||||
function reverse(comp, descending) {
|
function reverse(comp, descending) {
|
||||||
return toBoolean(descending) ?
|
return toBoolean(descending) ?
|
||||||
function(a,b){return comp(b,a);} : comp;
|
function(a,b){return comp(b,a);} : comp;
|
||||||
}
|
}
|
||||||
function compare(v1, v2){
|
function compare(v1, v2){
|
||||||
|
|
@ -255,7 +255,7 @@ var angularString = {
|
||||||
},
|
},
|
||||||
'toDate':function(string){
|
'toDate':function(string){
|
||||||
var match;
|
var match;
|
||||||
if (typeof string == 'string' &&
|
if (typeof string == 'string' &&
|
||||||
(match = string.match(/^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/))){
|
(match = string.match(/^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/))){
|
||||||
var date = new Date(0);
|
var date = new Date(0);
|
||||||
date.setUTCFullYear(match[1], match[2] - 1, match[3]);
|
date.setUTCFullYear(match[1], match[2] - 1, match[3]);
|
||||||
|
|
@ -269,12 +269,13 @@ var angularString = {
|
||||||
var angularDate = {
|
var angularDate = {
|
||||||
'toString':function(date){
|
'toString':function(date){
|
||||||
function pad(n) { return n < 10 ? "0" + n : n; }
|
function pad(n) { return n < 10 ? "0" + n : n; }
|
||||||
return (date.getUTCFullYear()) + '-' +
|
return !date ? date :
|
||||||
|
date.getUTCFullYear() + '-' +
|
||||||
pad(date.getUTCMonth() + 1) + '-' +
|
pad(date.getUTCMonth() + 1) + '-' +
|
||||||
pad(date.getUTCDate()) + 'T' +
|
pad(date.getUTCDate()) + 'T' +
|
||||||
pad(date.getUTCHours()) + ':' +
|
pad(date.getUTCHours()) + ':' +
|
||||||
pad(date.getUTCMinutes()) + ':' +
|
pad(date.getUTCMinutes()) + ':' +
|
||||||
pad(date.getUTCSeconds()) + 'Z';
|
pad(date.getUTCSeconds()) + 'Z' ;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -295,25 +296,27 @@ var angularFunction = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function defineApi(dst, chain, underscoreNames){
|
function defineApi(dst, chain, underscoreNames){
|
||||||
var lastChain = _.last(chain);
|
if (_) {
|
||||||
foreach(underscoreNames, function(name){
|
var lastChain = _.last(chain);
|
||||||
lastChain[name] = _[name];
|
foreach(underscoreNames, function(name){
|
||||||
});
|
lastChain[name] = _[name];
|
||||||
|
});
|
||||||
|
}
|
||||||
angular[dst] = angular[dst] || {};
|
angular[dst] = angular[dst] || {};
|
||||||
foreach(chain, function(parent){
|
foreach(chain, function(parent){
|
||||||
extend(angular[dst], parent);
|
extend(angular[dst], parent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
defineApi('Global', [angularGlobal],
|
defineApi('Global', [angularGlobal],
|
||||||
['extend', 'clone','isEqual',
|
['extend', 'clone','isEqual',
|
||||||
'isElement', 'isArray', 'isFunction', 'isUndefined']);
|
'isElement', 'isArray', 'isFunction', 'isUndefined']);
|
||||||
defineApi('Collection', [angularGlobal, angularCollection],
|
defineApi('Collection', [angularGlobal, angularCollection],
|
||||||
['each', 'map', 'reduce', 'reduceRight', 'detect',
|
['each', 'map', 'reduce', 'reduceRight', 'detect',
|
||||||
'select', 'reject', 'all', 'any', 'include',
|
'select', 'reject', 'all', 'any', 'include',
|
||||||
'invoke', 'pluck', 'max', 'min', 'sortBy',
|
'invoke', 'pluck', 'max', 'min', 'sortBy',
|
||||||
'sortedIndex', 'toArray', 'size']);
|
'sortedIndex', 'toArray', 'size']);
|
||||||
defineApi('Array', [angularGlobal, angularCollection, angularArray],
|
defineApi('Array', [angularGlobal, angularCollection, angularArray],
|
||||||
['first', 'last', 'compact', 'flatten', 'without',
|
['first', 'last', 'compact', 'flatten', 'without',
|
||||||
'uniq', 'intersect', 'zip', 'indexOf', 'lastIndexOf']);
|
'uniq', 'intersect', 'zip', 'indexOf', 'lastIndexOf']);
|
||||||
defineApi('Object', [angularGlobal, angularCollection, angularObject],
|
defineApi('Object', [angularGlobal, angularCollection, angularObject],
|
||||||
['keys', 'values']);
|
['keys', 'values']);
|
||||||
407
src/delete/Scope.js
Normal file
407
src/delete/Scope.js
Normal file
|
|
@ -0,0 +1,407 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
806
src/delete/Widgets.js
Normal file
806
src/delete/Widgets.js
Normal file
|
|
@ -0,0 +1,806 @@
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -11,9 +11,15 @@ angularDirective("ng-eval", function(expression){
|
||||||
});
|
});
|
||||||
|
|
||||||
angularDirective("ng-bind", function(expression){
|
angularDirective("ng-bind", function(expression){
|
||||||
|
var templateFn = compileBindTemplate("{{" + expression + "}}");
|
||||||
return function(element) {
|
return function(element) {
|
||||||
this.$watch(expression, function(value){
|
var lastValue;
|
||||||
element.text(value);
|
this.$onEval(function() {
|
||||||
|
var value = templateFn.call(this);
|
||||||
|
if (value != lastValue) {
|
||||||
|
element.text(value);
|
||||||
|
lastValue = value;
|
||||||
|
}
|
||||||
}, element);
|
}, element);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -34,7 +40,9 @@ function compileBindTemplate(template){
|
||||||
bindTemplateCache[template] = fn = function(){
|
bindTemplateCache[template] = fn = function(){
|
||||||
var parts = [], self = this;
|
var parts = [], self = this;
|
||||||
foreach(bindings, function(fn){
|
foreach(bindings, function(fn){
|
||||||
parts.push(fn.call(self));
|
var value = fn.call(self);
|
||||||
|
if (isObject(value)) value = toJson(value, true);
|
||||||
|
parts.push(value);
|
||||||
});
|
});
|
||||||
return parts.join('');
|
return parts.join('');
|
||||||
};
|
};
|
||||||
|
|
@ -125,6 +133,7 @@ angularDirective("ng-action", function(expression, element){
|
||||||
var self = this;
|
var self = this;
|
||||||
element.click(function(){
|
element.click(function(){
|
||||||
self.$tryEval(expression, element);
|
self.$tryEval(expression, element);
|
||||||
|
self.$eval();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@ function JQLite(element) {
|
||||||
this[0] = element;
|
this[0] = element;
|
||||||
}
|
}
|
||||||
|
|
||||||
function jqLite(element) {
|
|
||||||
|
function jqLiteWrap(element) {
|
||||||
if (typeof element == 'string') {
|
if (typeof element == 'string') {
|
||||||
var div = document.createElement('div');
|
var div = document.createElement('div');
|
||||||
div.innerHTML = element;
|
div.innerHTML = element;
|
||||||
|
|
@ -47,6 +48,8 @@ function jqLite(element) {
|
||||||
return element instanceof JQLite ? element : new JQLite(element);
|
return element instanceof JQLite ? element : new JQLite(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jqLite = jqLite || jqLiteWrap;
|
||||||
|
|
||||||
JQLite.prototype = {
|
JQLite.prototype = {
|
||||||
data: function(key, value) {
|
data: function(key, value) {
|
||||||
var element = this[0],
|
var element = this[0],
|
||||||
|
|
@ -85,12 +88,15 @@ JQLite.prototype = {
|
||||||
foreach(type.split(' '), function(type){
|
foreach(type.split(' '), function(type){
|
||||||
eventHandler = bind[type];
|
eventHandler = bind[type];
|
||||||
if (!eventHandler) {
|
if (!eventHandler) {
|
||||||
bind[type] = eventHandler = function() {
|
bind[type] = eventHandler = function(event) {
|
||||||
var value = false;
|
var bubbleEvent = false;
|
||||||
foreach(eventHandler.fns, function(fn){
|
foreach(eventHandler.fns, function(fn){
|
||||||
value = value || fn.apply(self, arguments);
|
bubbleEvent = bubbleEvent || fn.apply(self, arguments);
|
||||||
});
|
});
|
||||||
return value;
|
if (!bubbleEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
eventHandler.fns = [];
|
eventHandler.fns = [];
|
||||||
addEventListener(element, type, eventHandler);
|
addEventListener(element, type, eventHandler);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ function binding(string) {
|
||||||
};
|
};
|
||||||
|
|
||||||
function hasBindings(bindings) {
|
function hasBindings(bindings) {
|
||||||
return bindings.length > 1 || Binder.binding(bindings[0]) !== null;
|
return bindings.length > 1 || binding(bindings[0]) !== null;
|
||||||
};
|
};
|
||||||
|
|
||||||
angularTextMarkup('{{}}', function(text, textNode, parentElement) {
|
angularTextMarkup('{{}}', function(text, textNode, parentElement) {
|
||||||
129
src/widgets2.js
129
src/widgets2.js
|
|
@ -1,129 +0,0 @@
|
||||||
function modelAccessor(scope, element) {
|
|
||||||
var expr = element.attr('name'),
|
|
||||||
farmatterName = element.attr('ng-format') || NOOP,
|
|
||||||
formatter = angularFormatter(farmatterName);
|
|
||||||
if (!expr) throw "Required field 'name' not found.";
|
|
||||||
if (!formatter) throw "Formatter named '" + farmatterName + "' not found.";
|
|
||||||
return {
|
|
||||||
get: function() {
|
|
||||||
return formatter['format'](scope.$eval(expr));
|
|
||||||
},
|
|
||||||
set: function(value) {
|
|
||||||
scope.$eval(expr + '=' + toJson(formatter['parse'](value)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function valueAccessor(element) {
|
|
||||||
var validatorName = element.attr('ng-validate') || NOOP,
|
|
||||||
validator = angularValidator(validatorName),
|
|
||||||
required = element.attr('ng-required'),
|
|
||||||
lastError;
|
|
||||||
required = required || required == '';
|
|
||||||
if (!validator) throw "Validator named '" + validatorName + "' not found.";
|
|
||||||
function validate(value) {
|
|
||||||
var error = required && !trim(value) ? "Required" : validator(value);
|
|
||||||
if (error !== lastError) {
|
|
||||||
if (error) {
|
|
||||||
element.addClass(NG_VALIDATION_ERROR);
|
|
||||||
element.attr(NG_ERROR, error);
|
|
||||||
} else {
|
|
||||||
element.removeClass(NG_VALIDATION_ERROR);
|
|
||||||
element.removeAttr(NG_ERROR);
|
|
||||||
}
|
|
||||||
lastError = error;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
get: function(){ return validate(element.val()); },
|
|
||||||
set: function(value){ element.val(validate(value)); }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkedAccessor(element) {
|
|
||||||
var domElement = element[0];
|
|
||||||
return {
|
|
||||||
get: function(){ return !!domElement.checked; },
|
|
||||||
set: function(value){ domElement.checked = !!value; }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function radioAccessor(element) {
|
|
||||||
var domElement = element[0];
|
|
||||||
return {
|
|
||||||
get: function(){ return domElement.checked ? domElement.value : null; },
|
|
||||||
set: function(value){ domElement.checked = value == domElement.value; }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function optionsAccessor(element) {
|
|
||||||
var options = element[0].options;
|
|
||||||
return {
|
|
||||||
get: function(){
|
|
||||||
var values = [];
|
|
||||||
foreach(options, function(option){
|
|
||||||
if (option.selected) values.push(option.value);
|
|
||||||
});
|
|
||||||
return values;
|
|
||||||
},
|
|
||||||
set: function(values){
|
|
||||||
var keys = {};
|
|
||||||
foreach(values, function(value){ keys[value] = true; });
|
|
||||||
foreach(options, function(option){
|
|
||||||
option.selected = keys[option.value];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function noopAccessor() { return { get: noop, set: noop }; }
|
|
||||||
|
|
||||||
var NG_ERROR = 'ng-error',
|
|
||||||
NG_VALIDATION_ERROR = 'ng-validation-error',
|
|
||||||
textWidget = inputWidget('keyup change', modelAccessor, valueAccessor, ''),
|
|
||||||
buttonWidget = inputWidget('click', noopAccessor, noopAccessor, undefined),
|
|
||||||
INPUT_TYPE = {
|
|
||||||
'text': textWidget,
|
|
||||||
'textarea': textWidget,
|
|
||||||
'hidden': textWidget,
|
|
||||||
'password': textWidget,
|
|
||||||
'button': buttonWidget,
|
|
||||||
'submit': buttonWidget,
|
|
||||||
'reset': buttonWidget,
|
|
||||||
'image': buttonWidget,
|
|
||||||
'checkbox': inputWidget('click', modelAccessor, checkedAccessor, false),
|
|
||||||
'radio': inputWidget('click', modelAccessor, radioAccessor, undefined),
|
|
||||||
'select-one': inputWidget('click', modelAccessor, valueAccessor, null),
|
|
||||||
'select-multiple': inputWidget('click', modelAccessor, optionsAccessor, [])
|
|
||||||
// 'file': fileWidget???
|
|
||||||
};
|
|
||||||
|
|
||||||
function inputWidget(events, modelAccessor, viewAccessor, initValue) {
|
|
||||||
return function(element) {
|
|
||||||
var scope = this,
|
|
||||||
model = modelAccessor(scope, element),
|
|
||||||
view = viewAccessor(element),
|
|
||||||
action = element.attr('ng-action') || '',
|
|
||||||
value = view.get() || copy(initValue);
|
|
||||||
if (isDefined(value)) model.set(value);
|
|
||||||
this.$eval(element.attr('ng-init')||'');
|
|
||||||
element.bind(events, function(){
|
|
||||||
model.set(view.get());
|
|
||||||
scope.$tryEval(action, element);
|
|
||||||
});
|
|
||||||
scope.$watch(model.get, view.set);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function inputWidgetSelector(element){
|
|
||||||
return INPUT_TYPE[lowercase(element[0].type)] || noop;
|
|
||||||
}
|
|
||||||
|
|
||||||
angularWidget('INPUT', inputWidgetSelector);
|
|
||||||
angularWidget('TEXTAREA', inputWidgetSelector);
|
|
||||||
angularWidget('BUTTON', inputWidgetSelector);
|
|
||||||
angularWidget('SELECT', function(element){
|
|
||||||
this.descend(true);
|
|
||||||
return inputWidgetSelector.call(this, element);
|
|
||||||
});
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
describe('scope/model', function(){
|
describe('scope/model', function(){
|
||||||
|
|
||||||
it('should create a scope with parent', function(){
|
it('should create a scope with parent', function(){
|
||||||
var model = scope({name:'Misko'});
|
var model = createScope({name:'Misko'});
|
||||||
expect(model.name).toEqual('Misko');
|
expect(model.name).toEqual('Misko');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have $get/set$/parent$', function(){
|
it('should have $get/set$/parent$', function(){
|
||||||
var parent = {};
|
var parent = {};
|
||||||
var model = scope(parent);
|
var model = createScope(parent);
|
||||||
model.$set('name', 'adam');
|
model.$set('name', 'adam');
|
||||||
expect(model.name).toEqual('adam');
|
expect(model.name).toEqual('adam');
|
||||||
expect(model.$get('name')).toEqual('adam');
|
expect(model.$get('name')).toEqual('adam');
|
||||||
|
|
@ -16,7 +16,7 @@ describe('scope/model', function(){
|
||||||
|
|
||||||
//$eval
|
//$eval
|
||||||
it('should eval function with correct this and pass arguments', function(){
|
it('should eval function with correct this and pass arguments', function(){
|
||||||
var model = scope();
|
var model = createScope();
|
||||||
model.$eval(function(name){
|
model.$eval(function(name){
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}, 'works');
|
}, 'works');
|
||||||
|
|
@ -24,14 +24,14 @@ describe('scope/model', function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should eval expression with correct this', function(){
|
it('should eval expression with correct this', function(){
|
||||||
var model = scope();
|
var model = createScope();
|
||||||
model.$eval('name="works"');
|
model.$eval('name="works"');
|
||||||
expect(model.name).toEqual('works');
|
expect(model.name).toEqual('works');
|
||||||
});
|
});
|
||||||
|
|
||||||
//$onEval
|
//$onEval
|
||||||
it('should watch an expression for change', function(){
|
it('should watch an expression for change', function(){
|
||||||
var model = scope();
|
var model = createScope();
|
||||||
model.oldValue = "";
|
model.oldValue = "";
|
||||||
var count = 0;
|
var count = 0;
|
||||||
model.name = 'adam';
|
model.name = 'adam';
|
||||||
|
|
@ -48,7 +48,7 @@ describe('scope/model', function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should eval with no arguments', function(){
|
it('should eval with no arguments', function(){
|
||||||
var model = scope();
|
var model = createScope();
|
||||||
var count = 0;
|
var count = 0;
|
||||||
model.$onEval(function(){count++;});
|
model.$onEval(function(){count++;});
|
||||||
model.$eval();
|
model.$eval();
|
||||||
|
|
@ -57,7 +57,7 @@ describe('scope/model', function(){
|
||||||
|
|
||||||
//$bind
|
//$bind
|
||||||
it('should curry a function with respect to scope', function(){
|
it('should curry a function with respect to scope', function(){
|
||||||
var model = scope();
|
var model = createScope();
|
||||||
model.name = 'misko';
|
model.name = 'misko';
|
||||||
expect(model.$bind(function(){return this.name;})()).toEqual('misko');
|
expect(model.$bind(function(){return this.name;})()).toEqual('misko');
|
||||||
});
|
});
|
||||||
|
|
@ -70,7 +70,7 @@ describe('scope/model', function(){
|
||||||
Printer.prototype.print = function(){
|
Printer.prototype.print = function(){
|
||||||
this.printed = true;
|
this.printed = true;
|
||||||
};
|
};
|
||||||
var model = scope({ name: 'parent' }, Printer, 'hp');
|
var model = createScope({ name: 'parent' }, Printer, 'hp');
|
||||||
expect(model.brand).toEqual('hp');
|
expect(model.brand).toEqual('hp');
|
||||||
model.print();
|
model.print();
|
||||||
expect(model.printed).toEqual(true);
|
expect(model.printed).toEqual(true);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue