mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-08 14:54:44 +00:00
remove old unneded files
This commit is contained in:
parent
b288cb08b4
commit
94759f4c2c
19 changed files with 0 additions and 3537 deletions
|
|
@ -1,356 +0,0 @@
|
||||||
function Binder(doc, widgetFactory, datastore, location, config) {
|
|
||||||
this.doc = doc;
|
|
||||||
this.location = location;
|
|
||||||
this.datastore = datastore;
|
|
||||||
this.anchor = {};
|
|
||||||
this.widgetFactory = widgetFactory;
|
|
||||||
this.config = config || {};
|
|
||||||
this.updateListeners = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
Binder.parseBindings = function(string) {
|
|
||||||
var results = [];
|
|
||||||
var lastIndex = 0;
|
|
||||||
var index;
|
|
||||||
while((index = string.indexOf('{{', lastIndex)) > -1) {
|
|
||||||
if (lastIndex < index)
|
|
||||||
results.push(string.substr(lastIndex, index - lastIndex));
|
|
||||||
lastIndex = index;
|
|
||||||
|
|
||||||
index = string.indexOf('}}', index);
|
|
||||||
index = index < 0 ? string.length : index + 2;
|
|
||||||
|
|
||||||
results.push(string.substr(lastIndex, index - lastIndex));
|
|
||||||
lastIndex = index;
|
|
||||||
}
|
|
||||||
if (lastIndex != string.length)
|
|
||||||
results.push(string.substr(lastIndex, string.length - lastIndex));
|
|
||||||
return results.length === 0 ? [ string ] : results;
|
|
||||||
};
|
|
||||||
|
|
||||||
Binder.hasBinding = function(string) {
|
|
||||||
var bindings = Binder.parseBindings(string);
|
|
||||||
return bindings.length > 1 || Binder.binding(bindings[0]) !== null;
|
|
||||||
};
|
|
||||||
|
|
||||||
Binder.binding = function(string) {
|
|
||||||
var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/);
|
|
||||||
return binding ? binding[1] : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Binder.prototype = {
|
|
||||||
parseQueryString: function(query) {
|
|
||||||
var params = {};
|
|
||||||
query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,
|
|
||||||
function (match, left, right) {
|
|
||||||
if (left) params[decodeURIComponent(left)] = decodeURIComponent(right);
|
|
||||||
});
|
|
||||||
return params;
|
|
||||||
},
|
|
||||||
|
|
||||||
parseAnchor: function() {
|
|
||||||
var self = this, url = this.location['get']() || "";
|
|
||||||
|
|
||||||
var anchorIndex = url.indexOf('#');
|
|
||||||
if (anchorIndex < 0) return;
|
|
||||||
var anchor = url.substring(anchorIndex + 1);
|
|
||||||
|
|
||||||
var anchorQuery = this.parseQueryString(anchor);
|
|
||||||
foreach(self.anchor, function(newValue, key) {
|
|
||||||
delete self.anchor[key];
|
|
||||||
});
|
|
||||||
foreach(anchorQuery, function(newValue, key) {
|
|
||||||
self.anchor[key] = newValue;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onUrlChange: function() {
|
|
||||||
this.parseAnchor();
|
|
||||||
this.updateView();
|
|
||||||
},
|
|
||||||
|
|
||||||
updateAnchor: function() {
|
|
||||||
var url = this.location['get']() || "";
|
|
||||||
var anchorIndex = url.indexOf('#');
|
|
||||||
if (anchorIndex > -1)
|
|
||||||
url = url.substring(0, anchorIndex);
|
|
||||||
url += "#";
|
|
||||||
var sep = '';
|
|
||||||
for (var key in this.anchor) {
|
|
||||||
var value = this.anchor[key];
|
|
||||||
if (typeof value === 'undefined' || value === null) {
|
|
||||||
delete this.anchor[key];
|
|
||||||
} else {
|
|
||||||
url += sep + encodeURIComponent(key);
|
|
||||||
if (value !== true)
|
|
||||||
url += "=" + encodeURIComponent(value);
|
|
||||||
sep = '&';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.location['set'](url);
|
|
||||||
return url;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateView: function() {
|
|
||||||
var start = new Date().getTime();
|
|
||||||
var scope = jQuery(this.doc).scope();
|
|
||||||
scope.clearInvalid();
|
|
||||||
scope.updateView();
|
|
||||||
var end = new Date().getTime();
|
|
||||||
this.updateAnchor();
|
|
||||||
foreach(this.updateListeners, function(fn) {fn();});
|
|
||||||
},
|
|
||||||
|
|
||||||
docFindWithSelf: function(exp){
|
|
||||||
var doc = jQuery(this.doc);
|
|
||||||
var selection = doc.find(exp);
|
|
||||||
if (doc.is(exp)){
|
|
||||||
selection = selection.andSelf();
|
|
||||||
}
|
|
||||||
return selection;
|
|
||||||
},
|
|
||||||
|
|
||||||
executeInit: function() {
|
|
||||||
this.docFindWithSelf("[ng:init]").each(function() {
|
|
||||||
var jThis = jQuery(this);
|
|
||||||
var scope = jThis.scope();
|
|
||||||
try {
|
|
||||||
scope.eval(jThis.attr('ng:init'));
|
|
||||||
} catch (e) {
|
|
||||||
alert("EVAL ERROR:\n" + jThis.attr('ng:init') + '\n' + toJson(e, true));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
entity: function (scope) {
|
|
||||||
var self = this;
|
|
||||||
this.docFindWithSelf("[ng-entity]").attr("ng:watch", function() {
|
|
||||||
try {
|
|
||||||
var jNode = jQuery(this);
|
|
||||||
var decl = scope.entity(jNode.attr("ng-entity"), self.datastore);
|
|
||||||
return decl + (jNode.attr('ng:watch') || "");
|
|
||||||
} catch (e) {
|
|
||||||
log(e);
|
|
||||||
alert(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
compile: function() {
|
|
||||||
var jNode = jQuery(this.doc);
|
|
||||||
if (this.config['autoSubmit']) {
|
|
||||||
var submits = this.docFindWithSelf(":submit").not("[ng-action]");
|
|
||||||
submits.attr("ng-action", "$save()");
|
|
||||||
submits.not(":disabled").not("ng:bind-attr").attr("ng:bind-attr", '{disabled:"{{$invalidWidgets}}"}');
|
|
||||||
}
|
|
||||||
this.precompile(this.doc)(this.doc, jNode.scope(), "");
|
|
||||||
this.docFindWithSelf("a[ng-action]").live('click', function (event) {
|
|
||||||
var jNode = jQuery(this);
|
|
||||||
var scope = jNode.scope();
|
|
||||||
try {
|
|
||||||
scope.eval(jNode.attr('ng-action'));
|
|
||||||
jNode.removeAttr('ng-error');
|
|
||||||
jNode.removeClass("ng-exception");
|
|
||||||
} catch (e) {
|
|
||||||
jNode.addClass("ng-exception");
|
|
||||||
jNode.attr('ng-error', toJson(e, true));
|
|
||||||
}
|
|
||||||
scope.get('$updateView')();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
translateBinding: function(node, parentPath, factories) {
|
|
||||||
var path = parentPath.concat();
|
|
||||||
var offset = path.pop();
|
|
||||||
var parts = Binder.parseBindings(node.nodeValue);
|
|
||||||
if (parts.length > 1 || Binder.binding(parts[0])) {
|
|
||||||
var parent = node.parentNode;
|
|
||||||
if (isLeafNode(parent)) {
|
|
||||||
parent.setAttribute('ng:bind-template', node.nodeValue);
|
|
||||||
factories.push({path:path, fn:function(node, scope, prefix) {
|
|
||||||
return new BindUpdater(node, node.getAttribute('ng:bind-template'));
|
|
||||||
}});
|
|
||||||
} else {
|
|
||||||
for (var i = 0; i < parts.length; i++) {
|
|
||||||
var part = parts[i];
|
|
||||||
var binding = Binder.binding(part);
|
|
||||||
var newNode;
|
|
||||||
if (binding) {
|
|
||||||
newNode = document.createElement("span");
|
|
||||||
var jNewNode = jQuery(newNode);
|
|
||||||
jNewNode.attr("ng:bind", binding);
|
|
||||||
if (i === 0) {
|
|
||||||
factories.push({path:path.concat(offset + i), fn:this.ng_bind});
|
|
||||||
}
|
|
||||||
} else if (msie && part.charAt(0) == ' ') {
|
|
||||||
newNode = document.createElement("span");
|
|
||||||
newNode.innerHTML = ' ' + part.substring(1);
|
|
||||||
} else {
|
|
||||||
newNode = document.createTextNode(part);
|
|
||||||
}
|
|
||||||
parent.insertBefore(newNode, node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parent.removeChild(node);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
precompile: function(root) {
|
|
||||||
var factories = [];
|
|
||||||
this.precompileNode(root, [], factories);
|
|
||||||
return function (template, scope, prefix) {
|
|
||||||
var len = factories.length;
|
|
||||||
for (var i = 0; i < len; i++) {
|
|
||||||
var factory = factories[i];
|
|
||||||
var node = template;
|
|
||||||
var path = factory.path;
|
|
||||||
for (var j = 0; j < path.length; j++) {
|
|
||||||
node = node.childNodes[path[j]];
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
scope.addWidget(factory.fn(node, scope, prefix));
|
|
||||||
} catch (e) {
|
|
||||||
alert(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
precompileNode: function(node, path, factories) {
|
|
||||||
var nodeType = node.nodeType;
|
|
||||||
if (nodeType == Node.TEXT_NODE) {
|
|
||||||
this.translateBinding(node, path, factories);
|
|
||||||
return;
|
|
||||||
} else if (nodeType != Node.ELEMENT_NODE && nodeType != Node.DOCUMENT_NODE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!node.getAttribute) return;
|
|
||||||
var nonBindable = node.getAttribute('ng:non-bindable');
|
|
||||||
if (nonBindable || nonBindable === "") return;
|
|
||||||
|
|
||||||
var attributes = node.attributes;
|
|
||||||
if (attributes) {
|
|
||||||
var bindings = node.getAttribute('ng:bind-attr');
|
|
||||||
node.removeAttribute('ng:bind-attr');
|
|
||||||
bindings = bindings ? fromJson(bindings) : {};
|
|
||||||
var attrLen = attributes.length;
|
|
||||||
for (var i = 0; i < attrLen; i++) {
|
|
||||||
var attr = attributes[i];
|
|
||||||
var attrName = attr.name;
|
|
||||||
// http://www.glennjones.net/Post/809/getAttributehrefbug.htm
|
|
||||||
var attrValue = msie && attrName == 'href' ?
|
|
||||||
decodeURI(node.getAttribute(attrName, 2)) : attr.value;
|
|
||||||
if (Binder.hasBinding(attrValue)) {
|
|
||||||
bindings[attrName] = attrValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var json = toJson(bindings);
|
|
||||||
if (json.length > 2) {
|
|
||||||
node.setAttribute("ng:bind-attr", json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!node.getAttribute) log(node);
|
|
||||||
var repeaterExpression = node.getAttribute('ng:repeat');
|
|
||||||
if (repeaterExpression) {
|
|
||||||
node.removeAttribute('ng:repeat');
|
|
||||||
var precompiled = this.precompile(node);
|
|
||||||
var view = document.createComment("ng:repeat: " + repeaterExpression);
|
|
||||||
var parentNode = node.parentNode;
|
|
||||||
parentNode.insertBefore(view, node);
|
|
||||||
parentNode.removeChild(node);
|
|
||||||
function template(childScope, prefix, i) {
|
|
||||||
var clone = jQuery(node).clone();
|
|
||||||
clone.css('display', '');
|
|
||||||
clone.attr('ng:repeat-index', "" + i);
|
|
||||||
clone.data('scope', childScope);
|
|
||||||
precompiled(clone[0], childScope, prefix + i + ":");
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
factories.push({path:path, fn:function(node, scope, prefix) {
|
|
||||||
return new RepeaterUpdater(jQuery(node), repeaterExpression, template, prefix);
|
|
||||||
}});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.getAttribute('ng:eval')) factories.push({path:path, fn:this.ng_eval});
|
|
||||||
if (node.getAttribute('ng:bind')) factories.push({path:path, fn:this.ng_bind});
|
|
||||||
if (node.getAttribute('ng:bind-attr')) factories.push({path:path, fn:this.ng_bind_attr});
|
|
||||||
if (node.getAttribute('ng:hide')) factories.push({path:path, fn:this.ng_hide});
|
|
||||||
if (node.getAttribute('ng:show')) factories.push({path:path, fn:this.ng_show});
|
|
||||||
if (node.getAttribute('ng:class')) factories.push({path:path, fn:this.ng_class});
|
|
||||||
if (node.getAttribute('ng:class-odd')) factories.push({path:path, fn:this.ng_class_odd});
|
|
||||||
if (node.getAttribute('ng:class-even')) factories.push({path:path, fn:this.ng_class_even});
|
|
||||||
if (node.getAttribute('ng:style')) factories.push({path:path, fn:this.ng_style});
|
|
||||||
if (node.getAttribute('ng:watch')) factories.push({path:path, fn:this.ng_watch});
|
|
||||||
var nodeName = node.nodeName;
|
|
||||||
if ((nodeName == 'INPUT' ) ||
|
|
||||||
nodeName == 'TEXTAREA' ||
|
|
||||||
nodeName == 'SELECT' ||
|
|
||||||
nodeName == 'BUTTON') {
|
|
||||||
var self = this;
|
|
||||||
factories.push({path:path, fn:function(node, scope, prefix) {
|
|
||||||
node.name = prefix + node.name.split(":").pop();
|
|
||||||
return self.widgetFactory.createController(jQuery(node), scope);
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
if (nodeName == 'OPTION') {
|
|
||||||
var html = jQuery('<select/>').append(jQuery(node).clone()).html();
|
|
||||||
if (!html.match(/<option(\s.*\s|\s)value\s*=\s*.*>.*<\/\s*option\s*>/gi)) {
|
|
||||||
if (Binder.hasBinding(node.text)) {
|
|
||||||
jQuery(node).attr('ng:bind-attr', angular.toJson({'value':node.text}));
|
|
||||||
} else {
|
|
||||||
node.value = node.text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var children = node.childNodes;
|
|
||||||
for (var k = 0; k < children.length; k++) {
|
|
||||||
this.precompileNode(children[k], path.concat(k), factories);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
ng_eval: function(node) {
|
|
||||||
return new EvalUpdater(node, node.getAttribute('ng:eval'));
|
|
||||||
},
|
|
||||||
|
|
||||||
ng_bind: function(node) {
|
|
||||||
return new BindUpdater(node, "{{" + node.getAttribute('ng:bind') + "}}");
|
|
||||||
},
|
|
||||||
|
|
||||||
ng_bind_attr: function(node) {
|
|
||||||
return new BindAttrUpdater(node, fromJson(node.getAttribute('ng:bind-attr')));
|
|
||||||
},
|
|
||||||
|
|
||||||
ng_hide: function(node) {
|
|
||||||
return new HideUpdater(node, node.getAttribute('ng:hide'));
|
|
||||||
},
|
|
||||||
|
|
||||||
ng_show: function(node) {
|
|
||||||
return new ShowUpdater(node, node.getAttribute('ng:show'));
|
|
||||||
},
|
|
||||||
|
|
||||||
ng_class: function(node) {
|
|
||||||
return new ClassUpdater(node, node.getAttribute('ng:class'));
|
|
||||||
},
|
|
||||||
|
|
||||||
ng_class_even: function(node) {
|
|
||||||
return new ClassEvenUpdater(node, node.getAttribute('ng:class-even'));
|
|
||||||
},
|
|
||||||
|
|
||||||
ng_class_odd: function(node) {
|
|
||||||
return new ClassOddUpdater(node, node.getAttribute('ng:class-odd'));
|
|
||||||
},
|
|
||||||
|
|
||||||
ng_style: function(node) {
|
|
||||||
return new StyleUpdater(node, node.getAttribute('ng:style'));
|
|
||||||
},
|
|
||||||
|
|
||||||
ng_watch: function(node, scope) {
|
|
||||||
scope.watch(node.getAttribute('ng:watch'));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
// Single $ is special and does not get searched
|
|
||||||
// Double $$ is special an is client only (does not get sent to server)
|
|
||||||
|
|
||||||
function Model(entity, initial) {
|
|
||||||
this['$$entity'] = entity;
|
|
||||||
this['$loadFrom'](initial||{});
|
|
||||||
this['$entity'] = entity['title'];
|
|
||||||
this['$migrate']();
|
|
||||||
};
|
|
||||||
|
|
||||||
Model.copyDirectFields = function(src, dst) {
|
|
||||||
if (src === dst || !src || !dst) return;
|
|
||||||
var isDataField = function(src, dst, field) {
|
|
||||||
return (field.substring(0,2) !== '$$') &&
|
|
||||||
(typeof src[field] !== 'function') &&
|
|
||||||
(typeof dst[field] !== 'function');
|
|
||||||
};
|
|
||||||
for (var field in dst) {
|
|
||||||
if (isDataField(src, dst, field))
|
|
||||||
delete dst[field];
|
|
||||||
}
|
|
||||||
for (field in src) {
|
|
||||||
if (isDataField(src, dst, field))
|
|
||||||
dst[field] = src[field];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
extend(Model.prototype, {
|
|
||||||
'$migrate': function() {
|
|
||||||
merge(this['$$entity']['defaults'], this);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
'$merge': function(other) {
|
|
||||||
merge(other, this);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
'$save': function(callback) {
|
|
||||||
this['$$entity'].datastore.save(this, callback === true ? undefined : callback);
|
|
||||||
if (callback === true) this['$$entity'].datastore.flush();
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
'$delete': function(callback) {
|
|
||||||
this['$$entity'].datastore.remove(this, callback === true ? undefined : callback);
|
|
||||||
if (callback === true) this['$$entity'].datastore.flush();
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
'$loadById': function(id, callback) {
|
|
||||||
this['$$entity'].datastore.load(this, id, callback);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
'$loadFrom': function(other) {
|
|
||||||
Model.copyDirectFields(other, this);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
'$saveTo': function(other) {
|
|
||||||
Model.copyDirectFields(this, other);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,407 +0,0 @@
|
||||||
function Scope(initialState, name) {
|
|
||||||
var self = this;
|
|
||||||
self.widgets = [];
|
|
||||||
self.evals = [];
|
|
||||||
self.watchListeners = {};
|
|
||||||
self.name = name;
|
|
||||||
initialState = initialState || {};
|
|
||||||
var State = function(){};
|
|
||||||
State.prototype = initialState;
|
|
||||||
self.state = new State();
|
|
||||||
extend(self.state, {
|
|
||||||
'$parent': initialState,
|
|
||||||
'$watch': bind(self, self.addWatchListener),
|
|
||||||
'$eval': bind(self, self.eval),
|
|
||||||
'$bind': bind(self, bind, self),
|
|
||||||
// change name to autoEval?
|
|
||||||
'$addEval': bind(self, self.addEval),
|
|
||||||
'$updateView': bind(self, self.updateView)
|
|
||||||
});
|
|
||||||
if (name == "ROOT") {
|
|
||||||
self.state['$root'] = self.state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Scope.expressionCache = {};
|
|
||||||
Scope.getter = function(instance, path) {
|
|
||||||
if (!path) return instance;
|
|
||||||
var element = path.split('.');
|
|
||||||
var key;
|
|
||||||
var lastInstance = instance;
|
|
||||||
var len = element.length;
|
|
||||||
for ( var i = 0; i < len; i++) {
|
|
||||||
key = element[i];
|
|
||||||
if (!key.match(/^[\$\w][\$\w\d]*$/))
|
|
||||||
throw "Expression '" + path + "' is not a valid expression for accesing variables.";
|
|
||||||
if (instance) {
|
|
||||||
lastInstance = instance;
|
|
||||||
instance = instance[key];
|
|
||||||
}
|
|
||||||
if (_.isUndefined(instance) && key.charAt(0) == '$') {
|
|
||||||
var type = angular['Global']['typeOf'](lastInstance);
|
|
||||||
type = angular[type.charAt(0).toUpperCase()+type.substring(1)];
|
|
||||||
var fn = type ? type[[key.substring(1)]] : undefined;
|
|
||||||
if (fn) {
|
|
||||||
instance = _.bind(fn, lastInstance, lastInstance);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (typeof instance === 'function' && !instance['$$factory']) {
|
|
||||||
return bind(lastInstance, instance);
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
Scope.setter = function(instance, path, value){
|
|
||||||
var element = path.split('.');
|
|
||||||
for ( var i = 0; element.length > 1; i++) {
|
|
||||||
var key = element.shift();
|
|
||||||
var newInstance = instance[key];
|
|
||||||
if (!newInstance) {
|
|
||||||
newInstance = {};
|
|
||||||
instance[key] = newInstance;
|
|
||||||
}
|
|
||||||
instance = newInstance;
|
|
||||||
}
|
|
||||||
instance[element.shift()] = value;
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
Scope.prototype = {
|
|
||||||
// TODO: rename to update? or eval?
|
|
||||||
updateView: function() {
|
|
||||||
var self = this;
|
|
||||||
this.fireWatchers();
|
|
||||||
foreach(this.widgets, function(widget){
|
|
||||||
self.evalWidget(widget, "", {}, function(){
|
|
||||||
this.updateView(self);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
foreach(this.evals, bind(this, this.apply));
|
|
||||||
},
|
|
||||||
|
|
||||||
addWidget: function(controller) {
|
|
||||||
if (controller) this.widgets.push(controller);
|
|
||||||
},
|
|
||||||
|
|
||||||
addEval: function(fn, listener) {
|
|
||||||
// todo: this should take a function/string and a listener
|
|
||||||
// todo: this is a hack, which will need to be cleaned up.
|
|
||||||
var self = this,
|
|
||||||
listenFn = listener || noop,
|
|
||||||
expr = self.compile(fn);
|
|
||||||
this.evals.push(function(){
|
|
||||||
self.apply(listenFn, expr());
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
isProperty: function(exp) {
|
|
||||||
for ( var i = 0; i < exp.length; i++) {
|
|
||||||
var ch = exp.charAt(i);
|
|
||||||
if (ch!='.' && !Lexer.prototype.isIdent(ch)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
get: function(path) {
|
|
||||||
// log('SCOPE.get', path, Scope.getter(this.state, path));
|
|
||||||
return Scope.getter(this.state, path);
|
|
||||||
},
|
|
||||||
|
|
||||||
set: function(path, value) {
|
|
||||||
// log('SCOPE.set', path, value);
|
|
||||||
var instance = this.state;
|
|
||||||
return Scope.setter(instance, path, value);
|
|
||||||
},
|
|
||||||
|
|
||||||
setEval: function(expressionText, value) {
|
|
||||||
this.eval(expressionText + "=" + toJson(value));
|
|
||||||
},
|
|
||||||
|
|
||||||
compile: function(exp) {
|
|
||||||
if (isFunction(exp)) return bind(this.state, exp);
|
|
||||||
var expFn = Scope.expressionCache[exp], self = this;
|
|
||||||
if (!expFn) {
|
|
||||||
var parser = new Parser(exp);
|
|
||||||
expFn = parser.statements();
|
|
||||||
parser.assertAllConsumed();
|
|
||||||
Scope.expressionCache[exp] = expFn;
|
|
||||||
}
|
|
||||||
return function(context){
|
|
||||||
context = context || {};
|
|
||||||
context.self = self.state;
|
|
||||||
context.scope = self;
|
|
||||||
return expFn.call(self, context);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
eval: function(exp, context) {
|
|
||||||
// log('Scope.eval', expressionText);
|
|
||||||
return this.compile(exp)(context);
|
|
||||||
},
|
|
||||||
|
|
||||||
//TODO: Refactor. This function needs to be an execution closure for widgets
|
|
||||||
// move to widgets
|
|
||||||
// remove expression, just have inner closure.
|
|
||||||
evalWidget: function(widget, expression, context, onSuccess, onFailure) {
|
|
||||||
try {
|
|
||||||
var value = this.eval(expression, context);
|
|
||||||
if (widget.hasError) {
|
|
||||||
widget.hasError = false;
|
|
||||||
jQuery(widget.view).
|
|
||||||
removeClass('ng-exception').
|
|
||||||
removeAttr('ng-error');
|
|
||||||
}
|
|
||||||
if (onSuccess) {
|
|
||||||
value = onSuccess.apply(widget, [value]);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (e){
|
|
||||||
var jsonError = toJson(e, true);
|
|
||||||
error('Eval Widget Error:', jsonError);
|
|
||||||
widget.hasError = true;
|
|
||||||
jQuery(widget.view).
|
|
||||||
addClass('ng-exception').
|
|
||||||
attr('ng-error', jsonError);
|
|
||||||
if (onFailure) {
|
|
||||||
onFailure.apply(widget, [e, jsonError]);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
validate: function(expressionText, value, element) {
|
|
||||||
var expression = Scope.expressionCache[expressionText];
|
|
||||||
if (!expression) {
|
|
||||||
expression = new Parser(expressionText).validator();
|
|
||||||
Scope.expressionCache[expressionText] = expression;
|
|
||||||
}
|
|
||||||
var self = {scope:this, self:this.state, '$element':element};
|
|
||||||
return expression(self)(self, value);
|
|
||||||
},
|
|
||||||
|
|
||||||
entity: function(entityDeclaration, datastore) {
|
|
||||||
var expression = new Parser(entityDeclaration).entityDeclaration();
|
|
||||||
return expression({scope:this, datastore:datastore});
|
|
||||||
},
|
|
||||||
|
|
||||||
clearInvalid: function() {
|
|
||||||
var invalid = this.state['$invalidWidgets'];
|
|
||||||
while(invalid.length > 0) {invalid.pop();}
|
|
||||||
},
|
|
||||||
|
|
||||||
markInvalid: function(widget) {
|
|
||||||
this.state['$invalidWidgets'].push(widget);
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: function(declaration) {
|
|
||||||
var self = this;
|
|
||||||
new Parser(declaration).watch()({
|
|
||||||
scope:this,
|
|
||||||
addListener:function(watch, exp){
|
|
||||||
self.addWatchListener(watch, function(n,o){
|
|
||||||
try {
|
|
||||||
return exp({scope:self}, n, o);
|
|
||||||
} catch(e) {
|
|
||||||
alert(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
addWatchListener: function(watchExpression, listener) {
|
|
||||||
// TODO: clean me up!
|
|
||||||
if (!isFunction(listener)) {
|
|
||||||
listener = this.compile(listener);
|
|
||||||
}
|
|
||||||
var watcher = this.watchListeners[watchExpression];
|
|
||||||
if (!watcher) {
|
|
||||||
watcher = {listeners:[], expression:watchExpression};
|
|
||||||
this.watchListeners[watchExpression] = watcher;
|
|
||||||
}
|
|
||||||
watcher.listeners.push(listener);
|
|
||||||
},
|
|
||||||
|
|
||||||
fireWatchers: function() {
|
|
||||||
var self = this, fired = false;
|
|
||||||
foreach(this.watchListeners, function(watcher) {
|
|
||||||
var value = self.eval(watcher.expression);
|
|
||||||
if (value !== watcher.lastValue) {
|
|
||||||
foreach(watcher.listeners, function(listener){
|
|
||||||
listener(value, watcher.lastValue);
|
|
||||||
fired = true;
|
|
||||||
});
|
|
||||||
watcher.lastValue = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return fired;
|
|
||||||
},
|
|
||||||
|
|
||||||
apply: function(fn) {
|
|
||||||
fn.apply(this.state, slice.call(arguments, 1, arguments.length));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////
|
|
||||||
|
|
||||||
function getter(instance, path) {
|
|
||||||
if (!path) return instance;
|
|
||||||
var element = path.split('.');
|
|
||||||
var key;
|
|
||||||
var lastInstance = instance;
|
|
||||||
var len = element.length;
|
|
||||||
for ( var i = 0; i < len; i++) {
|
|
||||||
key = element[i];
|
|
||||||
if (!key.match(/^[\$\w][\$\w\d]*$/))
|
|
||||||
throw "Expression '" + path + "' is not a valid expression for accesing variables.";
|
|
||||||
if (instance) {
|
|
||||||
lastInstance = instance;
|
|
||||||
instance = instance[key];
|
|
||||||
}
|
|
||||||
if (_.isUndefined(instance) && key.charAt(0) == '$') {
|
|
||||||
var type = angular['Global']['typeOf'](lastInstance);
|
|
||||||
type = angular[type.charAt(0).toUpperCase()+type.substring(1)];
|
|
||||||
var fn = type ? type[[key.substring(1)]] : undefined;
|
|
||||||
if (fn) {
|
|
||||||
instance = _.bind(fn, lastInstance, lastInstance);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (typeof instance === 'function' && !instance['$$factory']) {
|
|
||||||
return bind(lastInstance, instance);
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
function setter(instance, path, value){
|
|
||||||
var element = path.split('.');
|
|
||||||
for ( var i = 0; element.length > 1; i++) {
|
|
||||||
var key = element.shift();
|
|
||||||
var newInstance = instance[key];
|
|
||||||
if (!newInstance) {
|
|
||||||
newInstance = {};
|
|
||||||
instance[key] = newInstance;
|
|
||||||
}
|
|
||||||
instance = newInstance;
|
|
||||||
}
|
|
||||||
instance[element.shift()] = value;
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
var compileCache = {};
|
|
||||||
function expressionCompile(exp){
|
|
||||||
if (isFunction(exp)) return exp;
|
|
||||||
var expFn = compileCache[exp];
|
|
||||||
if (!expFn) {
|
|
||||||
var parser = new Parser(exp);
|
|
||||||
expFn = parser.statements();
|
|
||||||
parser.assertAllConsumed();
|
|
||||||
compileCache[exp] = expFn;
|
|
||||||
}
|
|
||||||
// return expFn
|
|
||||||
// TODO(remove this hack)
|
|
||||||
return function(){
|
|
||||||
return expFn({
|
|
||||||
scope: {
|
|
||||||
set: this.$set,
|
|
||||||
get: this.$get
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var NON_RENDERABLE_ELEMENTS = {
|
|
||||||
'#text': 1, '#comment':1, 'TR':1, 'TH':1
|
|
||||||
};
|
|
||||||
|
|
||||||
function isRenderableElement(element){
|
|
||||||
return element && element[0] && !NON_RENDERABLE_ELEMENTS[element[0].nodeName];
|
|
||||||
}
|
|
||||||
|
|
||||||
function rethrow(e) { throw e; }
|
|
||||||
function errorHandlerFor(element) {
|
|
||||||
while (!isRenderableElement(element)) {
|
|
||||||
element = element.parent() || jqLite(document.body);
|
|
||||||
}
|
|
||||||
return function(error) {
|
|
||||||
element.attr('ng-error', angular.toJson(error));
|
|
||||||
element.addClass('ng-exception');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createScope(parent, Class) {
|
|
||||||
function Parent(){}
|
|
||||||
function API(){}
|
|
||||||
function Behavior(){}
|
|
||||||
|
|
||||||
var instance, behavior, api, watchList = [], evalList = [];
|
|
||||||
|
|
||||||
Class = Class || noop;
|
|
||||||
parent = Parent.prototype = parent || {};
|
|
||||||
api = API.prototype = new Parent();
|
|
||||||
behavior = Behavior.prototype = extend(new API(), Class.prototype);
|
|
||||||
instance = new Behavior();
|
|
||||||
|
|
||||||
extend(api, {
|
|
||||||
$parent: parent,
|
|
||||||
$bind: bind(instance, bind, instance),
|
|
||||||
$get: bind(instance, getter, instance),
|
|
||||||
$set: bind(instance, setter, instance),
|
|
||||||
|
|
||||||
$eval: function(exp) {
|
|
||||||
if (isDefined(exp)) {
|
|
||||||
return expressionCompile(exp).apply(instance, slice.call(arguments, 1, arguments.length));
|
|
||||||
} else {
|
|
||||||
foreach(evalList, function(eval) {
|
|
||||||
instance.$tryEval(eval.fn, eval.handler);
|
|
||||||
});
|
|
||||||
foreach(watchList, function(watch) {
|
|
||||||
var value = instance.$tryEval(watch.watch, watch.handler);
|
|
||||||
if (watch.last !== value) {
|
|
||||||
instance.$tryEval(watch.listener, watch.handler, value, watch.last);
|
|
||||||
watch.last = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
$tryEval: function (expression, exceptionHandler) {
|
|
||||||
try {
|
|
||||||
return expressionCompile(expression).apply(instance, slice.call(arguments, 2, arguments.length));
|
|
||||||
} catch (e) {
|
|
||||||
error(e);
|
|
||||||
if (isFunction(exceptionHandler)) {
|
|
||||||
exceptionHandler(e);
|
|
||||||
} else if (exceptionHandler) {
|
|
||||||
errorHandlerFor(exceptionHandler)(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
$watch: function(watchExp, listener, exceptionHandler) {
|
|
||||||
var watch = expressionCompile(watchExp);
|
|
||||||
watchList.push({
|
|
||||||
watch: watch,
|
|
||||||
last: watch.call(instance),
|
|
||||||
handler: exceptionHandler,
|
|
||||||
listener:expressionCompile(listener)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
$onEval: function(expr, exceptionHandler){
|
|
||||||
evalList.push({
|
|
||||||
fn: expressionCompile(expr),
|
|
||||||
handler: exceptionHandler
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Class.apply(instance, slice.call(arguments, 2, arguments.length));
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
@ -1,806 +0,0 @@
|
||||||
function WidgetFactory(serverUrl, database) {
|
|
||||||
this.nextUploadId = 0;
|
|
||||||
this.serverUrl = serverUrl;
|
|
||||||
this.database = database;
|
|
||||||
if (window['swfobject']) {
|
|
||||||
this.createSWF = window['swfobject']['createSWF'];
|
|
||||||
} else {
|
|
||||||
this.createSWF = function(){
|
|
||||||
alert("ERROR: swfobject not loaded!");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
WidgetFactory.prototype = {
|
|
||||||
createController: function(input, scope) {
|
|
||||||
var controller;
|
|
||||||
var type = input.attr('type').toLowerCase();
|
|
||||||
var exp = input.attr('name');
|
|
||||||
if (exp) exp = exp.split(':').pop();
|
|
||||||
var event = "change";
|
|
||||||
var bubbleEvent = true;
|
|
||||||
var formatter = angularFormatter[input.attr('ng:format')] || angularFormatter['noop'];
|
|
||||||
if (type == 'button' || type == 'submit' || type == 'reset' || type == 'image') {
|
|
||||||
controller = new ButtonController(input[0], exp, formatter);
|
|
||||||
event = "click";
|
|
||||||
bubbleEvent = false;
|
|
||||||
} else if (type == 'text' || type == 'textarea' || type == 'hidden' || type == 'password') {
|
|
||||||
controller = new TextController(input[0], exp, formatter);
|
|
||||||
event = "keyup change";
|
|
||||||
} else if (type == 'checkbox') {
|
|
||||||
controller = new CheckboxController(input[0], exp, formatter);
|
|
||||||
event = "click";
|
|
||||||
} else if (type == 'radio') {
|
|
||||||
controller = new RadioController(input[0], exp, formatter);
|
|
||||||
event="click";
|
|
||||||
} else if (type == 'select-one') {
|
|
||||||
controller = new SelectController(input[0], exp, formatter);
|
|
||||||
} else if (type == 'select-multiple') {
|
|
||||||
controller = new MultiSelectController(input[0], exp, formatter);
|
|
||||||
} else if (type == 'file') {
|
|
||||||
controller = this.createFileController(input, exp, formatter);
|
|
||||||
} else {
|
|
||||||
throw 'Unknown type: ' + type;
|
|
||||||
}
|
|
||||||
input.data('controller', controller);
|
|
||||||
var updateView = scope.get('$updateView');
|
|
||||||
var action = function() {
|
|
||||||
if (controller.updateModel(scope)) {
|
|
||||||
var action = jQuery(controller.view).attr('ng-action') || "";
|
|
||||||
if (scope.evalWidget(controller, action)) {
|
|
||||||
updateView(scope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bubbleEvent;
|
|
||||||
};
|
|
||||||
jQuery(controller.view, ":input").
|
|
||||||
bind(event, action);
|
|
||||||
return controller;
|
|
||||||
},
|
|
||||||
|
|
||||||
createFileController: function(fileInput) {
|
|
||||||
var uploadId = '__uploadWidget_' + (this.nextUploadId++);
|
|
||||||
var view = FileController.template(uploadId);
|
|
||||||
fileInput.after(view);
|
|
||||||
var att = {
|
|
||||||
'data':this.serverUrl + "/admin/ServerAPI.swf",
|
|
||||||
'width':"95", 'height':"20", 'align':"top",
|
|
||||||
'wmode':"transparent"};
|
|
||||||
var par = {
|
|
||||||
'flashvars':"uploadWidgetId=" + uploadId,
|
|
||||||
'allowScriptAccess':"always"};
|
|
||||||
var swfNode = this.createSWF(att, par, uploadId);
|
|
||||||
fileInput.remove();
|
|
||||||
var cntl = new FileController(view, fileInput[0].name, swfNode, this.serverUrl + "/data/" + this.database);
|
|
||||||
jQuery(swfNode).parent().data('controller', cntl);
|
|
||||||
return cntl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/////////////////////
|
|
||||||
// FileController
|
|
||||||
///////////////////////
|
|
||||||
|
|
||||||
function FileController(view, scopeName, uploader, databaseUrl) {
|
|
||||||
this.view = view;
|
|
||||||
this.uploader = uploader;
|
|
||||||
this.scopeName = scopeName;
|
|
||||||
this.attachmentsPath = databaseUrl + '/_attachments';
|
|
||||||
this.value = null;
|
|
||||||
this.lastValue = undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
angularCallbacks['flashEvent'] = function(id, event, args) {
|
|
||||||
var object = document.getElementById(id);
|
|
||||||
var jobject = jQuery(object);
|
|
||||||
var controller = jobject.parent().data("controller");
|
|
||||||
FileController.prototype[event].apply(controller, args);
|
|
||||||
_.defer(jobject.scope().get('$updateView'));
|
|
||||||
};
|
|
||||||
|
|
||||||
FileController.template = function(id) {
|
|
||||||
return jQuery('<span class="ng-upload-widget">' +
|
|
||||||
'<input type="checkbox" ng:non-bindable="true"/>' +
|
|
||||||
'<object id="' + id + '" />' +
|
|
||||||
'<a></a>' +
|
|
||||||
'<span/>' +
|
|
||||||
'</span>');
|
|
||||||
};
|
|
||||||
|
|
||||||
extend(FileController.prototype, {
|
|
||||||
'cancel': noop,
|
|
||||||
'complete': noop,
|
|
||||||
'httpStatus': function(status) {
|
|
||||||
alert("httpStatus:" + this.scopeName + " status:" + status);
|
|
||||||
},
|
|
||||||
'ioError': function() {
|
|
||||||
alert("ioError:" + this.scopeName);
|
|
||||||
},
|
|
||||||
'open': function() {
|
|
||||||
alert("open:" + this.scopeName);
|
|
||||||
},
|
|
||||||
'progress':noop,
|
|
||||||
'securityError': function() {
|
|
||||||
alert("securityError:" + this.scopeName);
|
|
||||||
},
|
|
||||||
'uploadCompleteData': function(data) {
|
|
||||||
var value = fromJson(data);
|
|
||||||
value.url = this.attachmentsPath + '/' + value.id + '/' + value.text;
|
|
||||||
this.view.find("input").attr('checked', true);
|
|
||||||
var scope = this.view.scope();
|
|
||||||
this.value = value;
|
|
||||||
this.updateModel(scope);
|
|
||||||
this.value = null;
|
|
||||||
},
|
|
||||||
'select': function(name, size, type) {
|
|
||||||
this.name = name;
|
|
||||||
this.view.find("a").text(name).attr('href', name);
|
|
||||||
this.view.find("span").text(angular['filter']['bytes'](size));
|
|
||||||
this.upload();
|
|
||||||
},
|
|
||||||
|
|
||||||
updateModel: function(scope) {
|
|
||||||
var isChecked = this.view.find("input").attr('checked');
|
|
||||||
var value = isChecked ? this.value : null;
|
|
||||||
if (this.lastValue === value) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
scope.set(this.scopeName, value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateView: function(scope) {
|
|
||||||
var modelValue = scope.get(this.scopeName);
|
|
||||||
if (modelValue && this.value !== modelValue) {
|
|
||||||
this.value = modelValue;
|
|
||||||
this.view.find("a").
|
|
||||||
attr("href", this.value.url).
|
|
||||||
text(this.value.text);
|
|
||||||
this.view.find("span").text(angular['filter']['bytes'](this.value.size));
|
|
||||||
}
|
|
||||||
this.view.find("input").attr('checked', !!modelValue);
|
|
||||||
},
|
|
||||||
|
|
||||||
upload: function() {
|
|
||||||
if (this.name) {
|
|
||||||
this.uploader['uploadFile'](this.attachmentsPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// NullController
|
|
||||||
///////////////////////
|
|
||||||
function NullController(view) {this.view = view;};
|
|
||||||
NullController.prototype = {
|
|
||||||
updateModel: function() { return true; },
|
|
||||||
updateView: noop
|
|
||||||
};
|
|
||||||
NullController.instance = new NullController();
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// ButtonController
|
|
||||||
///////////////////////
|
|
||||||
var ButtonController = NullController;
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// TextController
|
|
||||||
///////////////////////
|
|
||||||
function TextController(view, exp, formatter) {
|
|
||||||
this.view = view;
|
|
||||||
this.formatter = formatter;
|
|
||||||
this.exp = exp;
|
|
||||||
this.validator = view.getAttribute('ng:validate');
|
|
||||||
this.required = typeof view.attributes['ng:required'] != "undefined";
|
|
||||||
this.lastErrorText = null;
|
|
||||||
this.lastValue = undefined;
|
|
||||||
this.initialValue = this.formatter['parse'](view.value);
|
|
||||||
var widget = view.getAttribute('ng-widget');
|
|
||||||
if (widget === 'datepicker') {
|
|
||||||
jQuery(view).datepicker();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TextController.prototype = {
|
|
||||||
updateModel: function(scope) {
|
|
||||||
var value = this.formatter['parse'](this.view.value);
|
|
||||||
if (this.lastValue === value) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
scope.setEval(this.exp, value);
|
|
||||||
this.lastValue = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateView: function(scope) {
|
|
||||||
var view = this.view;
|
|
||||||
var value = scope.get(this.exp);
|
|
||||||
if (typeof value === "undefined") {
|
|
||||||
value = this.initialValue;
|
|
||||||
scope.setEval(this.exp, value);
|
|
||||||
}
|
|
||||||
value = value ? value : '';
|
|
||||||
if (!_(this.lastValue).isEqual(value)) {
|
|
||||||
view.value = this.formatter['format'](value);
|
|
||||||
this.lastValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var isValidationError = false;
|
|
||||||
view.removeAttribute('ng-error');
|
|
||||||
if (this.required) {
|
|
||||||
isValidationError = !(value && $.trim("" + value).length > 0);
|
|
||||||
}
|
|
||||||
var errorText = isValidationError ? "Required Value" : null;
|
|
||||||
if (!isValidationError && this.validator && value) {
|
|
||||||
errorText = scope.validate(this.validator, value, view);
|
|
||||||
isValidationError = !!errorText;
|
|
||||||
}
|
|
||||||
if (this.lastErrorText !== errorText) {
|
|
||||||
this.lastErrorText = isValidationError;
|
|
||||||
if (errorText && isVisible(view)) {
|
|
||||||
view.setAttribute('ng-error', errorText);
|
|
||||||
scope.markInvalid(this);
|
|
||||||
}
|
|
||||||
jQuery(view).toggleClass('ng-validation-error', isValidationError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// CheckboxController
|
|
||||||
///////////////////////
|
|
||||||
function CheckboxController(view, exp, formatter) {
|
|
||||||
this.view = view;
|
|
||||||
this.exp = exp;
|
|
||||||
this.lastValue = undefined;
|
|
||||||
this.formatter = formatter;
|
|
||||||
this.initialValue = this.formatter['parse'](view.checked ? view.value : "");
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckboxController.prototype = {
|
|
||||||
updateModel: function(scope) {
|
|
||||||
var input = this.view;
|
|
||||||
var value = input.checked ? input.value : '';
|
|
||||||
value = this.formatter['parse'](value);
|
|
||||||
value = this.formatter['format'](value);
|
|
||||||
if (this.lastValue === value) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
scope.setEval(this.exp, this.formatter['parse'](value));
|
|
||||||
this.lastValue = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateView: function(scope) {
|
|
||||||
var input = this.view;
|
|
||||||
var value = scope.eval(this.exp);
|
|
||||||
if (typeof value === "undefined") {
|
|
||||||
value = this.initialValue;
|
|
||||||
scope.setEval(this.exp, value);
|
|
||||||
}
|
|
||||||
input.checked = this.formatter['parse'](input.value) == value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// SelectController
|
|
||||||
///////////////////////
|
|
||||||
function SelectController(view, exp) {
|
|
||||||
this.view = view;
|
|
||||||
this.exp = exp;
|
|
||||||
this.lastValue = undefined;
|
|
||||||
this.initialValue = view.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
SelectController.prototype = {
|
|
||||||
updateModel: function(scope) {
|
|
||||||
var input = this.view;
|
|
||||||
if (input.selectedIndex < 0) {
|
|
||||||
scope.setEval(this.exp, null);
|
|
||||||
} else {
|
|
||||||
var value = this.view.value;
|
|
||||||
if (this.lastValue === value) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
scope.setEval(this.exp, value);
|
|
||||||
this.lastValue = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateView: function(scope) {
|
|
||||||
var input = this.view;
|
|
||||||
var value = scope.get(this.exp);
|
|
||||||
if (typeof value === 'undefined') {
|
|
||||||
value = this.initialValue;
|
|
||||||
scope.setEval(this.exp, value);
|
|
||||||
}
|
|
||||||
if (value !== this.lastValue) {
|
|
||||||
input.value = value ? value : "";
|
|
||||||
this.lastValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// MultiSelectController
|
|
||||||
///////////////////////
|
|
||||||
function MultiSelectController(view, exp) {
|
|
||||||
this.view = view;
|
|
||||||
this.exp = exp;
|
|
||||||
this.lastValue = undefined;
|
|
||||||
this.initialValue = this.selected();
|
|
||||||
};
|
|
||||||
|
|
||||||
MultiSelectController.prototype = {
|
|
||||||
selected: function () {
|
|
||||||
var value = [];
|
|
||||||
var options = this.view.options;
|
|
||||||
for ( var i = 0; i < options.length; i++) {
|
|
||||||
var option = options[i];
|
|
||||||
if (option.selected) {
|
|
||||||
value.push(option.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateModel: function(scope) {
|
|
||||||
var value = this.selected();
|
|
||||||
// TODO: This is wrong! no caching going on here as we are always comparing arrays
|
|
||||||
if (this.lastValue === value) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
scope.setEval(this.exp, value);
|
|
||||||
this.lastValue = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateView: function(scope) {
|
|
||||||
var input = this.view;
|
|
||||||
var selected = scope.get(this.exp);
|
|
||||||
if (typeof selected === "undefined") {
|
|
||||||
selected = this.initialValue;
|
|
||||||
scope.setEval(this.exp, selected);
|
|
||||||
}
|
|
||||||
if (selected !== this.lastValue) {
|
|
||||||
var options = input.options;
|
|
||||||
for ( var i = 0; i < options.length; i++) {
|
|
||||||
var option = options[i];
|
|
||||||
option.selected = _.include(selected, option.value);
|
|
||||||
}
|
|
||||||
this.lastValue = selected;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// RadioController
|
|
||||||
///////////////////////
|
|
||||||
function RadioController(view, exp) {
|
|
||||||
this.view = view;
|
|
||||||
this.exp = exp;
|
|
||||||
this.lastChecked = undefined;
|
|
||||||
this.lastValue = undefined;
|
|
||||||
this.inputValue = view.value;
|
|
||||||
this.initialValue = view.checked ? view.value : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
RadioController.prototype = {
|
|
||||||
updateModel: function(scope) {
|
|
||||||
var input = this.view;
|
|
||||||
if (this.lastChecked) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
input.checked = true;
|
|
||||||
this.lastValue = scope.setEval(this.exp, this.inputValue);
|
|
||||||
this.lastChecked = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateView: function(scope) {
|
|
||||||
var input = this.view;
|
|
||||||
var value = scope.get(this.exp);
|
|
||||||
if (this.initialValue && typeof value === "undefined") {
|
|
||||||
value = this.initialValue;
|
|
||||||
scope.setEval(this.exp, value);
|
|
||||||
}
|
|
||||||
if (this.lastValue != value) {
|
|
||||||
this.lastChecked = input.checked = this.inputValue == (''+value);
|
|
||||||
this.lastValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
//ElementController
|
|
||||||
///////////////////////
|
|
||||||
function BindUpdater(view, exp) {
|
|
||||||
this.view = view;
|
|
||||||
this.exp = Binder.parseBindings(exp);
|
|
||||||
this.hasError = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
BindUpdater.toText = function(obj) {
|
|
||||||
var e = escapeHtml;
|
|
||||||
switch(typeof obj) {
|
|
||||||
case "string":
|
|
||||||
case "boolean":
|
|
||||||
case "number":
|
|
||||||
return e(obj);
|
|
||||||
case "function":
|
|
||||||
return BindUpdater.toText(obj());
|
|
||||||
case "object":
|
|
||||||
if (isNode(obj)) {
|
|
||||||
return outerHTML(obj);
|
|
||||||
} else if (obj instanceof angular.filter.Meta) {
|
|
||||||
switch(typeof obj.html) {
|
|
||||||
case "string":
|
|
||||||
case "number":
|
|
||||||
return obj.html;
|
|
||||||
case "function":
|
|
||||||
return obj.html();
|
|
||||||
case "object":
|
|
||||||
if (isNode(obj.html))
|
|
||||||
return outerHTML(obj.html);
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch(typeof obj.text) {
|
|
||||||
case "string":
|
|
||||||
case "number":
|
|
||||||
return e(obj.text);
|
|
||||||
case "function":
|
|
||||||
return e(obj.text());
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (obj === null)
|
|
||||||
return "";
|
|
||||||
return e(toJson(obj, true));
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
BindUpdater.prototype = {
|
|
||||||
updateModel: noop,
|
|
||||||
updateView: function(scope) {
|
|
||||||
var html = [];
|
|
||||||
var parts = this.exp;
|
|
||||||
var length = parts.length;
|
|
||||||
for(var i=0; i<length; i++) {
|
|
||||||
var part = parts[i];
|
|
||||||
var binding = Binder.binding(part);
|
|
||||||
if (binding) {
|
|
||||||
scope.evalWidget(this, binding, {$element:this.view}, function(value){
|
|
||||||
html.push(BindUpdater.toText(value));
|
|
||||||
}, function(e, text){
|
|
||||||
setHtml(this.view, text);
|
|
||||||
});
|
|
||||||
if (this.hasError) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
html.push(escapeHtml(part));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setHtml(this.view, html.join(''));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function BindAttrUpdater(view, attrs) {
|
|
||||||
this.view = view;
|
|
||||||
this.attrs = attrs;
|
|
||||||
};
|
|
||||||
|
|
||||||
BindAttrUpdater.prototype = {
|
|
||||||
updateModel: noop,
|
|
||||||
updateView: function(scope) {
|
|
||||||
var jNode = jQuery(this.view);
|
|
||||||
var attributeTemplates = this.attrs;
|
|
||||||
if (this.hasError) {
|
|
||||||
this.hasError = false;
|
|
||||||
jNode.
|
|
||||||
removeClass('ng-exception').
|
|
||||||
removeAttr('ng-error');
|
|
||||||
}
|
|
||||||
var isImage = jNode.is('img');
|
|
||||||
for (var attrName in attributeTemplates) {
|
|
||||||
var attributeTemplate = Binder.parseBindings(attributeTemplates[attrName]);
|
|
||||||
var attrValues = [];
|
|
||||||
for ( var i = 0; i < attributeTemplate.length; i++) {
|
|
||||||
var binding = Binder.binding(attributeTemplate[i]);
|
|
||||||
if (binding) {
|
|
||||||
try {
|
|
||||||
var value = scope.eval(binding, {$element:jNode[0], attrName:attrName});
|
|
||||||
if (value && (value.constructor !== array || value.length !== 0))
|
|
||||||
attrValues.push(value);
|
|
||||||
} catch (e) {
|
|
||||||
this.hasError = true;
|
|
||||||
error('BindAttrUpdater', e);
|
|
||||||
var jsonError = toJson(e, true);
|
|
||||||
attrValues.push('[' + jsonError + ']');
|
|
||||||
jNode.
|
|
||||||
addClass('ng-exception').
|
|
||||||
attr('ng-error', jsonError);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
attrValues.push(attributeTemplate[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var attrValue = attrValues.length ? attrValues.join('') : null;
|
|
||||||
if(isImage && attrName == 'src' && !attrValue)
|
|
||||||
attrValue = scope.get('$config.blankImage');
|
|
||||||
jNode.attr(attrName, attrValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function EvalUpdater(view, exp) {
|
|
||||||
this.view = view;
|
|
||||||
this.exp = exp;
|
|
||||||
this.hasError = false;
|
|
||||||
};
|
|
||||||
EvalUpdater.prototype = {
|
|
||||||
updateModel: noop,
|
|
||||||
updateView: function(scope) {
|
|
||||||
scope.evalWidget(this, this.exp);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function HideUpdater(view, exp) { this.view = view; this.exp = exp; };
|
|
||||||
HideUpdater.prototype = {
|
|
||||||
updateModel: noop,
|
|
||||||
updateView: function(scope) {
|
|
||||||
scope.evalWidget(this, this.exp, {}, function(hideValue){
|
|
||||||
var view = jQuery(this.view);
|
|
||||||
if (toBoolean(hideValue)) {
|
|
||||||
view.hide();
|
|
||||||
} else {
|
|
||||||
view.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function ShowUpdater(view, exp) { this.view = view; this.exp = exp; };
|
|
||||||
ShowUpdater.prototype = {
|
|
||||||
updateModel: noop,
|
|
||||||
updateView: function(scope) {
|
|
||||||
scope.evalWidget(this, this.exp, {}, function(hideValue){
|
|
||||||
var view = jQuery(this.view);
|
|
||||||
if (toBoolean(hideValue)) {
|
|
||||||
view.show();
|
|
||||||
} else {
|
|
||||||
view.hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function ClassUpdater(view, exp) { this.view = view; this.exp = exp; };
|
|
||||||
ClassUpdater.prototype = {
|
|
||||||
updateModel: noop,
|
|
||||||
updateView: function(scope) {
|
|
||||||
scope.evalWidget(this, this.exp, {}, function(classValue){
|
|
||||||
if (classValue !== null && classValue !== undefined) {
|
|
||||||
this.view.className = classValue;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function ClassEvenUpdater(view, exp) { this.view = view; this.exp = exp; };
|
|
||||||
ClassEvenUpdater.prototype = {
|
|
||||||
updateModel: noop,
|
|
||||||
updateView: function(scope) {
|
|
||||||
scope.evalWidget(this, this.exp, {}, function(classValue){
|
|
||||||
var index = scope.get('$index');
|
|
||||||
jQuery(this.view).toggleClass(classValue, index % 2 === 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function ClassOddUpdater(view, exp) { this.view = view; this.exp = exp; };
|
|
||||||
ClassOddUpdater.prototype = {
|
|
||||||
updateModel: noop,
|
|
||||||
updateView: function(scope) {
|
|
||||||
scope.evalWidget(this, this.exp, {}, function(classValue){
|
|
||||||
var index = scope.get('$index');
|
|
||||||
jQuery(this.view).toggleClass(classValue, index % 2 === 0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function StyleUpdater(view, exp) { this.view = view; this.exp = exp; };
|
|
||||||
StyleUpdater.prototype = {
|
|
||||||
updateModel: noop,
|
|
||||||
updateView: function(scope) {
|
|
||||||
scope.evalWidget(this, this.exp, {}, function(styleValue){
|
|
||||||
jQuery(this.view).attr('style', "").css(styleValue);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// RepeaterUpdater
|
|
||||||
///////////////////////
|
|
||||||
function RepeaterUpdater(view, repeaterExpression, template, prefix) {
|
|
||||||
this.view = view;
|
|
||||||
this.template = template;
|
|
||||||
this.prefix = prefix;
|
|
||||||
this.children = [];
|
|
||||||
var match = repeaterExpression.match(/^\s*(.+)\s+in\s+(.*)\s*$/);
|
|
||||||
if (! match) {
|
|
||||||
throw "Expected ng:repeat in form of 'item in collection' but got '" +
|
|
||||||
repeaterExpression + "'.";
|
|
||||||
}
|
|
||||||
var keyValue = match[1];
|
|
||||||
this.iteratorExp = match[2];
|
|
||||||
match = keyValue.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);
|
|
||||||
if (!match) {
|
|
||||||
throw "'item' in 'item in collection' should be identifier or (key, value) but get '" +
|
|
||||||
keyValue + "'.";
|
|
||||||
}
|
|
||||||
this.valueExp = match[3] || match[1];
|
|
||||||
this.keyExp = match[2];
|
|
||||||
};
|
|
||||||
|
|
||||||
RepeaterUpdater.prototype = {
|
|
||||||
updateModel: noop,
|
|
||||||
updateView: function(scope) {
|
|
||||||
scope.evalWidget(this, this.iteratorExp, {}, function(iterator){
|
|
||||||
var self = this;
|
|
||||||
if (!iterator) {
|
|
||||||
iterator = [];
|
|
||||||
if (scope.isProperty(this.iteratorExp)) {
|
|
||||||
scope.set(this.iteratorExp, iterator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var childrenLength = this.children.length;
|
|
||||||
var cursor = this.view;
|
|
||||||
var time = 0;
|
|
||||||
var child = null;
|
|
||||||
var keyExp = this.keyExp;
|
|
||||||
var valueExp = this.valueExp;
|
|
||||||
var iteratorCounter = 0;
|
|
||||||
foreach(iterator, function(value, key){
|
|
||||||
if (iteratorCounter < childrenLength) {
|
|
||||||
// reuse children
|
|
||||||
child = self.children[iteratorCounter];
|
|
||||||
child.scope.set(valueExp, value);
|
|
||||||
} else {
|
|
||||||
// grow children
|
|
||||||
var name = self.prefix +
|
|
||||||
valueExp + " in " + self.iteratorExp + "[" + iteratorCounter + "]";
|
|
||||||
var childScope = new Scope(scope.state, name);
|
|
||||||
childScope.set('$index', iteratorCounter);
|
|
||||||
if (keyExp)
|
|
||||||
childScope.set(keyExp, key);
|
|
||||||
childScope.set(valueExp, value);
|
|
||||||
child = { scope:childScope, element:self.template(childScope, self.prefix, iteratorCounter) };
|
|
||||||
cursor.after(child.element);
|
|
||||||
self.children.push(child);
|
|
||||||
}
|
|
||||||
cursor = child.element;
|
|
||||||
var s = new Date().getTime();
|
|
||||||
child.scope.updateView();
|
|
||||||
time += new Date().getTime() - s;
|
|
||||||
iteratorCounter++;
|
|
||||||
});
|
|
||||||
// shrink children
|
|
||||||
for ( var r = childrenLength; r > iteratorCounter; --r) {
|
|
||||||
this.children.pop().element.remove();
|
|
||||||
}
|
|
||||||
// Special case for option in select
|
|
||||||
if (child && child.element[0].nodeName === "OPTION") {
|
|
||||||
var select = jQuery(child.element[0].parentNode);
|
|
||||||
var cntl = select.data('controller');
|
|
||||||
if (cntl) {
|
|
||||||
cntl.lastValue = undefined;
|
|
||||||
cntl.updateView(scope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////
|
|
||||||
// PopUp
|
|
||||||
//////////////////////////////////
|
|
||||||
|
|
||||||
function PopUp(doc) {
|
|
||||||
this.doc = doc;
|
|
||||||
};
|
|
||||||
|
|
||||||
PopUp.OUT_EVENT = "mouseleave mouseout click dblclick keypress keyup";
|
|
||||||
|
|
||||||
PopUp.onOver = function(e) {
|
|
||||||
PopUp.onOut();
|
|
||||||
var jNode = jQuery(this);
|
|
||||||
jNode.bind(PopUp.OUT_EVENT, PopUp.onOut);
|
|
||||||
var position = jNode.position();
|
|
||||||
var de = document.documentElement;
|
|
||||||
var w = self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
|
|
||||||
var hasArea = w - position.left;
|
|
||||||
var width = 300;
|
|
||||||
var title = jNode.hasClass("ng-exception") ? "EXCEPTION:" : "Validation error...";
|
|
||||||
var msg = jNode.attr("ng-error");
|
|
||||||
|
|
||||||
var x;
|
|
||||||
var arrowPos = hasArea>(width+75) ? "left" : "right";
|
|
||||||
var tip = jQuery(
|
|
||||||
"<div id='ng-callout' style='width:"+width+"px'>" +
|
|
||||||
"<div class='ng-arrow-"+arrowPos+"'/>" +
|
|
||||||
"<div class='ng-title'>"+title+"</div>" +
|
|
||||||
"<div class='ng-content'>"+msg+"</div>" +
|
|
||||||
"</div>");
|
|
||||||
jQuery("body").append(tip);
|
|
||||||
if(arrowPos === 'left'){
|
|
||||||
x = position.left + this.offsetWidth + 11;
|
|
||||||
}else{
|
|
||||||
x = position.left - (width + 15);
|
|
||||||
tip.find('.ng-arrow-right').css({left:width+1});
|
|
||||||
}
|
|
||||||
|
|
||||||
tip.css({left: x+"px", top: (position.top - 3)+"px"});
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
PopUp.onOut = function() {
|
|
||||||
jQuery('#ng-callout').
|
|
||||||
unbind(PopUp.OUT_EVENT, PopUp.onOut).
|
|
||||||
remove();
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
PopUp.prototype = {
|
|
||||||
bind: function () {
|
|
||||||
var self = this;
|
|
||||||
this.doc.find('.ng-validation-error,.ng-exception').
|
|
||||||
live("mouseover", PopUp.onOver);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////
|
|
||||||
// Status
|
|
||||||
//////////////////////////////////
|
|
||||||
|
|
||||||
function NullStatus(body) {
|
|
||||||
};
|
|
||||||
|
|
||||||
NullStatus.prototype = {
|
|
||||||
beginRequest:function(){},
|
|
||||||
endRequest:function(){}
|
|
||||||
};
|
|
||||||
|
|
||||||
function Status(body) {
|
|
||||||
this.requestCount = 0;
|
|
||||||
this.body = body;
|
|
||||||
};
|
|
||||||
|
|
||||||
Status.DOM ='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>';
|
|
||||||
|
|
||||||
Status.prototype = {
|
|
||||||
beginRequest: function () {
|
|
||||||
if (this.requestCount === 0) {
|
|
||||||
(this.loader = this.loader || this.body.append(Status.DOM).find("#ng-loading")).show();
|
|
||||||
}
|
|
||||||
this.requestCount++;
|
|
||||||
},
|
|
||||||
|
|
||||||
endRequest: function () {
|
|
||||||
this.requestCount--;
|
|
||||||
if (this.requestCount === 0) {
|
|
||||||
this.loader.hide("fold");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
function ControlBar(document, serverUrl, database) {
|
|
||||||
this._document = document;
|
|
||||||
this.serverUrl = serverUrl;
|
|
||||||
this.database = database;
|
|
||||||
this._window = window;
|
|
||||||
this.callbacks = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
ControlBar.HTML =
|
|
||||||
'<div>' +
|
|
||||||
'<div class="ui-widget-overlay"></div>' +
|
|
||||||
'<div id="ng-login" ng:non-bindable="true">' +
|
|
||||||
'<div class="ng-login-container"></div>' +
|
|
||||||
'</div>' +
|
|
||||||
'</div>';
|
|
||||||
|
|
||||||
|
|
||||||
ControlBar.FORBIDEN =
|
|
||||||
'<div ng:non-bindable="true" title="Permission Error:">' +
|
|
||||||
'Sorry, you do not have permission for this!'+
|
|
||||||
'</div>';
|
|
||||||
|
|
||||||
ControlBar.prototype = {
|
|
||||||
bind: function () {
|
|
||||||
},
|
|
||||||
|
|
||||||
login: function (loginSubmitFn) {
|
|
||||||
this.callbacks.push(loginSubmitFn);
|
|
||||||
if (this.callbacks.length == 1) {
|
|
||||||
this.doTemplate("/user_session/new.mini?database="+encodeURIComponent(this.database)+"&return_url=" + encodeURIComponent(this.urlWithoutAnchor()));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
logout: function (loginSubmitFn) {
|
|
||||||
this.callbacks.push(loginSubmitFn);
|
|
||||||
if (this.callbacks.length == 1) {
|
|
||||||
this.doTemplate("/user_session/do_destroy.mini");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
urlWithoutAnchor: function (path) {
|
|
||||||
return this._window['location']['href'].split("#")[0];
|
|
||||||
},
|
|
||||||
|
|
||||||
doTemplate: function (path) {
|
|
||||||
var self = this;
|
|
||||||
var id = new Date().getTime();
|
|
||||||
var url = this.urlWithoutAnchor() + "#$iframe_notify=" + id;
|
|
||||||
var iframeHeight = 330;
|
|
||||||
var loginView = jQuery('<div style="overflow:hidden; padding:2px 0 0 0;"><iframe name="'+ url +'" src="'+this.serverUrl + path + '" width="500" height="'+ iframeHeight +'"/></div>');
|
|
||||||
this._document.append(loginView);
|
|
||||||
loginView['dialog']({
|
|
||||||
'height':iframeHeight + 33, 'width':500,
|
|
||||||
'resizable': false, 'modal':true,
|
|
||||||
'title': 'Authentication: <a href="http://www.getangular.com"><tt><angular/></tt></a>'
|
|
||||||
});
|
|
||||||
angularCallbacks["_iframe_notify_" + id] = function() {
|
|
||||||
loginView['dialog']("destroy");
|
|
||||||
loginView['remove']();
|
|
||||||
foreach(self.callbacks, function(callback){
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
self.callbacks = [];
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
notAuthorized: function () {
|
|
||||||
if (this.forbidenView) return;
|
|
||||||
this.forbidenView = jQuery(ControlBar.FORBIDEN);
|
|
||||||
this.forbidenView.dialog({bgiframe:true, height:70, modal:true});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,330 +0,0 @@
|
||||||
function DataStore(post, users, anchor) {
|
|
||||||
this.post = post;
|
|
||||||
this.users = users;
|
|
||||||
this._cache_collections = [];
|
|
||||||
this._cache = {'$collections':this._cache_collections};
|
|
||||||
this.anchor = anchor;
|
|
||||||
this.bulkRequest = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStore.NullEntity = extend(function(){}, {
|
|
||||||
'all': function(){return [];},
|
|
||||||
'query': function(){return [];},
|
|
||||||
'load': function(){return {};},
|
|
||||||
'title': undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
DataStore.prototype = {
|
|
||||||
cache: function(document) {
|
|
||||||
if (! document.datastore === this) {
|
|
||||||
throw "Parameter must be an instance of Entity! " + toJson(document);
|
|
||||||
}
|
|
||||||
var key = document['$entity'] + '/' + document['$id'];
|
|
||||||
var cachedDocument = this._cache[key];
|
|
||||||
if (cachedDocument) {
|
|
||||||
Model.copyDirectFields(document, cachedDocument);
|
|
||||||
} else {
|
|
||||||
this._cache[key] = document;
|
|
||||||
cachedDocument = document;
|
|
||||||
}
|
|
||||||
return cachedDocument;
|
|
||||||
},
|
|
||||||
|
|
||||||
load: function(instance, id, callback, failure) {
|
|
||||||
if (id && id !== '*') {
|
|
||||||
var self = this;
|
|
||||||
this._jsonRequest(["GET", instance['$entity'] + "/" + id], function(response) {
|
|
||||||
instance['$loadFrom'](response);
|
|
||||||
instance['$migrate']();
|
|
||||||
var clone = instance['$$entity'](instance);
|
|
||||||
self.cache(clone);
|
|
||||||
(callback||noop)(instance);
|
|
||||||
}, failure);
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
},
|
|
||||||
|
|
||||||
loadMany: function(entity, ids, callback) {
|
|
||||||
var self=this;
|
|
||||||
var list = [];
|
|
||||||
var callbackCount = 0;
|
|
||||||
foreach(ids, function(id){
|
|
||||||
list.push(self.load(entity(), id, function(){
|
|
||||||
callbackCount++;
|
|
||||||
if (callbackCount == ids.length) {
|
|
||||||
(callback||noop)(list);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
return list;
|
|
||||||
},
|
|
||||||
|
|
||||||
loadOrCreate: function(instance, id, callback) {
|
|
||||||
var self=this;
|
|
||||||
return this.load(instance, id, callback, function(response){
|
|
||||||
if (response['$status_code'] == 404) {
|
|
||||||
instance['$id'] = id;
|
|
||||||
(callback||noop)(instance);
|
|
||||||
} else {
|
|
||||||
throw response;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
loadAll: function(entity, callback) {
|
|
||||||
var self = this;
|
|
||||||
var list = [];
|
|
||||||
list['$$accept'] = function(doc){
|
|
||||||
return doc['$entity'] == entity['title'];
|
|
||||||
};
|
|
||||||
this._cache_collections.push(list);
|
|
||||||
this._jsonRequest(["GET", entity['title']], function(response) {
|
|
||||||
var rows = response;
|
|
||||||
for ( var i = 0; i < rows.length; i++) {
|
|
||||||
var document = entity();
|
|
||||||
document['$loadFrom'](rows[i]);
|
|
||||||
list.push(self.cache(document));
|
|
||||||
}
|
|
||||||
(callback||noop)(list);
|
|
||||||
});
|
|
||||||
return list;
|
|
||||||
},
|
|
||||||
|
|
||||||
save: function(document, callback) {
|
|
||||||
var self = this;
|
|
||||||
var data = {};
|
|
||||||
document['$saveTo'](data);
|
|
||||||
this._jsonRequest(["POST", "", data], function(response) {
|
|
||||||
document['$loadFrom'](response);
|
|
||||||
var cachedDoc = self.cache(document);
|
|
||||||
_.each(self._cache_collections, function(collection){
|
|
||||||
if (collection['$$accept'](document)) {
|
|
||||||
angularArray['includeIf'](collection, cachedDoc, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (document['$$anchor']) {
|
|
||||||
self.anchor[document['$$anchor']] = document['$id'];
|
|
||||||
}
|
|
||||||
if (callback)
|
|
||||||
callback(document);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
remove: function(document, callback) {
|
|
||||||
var self = this;
|
|
||||||
var data = {};
|
|
||||||
document['$saveTo'](data);
|
|
||||||
this._jsonRequest(["DELETE", "", data], function(response) {
|
|
||||||
delete self._cache[document['$entity'] + '/' + document['$id']];
|
|
||||||
_.each(self._cache_collections, function(collection){
|
|
||||||
for ( var i = 0; i < collection.length; i++) {
|
|
||||||
var item = collection[i];
|
|
||||||
if (item['$id'] == document['$id']) {
|
|
||||||
collection.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
(callback||noop)(response);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_jsonRequest: function(request, callback, failure) {
|
|
||||||
request['$$callback'] = callback;
|
|
||||||
request['$$failure'] = failure||function(response){
|
|
||||||
throw response;
|
|
||||||
};
|
|
||||||
this.bulkRequest.push(request);
|
|
||||||
},
|
|
||||||
|
|
||||||
flush: function() {
|
|
||||||
if (this.bulkRequest.length === 0) return;
|
|
||||||
var self = this;
|
|
||||||
var bulkRequest = this.bulkRequest;
|
|
||||||
this.bulkRequest = [];
|
|
||||||
log('REQUEST:', bulkRequest);
|
|
||||||
function callback(code, bulkResponse){
|
|
||||||
log('RESPONSE[' + code + ']: ', bulkResponse);
|
|
||||||
if(bulkResponse['$status_code'] == 401) {
|
|
||||||
self.users['login'](function(){
|
|
||||||
self.post(bulkRequest, callback);
|
|
||||||
});
|
|
||||||
} else if(bulkResponse['$status_code']) {
|
|
||||||
alert(toJson(bulkResponse));
|
|
||||||
} else {
|
|
||||||
for ( var i = 0; i < bulkResponse.length; i++) {
|
|
||||||
var response = bulkResponse[i];
|
|
||||||
var request = bulkRequest[i];
|
|
||||||
var responseCode = response['$status_code'];
|
|
||||||
if(responseCode) {
|
|
||||||
if(responseCode == 403) {
|
|
||||||
self.users['notAuthorized']();
|
|
||||||
} else {
|
|
||||||
request['$$failure'](response);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
request['$$callback'](response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.post(bulkRequest, callback);
|
|
||||||
},
|
|
||||||
|
|
||||||
saveScope: function(scope, callback) {
|
|
||||||
var saveCounter = 1;
|
|
||||||
function onSaveDone() {
|
|
||||||
saveCounter--;
|
|
||||||
if (saveCounter === 0 && callback)
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
for(var key in scope) {
|
|
||||||
var item = scope[key];
|
|
||||||
if (item && item['$save'] == Model.prototype['$save']) {
|
|
||||||
saveCounter++;
|
|
||||||
item['$save'](onSaveDone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onSaveDone();
|
|
||||||
},
|
|
||||||
|
|
||||||
query: function(type, query, arg, callback){
|
|
||||||
var self = this;
|
|
||||||
var queryList = [];
|
|
||||||
queryList['$$accept'] = function(doc){
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
this._cache_collections.push(queryList);
|
|
||||||
var request = type['title'] + '/' + query + '=' + arg;
|
|
||||||
this._jsonRequest(["GET", request], function(response){
|
|
||||||
var list = response;
|
|
||||||
foreach(list, function(item){
|
|
||||||
var document = type()['$loadFrom'](item);
|
|
||||||
queryList.push(self.cache(document));
|
|
||||||
});
|
|
||||||
(callback||noop)(queryList);
|
|
||||||
});
|
|
||||||
return queryList;
|
|
||||||
},
|
|
||||||
|
|
||||||
entities: function(callback) {
|
|
||||||
var entities = [];
|
|
||||||
var self = this;
|
|
||||||
this._jsonRequest(["GET", "$entities"], function(response) {
|
|
||||||
foreach(response, function(value, entityName){
|
|
||||||
entities.push(self.entity(entityName));
|
|
||||||
});
|
|
||||||
entities.sort(function(a,b){return a.title > b.title ? 1 : -1;});
|
|
||||||
(callback||noop)(entities);
|
|
||||||
});
|
|
||||||
return entities;
|
|
||||||
},
|
|
||||||
|
|
||||||
documentCountsByUser: function(){
|
|
||||||
var counts = {};
|
|
||||||
var self = this;
|
|
||||||
self.post([["GET", "$users"]], function(code, response){
|
|
||||||
extend(counts, response[0]);
|
|
||||||
});
|
|
||||||
return counts;
|
|
||||||
},
|
|
||||||
|
|
||||||
userDocumentIdsByEntity: function(user){
|
|
||||||
var ids = {};
|
|
||||||
var self = this;
|
|
||||||
self.post([["GET", "$users/" + user]], function(code, response){
|
|
||||||
extend(ids, response[0]);
|
|
||||||
});
|
|
||||||
return ids;
|
|
||||||
},
|
|
||||||
|
|
||||||
entity: function(name, defaults){
|
|
||||||
if (!name) {
|
|
||||||
return DataStore.NullEntity;
|
|
||||||
}
|
|
||||||
var self = this;
|
|
||||||
var entity = extend(function(initialState){
|
|
||||||
return new Model(entity, initialState);
|
|
||||||
}, {
|
|
||||||
// entity.name does not work as name seems to be reserved for functions
|
|
||||||
'title': name,
|
|
||||||
'$$factory': true,
|
|
||||||
datastore: this, //private, obfuscate
|
|
||||||
'defaults': defaults || {},
|
|
||||||
'load': function(id, callback){
|
|
||||||
return self.load(entity(), id, callback);
|
|
||||||
},
|
|
||||||
'loadMany': function(ids, callback){
|
|
||||||
return self.loadMany(entity, ids, callback);
|
|
||||||
},
|
|
||||||
'loadOrCreate': function(id, callback){
|
|
||||||
return self.loadOrCreate(entity(), id, callback);
|
|
||||||
},
|
|
||||||
'all': function(callback){
|
|
||||||
return self.loadAll(entity, callback);
|
|
||||||
},
|
|
||||||
'query': function(query, queryArgs, callback){
|
|
||||||
return self.query(entity, query, queryArgs, callback);
|
|
||||||
},
|
|
||||||
'properties': function(callback) {
|
|
||||||
self._jsonRequest(["GET", name + "/$properties"], callback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return entity;
|
|
||||||
},
|
|
||||||
|
|
||||||
join: function(join){
|
|
||||||
function fn(){
|
|
||||||
throw "Joined entities can not be instantiated into a document.";
|
|
||||||
};
|
|
||||||
function base(name){return name ? name.substring(0, name.indexOf('.')) : undefined;}
|
|
||||||
function next(name){return name.substring(name.indexOf('.') + 1);}
|
|
||||||
var joinOrder = _(join).chain().
|
|
||||||
map(function($, name){
|
|
||||||
return name;}).
|
|
||||||
sortBy(function(name){
|
|
||||||
var path = [];
|
|
||||||
do {
|
|
||||||
if (_(path).include(name)) throw "Infinite loop in join: " + path.join(" -> ");
|
|
||||||
path.push(name);
|
|
||||||
if (!join[name]) throw _("Named entity '<%=name%>' is undefined.").template({name:name});
|
|
||||||
name = base(join[name].on);
|
|
||||||
} while(name);
|
|
||||||
return path.length;
|
|
||||||
}).
|
|
||||||
value();
|
|
||||||
if (_(joinOrder).select(function($){return join[$].on;}).length != joinOrder.length - 1)
|
|
||||||
throw "Exactly one entity needs to be primary.";
|
|
||||||
fn['query'] = function(exp, value) {
|
|
||||||
var joinedResult = [];
|
|
||||||
var baseName = base(exp);
|
|
||||||
if (baseName != joinOrder[0]) throw _("Named entity '<%=name%>' is not a primary entity.").template({name:baseName});
|
|
||||||
var Entity = join[baseName].join;
|
|
||||||
var joinIndex = 1;
|
|
||||||
Entity['query'](next(exp), value, function(result){
|
|
||||||
var nextJoinName = joinOrder[joinIndex++];
|
|
||||||
var nextJoin = join[nextJoinName];
|
|
||||||
var nextJoinOn = nextJoin.on;
|
|
||||||
var joinIds = {};
|
|
||||||
_(result).each(function(doc){
|
|
||||||
var row = {};
|
|
||||||
joinedResult.push(row);
|
|
||||||
row[baseName] = doc;
|
|
||||||
var id = Scope.getter(row, nextJoinOn);
|
|
||||||
joinIds[id] = id;
|
|
||||||
});
|
|
||||||
nextJoin.join.loadMany(_.toArray(joinIds), function(result){
|
|
||||||
var byId = {};
|
|
||||||
_(result).each(function(doc){
|
|
||||||
byId[doc.$id] = doc;
|
|
||||||
});
|
|
||||||
_(joinedResult).each(function(row){
|
|
||||||
var id = Scope.getter(row, nextJoinOn);
|
|
||||||
row[nextJoinName] = byId[id];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return joinedResult;
|
|
||||||
};
|
|
||||||
return fn;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
function Server(url, getScript) {
|
|
||||||
this.url = url;
|
|
||||||
this.nextId = 0;
|
|
||||||
this.getScript = getScript;
|
|
||||||
this.uuid = "_" + ("" + Math.random()).substr(2) + "_";
|
|
||||||
this.maxSize = 1800;
|
|
||||||
};
|
|
||||||
|
|
||||||
Server.prototype = {
|
|
||||||
base64url: function(txt) {
|
|
||||||
return Base64.encode(txt);
|
|
||||||
},
|
|
||||||
|
|
||||||
request: function(method, url, request, callback) {
|
|
||||||
var requestId = this.uuid + (this.nextId++);
|
|
||||||
var payload = this.base64url(toJson({'u':url, 'm':method, 'p':request}));
|
|
||||||
var totalPockets = Math.ceil(payload.length / this.maxSize);
|
|
||||||
var baseUrl = this.url + "/$/" + requestId + "/" + totalPockets + "/";
|
|
||||||
angularCallbacks[requestId] = function(response) {
|
|
||||||
delete angularCallbacks[requestId];
|
|
||||||
callback(200, response);
|
|
||||||
};
|
|
||||||
for ( var pocketNo = 0; pocketNo < totalPockets; pocketNo++) {
|
|
||||||
var pocket = payload.substr(pocketNo * this.maxSize, this.maxSize);
|
|
||||||
this.getScript(baseUrl + (pocketNo+1) + "?h=" + pocket, noop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function FrameServer(frame) {
|
|
||||||
this.frame = frame;
|
|
||||||
};
|
|
||||||
FrameServer.PREFIX = "$DATASET:";
|
|
||||||
|
|
||||||
FrameServer.prototype = {
|
|
||||||
read:function(){
|
|
||||||
this.data = fromJson(this.frame.name.substr(FrameServer.PREFIX.length));
|
|
||||||
},
|
|
||||||
write:function(){
|
|
||||||
this.frame.name = FrameServer.PREFIX + toJson(this.data);
|
|
||||||
},
|
|
||||||
request: function(method, url, request, callback) {
|
|
||||||
//alert(method + " " + url + " " + toJson(request) + " " + toJson(callback));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function VisualServer(delegate, status, update) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
this.update = update;
|
|
||||||
this.status = status;
|
|
||||||
};
|
|
||||||
|
|
||||||
VisualServer.prototype = {
|
|
||||||
request:function(method, url, request, callback) {
|
|
||||||
var self = this;
|
|
||||||
this.status.beginRequest(request);
|
|
||||||
this.delegate.request(method, url, request, function() {
|
|
||||||
self.status.endRequest();
|
|
||||||
try {
|
|
||||||
callback.apply(this, arguments);
|
|
||||||
} catch (e) {
|
|
||||||
alert(toJson(e));
|
|
||||||
}
|
|
||||||
self.update();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
function Users(server, controlBar) {
|
|
||||||
this.server = server;
|
|
||||||
this.controlBar = controlBar;
|
|
||||||
};
|
|
||||||
|
|
||||||
extend(Users.prototype, {
|
|
||||||
'fetchCurrentUser':function(callback) {
|
|
||||||
var self = this;
|
|
||||||
this.server.request("GET", "/account.json", {}, function(code, response){
|
|
||||||
self['current'] = response['user'];
|
|
||||||
callback(response['user']);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
'logout': function(callback) {
|
|
||||||
var self = this;
|
|
||||||
this.controlBar.logout(function(){
|
|
||||||
delete self['current'];
|
|
||||||
(callback||noop)();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
'login': function(callback) {
|
|
||||||
var self = this;
|
|
||||||
this.controlBar.login(function(){
|
|
||||||
self['fetchCurrentUser'](function(){
|
|
||||||
(callback||noop)();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
'notAuthorized': function(){
|
|
||||||
this.controlBar.notAuthorized();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
|
|
||||||
angular.directive("auth", function(expression, element){
|
|
||||||
return function(){
|
|
||||||
if(expression == "eager") {
|
|
||||||
this.$users.fetchCurrent();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//expression = "book=Book:{year=2000}"
|
|
||||||
angular.directive("entity", function(expression, element){
|
|
||||||
//parse expression, ignore element
|
|
||||||
var entityName; // "Book";
|
|
||||||
var instanceName; // "book";
|
|
||||||
var defaults; // {year: 2000};
|
|
||||||
|
|
||||||
parse(expression);
|
|
||||||
|
|
||||||
return function(){
|
|
||||||
this[entityName] = this.$datastore.entity(entityName, defaults);
|
|
||||||
this[instanceName] = this[entityName]();
|
|
||||||
this.$watch("$anchor."+instanceName, function(newAnchor){
|
|
||||||
this[instanceName] = this[entityName].get(this.$anchor[instanceName]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
ScopeTest = TestCase('ScopeTest');
|
|
||||||
|
|
||||||
ScopeTest.prototype.testGetScopeRetrieval = function(){
|
|
||||||
var scope = {};
|
|
||||||
var form = jQuery("<a><b><c></c></b></a>");
|
|
||||||
form.data('scope', scope);
|
|
||||||
var c = form.find('c');
|
|
||||||
assertTrue(scope === c.scope());
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testGetScopeRetrievalIntermediateNode = function(){
|
|
||||||
var scope = {};
|
|
||||||
var form = jQuery("<a><b><c></c></b></a>");
|
|
||||||
form.find("b").data('scope', scope);
|
|
||||||
var b = form.find('b');
|
|
||||||
assertTrue(scope === b.scope());
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testNoScopeDoesNotCauseInfiniteRecursion = function(){
|
|
||||||
var form = jQuery("<a><b><c></c></b></a>");
|
|
||||||
var c = form.find('c');
|
|
||||||
assertTrue(!c.scope());
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testScopeEval = function(){
|
|
||||||
var scope = new Scope({b:345});
|
|
||||||
assertEquals(scope.eval('b = 123'), 123);
|
|
||||||
assertEquals(scope.get('b'), 123);
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testScopeFromPrototype = function(){
|
|
||||||
var scope = new Scope({b:123});
|
|
||||||
scope.eval('a = b');
|
|
||||||
scope.eval('b = 456');
|
|
||||||
assertEquals(scope.get('a'), 123);
|
|
||||||
assertEquals(scope.get('b'), 456);
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testSetScopeGet = function(){
|
|
||||||
var scope = new Scope();
|
|
||||||
assertEquals(987, scope.set('a', 987));
|
|
||||||
assertEquals(scope.get('a'), 987);
|
|
||||||
assertEquals(scope.eval('a'), 987);
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testGetChain = function(){
|
|
||||||
var scope = new Scope({a:{b:987}});
|
|
||||||
assertEquals(scope.get('a.b'), 987);
|
|
||||||
assertEquals(scope.eval('a.b'), 987);
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testGetUndefinedChain = function(){
|
|
||||||
var scope = new Scope();
|
|
||||||
assertEquals(typeof scope.get('a.b'), 'undefined');
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testSetChain = function(){
|
|
||||||
var scope = new Scope({a:{}});
|
|
||||||
scope.set('a.b', 987);
|
|
||||||
assertEquals(scope.get('a.b'), 987);
|
|
||||||
assertEquals(scope.eval('a.b'), 987);
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testSetGetOnChain = function(){
|
|
||||||
var scope = new Scope();
|
|
||||||
scope.set('a.b', 987);
|
|
||||||
assertEquals(scope.get('a.b'), 987);
|
|
||||||
assertEquals(scope.eval('a.b'), 987);
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testGlobalFunctionAccess =function(){
|
|
||||||
window['scopeAddTest'] = function (a, b) {return a+b;};
|
|
||||||
var scope = new Scope({window:window});
|
|
||||||
assertEquals(scope.eval('window.scopeAddTest(1,2)'), 3);
|
|
||||||
|
|
||||||
scope.set('add', function (a, b) {return a+b;});
|
|
||||||
assertEquals(scope.eval('add(1,2)'), 3);
|
|
||||||
|
|
||||||
scope.set('math.add', function (a, b) {return a+b;});
|
|
||||||
assertEquals(scope.eval('math.add(1,2)'), 3);
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testValidationEval = function(){
|
|
||||||
expectAsserts(4);
|
|
||||||
var scope = new Scope();
|
|
||||||
scope.set("name", "misko");
|
|
||||||
angular.validator.testValidator = function(value, expect){
|
|
||||||
assertEquals("misko", this.name);
|
|
||||||
return value == expect ? null : "Error text";
|
|
||||||
};
|
|
||||||
|
|
||||||
assertEquals("Error text", scope.validate("testValidator:'abc'", 'x'));
|
|
||||||
assertEquals(null, scope.validate("testValidator:'abc'", 'abc'));
|
|
||||||
|
|
||||||
delete angular.validator['testValidator'];
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testCallingNonExistantMethodShouldProduceFriendlyException = function() {
|
|
||||||
expectAsserts(1);
|
|
||||||
var scope = new Scope({obj:{}});
|
|
||||||
try {
|
|
||||||
scope.eval("obj.iDontExist()");
|
|
||||||
fail();
|
|
||||||
} catch (e) {
|
|
||||||
assertEquals("Expression 'obj.iDontExist' is not a function.", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testAccessingWithInvalidPathShouldThrowError = function() {
|
|
||||||
var scope = new Scope();
|
|
||||||
try {
|
|
||||||
scope.get('a.{{b}}');
|
|
||||||
fail();
|
|
||||||
} catch (e) {
|
|
||||||
assertEquals("Expression 'a.{{b}}' is not a valid expression for accesing variables.", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testItShouldHave$parent = function() {
|
|
||||||
var parent = new Scope({}, "ROOT");
|
|
||||||
var child = new Scope(parent.state);
|
|
||||||
assertSame("parent", child.state.$parent, parent.state);
|
|
||||||
assertSame("root", child.state.$root, parent.state);
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testItShouldHave$root = function() {
|
|
||||||
var scope = new Scope({}, "ROOT");
|
|
||||||
assertSame(scope.state.$root, scope.state);
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testItShouldBuildPathOnUndefined = function(){
|
|
||||||
var scope = new Scope({}, "ROOT");
|
|
||||||
scope.setEval("a.$b.c", 1);
|
|
||||||
assertJsonEquals({$b:{c:1}}, scope.get("a"));
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeTest.prototype.testItShouldMapUnderscoreFunctions = function(){
|
|
||||||
var scope = new Scope({}, "ROOT");
|
|
||||||
scope.set("a", [1,2,3]);
|
|
||||||
assertEquals('function', typeof scope.get("a.$size"));
|
|
||||||
scope.eval("a.$includeIf(4,true)");
|
|
||||||
assertEquals(4, scope.get("a.$size")());
|
|
||||||
assertEquals(4, scope.eval("a.$size()"));
|
|
||||||
assertEquals('undefined', typeof scope.get("a.dontExist"));
|
|
||||||
};
|
|
||||||
|
|
@ -1,268 +0,0 @@
|
||||||
WidgetTest = TestCase('WidgetTest');
|
|
||||||
|
|
||||||
WidgetTest.prototype.testRequired = function () {
|
|
||||||
var view = $('<input name="a" ng:required>');
|
|
||||||
var scope = new Scope({$invalidWidgets:[]});
|
|
||||||
var cntl = new TextController(view[0], 'a', angularFormatter.noop);
|
|
||||||
cntl.updateView(scope);
|
|
||||||
assertTrue(view.hasClass('ng-validation-error'));
|
|
||||||
assertEquals("Required Value", view.attr('ng-error'));
|
|
||||||
scope.set('a', 'A');
|
|
||||||
cntl.updateView(scope);
|
|
||||||
assertFalse(view.hasClass('ng-validation-error'));
|
|
||||||
assertEquals("undefined", typeof view.attr('ng-error'));
|
|
||||||
};
|
|
||||||
|
|
||||||
WidgetTest.prototype.testValidator = function () {
|
|
||||||
var view = $('<input name="a" ng:validate="testValidator:\'ABC\'">');
|
|
||||||
var scope = new Scope({$invalidWidgets:[]});
|
|
||||||
var cntl = new TextController(view[0], 'a', angularFormatter.noop);
|
|
||||||
angular.validator.testValidator = function(value, expect){
|
|
||||||
return value == expect ? false : "Error text";
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.set('a', '');
|
|
||||||
cntl.updateView(scope);
|
|
||||||
assertEquals(view.hasClass('ng-validation-error'), false);
|
|
||||||
assertEquals(null, view.attr('ng-error'));
|
|
||||||
|
|
||||||
scope.set('a', 'X');
|
|
||||||
cntl.updateView(scope);
|
|
||||||
assertEquals(view.hasClass('ng-validation-error'), true);
|
|
||||||
assertEquals(view.attr('ng-error'), "Error text");
|
|
||||||
assertEquals("Error text", view.attr('ng-error'));
|
|
||||||
|
|
||||||
scope.set('a', 'ABC');
|
|
||||||
cntl.updateView(scope);
|
|
||||||
assertEquals(view.hasClass('ng-validation-error'), false);
|
|
||||||
assertEquals(view.attr('ng-error'), null);
|
|
||||||
assertEquals(null, view.attr('ng-error'));
|
|
||||||
|
|
||||||
delete angular.validator['testValidator'];
|
|
||||||
};
|
|
||||||
|
|
||||||
WidgetTest.prototype.testRequiredValidator = function () {
|
|
||||||
var view = $('<input name="a" ng:required ng:validate="testValidator:\'ABC\'">');
|
|
||||||
var scope = new Scope({$invalidWidgets:[]});
|
|
||||||
var cntl = new TextController(view[0], 'a', angularFormatter.noop);
|
|
||||||
angular.validator.testValidator = function(value, expect){
|
|
||||||
return value == expect ? null : "Error text";
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.set('a', '');
|
|
||||||
cntl.updateView(scope);
|
|
||||||
assertEquals(view.hasClass('ng-validation-error'), true);
|
|
||||||
assertEquals("Required Value", view.attr('ng-error'));
|
|
||||||
|
|
||||||
scope.set('a', 'X');
|
|
||||||
cntl.updateView(scope);
|
|
||||||
assertEquals(view.hasClass('ng-validation-error'), true);
|
|
||||||
assertEquals("Error text", view.attr('ng-error'));
|
|
||||||
|
|
||||||
scope.set('a', 'ABC');
|
|
||||||
cntl.updateView(scope);
|
|
||||||
assertEquals(view.hasClass('ng-validation-error'), false);
|
|
||||||
assertEquals(null, view.attr('ng-error'));
|
|
||||||
|
|
||||||
delete angular.validator['testValidator'];
|
|
||||||
};
|
|
||||||
|
|
||||||
TextControllerTest = TestCase("TextControllerTest");
|
|
||||||
|
|
||||||
TextControllerTest.prototype.testDatePicker = function() {
|
|
||||||
var input = $('<input type="text" ng-widget="datepicker">');
|
|
||||||
input.data('scope', new Scope());
|
|
||||||
var body = $(document.body);
|
|
||||||
body.append(input);
|
|
||||||
var binder = new Binder(input[0], new WidgetFactory());
|
|
||||||
assertTrue('before', input.data('datepicker') === undefined);
|
|
||||||
binder.compile();
|
|
||||||
assertTrue('after', input.data('datepicker') !== null);
|
|
||||||
assertTrue(body.html(), input.hasClass('hasDatepicker'));
|
|
||||||
};
|
|
||||||
|
|
||||||
RepeaterUpdaterTest = TestCase("RepeaterUpdaterTest");
|
|
||||||
|
|
||||||
RepeaterUpdaterTest.prototype.testRemoveThenAdd = function() {
|
|
||||||
var view = $("<div><span/></div>");
|
|
||||||
var template = function () {
|
|
||||||
return $("<li/>");
|
|
||||||
};
|
|
||||||
var repeater = new RepeaterUpdater(view.find("span"), "a in b", template, "");
|
|
||||||
var scope = new Scope();
|
|
||||||
scope.set('b', [1,2]);
|
|
||||||
|
|
||||||
repeater.updateView(scope);
|
|
||||||
|
|
||||||
scope.set('b', []);
|
|
||||||
repeater.updateView(scope);
|
|
||||||
|
|
||||||
scope.set('b', [1]);
|
|
||||||
repeater.updateView(scope);
|
|
||||||
assertEquals(1, view.find("li").size());
|
|
||||||
};
|
|
||||||
|
|
||||||
RepeaterUpdaterTest.prototype.testShouldBindWidgetOnRepeaterClone = function(){
|
|
||||||
//fail();
|
|
||||||
};
|
|
||||||
|
|
||||||
RepeaterUpdaterTest.prototype.testShouldThrowInformativeSyntaxError= function(){
|
|
||||||
expectAsserts(1);
|
|
||||||
try {
|
|
||||||
var repeater = new RepeaterUpdater(null, "a=b");
|
|
||||||
} catch (e) {
|
|
||||||
assertEquals("Expected ng:repeat in form of 'item in collection' but got 'a=b'.", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
SelectControllerTest = TestCase("SelectControllerTest");
|
|
||||||
|
|
||||||
SelectControllerTest.prototype.testShouldUpdateModelNullOnNothingSelected = function(){
|
|
||||||
var scope = new Scope();
|
|
||||||
var view = {selectedIndex:-1, options:[]};
|
|
||||||
var cntl = new SelectController(view, 'abc');
|
|
||||||
cntl.updateModel(scope);
|
|
||||||
assertNull(scope.get('abc'));
|
|
||||||
};
|
|
||||||
|
|
||||||
SelectControllerTest.prototype.testShouldUpdateModelWhenNothingSelected = function(){
|
|
||||||
var scope = new Scope();
|
|
||||||
var view = {value:'123'};
|
|
||||||
var cntl = new SelectController(view, 'abc');
|
|
||||||
cntl.updateView(scope);
|
|
||||||
assertEquals("123", scope.get('abc'));
|
|
||||||
};
|
|
||||||
|
|
||||||
BindUpdaterTest = TestCase("BindUpdaterTest");
|
|
||||||
|
|
||||||
BindUpdaterTest.prototype.testShouldDisplayNothingForUndefined = function () {
|
|
||||||
var view = $('<span />');
|
|
||||||
var controller = new BindUpdater(view[0], "{{a}}");
|
|
||||||
var scope = new Scope();
|
|
||||||
|
|
||||||
scope.set('a', undefined);
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals("", view.text());
|
|
||||||
|
|
||||||
scope.set('a', null);
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals("", view.text());
|
|
||||||
};
|
|
||||||
|
|
||||||
BindUpdaterTest.prototype.testShouldDisplayJsonForNonStrings = function () {
|
|
||||||
var view = $('<span />');
|
|
||||||
var controller = new BindUpdater(view[0], "{{obj}}");
|
|
||||||
|
|
||||||
controller.updateView(new Scope({obj:[]}));
|
|
||||||
assertEquals("[]", view.text());
|
|
||||||
|
|
||||||
controller.updateView(new Scope({obj:{text:'abc'}}));
|
|
||||||
assertEquals('abc', fromJson(view.text()).text);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
BindUpdaterTest.prototype.testShouldInsertHtmlNode = function () {
|
|
||||||
var view = $('<span />');
|
|
||||||
var controller = new BindUpdater(view[0], "<fake>&{{obj}}</fake>");
|
|
||||||
var scope = new Scope();
|
|
||||||
|
|
||||||
scope.set("obj", $('<div>myDiv</div>')[0]);
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals("<fake>&myDiv</fake>", view.text());
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
BindUpdaterTest.prototype.testShouldDisplayTextMethod = function () {
|
|
||||||
var view = $('<div />');
|
|
||||||
var controller = new BindUpdater(view[0], "{{obj}}");
|
|
||||||
var scope = new Scope();
|
|
||||||
|
|
||||||
scope.set("obj", new angular.filter.Meta({text:function(){return "abc";}}));
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals("abc", view.text());
|
|
||||||
|
|
||||||
scope.set("obj", new angular.filter.Meta({text:"123"}));
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals("123", view.text());
|
|
||||||
|
|
||||||
scope.set("obj", {text:"123"});
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals("123", fromJson(view.text()).text);
|
|
||||||
};
|
|
||||||
|
|
||||||
BindUpdaterTest.prototype.testShouldDisplayHtmlMethod = function () {
|
|
||||||
var view = $('<div />');
|
|
||||||
var controller = new BindUpdater(view[0], "{{obj}}");
|
|
||||||
var scope = new Scope();
|
|
||||||
|
|
||||||
scope.set("obj", new angular.filter.Meta({html:function(){return "a<div>b</div>c";}}));
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals("abc", view.text());
|
|
||||||
|
|
||||||
scope.set("obj", new angular.filter.Meta({html:"1<div>2</div>3"}));
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals("123", view.text());
|
|
||||||
|
|
||||||
scope.set("obj", {html:"123"});
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals("123", fromJson(view.text()).html);
|
|
||||||
};
|
|
||||||
|
|
||||||
BindUpdaterTest.prototype.testUdateBoolean = function() {
|
|
||||||
var view = $('<div />');
|
|
||||||
var controller = new BindUpdater(view[0], "{{true}}, {{false}}");
|
|
||||||
controller.updateView(new Scope());
|
|
||||||
assertEquals('true, false', view.text());
|
|
||||||
};
|
|
||||||
|
|
||||||
BindAttrUpdaterTest = TestCase("BindAttrUpdaterTest");
|
|
||||||
|
|
||||||
BindAttrUpdaterTest.prototype.testShouldLoadBlankImageWhenBindingIsUndefined = function () {
|
|
||||||
var view = $('<img />');
|
|
||||||
var controller = new BindAttrUpdater(view[0], {src: '{{imageUrl}}'});
|
|
||||||
|
|
||||||
var scope = new Scope();
|
|
||||||
scope.set('imageUrl', undefined);
|
|
||||||
scope.set('$config.blankImage', 'http://server/blank.gif');
|
|
||||||
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals("http://server/blank.gif", view.attr('src'));
|
|
||||||
};
|
|
||||||
|
|
||||||
RepeaterUpdaterTest.prototype.testShouldNotDieWhenRepeatExpressionIsNull = function() {
|
|
||||||
var rep = new RepeaterUpdater(null, "$item in items", null, null);
|
|
||||||
var scope = new Scope();
|
|
||||||
scope.set('items', undefined);
|
|
||||||
rep.updateView(scope);
|
|
||||||
};
|
|
||||||
|
|
||||||
RepeaterUpdaterTest.prototype.testShouldIterateOverKeys = function() {
|
|
||||||
var rep = new RepeaterUpdater(null, "($k,_v) in items", null, null);
|
|
||||||
assertEquals("items", rep.iteratorExp);
|
|
||||||
assertEquals("_v", rep.valueExp);
|
|
||||||
assertEquals("$k", rep.keyExp);
|
|
||||||
};
|
|
||||||
|
|
||||||
EvalUpdaterTest = TestCase("EvalUpdaterTest");
|
|
||||||
EvalUpdaterTest.prototype.testEvalThrowsException = function(){
|
|
||||||
var view = $('<div/>');
|
|
||||||
var eval = new EvalUpdater(view[0], 'undefined()');
|
|
||||||
|
|
||||||
eval.updateView(new Scope());
|
|
||||||
assertTrue(!!view.attr('ng-error'));
|
|
||||||
assertTrue(view.hasClass('ng-exception'));
|
|
||||||
|
|
||||||
eval.exp = "1";
|
|
||||||
eval.updateView(new Scope());
|
|
||||||
assertFalse(!!view.attr('ng-error'));
|
|
||||||
assertFalse(view.hasClass('ng-exception'));
|
|
||||||
};
|
|
||||||
|
|
||||||
RadioControllerTest = TestCase("RadioController");
|
|
||||||
RadioControllerTest.prototype.testItShouldTreatTrueStringAsBoolean = function () {
|
|
||||||
var view = $('<input type="radio" name="select" value="true"/>');
|
|
||||||
var radio = new RadioController(view[0], 'select');
|
|
||||||
var scope = new Scope({select:true});
|
|
||||||
radio.updateView(scope);
|
|
||||||
assertTrue(view[0].checked);
|
|
||||||
};
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
Base64Test = TestCase('Base64Test');
|
|
||||||
|
|
||||||
Base64Test.prototype.testEncodeDecode = function(){
|
|
||||||
assertEquals(Base64.decode(Base64.encode('hello')), 'hello');
|
|
||||||
};
|
|
||||||
|
|
@ -1,616 +0,0 @@
|
||||||
DataStoreTest = TestCase('DataStoreTest');
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testSavePostsToServer = function(){
|
|
||||||
expectAsserts(10);
|
|
||||||
var response;
|
|
||||||
var post = function(data, callback){
|
|
||||||
var method = data[0][0];
|
|
||||||
var posted = data[0][2];
|
|
||||||
assertEquals("POST", method);
|
|
||||||
assertEquals("abc", posted.$entity);
|
|
||||||
assertEquals("123", posted.$id);
|
|
||||||
assertEquals("1", posted.$version);
|
|
||||||
assertFalse('function' == typeof posted.save);
|
|
||||||
response = fromJson(toJson(posted));
|
|
||||||
response.$entity = "abc";
|
|
||||||
response.$id = "123";
|
|
||||||
response.$version = "2";
|
|
||||||
callback(200, [response]);
|
|
||||||
};
|
|
||||||
var datastore = new DataStore(post);
|
|
||||||
var model = datastore.entity('abc', {name: "value"})();
|
|
||||||
model.$id = "123";
|
|
||||||
model.$version = "1";
|
|
||||||
|
|
||||||
datastore.save(model, function(obj){
|
|
||||||
assertTrue(obj === model);
|
|
||||||
assertEquals(obj.$entity, "abc");
|
|
||||||
assertEquals(obj.$id, "123");
|
|
||||||
assertEquals(obj.$version, "2");
|
|
||||||
assertEquals(obj.name, "value");
|
|
||||||
obj.after = true;
|
|
||||||
});
|
|
||||||
datastore.flush();
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testLoadGetsFromServer = function(){
|
|
||||||
expectAsserts(12);
|
|
||||||
var post = function(data, callback){
|
|
||||||
var method = data[0][0];
|
|
||||||
var path = data[0][1];
|
|
||||||
assertEquals("GET", method);
|
|
||||||
assertEquals("abc/1", path);
|
|
||||||
response = [{$entity:'abc', $id:'1', $version:'2', key:"value"}];
|
|
||||||
callback(200, response);
|
|
||||||
};
|
|
||||||
var datastore = new DataStore(post);
|
|
||||||
|
|
||||||
var model = datastore.entity("abc", {merge:true})();
|
|
||||||
assertEquals(datastore.load(model, '1', function(obj){
|
|
||||||
assertEquals(obj.$entity, "abc");
|
|
||||||
assertEquals(obj.$id, "1");
|
|
||||||
assertEquals(obj.$version, "2");
|
|
||||||
assertEquals(obj.key, "value");
|
|
||||||
}), model);
|
|
||||||
datastore.flush();
|
|
||||||
assertEquals(model.$entity, "abc");
|
|
||||||
assertEquals(model.$id, "1");
|
|
||||||
assertEquals(model.$version, "2");
|
|
||||||
assertEquals(model.key, "value");
|
|
||||||
assertEquals(model.merge, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testRemove = function(){
|
|
||||||
expectAsserts(8);
|
|
||||||
var response;
|
|
||||||
var post = function(data, callback){
|
|
||||||
var method = data[0][0];
|
|
||||||
var posted = data[0][2];
|
|
||||||
assertEquals("DELETE", method);
|
|
||||||
assertEquals("abc", posted.$entity);
|
|
||||||
assertEquals("123", posted.$id);
|
|
||||||
assertEquals("1", posted.$version);
|
|
||||||
assertFalse('function' == typeof posted.save);
|
|
||||||
response = fromJson(toJson(posted));
|
|
||||||
response.$entity = "abc";
|
|
||||||
response.$id = "123";
|
|
||||||
response.$version = "2";
|
|
||||||
callback(200, [response]);
|
|
||||||
};
|
|
||||||
var model;
|
|
||||||
var datastore = new DataStore(post);
|
|
||||||
model = datastore.entity('abc', {name: "value"})();
|
|
||||||
model.$id = "123";
|
|
||||||
model.$version = "1";
|
|
||||||
|
|
||||||
datastore.remove(model, function(obj){
|
|
||||||
assertEquals(obj.$id, "123");
|
|
||||||
assertEquals(obj.$version, "2");
|
|
||||||
assertEquals(obj.name, "value");
|
|
||||||
obj.after = true;
|
|
||||||
});
|
|
||||||
datastore.flush();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
DataStoreTest.prototype.test401ResponseDoesNotCallCallback = function(){
|
|
||||||
expectAsserts(1);
|
|
||||||
var post = function(data, callback) {
|
|
||||||
callback(200, {$status_code: 401});
|
|
||||||
};
|
|
||||||
|
|
||||||
var datastore = new DataStore(post, {login:function(){
|
|
||||||
assertTrue(true);
|
|
||||||
}});
|
|
||||||
|
|
||||||
var onLoadAll = function(){
|
|
||||||
assertTrue(false, "onLoadAll should not be called when response is status 401");
|
|
||||||
};
|
|
||||||
datastore.bulkRequest.push({});
|
|
||||||
datastore.flush();
|
|
||||||
datastore.loadAll({type: "A"}, onLoadAll);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.test403ResponseDoesNotCallCallback = function(){
|
|
||||||
expectAsserts(1);
|
|
||||||
var post = function(data, callback) {
|
|
||||||
callback(200, [{$status_code: 403}]);
|
|
||||||
};
|
|
||||||
|
|
||||||
var datastore = new DataStore(post, {notAuthorized:function(){
|
|
||||||
assertTrue(true);
|
|
||||||
}});
|
|
||||||
|
|
||||||
var onLoadAll = function(){
|
|
||||||
assertTrue(false, "onLoadAll should not be called when response is status 403");
|
|
||||||
};
|
|
||||||
datastore.bulkRequest.push({});
|
|
||||||
datastore.flush();
|
|
||||||
datastore.loadAll({type: "A"}, onLoadAll);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testLoadCalledWithoutIdShouldBeNoop = function(){
|
|
||||||
expectAsserts(2);
|
|
||||||
var post = function(url, callback){
|
|
||||||
assertTrue(false);
|
|
||||||
};
|
|
||||||
var datastore = new DataStore(post);
|
|
||||||
var model = datastore.entity("abc")();
|
|
||||||
assertEquals(datastore.load(model, undefined), model);
|
|
||||||
assertEquals(model.$entity, "abc");
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testEntityFactory = function(){
|
|
||||||
var ds = new DataStore();
|
|
||||||
var Recipe = ds.entity("Recipe", {a:1, b:2});
|
|
||||||
assertEquals(Recipe.title, "Recipe");
|
|
||||||
assertEquals(Recipe.defaults.a, 1);
|
|
||||||
assertEquals(Recipe.defaults.b, 2);
|
|
||||||
|
|
||||||
var recipe = Recipe();
|
|
||||||
assertEquals(recipe.$entity, "Recipe");
|
|
||||||
assertEquals(recipe.a, 1);
|
|
||||||
assertEquals(recipe.b, 2);
|
|
||||||
|
|
||||||
recipe = new Recipe();
|
|
||||||
assertEquals(recipe.$entity, "Recipe");
|
|
||||||
assertEquals(recipe.a, 1);
|
|
||||||
assertEquals(recipe.b, 2);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testEntityFactoryNoDefaults = function(){
|
|
||||||
var ds = new DataStore();
|
|
||||||
var Recipe = ds.entity("Recipe");
|
|
||||||
assertEquals(Recipe.title, "Recipe");
|
|
||||||
|
|
||||||
recipe = new Recipe();
|
|
||||||
assertEquals(recipe.$entity, "Recipe");
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testEntityFactoryWithInitialValues = function(){
|
|
||||||
var ds = new DataStore();
|
|
||||||
var Recipe = ds.entity("Recipe");
|
|
||||||
|
|
||||||
var recipe = Recipe({name: "name"});
|
|
||||||
assertEquals("name", recipe.name);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testEntityLoad = function(){
|
|
||||||
var ds = new DataStore();
|
|
||||||
var Recipe = ds.entity("Recipe", {a:1, b:2});
|
|
||||||
ds.load = function(instance, id, callback){
|
|
||||||
callback.apply(instance);
|
|
||||||
return instance;
|
|
||||||
};
|
|
||||||
var instance = null;
|
|
||||||
var recipe2 = Recipe.load("ID", function(){
|
|
||||||
instance = this;
|
|
||||||
});
|
|
||||||
assertTrue(recipe2 === instance);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testSaveScope = function(){
|
|
||||||
var ds = new DataStore();
|
|
||||||
var log = "";
|
|
||||||
var Person = ds.entity("Person");
|
|
||||||
var person1 = Person({name:"A", $entity:"Person", $id:"1", $version:"1"}, ds);
|
|
||||||
person1.$$anchor = "A";
|
|
||||||
var person2 = Person({name:"B", $entity:"Person", $id:"2", $version:"2"}, ds);
|
|
||||||
person2.$$anchor = "B";
|
|
||||||
var anchor = {};
|
|
||||||
ds.anchor = anchor;
|
|
||||||
ds._jsonRequest = function(request, callback){
|
|
||||||
log += "save(" + request[2].$id + ");";
|
|
||||||
callback({$id:request[2].$id});
|
|
||||||
};
|
|
||||||
ds.saveScope({person1:person1, person2:person2,
|
|
||||||
ignoreMe:{name: "ignore", save:function(callback){callback();}}}, function(){
|
|
||||||
log += "done();";
|
|
||||||
});
|
|
||||||
assertEquals("save(1);save(2);done();", log);
|
|
||||||
assertEquals(1, anchor.A);
|
|
||||||
assertEquals(2, anchor.B);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testEntityLoadAllRows = function(){
|
|
||||||
var ds = new DataStore();
|
|
||||||
var Recipe = ds.entity("Recipe");
|
|
||||||
var list = [];
|
|
||||||
ds.loadAll = function(entity, callback){
|
|
||||||
assertTrue(Recipe === entity);
|
|
||||||
callback.apply(list);
|
|
||||||
return list;
|
|
||||||
};
|
|
||||||
var items = Recipe.all(function(){
|
|
||||||
assertTrue(list === this);
|
|
||||||
});
|
|
||||||
assertTrue(items === list);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testLoadAll = function(){
|
|
||||||
expectAsserts(8);
|
|
||||||
var post = function(data, callback){
|
|
||||||
assertEquals("GET", data[0][0]);
|
|
||||||
assertEquals("A", data[0][1]);
|
|
||||||
callback(200, [[{$entity:'A', $id:'1'},{$entity:'A', $id:'2'}]]);
|
|
||||||
};
|
|
||||||
var datastore = new DataStore(post);
|
|
||||||
var list = datastore.entity("A").all(function(){
|
|
||||||
assertTrue(true);
|
|
||||||
});
|
|
||||||
datastore.flush();
|
|
||||||
assertEquals(list.length, 2);
|
|
||||||
assertEquals(list[0].$entity, "A");
|
|
||||||
assertEquals(list[0].$id, "1");
|
|
||||||
assertEquals(list[1].$entity, "A");
|
|
||||||
assertEquals(list[1].$id, "2");
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testQuery = function(){
|
|
||||||
expectAsserts(5);
|
|
||||||
var post = function(data, callback) {
|
|
||||||
assertEquals("GET", data[0][0]);
|
|
||||||
assertEquals("Employee/managerId=123abc", data[0][1]);
|
|
||||||
callback(200, [[{$entity:"Employee", $id: "456", managerId: "123ABC"}]]);
|
|
||||||
|
|
||||||
};
|
|
||||||
var datastore = new DataStore(post);
|
|
||||||
var Employee = datastore.entity("Employee");
|
|
||||||
var list = Employee.query('managerId', "123abc", function(){
|
|
||||||
assertTrue(true);
|
|
||||||
});
|
|
||||||
datastore.flush();
|
|
||||||
assertJsonEquals([[{$entity:"Employee", $id: "456", managerId: "123ABC"}]], datastore._cache.$collections);
|
|
||||||
assertEquals(list[0].$id, "456");
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testLoadingDocumentRefreshesExistingArrays = function() {
|
|
||||||
expectAsserts(12);
|
|
||||||
var post;
|
|
||||||
var datastore = new DataStore(function(r, c){post(r,c);});
|
|
||||||
var Book = datastore.entity('Book');
|
|
||||||
post = function(req, callback) {
|
|
||||||
callback(200, [[{$id:1, $entity:"Book", name:"Moby"},
|
|
||||||
{$id:2, $entity:"Book", name:"Dick"}]]);
|
|
||||||
};
|
|
||||||
var allBooks = Book.all();
|
|
||||||
datastore.flush();
|
|
||||||
var queryBooks = Book.query("a", "b");
|
|
||||||
datastore.flush();
|
|
||||||
assertEquals("Moby", allBooks[0].name);
|
|
||||||
assertEquals("Dick", allBooks[1].name);
|
|
||||||
assertEquals("Moby", queryBooks[0].name);
|
|
||||||
assertEquals("Dick", queryBooks[1].name);
|
|
||||||
|
|
||||||
post = function(req, callback) {
|
|
||||||
assertEquals('[["GET","Book/1"]]', toJson(req));
|
|
||||||
callback(200, [{$id:1, $entity:"Book", name:"Moby Dick"}]);
|
|
||||||
};
|
|
||||||
var book = Book.load(1);
|
|
||||||
datastore.flush();
|
|
||||||
assertEquals("Moby Dick", book.name);
|
|
||||||
assertEquals("Moby Dick", allBooks[0].name);
|
|
||||||
assertEquals("Moby Dick", queryBooks[0].name);
|
|
||||||
|
|
||||||
post = function(req, callback) {
|
|
||||||
assertEquals('POST', req[0][0]);
|
|
||||||
callback(200, [{$id:1, $entity:"Book", name:"The Big Fish"}]);
|
|
||||||
};
|
|
||||||
book.$save();
|
|
||||||
datastore.flush();
|
|
||||||
assertEquals("The Big Fish", book.name);
|
|
||||||
assertEquals("The Big Fish", allBooks[0].name);
|
|
||||||
assertEquals("The Big Fish", queryBooks[0].name);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testEntityProperties = function() {
|
|
||||||
expectAsserts(2);
|
|
||||||
var datastore = new DataStore();
|
|
||||||
var callback = {};
|
|
||||||
|
|
||||||
datastore._jsonRequest = function(request, callbackFn) {
|
|
||||||
assertJsonEquals(["GET", "Cheese/$properties"], request);
|
|
||||||
assertEquals(callback, callbackFn);
|
|
||||||
};
|
|
||||||
|
|
||||||
var Cheese = datastore.entity("Cheese");
|
|
||||||
Cheese.properties(callback);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testLoadInstanceIsNotFromCache = function() {
|
|
||||||
var post;
|
|
||||||
var datastore = new DataStore(function(r, c){post(r,c);});
|
|
||||||
var Book = datastore.entity('Book');
|
|
||||||
|
|
||||||
post = function(req, callback) {
|
|
||||||
assertEquals('[["GET","Book/1"]]', toJson(req));
|
|
||||||
callback(200, [{$id:1, $entity:"Book", name:"Moby Dick"}]);
|
|
||||||
};
|
|
||||||
var book = Book.load(1);
|
|
||||||
datastore.flush();
|
|
||||||
assertEquals("Moby Dick", book.name);
|
|
||||||
assertFalse(book === datastore._cache['Book/1']);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testLoadStarsIsNewDocument = function() {
|
|
||||||
var datastore = new DataStore();
|
|
||||||
var Book = datastore.entity('Book');
|
|
||||||
var book = Book.load('*');
|
|
||||||
assertEquals('Book', book.$entity);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testUndefinedEntityReturnsNullValueObject = function() {
|
|
||||||
var datastore = new DataStore();
|
|
||||||
var Entity = datastore.entity(undefined);
|
|
||||||
var all = Entity.all();
|
|
||||||
assertEquals(0, all.length);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testFetchEntities = function(){
|
|
||||||
expectAsserts(6);
|
|
||||||
var post = function(data, callback){
|
|
||||||
assertJsonEquals(["GET", "$entities"], data[0]);
|
|
||||||
callback(200, [{A:0, B:0}]);
|
|
||||||
};
|
|
||||||
var datastore = new DataStore(post);
|
|
||||||
var entities = datastore.entities(function(){
|
|
||||||
assertTrue(true);
|
|
||||||
});
|
|
||||||
datastore.flush();
|
|
||||||
assertJsonEquals([], datastore.bulkRequest);
|
|
||||||
assertEquals(2, entities.length);
|
|
||||||
assertEquals("A", entities[0].title);
|
|
||||||
assertEquals("B", entities[1].title);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testItShouldMigrateSchema = function() {
|
|
||||||
var datastore = new DataStore();
|
|
||||||
var Entity = datastore.entity("Entity", {a:[], user:{name:"Misko", email:""}});
|
|
||||||
var doc = Entity().$loadFrom({b:'abc', user:{email:"misko@hevery.com"}});
|
|
||||||
assertFalse(
|
|
||||||
toJson({a:[], b:'abc', user:{name:"Misko", email:"misko@hevery.com"}}) ==
|
|
||||||
toJson(doc));
|
|
||||||
doc.$migrate();
|
|
||||||
assertEquals(
|
|
||||||
toJson({a:[], b:'abc', user:{name:"Misko", email:"misko@hevery.com"}}),
|
|
||||||
toJson(doc));
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testItShouldCollectRequestsForBulk = function() {
|
|
||||||
var ds = new DataStore();
|
|
||||||
var Book = ds.entity("Book");
|
|
||||||
var Library = ds.entity("Library");
|
|
||||||
Book.all();
|
|
||||||
Library.load("123");
|
|
||||||
assertEquals(2, ds.bulkRequest.length);
|
|
||||||
assertJsonEquals(["GET", "Book"], ds.bulkRequest[0]);
|
|
||||||
assertJsonEquals(["GET", "Library/123"], ds.bulkRequest[1]);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testEmptyFlushShouldDoNothing = function () {
|
|
||||||
var ds = new DataStore(function(){
|
|
||||||
fail("expecting noop");
|
|
||||||
});
|
|
||||||
ds.flush();
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testFlushShouldCallAllCallbacks = function() {
|
|
||||||
var log = "";
|
|
||||||
function post(request, callback){
|
|
||||||
log += 'BulkRequest:' + toJson(request) + ';';
|
|
||||||
callback(200, [[{$id:'ABC'}], {$id:'XYZ'}]);
|
|
||||||
}
|
|
||||||
var ds = new DataStore(post);
|
|
||||||
var Book = ds.entity("Book");
|
|
||||||
var Library = ds.entity("Library");
|
|
||||||
Book.all(function(instance){
|
|
||||||
log += toJson(instance) + ';';
|
|
||||||
});
|
|
||||||
Library.load("123", function(instance){
|
|
||||||
log += toJson(instance) + ';';
|
|
||||||
});
|
|
||||||
assertEquals("", log);
|
|
||||||
ds.flush();
|
|
||||||
assertJsonEquals([], ds.bulkRequest);
|
|
||||||
assertEquals('BulkRequest:[["GET","Book"],["GET","Library/123"]];[{"$id":"ABC"}];{"$id":"XYZ"};', log);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testSaveOnNotLoggedInRetriesAfterLoggin = function(){
|
|
||||||
var log = "";
|
|
||||||
var book;
|
|
||||||
var ds = new DataStore(null, {login:function(c){c();}});
|
|
||||||
ds.post = function (request, callback){
|
|
||||||
assertJsonEquals([["POST", "", book]], request);
|
|
||||||
ds.post = function(request, callback){
|
|
||||||
assertJsonEquals([["POST", "", book]], request);
|
|
||||||
ds.post = function(){fail("too much recursion");};
|
|
||||||
callback(200, [{saved:"ok"}]);
|
|
||||||
};
|
|
||||||
callback(200, {$status_code:401});
|
|
||||||
};
|
|
||||||
book = ds.entity("Book")({name:"misko"});
|
|
||||||
book.$save();
|
|
||||||
ds.flush();
|
|
||||||
assertJsonEquals({saved:"ok"}, book);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testItShouldRemoveItemFromCollectionWhenDeleted = function() {
|
|
||||||
expectAsserts(6);
|
|
||||||
var ds = new DataStore();
|
|
||||||
ds.post = function(request, callback){
|
|
||||||
assertJsonEquals([["GET", "Book"]], request);
|
|
||||||
callback(200, [[{name:"Moby Dick", $id:123, $entity:'Book'}]]);
|
|
||||||
};
|
|
||||||
var Book = ds.entity("Book");
|
|
||||||
var books = Book.all();
|
|
||||||
ds.flush();
|
|
||||||
assertJsonEquals([[{name:"Moby Dick", $id:123, $entity:'Book'}]], ds._cache.$collections);
|
|
||||||
assertDefined(ds._cache['Book/123']);
|
|
||||||
var book = Book({$id:123});
|
|
||||||
ds.post = function(request, callback){
|
|
||||||
assertJsonEquals([["DELETE", "", book]], request);
|
|
||||||
callback(200, [book]);
|
|
||||||
};
|
|
||||||
ds.remove(book);
|
|
||||||
ds.flush();
|
|
||||||
assertUndefined(ds._cache['Book/123']);
|
|
||||||
assertJsonEquals([[]],ds._cache.$collections);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testItShouldAddToAll = function() {
|
|
||||||
expectAsserts(8);
|
|
||||||
var ds = new DataStore();
|
|
||||||
ds.post = function(request, callback){
|
|
||||||
assertJsonEquals([["GET", "Book"]], request);
|
|
||||||
callback(200, [[]]);
|
|
||||||
};
|
|
||||||
var Book = ds.entity("Book");
|
|
||||||
var books = Book.all();
|
|
||||||
assertEquals(0, books.length);
|
|
||||||
ds.flush();
|
|
||||||
var moby = Book({name:'moby'});
|
|
||||||
moby.$save();
|
|
||||||
ds.post = function(request, callback){
|
|
||||||
assertJsonEquals([["POST", "", moby]], request);
|
|
||||||
moby.$id = '123';
|
|
||||||
callback(200, [moby]);
|
|
||||||
};
|
|
||||||
ds.flush();
|
|
||||||
assertEquals(1, books.length);
|
|
||||||
assertEquals(moby, books[0]);
|
|
||||||
|
|
||||||
moby.$save();
|
|
||||||
ds.flush();
|
|
||||||
assertEquals(1, books.length);
|
|
||||||
assertEquals(moby, books[0]);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testItShouldReturnCreatedDocumentCountByUser = function(){
|
|
||||||
expectAsserts(2);
|
|
||||||
var datastore = new DataStore(
|
|
||||||
function(request, callback){
|
|
||||||
assertJsonEquals([["GET", "$users"]], request);
|
|
||||||
callback(200, [{misko:1, adam:1}]);
|
|
||||||
});
|
|
||||||
var users = datastore.documentCountsByUser();
|
|
||||||
assertJsonEquals({misko:1, adam:1}, users);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testItShouldReturnDocumentIdsForUeserByEntity = function(){
|
|
||||||
expectAsserts(2);
|
|
||||||
var datastore = new DataStore(
|
|
||||||
function(request, callback){
|
|
||||||
assertJsonEquals([["GET", "$users/misko@hevery.com"]], request);
|
|
||||||
callback(200, [{Book:["1"], Library:["2"]}]);
|
|
||||||
});
|
|
||||||
var users = datastore.userDocumentIdsByEntity("misko@hevery.com");
|
|
||||||
assertJsonEquals({Book:["1"], Library:["2"]}, users);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testItShouldReturnNewInstanceOn404 = function(){
|
|
||||||
expectAsserts(7);
|
|
||||||
var log = "";
|
|
||||||
var datastore = new DataStore(
|
|
||||||
function(request, callback){
|
|
||||||
assertJsonEquals([["GET", "User/misko"]], request);
|
|
||||||
callback(200, [{$status_code:404}]);
|
|
||||||
});
|
|
||||||
var User = datastore.entity("User", {admin:false});
|
|
||||||
var user = User.loadOrCreate('misko', function(i){log+="cb "+i.$id+";";});
|
|
||||||
datastore.flush();
|
|
||||||
assertEquals("misko", user.$id);
|
|
||||||
assertEquals("User", user.$entity);
|
|
||||||
assertEquals(false, user.admin);
|
|
||||||
assertEquals("undefined", typeof user.$secret);
|
|
||||||
assertEquals("undefined", typeof user.$version);
|
|
||||||
assertEquals("cb misko;", log);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testItShouldReturnNewInstanceOn404 = function(){
|
|
||||||
var log = "";
|
|
||||||
var datastore = new DataStore(
|
|
||||||
function(request, callback){
|
|
||||||
assertJsonEquals([["GET", "User/misko"],["GET", "User/adam"]], request);
|
|
||||||
callback(200, [{$id:'misko'},{$id:'adam'}]);
|
|
||||||
});
|
|
||||||
var User = datastore.entity("User");
|
|
||||||
var users = User.loadMany(['misko', 'adam'], function(i){log+="cb "+toJson(i)+";";});
|
|
||||||
datastore.flush();
|
|
||||||
assertEquals("misko", users[0].$id);
|
|
||||||
assertEquals("adam", users[1].$id);
|
|
||||||
assertEquals('cb [{"$id":"misko"},{"$id":"adam"}];', log);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testItShouldCreateJoinAndQuery = function() {
|
|
||||||
var datastore = new DataStore();
|
|
||||||
var Invoice = datastore.entity("Invoice");
|
|
||||||
var Customer = datastore.entity("Customer");
|
|
||||||
var InvoiceWithCustomer = datastore.join({
|
|
||||||
invoice:{join:Invoice},
|
|
||||||
customer:{join:Customer, on:"invoice.customer"}
|
|
||||||
});
|
|
||||||
var invoiceWithCustomer = InvoiceWithCustomer.query("invoice.month", 1);
|
|
||||||
assertEquals([], invoiceWithCustomer);
|
|
||||||
assertJsonEquals([["GET", "Invoice/month=1"]], datastore.bulkRequest);
|
|
||||||
var request = datastore.bulkRequest.shift();
|
|
||||||
request.$$callback([{$id:1, customer:1},{$id:2, customer:1},{$id:3, customer:3}]);
|
|
||||||
assertJsonEquals([["GET","Customer/1"],["GET","Customer/3"]], datastore.bulkRequest);
|
|
||||||
datastore.bulkRequest.shift().$$callback({$id:1});
|
|
||||||
datastore.bulkRequest.shift().$$callback({$id:3});
|
|
||||||
assertJsonEquals([
|
|
||||||
{invoice:{$id:1,customer:1},customer:{$id:1}},
|
|
||||||
{invoice:{$id:2,customer:1},customer:{$id:1}},
|
|
||||||
{invoice:{$id:3,customer:3},customer:{$id:3}}], invoiceWithCustomer);
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testItShouldThrowIfMoreThanOneEntityIsPrimary = function() {
|
|
||||||
var datastore = new DataStore();
|
|
||||||
var Invoice = datastore.entity("Invoice");
|
|
||||||
var Customer = datastore.entity("Customer");
|
|
||||||
assertThrows("Exactly one entity needs to be primary.", function(){
|
|
||||||
datastore.join({
|
|
||||||
invoice:{join:Invoice},
|
|
||||||
customer:{join:Customer}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testItShouldThrowIfLoopInReferences = function() {
|
|
||||||
var datastore = new DataStore();
|
|
||||||
var Invoice = datastore.entity("Invoice");
|
|
||||||
var Customer = datastore.entity("Customer");
|
|
||||||
assertThrows("Infinite loop in join: invoice -> customer", function(){
|
|
||||||
datastore.join({
|
|
||||||
invoice:{join:Invoice, on:"customer.invoice"},
|
|
||||||
customer:{join:Customer, on:"invoice.customer"}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testItShouldThrowIfReferenceToNonExistantJoin = function() {
|
|
||||||
var datastore = new DataStore();
|
|
||||||
var Invoice = datastore.entity("Invoice");
|
|
||||||
var Customer = datastore.entity("Customer");
|
|
||||||
assertThrows("Named entity 'x' is undefined.", function(){
|
|
||||||
datastore.join({
|
|
||||||
invoice:{join:Invoice, on:"x.invoice"},
|
|
||||||
customer:{join:Customer, on:"invoice.customer"}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
DataStoreTest.prototype.testItShouldThrowIfQueryOnNonPrimary = function() {
|
|
||||||
var datastore = new DataStore();
|
|
||||||
var Invoice = datastore.entity("Invoice");
|
|
||||||
var Customer = datastore.entity("Customer");
|
|
||||||
var InvoiceWithCustomer = datastore.join({
|
|
||||||
invoice:{join:Invoice},
|
|
||||||
customer:{join:Customer, on:"invoice.customer"}
|
|
||||||
});
|
|
||||||
assertThrows("Named entity 'customer' is not a primary entity.", function(){
|
|
||||||
InvoiceWithCustomer.query("customer.month", 1);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
EntityDeclarationTest = TestCase('EntityDeclarationTest');
|
|
||||||
|
|
||||||
EntityDeclarationTest.prototype.testEntityTypeOnly = function(){
|
|
||||||
expectAsserts(2);
|
|
||||||
var datastore = {entity:function(name){
|
|
||||||
assertEquals("Person", name);
|
|
||||||
}};
|
|
||||||
var scope = new Scope();
|
|
||||||
var init = scope.entity("Person", datastore);
|
|
||||||
assertEquals("", init);
|
|
||||||
};
|
|
||||||
|
|
||||||
EntityDeclarationTest.prototype.testWithDefaults = function(){
|
|
||||||
expectAsserts(4);
|
|
||||||
var datastore = {entity:function(name, init){
|
|
||||||
assertEquals("Person", name);
|
|
||||||
assertEquals("=a:", init.a);
|
|
||||||
assertEquals(0, init.b.length);
|
|
||||||
}};
|
|
||||||
var scope = new Scope();
|
|
||||||
var init = scope.entity('Person:{a:"=a:", b:[]}', datastore);
|
|
||||||
assertEquals("", init);
|
|
||||||
};
|
|
||||||
|
|
||||||
EntityDeclarationTest.prototype.testWithName = function(){
|
|
||||||
expectAsserts(2);
|
|
||||||
var datastore = {entity:function(name, init){
|
|
||||||
assertEquals("Person", name);
|
|
||||||
return function (){ return {}; };
|
|
||||||
}};
|
|
||||||
var scope = new Scope();
|
|
||||||
var init = scope.entity('friend=Person', datastore);
|
|
||||||
assertEquals("$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};", init);
|
|
||||||
};
|
|
||||||
|
|
||||||
EntityDeclarationTest.prototype.testMultipleEntities = function(){
|
|
||||||
expectAsserts(3);
|
|
||||||
var expect = ['Person', 'Book'];
|
|
||||||
var i=0;
|
|
||||||
var datastore = {entity:function(name, init){
|
|
||||||
assertEquals(expect[i], name);
|
|
||||||
i++;
|
|
||||||
return function (){ return {}; };
|
|
||||||
}};
|
|
||||||
var scope = new Scope();
|
|
||||||
var init = scope.entity('friend=Person;book=Book;', datastore);
|
|
||||||
assertEquals("$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};" +
|
|
||||||
"$anchor.book:{book=Book.load($anchor.book);book.$$anchor=\"book\";};",
|
|
||||||
init);
|
|
||||||
};
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
FileControllerTest = TestCase('FileControllerTest');
|
|
||||||
|
|
||||||
FileControllerTest.prototype.XtestOnSelectUpdateView = function(){
|
|
||||||
var view = jQuery('<span><a/><span/></span>');
|
|
||||||
var swf = {};
|
|
||||||
var controller = new FileController(view, null, swf);
|
|
||||||
swf.uploadFile = function(path){};
|
|
||||||
controller.select('A', 9, '9 bytes');
|
|
||||||
assertEquals(view.find('a').text(), "A");
|
|
||||||
assertEquals(view.find('span').text(), "9 bytes");
|
|
||||||
};
|
|
||||||
|
|
||||||
FileControllerTest.prototype.XtestUpdateModelView = function(){
|
|
||||||
var view = FileController.template('');
|
|
||||||
var input = $('<input name="value.input">');
|
|
||||||
var controller;
|
|
||||||
var scope = new Scope({value:{}, $binder:{updateView:function(){
|
|
||||||
controller.updateView(scope);
|
|
||||||
}}});
|
|
||||||
view.data('scope', scope);
|
|
||||||
controller = new FileController(view, 'value.input', null, "http://server_base");
|
|
||||||
var value = '{"text":"A", "size":123, "id":"890"}';
|
|
||||||
controller.uploadCompleteData(value);
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals(scope.get('value.input.text'), 'A');
|
|
||||||
assertEquals(scope.get('value.input.size'), 123);
|
|
||||||
assertEquals(scope.get('value.input.id'), '890');
|
|
||||||
assertEquals(scope.get('value.input.url'), 'http://server_base/_attachments/890/A');
|
|
||||||
assertEquals(view.find('a').text(), "A");
|
|
||||||
assertEquals(view.find('a').attr('href'), "http://server_base/_attachments/890/A");
|
|
||||||
assertEquals(view.find('span').text(), "123 bytes");
|
|
||||||
};
|
|
||||||
|
|
||||||
FileControllerTest.prototype.XtestFileUpload = function(){
|
|
||||||
expectAsserts(1);
|
|
||||||
var swf = {};
|
|
||||||
var controller = new FileController(null, null, swf, "http://server_base");
|
|
||||||
swf.uploadFile = function(path){
|
|
||||||
assertEquals("http://server_base/_attachments", path);
|
|
||||||
};
|
|
||||||
controller.name = "Name";
|
|
||||||
controller.upload();
|
|
||||||
};
|
|
||||||
|
|
||||||
FileControllerTest.prototype.XtestFileUploadNoFileIsNoop = function(){
|
|
||||||
expectAsserts(0);
|
|
||||||
var swf = {uploadFile:function(path){
|
|
||||||
fail();
|
|
||||||
}};
|
|
||||||
var controller = new FileController(null, swf);
|
|
||||||
controller.upload("basePath", null);
|
|
||||||
};
|
|
||||||
|
|
||||||
FileControllerTest.prototype.XtestRemoveAttachment = function(){
|
|
||||||
var doc = FileController.template();
|
|
||||||
var input = $('<input name="file">');
|
|
||||||
var scope = new Scope();
|
|
||||||
input.data('scope', scope);
|
|
||||||
var controller = new FileController(doc, 'file', null, null);
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals(false, doc.find('input').attr('checked'));
|
|
||||||
|
|
||||||
scope.set('file', {url:'url', size:123});
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals(true, doc.find('input').attr('checked'));
|
|
||||||
|
|
||||||
doc.find('input').attr('checked', false);
|
|
||||||
controller.updateModel(scope);
|
|
||||||
assertNull(scope.get('file'));
|
|
||||||
|
|
||||||
doc.find('input').attr('checked', true);
|
|
||||||
controller.updateModel(scope);
|
|
||||||
assertEquals('url', scope.get('file.url'));
|
|
||||||
assertEquals(123, scope.get('file.size'));
|
|
||||||
};
|
|
||||||
|
|
||||||
FileControllerTest.prototype.XtestShouldEmptyOutOnUndefined = function () {
|
|
||||||
var view = FileController.template('hello');
|
|
||||||
var controller = new FileController(view, 'abc', null, null);
|
|
||||||
|
|
||||||
var scope = new Scope();
|
|
||||||
scope.set('abc', {text: 'myname', url: 'myurl', size: 1234});
|
|
||||||
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals("myurl", view.find('a').attr('href'));
|
|
||||||
assertEquals("myname", view.find('a').text());
|
|
||||||
assertEquals(true, view.find('input').is(':checked'));
|
|
||||||
assertEquals("1.2 KB", view.find('span').text());
|
|
||||||
|
|
||||||
scope.set('abc', undefined);
|
|
||||||
controller.updateView(scope);
|
|
||||||
assertEquals("myurl", view.find('a').attr('href'));
|
|
||||||
assertEquals("myname", view.find('a').text());
|
|
||||||
assertEquals(false, view.find('input').is(':checked'));
|
|
||||||
assertEquals("1.2 KB", view.find('span').text());
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
BinderTest.prototype.testExpandEntityTagWithName = function(){
|
|
||||||
var c = this.compile('<div ng-entity="friend=Person"/>');
|
|
||||||
assertEquals(
|
|
||||||
'<div ng-entity="friend=Person" ng-watch="$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};"></div>',
|
|
||||||
sortedHtml(c.node));
|
|
||||||
assertEquals("Person", c.scope.$get("friend.$entity"));
|
|
||||||
assertEquals("friend", c.scope.$get("friend.$$anchor"));
|
|
||||||
};
|
|
||||||
|
|
||||||
BinderTest.prototype.testExpandSubmitButtonToAction = function(){
|
|
||||||
var html = this.compileToHtml('<input type="submit" value="Save">');
|
|
||||||
assertTrue(html, html.indexOf('ng-action="$save()"') > 0 );
|
|
||||||
assertTrue(html, html.indexOf('ng-bind-attr="{"disabled":"{{$invalidWidgets}}"}"') > 0 );
|
|
||||||
};
|
|
||||||
|
|
||||||
BinderTest.prototype.testReplaceFileUploadWithSwf = function(){
|
|
||||||
expectAsserts(1);
|
|
||||||
var form = jQuery("body").append('<div id="testTag"><input type="file"></div>');
|
|
||||||
form.data('scope', new Scope());
|
|
||||||
var factory = {};
|
|
||||||
var binder = new Binder(form.get(0), factory, new MockLocation());
|
|
||||||
factory.createController = function(node){
|
|
||||||
assertEquals(node.attr('type'), 'file');
|
|
||||||
return {updateModel:function(){}};
|
|
||||||
};
|
|
||||||
binder.compile();
|
|
||||||
jQuery("#testTag").remove();
|
|
||||||
};
|
|
||||||
|
|
||||||
BinderTest.prototype.testExpandEntityTagWithDefaults = function(){
|
|
||||||
assertEquals(
|
|
||||||
'<div ng-entity="Person:{a:\"a\"}" ng-watch=""></div>',
|
|
||||||
this.compileToHtml('<div ng-entity=\'Person:{a:"a"}\'/>'));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
ModelTest = TestCase('ModelTest');
|
|
||||||
|
|
||||||
ModelTest.prototype.testLoadSaveOperations = function(){
|
|
||||||
var m1 = new DataStore().entity('A')();
|
|
||||||
m1.a = 1;
|
|
||||||
|
|
||||||
var m2 = {b:1};
|
|
||||||
|
|
||||||
m1.$loadFrom(m2);
|
|
||||||
|
|
||||||
assertTrue(!m1.a);
|
|
||||||
assertEquals(m1.b, 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
ModelTest.prototype.testLoadfromDoesNotClobberFunctions = function(){
|
|
||||||
var m1 = new DataStore().entity('A')();
|
|
||||||
m1.id = function(){return 'OK';};
|
|
||||||
m1.$loadFrom({id:null});
|
|
||||||
assertEquals(m1.id(), 'OK');
|
|
||||||
|
|
||||||
m1.b = 'OK';
|
|
||||||
m1.$loadFrom({b:function(){}});
|
|
||||||
assertEquals(m1.b, 'OK');
|
|
||||||
};
|
|
||||||
|
|
||||||
ModelTest.prototype.testDataStoreDoesNotGetClobbered = function(){
|
|
||||||
var ds = new DataStore();
|
|
||||||
var m = ds.entity('A')();
|
|
||||||
assertTrue(m.$$entity.datastore === ds);
|
|
||||||
m.$loadFrom({});
|
|
||||||
assertTrue(m.$$entity.datastore === ds);
|
|
||||||
};
|
|
||||||
|
|
||||||
ModelTest.prototype.testManagedModelDelegatesMethodsToDataStore = function(){
|
|
||||||
expectAsserts(7);
|
|
||||||
var datastore = new DataStore();
|
|
||||||
var model = datastore.entity("A", {a:1})();
|
|
||||||
var fn = {};
|
|
||||||
datastore.save = function(instance, callback) {
|
|
||||||
assertTrue(model === instance);
|
|
||||||
assertTrue(callback === fn);
|
|
||||||
};
|
|
||||||
datastore.remove = function(instance, callback) {
|
|
||||||
assertTrue(model === instance);
|
|
||||||
assertTrue(callback === fn);
|
|
||||||
};
|
|
||||||
datastore.load = function(instance, id, callback) {
|
|
||||||
assertTrue(model === instance);
|
|
||||||
assertTrue(id === "123");
|
|
||||||
assertTrue(callback === fn);
|
|
||||||
};
|
|
||||||
model.$save(fn);
|
|
||||||
model.$delete(fn);
|
|
||||||
model.$loadById("123", fn);
|
|
||||||
};
|
|
||||||
|
|
||||||
ModelTest.prototype.testManagedModelCanBeForcedToFlush = function(){
|
|
||||||
expectAsserts(6);
|
|
||||||
var datastore = new DataStore();
|
|
||||||
var model = datastore.entity("A", {a:1})();
|
|
||||||
|
|
||||||
datastore.save = function(instance, callback) {
|
|
||||||
assertTrue(model === instance);
|
|
||||||
assertTrue(callback === undefined);
|
|
||||||
};
|
|
||||||
datastore.remove = function(instance, callback) {
|
|
||||||
assertTrue(model === instance);
|
|
||||||
assertTrue(callback === undefined);
|
|
||||||
};
|
|
||||||
datastore.flush = function(){
|
|
||||||
assertTrue(true);
|
|
||||||
};
|
|
||||||
model.$save(true);
|
|
||||||
model.$delete(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
ModelTest.prototype.testItShouldMakeDeepCopyOfInitialValues = function (){
|
|
||||||
var initial = {a:[]};
|
|
||||||
var entity = new DataStore().entity("A", initial);
|
|
||||||
var model = entity();
|
|
||||||
model.a.push(1);
|
|
||||||
assertEquals(0, entity().a.length);
|
|
||||||
};
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
ServerTest = TestCase("ServerTest");
|
|
||||||
ServerTest.prototype.testBreakLargeRequestIntoPackets = function() {
|
|
||||||
var log = "";
|
|
||||||
var server = new Server("http://server", function(url){
|
|
||||||
log += "|" + url;
|
|
||||||
});
|
|
||||||
server.maxSize = 30;
|
|
||||||
server.uuid = "uuid";
|
|
||||||
server.request("POST", "/data/database", {}, function(code, r){
|
|
||||||
assertEquals(200, code);
|
|
||||||
assertEquals("response", r);
|
|
||||||
});
|
|
||||||
angularCallbacks.uuid0("response");
|
|
||||||
assertEquals(
|
|
||||||
"|http://server/$/uuid0/2/1?h=eyJtIjoiUE9TVCIsInAiOnt9LCJ1Ij" +
|
|
||||||
"|http://server/$/uuid0/2/2?h=oiL2RhdGEvZGF0YWJhc2UifQ==",
|
|
||||||
log);
|
|
||||||
};
|
|
||||||
|
|
||||||
ServerTest.prototype.testItShouldEncodeUsingUrlRules = function() {
|
|
||||||
var server = new Server("http://server");
|
|
||||||
assertEquals("fn5-fn5-", server.base64url("~~~~~~"));
|
|
||||||
assertEquals("fn5_fn5_", server.base64url("~~\u007f~~\u007f"));
|
|
||||||
};
|
|
||||||
|
|
||||||
FrameServerTest = TestCase("FrameServerTest");
|
|
||||||
|
|
||||||
FrameServerTest.prototype = {
|
|
||||||
testRead:function(){
|
|
||||||
var window = {name:'$DATASET:"MyData"'};
|
|
||||||
var server = new FrameServer(window);
|
|
||||||
server.read();
|
|
||||||
assertEquals("MyData", server.data);
|
|
||||||
},
|
|
||||||
testWrite:function(){
|
|
||||||
var window = {};
|
|
||||||
var server = new FrameServer(window);
|
|
||||||
server.data = "TestData";
|
|
||||||
server.write();
|
|
||||||
assertEquals('$DATASET:"TestData"', window.name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
// Copyright (C) 2008,2009 BRAT Tech LLC
|
|
||||||
|
|
||||||
UsersTest = TestCase("UsersTest");
|
|
||||||
|
|
||||||
UsersTest.prototype = {
|
|
||||||
setUp:function(){},
|
|
||||||
|
|
||||||
tearDown:function(){},
|
|
||||||
|
|
||||||
testItShouldFetchCurrentUser:function(){
|
|
||||||
expectAsserts(5);
|
|
||||||
var user;
|
|
||||||
var users = new Users({request:function(method, url, request, callback){
|
|
||||||
assertEquals("GET", method);
|
|
||||||
assertEquals("/account.json", url);
|
|
||||||
assertEquals("{}", toJson(request));
|
|
||||||
callback(200, {$status_code:200, user:{name:'misko'}});
|
|
||||||
}});
|
|
||||||
users.fetchCurrentUser(function(u){
|
|
||||||
user = u;
|
|
||||||
assertEquals("misko", u.name);
|
|
||||||
assertEquals("misko", users.current.name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
Loading…
Reference in a new issue