diff options
| author | Misko Hevery | 2010-01-25 20:02:24 -0800 |
|---|---|---|
| committer | Misko Hevery | 2010-01-25 20:02:24 -0800 |
| commit | 0b630972b15676b1c1b6c59edd564e4ee331ec70 (patch) | |
| tree | bd31ca5b69b6ea03d906a3107dfe38a8c1adcb8e /src | |
| parent | 19bbee030ba012b8fc4835c1d17e039804b2b94b (diff) | |
| parent | 473e57e22532f9b85fc9dcc1bcc53e12a10154c2 (diff) | |
| download | angular.js-0b630972b15676b1c1b6c59edd564e4ee331ec70.tar.bz2 | |
merge
Diffstat (limited to 'src')
| -rw-r--r-- | src/API.js | 131 | ||||
| -rw-r--r-- | src/Angular.js | 361 | ||||
| -rw-r--r-- | src/Binder.js | 599 | ||||
| -rw-r--r-- | src/ControlBar.js | 111 | ||||
| -rw-r--r-- | src/DataStore.js | 604 | ||||
| -rw-r--r-- | src/Filters.js | 534 | ||||
| -rw-r--r-- | src/JSON.js | 40 | ||||
| -rw-r--r-- | src/Loader.js | 389 | ||||
| -rw-r--r-- | src/Model.js | 88 | ||||
| -rw-r--r-- | src/Parser.js | 1295 | ||||
| -rw-r--r-- | src/Scope.js | 312 | ||||
| -rw-r--r-- | src/Server.js | 57 | ||||
| -rw-r--r-- | src/Users.js | 25 | ||||
| -rw-r--r-- | src/Validators.js | 154 | ||||
| -rw-r--r-- | src/Widgets.js | 1030 | ||||
| -rw-r--r-- | src/Widgets.js.orig | 764 | ||||
| -rw-r--r-- | src/XSitePost.js | 100 | ||||
| -rw-r--r-- | src/angular-bootstrap.js | 100 | ||||
| -rw-r--r-- | src/angular.prefix | 24 | ||||
| -rw-r--r-- | src/angular.suffix | 1 | ||||
| -rw-r--r-- | src/test/Runner.js | 44 | ||||
| -rw-r--r-- | src/test/Steps.js | 2 | ||||
| -rw-r--r-- | src/test/_namespace.js | 10 |
23 files changed, 2946 insertions, 3829 deletions
@@ -1,9 +1,8 @@ -angular.Global = { - typeOf:function(obj){ +var angularGlobal = { + 'typeOf':function(obj){ + if (obj === null) return "null"; var type = typeof obj; - switch(type) { - case "object": - if (obj === null) return "null"; + if (type == "object") { if (obj instanceof Array) return "array"; if (obj instanceof Date) return "date"; if (obj.nodeType == 1) return "element"; @@ -12,10 +11,10 @@ angular.Global = { } }; -angular.Collection = {}; -angular.Object = {}; -angular.Array = { - includeIf:function(array, value, condition) { +var angularCollection = {}; +var angularObject = {}; +var angularArray = { + 'includeIf':function(array, value, condition) { var index = _.indexOf(array, value); if (condition) { if (index == -1) @@ -25,8 +24,8 @@ angular.Array = { } return array; }, - sum:function(array, expression) { - var fn = angular.Function.compile(expression); + 'sum':function(array, expression) { + var fn = angular['Function']['compile'](expression); var sum = 0; for (var i = 0; i < array.length; i++) { var value = 1 * fn(array[i]); @@ -36,15 +35,15 @@ angular.Array = { } return sum; }, - remove:function(array, value) { + 'remove':function(array, value) { var index = _.indexOf(array, value); if (index >=0) array.splice(index, 1); return value; }, - find:function(array, condition, defaultValue) { + 'find':function(array, condition, defaultValue) { if (!condition) return undefined; - var fn = angular.Function.compile(condition); + var fn = angular['Function']['compile'](condition); _.detect(array, function($){ if (fn($)){ defaultValue = $; @@ -53,10 +52,10 @@ angular.Array = { }); return defaultValue; }, - findById:function(array, id) { + 'findById':function(array, id) { return angular.Array.find(array, function($){return $.$id == id;}, null); }, - filter:function(array, expression) { + 'filter':function(array, expression) { var predicates = []; predicates.check = function(value) { for (var j = 0; j < predicates.length; j++) { @@ -66,7 +65,7 @@ angular.Array = { } return true; }; - var getter = nglr.Scope.getter; + var getter = Scope.getter; var search = function(obj, text){ if (text.charAt(0) === '!') { return !search(obj, text.substr(1)); @@ -136,18 +135,18 @@ angular.Array = { } return filtered; }, - add:function(array, value) { + 'add':function(array, value) { array.push(_.isUndefined(value)? {} : value); return array; }, - count:function(array, condition) { + 'count':function(array, condition) { if (!condition) return array.length; - var fn = angular.Function.compile(condition); + var fn = angular['Function']['compile'](condition); return _.reduce(array, 0, function(count, $){return count + (fn($)?1:0);}); }, - orderBy:function(array, expression, descend) { + 'orderBy':function(array, expression, descend) { function reverse(comp, descending) { - return nglr.toBoolean(descending) ? + return toBoolean(descending) ? function(a,b){return comp(b,a);} : comp; } function compare(v1, v2){ @@ -169,7 +168,7 @@ angular.Array = { descending = $.charAt(0) == '-'; $ = $.substring(1); } - var get = $ ? angular.Function.compile($) : _.identity; + var get = $ ? angular['Function']['compile']($) : _.identity; return reverse(function(a,b){ return compare(get(a),get(b)); }, descending); @@ -177,13 +176,13 @@ angular.Array = { var comparator = function(o1, o2){ for ( var i = 0; i < expression.length; i++) { var comp = expression[i](o1, o2); - if (comp != 0) return comp; + if (comp !== 0) return comp; } return 0; }; return _.clone(array).sort(reverse(comparator, descend)); }, - orderByToggle:function(predicate, attribute) { + 'orderByToggle':function(predicate, attribute) { var STRIP = /^([+|-])?(.*)/; var ascending = false; var index = -1; @@ -197,7 +196,7 @@ angular.Array = { ascending = $.charAt(0) == '+'; index = i; return true; - }; + } }); if (index >= 0) { predicate.splice(index, 1); @@ -205,7 +204,7 @@ angular.Array = { predicate.unshift((ascending ? "-" : "+") + attribute); return predicate; }, - orderByDirection:function(predicate, attribute, ascend, descend) { + 'orderByDirection':function(predicate, attribute, ascend, descend) { ascend = ascend || 'ng-ascend'; descend = descend || 'ng-descend'; var att = predicate[0] || ''; @@ -218,18 +217,19 @@ angular.Array = { } return att == attribute ? (direction ? ascend : descend) : ""; }, - merge:function(array, index, mergeValue) { + 'merge':function(array, index, mergeValue) { var value = array[index]; if (!value) { value = {}; array[index] = value; } - nglr.merge(mergeValue, value); + merge(mergeValue, value); return array; } }; -angular.String = { - quote:function(string) { + +var angularString = { + 'quote':function(string) { return '"' + string.replace(/\\/g, '\\\\'). replace(/"/g, '\\"'). replace(/\n/g, '\\n'). @@ -239,8 +239,8 @@ angular.String = { replace(/\v/g, '\\v') + '"'; }, - quoteUnicode:function(string) { - var str = angular.String.quote(string); + 'quoteUnicode':function(string) { + var str = angular['String']['quote'](string); var chars = []; for ( var i = 0; i < str.length; i++) { var ch = str.charCodeAt(i); @@ -253,7 +253,7 @@ angular.String = { } return chars.join(''); }, - toDate:function(string){ + 'toDate':function(string){ var match; if (typeof string == 'string' && (match = string.match(/^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/))){ @@ -265,8 +265,9 @@ angular.String = { return string; } }; -angular.Date = { - toString:function(date){ + +var angularDate = { + 'toString':function(date){ function pad(n) { return n < 10 ? "0" + n : n; } return (date.getUTCFullYear()) + '-' + pad(date.getUTCMonth() + 1) + '-' + @@ -276,12 +277,13 @@ angular.Date = { pad(date.getUTCSeconds()) + 'Z'; } }; -angular.Function = { - compile:function(expression) { + +var angularFunction = { + 'compile':function(expression) { if (_.isFunction(expression)){ return expression; } else if (expression){ - var scope = new nglr.Scope(); + var scope = new Scope(); return function($) { scope.state = $; return scope.eval(expression); @@ -292,27 +294,30 @@ angular.Function = { } }; -(function(){ - function extend(dst, src, names){ - _.extend(dst, src); - _.each((names||[]), function(name){ - dst[name] = _[name]; - }); - }; - extend(angular.Global, {}, - ['extend', 'clone','isEqual', - 'isElement', 'isArray', 'isFunction', 'isUndefined']); - extend(angular.Collection, angular.Global, - ['each', 'map', 'reduce', 'reduceRight', 'detect', - 'select', 'reject', 'all', 'any', 'include', - 'invoke', 'pluck', 'max', 'min', 'sortBy', - 'sortedIndex', 'toArray', 'size']); - extend(angular.Array, angular.Collection, - ['first', 'last', 'compact', 'flatten', 'without', - 'uniq', 'intersect', 'zip', 'indexOf', 'lastIndexOf']); - extend(angular.Object, angular.Collection, - ['keys', 'values']); - extend(angular.String, angular.Global); - extend(angular.Function, angular.Global, - ['bind', 'bindAll', 'delay', 'defer', 'wrap', 'compose']); -})();
\ No newline at end of file +function defineApi(dst, chain, underscoreNames){ + var lastChain = _.last(chain); + foreach(underscoreNames, function(name){ + lastChain[name] = _[name]; + }); + angular[dst] = angular[dst] || {}; + foreach(chain, function(parent){ + extend(angular[dst], parent); + }); +} +defineApi('Global', [angularGlobal], + ['extend', 'clone','isEqual', + 'isElement', 'isArray', 'isFunction', 'isUndefined']); +defineApi('Collection', [angularGlobal, angularCollection], + ['each', 'map', 'reduce', 'reduceRight', 'detect', + 'select', 'reject', 'all', 'any', 'include', + 'invoke', 'pluck', 'max', 'min', 'sortBy', + 'sortedIndex', 'toArray', 'size']); +defineApi('Array', [angularGlobal, angularCollection, angularArray], + ['first', 'last', 'compact', 'flatten', 'without', + 'uniq', 'intersect', 'zip', 'indexOf', 'lastIndexOf']); +defineApi('Object', [angularGlobal, angularCollection, angularObject], + ['keys', 'values']); +defineApi('String', [angularGlobal, angularString], []); +defineApi('Date', [angularGlobal, angularDate], []); +defineApi('Function', [angularGlobal, angularCollection, angularFunction], + ['bind', 'bindAll', 'delay', 'defer', 'wrap', 'compose']); diff --git a/src/Angular.js b/src/Angular.js new file mode 100644 index 00000000..bfbe8ee9 --- /dev/null +++ b/src/Angular.js @@ -0,0 +1,361 @@ +if (typeof document.getAttribute == 'undefined') + document.getAttribute = function() {}; +if (typeof Node == 'undefined') { + Node = { + ELEMENT_NODE : 1, + ATTRIBUTE_NODE : 2, + TEXT_NODE : 3, + CDATA_SECTION_NODE : 4, + ENTITY_REFERENCE_NODE : 5, + ENTITY_NODE : 6, + PROCESSING_INSTRUCTION_NODE : 7, + COMMENT_NODE : 8, + DOCUMENT_NODE : 9, + DOCUMENT_TYPE_NODE : 10, + DOCUMENT_FRAGMENT_NODE : 11, + NOTATION_NODE : 12 + }; +} + +function noop() {} +if (!window['console']) window['console']={'log':noop, 'error':noop}; + +var consoleNode, jQuery, msie, + foreach = _.each, + extend = _.extend, + angular = window['angular'] || (window['angular'] = {}), + angularValidator = angular['validator'] || (angular['validator'] = {}), + angularFilter = angular['filter'] || (angular['filter'] = {}), + angularCallbacks = angular['callbacks'] || (angular['callbacks'] = {}), + angularAlert = angular['alert'] || (angular['alert'] = function(){ + log(arguments); window.alert.apply(window, arguments); + }); + +function log(a, b, c){ + var console = window['console']; + switch(arguments.length) { + case 1: + console['log'](a); + break; + case 2: + console['log'](a, b); + break; + default: + console['log'](a, b, c); + break; + } +} + +function error(a, b, c){ + var console = window['console']; + switch(arguments.length) { + case 1: + console['error'](a); + break; + case 2: + console['error'](a, b); + break; + default: + console['error'](a, b, c); + break; + } +} + +function consoleLog(level, objs) { + var log = document.createElement("div"); + log.className = level; + var msg = ""; + var sep = ""; + for ( var i = 0; i < objs.length; i++) { + var obj = objs[i]; + msg += sep + (typeof obj == 'string' ? obj : toJson(obj)); + sep = " "; + } + log.appendChild(document.createTextNode(msg)); + consoleNode.appendChild(log); +} + +function isNode(inp) { + return inp && + inp.tagName && + inp.nodeName && + inp.ownerDocument && + inp.removeAttribute; +} + +function isLeafNode (node) { + switch (node.nodeName) { + case "OPTION": + case "PRE": + case "TITLE": + return true; + default: + return false; + } +} + +function setHtml(node, html) { + if (isLeafNode(node)) { + if (msie) { + node.innerText = html; + } else { + node.textContent = html; + } + } else { + node.innerHTML = html; + } +} + +function escapeHtml(html) { + if (!html || !html.replace) + return html; + return html. + replace(/&/g, '&'). + replace(/</g, '<'). + replace(/>/g, '>'); +} + +function escapeAttr(html) { + if (!html || !html.replace) + return html; + return html.replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, + '"'); +} + +function bind(_this, _function) { + if (!_this) + throw "Missing this"; + if (!_.isFunction(_function)) + throw "Missing function"; + return function() { + return _function.apply(_this, arguments); + }; +} + +function outerHTML(node) { + var temp = document.createElement('div'); + temp.appendChild(node); + var outerHTML = temp.innerHTML; + temp.removeChild(node); + return outerHTML; +} + +function trim(str) { + return str.replace(/^ */, '').replace(/ *$/, ''); +} + +function toBoolean(value) { + var v = ("" + value).toLowerCase(); + if (v == 'f' || v == '0' || v == 'false' || v == 'no') + value = false; + return !!value; +} + +function merge(src, dst) { + for ( var key in src) { + var value = dst[key]; + var type = typeof value; + if (type == 'undefined') { + dst[key] = fromJson(toJson(src[key])); + } else if (type == 'object' && value.constructor != array && + key.substring(0, 1) != "$") { + merge(src[key], value); + } + } +} + +// //////////////////////////// +// UrlWatcher +// //////////////////////////// + +function UrlWatcher(location) { + this.location = location; + this.delay = 25; + this.setTimeout = function(fn, delay) { + window.setTimeout(fn, delay); + }; + this.listener = function(url) { + return url; + }; + this.expectedUrl = location.href; +} + +UrlWatcher.prototype = { + listen: function(fn){ + this.listener = fn; + }, + watch: function() { + var self = this; + var pull = function() { + if (self.expectedUrl !== self.location.href) { + var notify = self.location.hash.match(/^#\$iframe_notify=(.*)$/); + if (notify) { + if (!self.expectedUrl.match(/#/)) { + self.expectedUrl += "#"; + } + self.location.href = self.expectedUrl; + var id = '_iframe_notify_' + notify[1]; + var notifyFn = angularCallbacks[id]; + delete angularCallbacks[id]; + try { + (notifyFn||noop)(); + } catch (e) { + alert(e); + } + } else { + self.listener(self.location.href); + self.expectedUrl = self.location.href; + } + } + self.setTimeout(pull, self.delay); + }; + pull(); + }, + + set: function(url) { + var existingURL = this.location.href; + if (!existingURL.match(/#/)) + existingURL += '#'; + if (existingURL != url) + this.location.href = url; + this.existingURL = url; + }, + + get: function() { + return window.location.href; + } +}; + +///////////////////////////////////////////////// +function configureJQueryPlugins() { + var fn = jQuery['fn']; + fn['scope'] = function() { + var element = this; + while (element && element.get(0)) { + var scope = element.data("scope"); + if (scope) + return scope; + element = element.parent(); + } + return null; + }; + fn['controller'] = function() { + return this.data('controller') || NullController.instance; + }; +} + +function configureLogging(config) { + if (config.debug == 'console' && !consoleNode) { + consoleNode = document.createElement("div"); + consoleNode.id = 'ng-console'; + document.getElementsByTagName('body')[0].appendChild(consoleNode); + log = function() { + consoleLog('ng-console-info', arguments); + }; + console.error = function() { + consoleLog('ng-console-error', arguments); + }; + } +} + +function exposeMethods(obj, methods){ + var bound = {}; + foreach(methods, function(fn, name){ + bound[name] = _(fn).bind(obj); + }); + return bound; +} + +function wireAngular(element, config) { + var widgetFactory = new WidgetFactory(config['server'], config['database']); + var binder = new Binder(element[0], widgetFactory, datastore, config['location'], config); + var controlBar = new ControlBar(element.find('body'), config['server']); + var onUpdate = function(){binder.updateView();}; + var server = config['database'] =="$MEMORY" ? + new FrameServer(window) : + new Server(config['server'], jQuery['getScript']); + server = new VisualServer(server, new Status(jQuery(element.body)), onUpdate); + var users = new Users(server, controlBar); + var databasePath = '/data/' + config['database']; + var post = function(request, callback){ + server.request("POST", databasePath, request, callback); + }; + var datastore = new DataStore(post, users, binder.anchor); + binder.datastore = datastore; + binder.updateListeners.push(function(){datastore.flush();}); + var scope = new Scope({ + '$anchor' : binder.anchor, + '$updateView': _(binder.updateView).bind(binder), + '$config' : config, + '$console' : window.console, + '$datastore' : exposeMethods(datastore, { + 'load': datastore.load, + 'loadMany': datastore.loadMany, + 'loadOrCreate': datastore.loadOrCreate, + 'loadAll': datastore.loadAll, + 'save': datastore.save, + 'remove': datastore.remove, + 'flush': datastore.flush, + 'query': datastore.query, + 'entity': datastore.entity, + 'entities': datastore.entities, + 'documentCountsByUser': datastore.documentCountsByUser, + 'userDocumentIdsByEntity': datastore.userDocumentIdsByEntity, + 'join': datastore.join + }), + '$save' : function(callback) { + datastore.saveScope(scope.state, callback, binder.anchor); + }, + '$window' : window, + '$uid' : function() { + return "" + new Date().getTime(); + }, + '$users' : users + }, "ROOT"); + + element.data('scope', scope); + binder.entity(scope); + binder.compile(); + controlBar.bind(); + + //TODO: remove this code + new PopUp(element).bind(); + + var self = _(exposeMethods(scope, { + 'updateView': scope.updateView, + 'set': scope.set, + 'get': scope.get, + 'eval': scope.eval + })).extend({ + 'init':function(){ + config['location']['listen'](_(binder.onUrlChange).bind(binder)); + binder.parseAnchor(); + binder.executeInit(); + binder.updateView(); + return self; + }, + 'element':element[0], + 'config':config + }); + return self; +} + +angular['startUrlWatcher'] = function(){ + var watcher = new UrlWatcher(window['location']); + watcher.watch(); + return exposeMethods(watcher, {'listen':watcher.listen, 'set':watcher.set, 'get':watcher.get}); +}; + +angular['compile'] = function(element, config) { + jQuery = window['jQuery']; + msie = jQuery['browser']['msie']; + config = _({ + 'server': "", + 'location': {'get':noop, 'set':noop, 'listen':noop} + }).extend(config||{}); + + configureLogging(config); + configureJQueryPlugins(); + + return wireAngular(jQuery(element), config); +};
\ No newline at end of file diff --git a/src/Binder.js b/src/Binder.js index 86e99fb8..e516ec32 100644 --- a/src/Binder.js +++ b/src/Binder.js @@ -1,14 +1,14 @@ -// Copyright (C) 2009 BRAT Tech LLC -nglr.Binder = function(doc, widgetFactory, urlWatcher, config) { +function Binder(doc, widgetFactory, datastore, location, config) { this.doc = doc; - this.urlWatcher = urlWatcher; + this.location = location; + this.datastore = datastore; this.anchor = {}; this.widgetFactory = widgetFactory; this.config = config || {}; this.updateListeners = []; -}; +} -nglr.Binder.parseBindings = function(string) { +Binder.parseBindings = function(string) { var results = []; var lastIndex = 0; var index; @@ -28,314 +28,325 @@ nglr.Binder.parseBindings = function(string) { return results.length === 0 ? [ string ] : results; }; -nglr.Binder.hasBinding = function(string) { - var bindings = nglr.Binder.parseBindings(string); - return bindings.length > 1 || nglr.Binder.binding(bindings[0]) !== null; +Binder.hasBinding = function(string) { + var bindings = Binder.parseBindings(string); + return bindings.length > 1 || Binder.binding(bindings[0]) !== null; }; -nglr.Binder.binding = function(string) { +Binder.binding = function(string) { var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/); return binding ? binding[1] : null; }; -nglr.Binder.prototype.parseQueryString = function(query) { - var params = {}; - query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, - function (match, left, right) { - if (left) params[decodeURIComponent(left)] = decodeURIComponent(right); - }); - return params; -}; - -nglr.Binder.prototype.parseAnchor = function(url) { - var self = this; - url = url || this.urlWatcher.getUrl(); - - var anchorIndex = url.indexOf('#'); - if (anchorIndex < 0) return; - var anchor = url.substring(anchorIndex + 1); - - var anchorQuery = this.parseQueryString(anchor); - jQuery.each(self.anchor, function(key, newValue) { - delete self.anchor[key]; - }); - jQuery.each(anchorQuery, function(key, newValue) { - self.anchor[key] = newValue; - }); -}; - -nglr.Binder.prototype.onUrlChange = function (url) { - console.log("URL change detected", url); - this.parseAnchor(url); - this.updateView(); -}; - -nglr.Binder.prototype.updateAnchor = function() { - var url = this.urlWatcher.getUrl(); - 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.urlWatcher.setUrl(url); - return url; -}; - -nglr.Binder.prototype.updateView = function() { - var start = new Date().getTime(); - var scope = jQuery(this.doc).scope(); - scope.set("$invalidWidgets", []); - scope.updateView(); - var end = new Date().getTime(); - this.updateAnchor(); - _.each(this.updateListeners, function(fn) {fn();}); -}; - -nglr.Binder.prototype.executeInit = function() { - jQuery("[ng-init]", this.doc).each(function() { - var jThis = jQuery(this); - var scope = jThis.scope(); - try { - scope.eval(jThis.attr('ng-init')); - } catch (e) { - nglr.alert("EVAL ERROR:\n" + jThis.attr('ng-init') + '\n' + nglr.toJson(e, true)); +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 = '&'; + } } - }); -}; - -nglr.Binder.prototype.entity = function (scope) { - jQuery("[ng-entity]", this.doc).attr("ng-watch", function() { - try { - var jNode = jQuery(this); - var decl = scope.entity(jNode.attr("ng-entity")); - return decl + (jNode.attr('ng-watch') || ""); - } catch (e) { - nglr.alert(e); + this.location['set'](url); + return url; + }, + + updateView: function() { + var start = new Date().getTime(); + var scope = jQuery(this.doc).scope(); + scope.set("$invalidWidgets", []); + scope.updateView(); + var end = new Date().getTime(); + this.updateAnchor(); + _.each(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(); } - }); -}; - -nglr.Binder.prototype.compile = function() { - var jNode = jQuery(this.doc); - var self = this; - if (this.config.autoSubmit) { - var submits = jQuery(":submit", this.doc).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(), ""); - jQuery("a[ng-action]", this.doc).live('click', function (event) { - var jNode = jQuery(this); - try { - jNode.scope().eval(jNode.attr('ng-action')); - jNode.removeAttr('ng-error'); - jNode.removeClass("ng-exception"); - } catch (e) { - jNode.addClass("ng-exception"); - jNode.attr('ng-error', nglr.toJson(e, true)); + 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}}"}'); } - self.updateView(); - return false; - }); -}; - -nglr.Binder.prototype.translateBinding = function(node, parentPath, factories) { - var path = parentPath.concat(); - var offset = path.pop(); - var parts = nglr.Binder.parseBindings(node.nodeValue); - if (parts.length > 1 || nglr.Binder.binding(parts[0])) { - var parent = node.parentNode; - if (nglr.isLeafNode(parent)) { - parent.setAttribute('ng-bind-template', node.nodeValue); - factories.push({path:path, fn:function(node, scope, prefix) { - return new nglr.BindUpdater(node, node.getAttribute('ng-bind-template')); - }}); - } else { - for (var i = 0; i < parts.length; i++) { - var part = parts[i]; - var binding = nglr.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:nglr.Binder.prototype.ng_bind}); + 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); } - } else if (nglr.msie && part.charAt(0) == ' ') { - newNode = document.createElement("span"); - newNode.innerHTML = ' ' + part.substring(1); - } else { - newNode = document.createTextNode(part); + parent.insertBefore(newNode, node); } - parent.insertBefore(newNode, node); } + parent.removeChild(node); } - parent.removeChild(node); - } -}; - -nglr.Binder.prototype.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]]; + }, + + 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); + } } - try { - scope.addWidget(factory.fn(node, scope, prefix)); - } catch (e) { - nglr.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); } } - }; -}; - -nglr.Binder.prototype.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 ? nglr.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 = nglr.msie && attrName == 'href' ? - decodeURI(node.getAttribute(attrName, 2)) : attr.value; - if (nglr.Binder.hasBinding(attrValue)) { - bindings[attrName] = attrValue; + + 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; } - var json = nglr.toJson(bindings); - if (json.length > 2) { - node.setAttribute("ng-bind-attr", json); + + 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 (!node.getAttribute) console.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); - var template = function(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 nglr.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)) { - node.value = node.text; + 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)) { + 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')); } - - var children = node.childNodes; - for (var k = 0; k < children.length; k++) { - this.precompileNode(children[k], path.concat(k), factories); - } -}; - -nglr.Binder.prototype.ng_eval = function(node) { - return new nglr.EvalUpdater(node, node.getAttribute('ng-eval')); -}; - -nglr.Binder.prototype.ng_bind = function(node) { - return new nglr.BindUpdater(node, "{{" + node.getAttribute('ng-bind') + "}}"); -}; - -nglr.Binder.prototype.ng_bind_attr = function(node) { - return new nglr.BindAttrUpdater(node, nglr.fromJson(node.getAttribute('ng-bind-attr'))); -}; - -nglr.Binder.prototype.ng_hide = function(node) { - return new nglr.HideUpdater(node, node.getAttribute('ng-hide')); -}; - -nglr.Binder.prototype.ng_show = function(node) { - return new nglr.ShowUpdater(node, node.getAttribute('ng-show')); -}; - -nglr.Binder.prototype.ng_class = function(node) { - return new nglr.ClassUpdater(node, node.getAttribute('ng-class')); -}; - -nglr.Binder.prototype.ng_class_even = function(node) { - return new nglr.ClassEvenUpdater(node, node.getAttribute('ng-class-even')); -}; - -nglr.Binder.prototype.ng_class_odd = function(node) { - return new nglr.ClassOddUpdater(node, node.getAttribute('ng-class-odd')); -}; - -nglr.Binder.prototype.ng_style = function(node) { - return new nglr.StyleUpdater(node, node.getAttribute('ng-style')); -}; - -nglr.Binder.prototype.ng_watch = function(node, scope) { - scope.watch(node.getAttribute('ng-watch')); -}; +};
\ No newline at end of file diff --git a/src/ControlBar.js b/src/ControlBar.js index bed7742f..73be74db 100644 --- a/src/ControlBar.js +++ b/src/ControlBar.js @@ -1,17 +1,11 @@ -// Copyright (C) 2008,2009 BRAT Tech LLC - -nglr.ControlBar = function (document, serverUrl, database) { - this.document = document; +function ControlBar(document, serverUrl) { + this._document = document; this.serverUrl = serverUrl; - this.database = database; - this.window = window; + this._window = window; this.callbacks = []; }; -nglr.ControlBar.prototype.bind = function () { -}; - -nglr.ControlBar.HTML = +ControlBar.HTML = '<div>' + '<div class="ui-widget-overlay"></div>' + '<div id="ng-login" ng-non-bindable="true">' + @@ -19,54 +13,59 @@ nglr.ControlBar.HTML = '</div>' + '</div>'; -nglr.ControlBar.prototype.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())); - } -}; - -nglr.ControlBar.prototype.logout = function (loginSubmitFn) { - this.callbacks.push(loginSubmitFn); - if (this.callbacks.length == 1) { - this.doTemplate("/user_session/do_destroy.mini"); - } -}; - -nglr.ControlBar.prototype.urlWithoutAnchor = function (path) { - return this.window.location.href.split("#")[0]; -}; - -nglr.ControlBar.prototype.doTemplate = function (path) { - var self = this; - var id = new Date().getTime(); - var url = this.urlWithoutAnchor(); - url += "#$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>' - }); - nglr["_iframe_notify_" + id] = function() { - loginView.dialog("destroy"); - loginView.remove(); - jQuery.each(self.callbacks, function(i, callback){ - callback(); - }); - self.callbacks = []; - }; -}; -nglr.ControlBar.FORBIDEN = +ControlBar.FORBIDEN = '<div ng-non-bindable="true" title="Permission Error:">' + 'Sorry, you do not have permission for this!'+ '</div>'; -nglr.ControlBar.prototype.notAuthorized = function () { - if (this.forbidenView) return; - this.forbidenView = jQuery(nglr.ControlBar.FORBIDEN); - this.forbidenView.dialog({bgiframe:true, height:70, modal:true}); -}; +ControlBar.prototype = { + bind: function () { + }, + + login: function (loginSubmitFn) { + this.callbacks.push(loginSubmitFn); + if (this.callbacks.length == 1) { + this.doTemplate("/user_session/new.mini?return_url=" + encodeURIComponent(this.urlWithoutAnchor())); + } + }, + + logout: function (loginSubmitFn) { + this.callbacks.push(loginSubmitFn); + if (this.callbacks.length == 1) { + this.doTemplate("/user_session/do_destroy.mini"); + } + }, + + urlWithoutAnchor: function (path) { + return this._window['location']['href'].split("#")[0]; + }, + + doTemplate: function (path) { + var self = this; + var id = new Date().getTime(); + var url = this.urlWithoutAnchor() + "#$iframe_notify=" + id; + var iframeHeight = 330; + var loginView = jQuery('<div style="overflow:hidden; padding:2px 0 0 0;"><iframe name="'+ url +'" src="'+this.serverUrl + path + '" width="500" height="'+ iframeHeight +'"/></div>'); + this._document.append(loginView); + loginView['dialog']({ + 'height':iframeHeight + 33, 'width':500, + 'resizable': false, 'modal':true, + 'title': 'Authentication: <a href="http://www.getangular.com"><tt><angular/></tt></a>' + }); + angularCallbacks["_iframe_notify_" + id] = function() { + loginView['dialog']("destroy"); + loginView['remove'](); + foreach(self.callbacks, function(callback){ + callback(); + }); + self.callbacks = []; + }; + }, + + notAuthorized: function () { + if (this.forbidenView) return; + this.forbidenView = jQuery(ControlBar.FORBIDEN); + this.forbidenView.dialog({bgiframe:true, height:70, modal:true}); + } +};
\ No newline at end of file diff --git a/src/DataStore.js b/src/DataStore.js index 97ab92ff..789b8f71 100644 --- a/src/DataStore.js +++ b/src/DataStore.js @@ -1,332 +1,330 @@ -// Copyright (C) 2009 BRAT Tech LLC - -nglr.DataStore = function(post, users, anchor) { +function DataStore(post, users, anchor) { this.post = post; this.users = users; - this._cache = {$collections:[]}; + this._cache_collections = []; + this._cache = {'$collections':this._cache_collections}; this.anchor = anchor; this.bulkRequest = []; }; -nglr.DataStore.prototype.cache = function(document) { - if (document.constructor != nglr.Model) { - throw "Parameter must be an instance of Entity! " + nglr.toJson(document); - } - var key = document.$entity + '/' + document.$id; - var cachedDocument = this._cache[key]; - if (cachedDocument) { - nglr.Model.copyDirectFields(document, cachedDocument); - } else { - this._cache[key] = document; - cachedDocument = document; - } - return cachedDocument; -}; - -nglr.DataStore.prototype.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||nglr.noop)(instance); - }, failure); - } - return instance; -}; - -nglr.DataStore.prototype.loadMany = function(entity, ids, callback) { - var self=this; - var list = []; - var callbackCount = 0; - jQuery.each(ids, function(i, id){ - list.push(self.load(entity(), id, function(){ - callbackCount++; - if (callbackCount == ids.length) { - (callback||nglr.noop)(list); - } - })); - }); - return list; -} +DataStore.NullEntity = extend(function(){}, { + 'all': function(){return [];}, + 'query': function(){return [];}, + 'load': function(){return {};}, + 'title': undefined +}); -nglr.DataStore.prototype.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||nglr.noop)(instance); +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 { - throw response; + this._cache[key] = document; + cachedDocument = document; } - }); -}; - -nglr.DataStore.prototype.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)); + 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); } - (callback||nglr.noop)(list); - }); - return list; -}; - -nglr.DataStore.prototype.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)) { - angular.Array.includeIf(collection, cachedDoc, true); + 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; } }); - if (document.$$anchor) { - self.anchor[document.$$anchor] = document.$id; - } - if (callback) - callback(document); - }); -}; - -nglr.DataStore.prototype.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); + }, + + 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); }); - (callback||nglr.noop)(response); - }); -}; - -nglr.DataStore.prototype._jsonRequest = function(request, callback, failure) { - request.$$callback = callback; - request.$$failure = failure||function(response){ - throw response; - }; - this.bulkRequest.push(request); -}; - -nglr.DataStore.prototype.flush = function() { - if (this.bulkRequest.length === 0) return; - var self = this; - var bulkRequest = this.bulkRequest; - this.bulkRequest = []; - console.log('REQUEST:', bulkRequest); - function callback(code, bulkResponse){ - console.log('RESPONSE[' + code + ']: ', bulkResponse); - if(bulkResponse.$status_code == 401) { - self.users.login(function(){ - self.post(bulkRequest, callback); + }, + + 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); + } + } }); - } else if(bulkResponse.$status_code) { - nglr.alert(nglr.toJson(bulkResponse)); - } else { - for ( var i = 0; i < bulkResponse.length; i++) { - var response = bulkResponse[i]; - var request = bulkRequest[i]; - var code = response.$status_code; - if(code) { - if(code == 403) { - self.users.notAuthorized(); + (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.$$failure(response); + request['$$callback'](response); } - } else { - request.$$callback(response); } } } - } - this.post(bulkRequest, callback); -}; - -nglr.DataStore.prototype.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 == nglr.Model.prototype.$save) { - saveCounter++; - item.$save(onSaveDone); - } - } - onSaveDone(); -}; - -nglr.DataStore.prototype.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; - for(var i = 0; i < list.length; i++) { - var document = new type().$loadFrom(list[i]); - queryList.push(self.cache(document)); + this.post(bulkRequest, callback); + }, + + saveScope: function(scope, callback) { + var saveCounter = 1; + function onSaveDone() { + saveCounter--; + if (saveCounter === 0 && callback) + callback(); } - if (callback) - callback(queryList); - }); - return queryList; -}; - -nglr.DataStore.prototype.entities = function(callback) { - var entities = []; - var self = this; - this._jsonRequest(["GET", "$entities"], function(response) { - for (var entityName in response) { - entities.push(self.entity(entityName)); + for(var key in scope) { + var item = scope[key]; + if (item && item['$save'] == Model.prototype['$save']) { + saveCounter++; + item['$save'](onSaveDone); + } } - entities.sort(function(a,b){return a.title > b.title ? 1 : -1;}); - if (callback) callback(entities); - }); - return entities; -}; - -nglr.DataStore.prototype.documentCountsByUser = function(){ - var counts = {}; - var self = this; - self.post([["GET", "$users"]], function(code, response){ - jQuery.each(response[0], function(key, value){ - counts[key] = value; - }); - }); - return counts; -}; - -nglr.DataStore.prototype.userDocumentIdsByEntity = function(user){ - var ids = {}; - var self = this; - self.post([["GET", "$users/" + user]], function(code, response){ - jQuery.each(response[0], function(key, value){ - ids[key] = value; + 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 ids; -}; - -nglr.DataStore.NullEntity = function(){}; -nglr.DataStore.NullEntity.all = function(){return [];}; -nglr.DataStore.NullEntity.query = function(){return [];}; -nglr.DataStore.NullEntity.load = function(){return {};}; -nglr.DataStore.NullEntity.title = undefined; - -nglr.DataStore.prototype.entity = function(name, defaults){ - if (!name) { - return nglr.DataStore.NullEntity; - } - var self = this; - var entity = function(initialState){ - return new nglr.Model(entity, initialState); - }; - // entity.name does not work as name seems to be reserved for functions - entity.title = name; - entity.$$factory = true; - entity.datastore = this; - entity.defaults = defaults || {}; - entity.load = function(id, callback){ - return self.load(entity(), id, callback); - }; - entity.loadMany = function(ids, callback){ - return self.loadMany(entity, ids, callback); - }; - entity.loadOrCreate = function(id, callback){ - return self.loadOrCreate(entity(), id, callback); - }; - entity.all = function(callback){ - return self.loadAll(entity, callback); - }; - entity.query = function(query, queryArgs, callback){ - return self.query(entity, query, queryArgs, callback); - }; - entity.properties = function(callback) { - self._jsonRequest(["GET", name + "/$properties"], callback); - }; - return entity; -}; - -nglr.DataStore.prototype.join = function(join){ - var fn = function(){ - 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 = nglr.Scope.getter(row, nextJoinOn); - joinIds[id] = id; + 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)); }); - nextJoin.join.loadMany(_.toArray(joinIds), function(result){ - var byId = {}; + 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){ - byId[doc.$id] = doc; + var row = {}; + joinedResult.push(row); + row[baseName] = doc; + var id = Scope.getter(row, nextJoinOn); + joinIds[id] = id; }); - _(joinedResult).each(function(row){ - var id = nglr.Scope.getter(row, nextJoinOn); - row[nextJoinName] = byId[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; -}; + return joinedResult; + }; + return fn; + } +};
\ No newline at end of file diff --git a/src/Filters.js b/src/Filters.js index f75f3603..77fa5ec7 100644 --- a/src/Filters.js +++ b/src/Filters.js @@ -1,13 +1,11 @@ -// Copyright (C) 2009 BRAT Tech LLC - -angular.filter.Meta = function(obj){ +angularFilter.Meta = function(obj){ if (obj) { for ( var key in obj) { this[key] = obj[key]; } } }; -angular.filter.Meta.get = function(obj, attr){ +angularFilter.Meta.get = function(obj, attr){ attr = attr || 'text'; switch(typeof obj) { case "string": @@ -22,269 +20,303 @@ angular.filter.Meta.get = function(obj, attr){ } }; -angular.filter.currency = function(amount){ - jQuery(this.element).toggleClass('ng-format-negative', amount < 0); - return '$' + angular.filter.number.apply(this, [amount, 2]); -}; +var angularFilterGoogleChartApi; -angular.filter.number = function(amount, fractionSize){ - if (isNaN(amount) || !isFinite(amount)) { - return ''; - } - fractionSize = typeof fractionSize == 'undefined' ? 2 : fractionSize; - var isNegative = amount < 0; - amount = Math.abs(amount); - var pow = Math.pow(10, fractionSize); - var text = "" + Math.round(amount * pow); - var whole = text.substring(0, text.length - fractionSize); - whole = whole || '0'; - var frc = text.substring(text.length - fractionSize); - text = isNegative ? '-' : ''; - for (var i = 0; i < whole.length; i++) { - if ((whole.length - i)%3 === 0 && i !== 0) { - text += ','; - } - text += whole.charAt(i); - } - if (fractionSize > 0) { - for (var j = frc.length; j < fractionSize; j++) { - frc += '0'; +foreach({ + 'currency': function(amount){ + jQuery(this.element).toggleClass('ng-format-negative', amount < 0); + return '$' + angularFilter['number'].apply(this, [amount, 2]); + }, + + 'number': function(amount, fractionSize){ + if (isNaN(amount) || !isFinite(amount)) { + return ''; } - text += '.' + frc.substring(0, fractionSize); - } - return text; -}; - -angular.filter.date = function(amount) { -}; - -angular.filter.json = function(object) { - jQuery(this.element).addClass("ng-monospace"); - return nglr.toJson(object, true); -}; - -angular.filter.trackPackage = function(trackingNo, noMatch) { - trackingNo = nglr.trim(trackingNo); - var tNo = trackingNo.replace(/ /g, ''); - var MATCHERS = angular.filter.trackPackage.MATCHERS; - for ( var i = 0; i < MATCHERS.length; i++) { - var carrier = MATCHERS[i]; - for ( var j = 0; j < carrier.regexp.length; j++) { - var regexp = carrier.regexp[j]; - if (regexp.test(tNo)) { - var text = carrier.name + ": " + trackingNo; - var url = carrier.url + trackingNo; - return new angular.filter.Meta({ - text:text, - url:url, - html: '<a href="' + nglr.escapeAttr(url) + '">' + text + '</a>', - trackingNo:trackingNo}); + fractionSize = typeof fractionSize == 'undefined' ? 2 : fractionSize; + var isNegative = amount < 0; + amount = Math.abs(amount); + var pow = Math.pow(10, fractionSize); + var text = "" + Math.round(amount * pow); + var whole = text.substring(0, text.length - fractionSize); + whole = whole || '0'; + var frc = text.substring(text.length - fractionSize); + text = isNegative ? '-' : ''; + for (var i = 0; i < whole.length; i++) { + if ((whole.length - i)%3 === 0 && i !== 0) { + text += ','; } + text += whole.charAt(i); } - } - if (trackingNo) - return noMatch || - new angular.filter.Meta({text:trackingNo + " is not recognized"}); - else - return null; -}; - -angular.filter.trackPackage.MATCHERS = [ - { name: "UPS", - url: "http://wwwapps.ups.com/WebTracking/processInputRequest?sort_by=status&tracknums_displayed=1&TypeOfInquiryNumber=T&loc=en_US&track.x=0&track.y=0&InquiryNumber1=", - regexp: [ - /^1Z[0-9A-Z]{16}$/i]}, - { name: "FedEx", - url: "http://www.fedex.com/Tracking?tracknumbers=", - regexp: [ - /^96\d{10}?$/i, - /^96\d{17}?$/i, - /^96\d{20}?$/i, - /^\d{15}$/i, - /^\d{12}$/i]}, - { name: "USPS", - url: "http://trkcnfrm1.smi.usps.com/PTSInternetWeb/InterLabelInquiry.do?origTrackNum=", - regexp: [ - /^(91\d{20})$/i, - /^(91\d{18})$/i]}]; - -angular.filter.link = function(obj, title) { - var text = title || angular.filter.Meta.get(obj); - var url = angular.filter.Meta.get(obj, "url") || angular.filter.Meta.get(obj); - if (url) { - if (angular.validator.email(url) === null) { - url = "mailto:" + url; + if (fractionSize > 0) { + for (var j = frc.length; j < fractionSize; j++) { + frc += '0'; + } + text += '.' + frc.substring(0, fractionSize); } - var html = '<a href="' + nglr.escapeHtml(url) + '">' + text + '</a>'; - return new angular.filter.Meta({text:text, url:url, html:html}); - } - return obj; -}; - - -angular.filter.bytes = function(size) { - if(size === null) return ""; - - var suffix = 0; - while (size > 1000) { - size = size / 1024; - suffix++; - } - var txt = "" + size; - var dot = txt.indexOf('.'); - if (dot > -1 && dot + 2 < txt.length) { - txt = txt.substring(0, dot + 2); - } - return txt + " " + angular.filter.bytes.SUFFIX[suffix]; -}; -angular.filter.bytes.SUFFIX = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; - -angular.filter.image = function(obj, width, height) { - if (obj && obj.url) { - var style = ""; - if (width) { - style = ' style="max-width: ' + width + - 'px; max-height: ' + (height || width) + 'px;"'; + return text; + }, + + 'date': function(amount) { + }, + + 'json': function(object) { + jQuery(this.element).addClass("ng-monospace"); + return toJson(object, true); + }, + + 'trackPackage': (function(){ + var MATCHERS = [ + { name: "UPS", + url: "http://wwwapps.ups.com/WebTracking/processInputRequest?sort_by=status&tracknums_displayed=1&TypeOfInquiryNumber=T&loc=en_US&track.x=0&track.y=0&InquiryNumber1=", + regexp: [ + /^1Z[0-9A-Z]{16}$/i]}, + { name: "FedEx", + url: "http://www.fedex.com/Tracking?tracknumbers=", + regexp: [ + /^96\d{10}?$/i, + /^96\d{17}?$/i, + /^96\d{20}?$/i, + /^\d{15}$/i, + /^\d{12}$/i]}, + { name: "USPS", + url: "http://trkcnfrm1.smi.usps.com/PTSInternetWeb/InterLabelInquiry.do?origTrackNum=", + regexp: [ + /^(91\d{20})$/i, + /^(91\d{18})$/i]}]; + return function(trackingNo, noMatch) { + trackingNo = trim(trackingNo); + var tNo = trackingNo.replace(/ /g, ''); + var returnValue; + foreach(MATCHERS, function(carrier){ + foreach(carrier.regexp, function(regexp){ + if (regexp.test(tNo)) { + var text = carrier.name + ": " + trackingNo; + var url = carrier.url + trackingNo; + returnValue = new angularFilter.Meta({ + text:text, + url:url, + html: '<a href="' + escapeAttr(url) + '">' + text + '</a>', + trackingNo:trackingNo}); + _.breakLoop(); + } + }); + if (returnValue) _.breakLoop(); + }); + if (returnValue) + return returnValue; + else if (trackingNo) + return noMatch || new angularFilter.Meta({text:trackingNo + " is not recognized"}); + else + return null; + };})(), + + 'link': function(obj, title) { + var text = title || angularFilter.Meta.get(obj); + var url = angularFilter.Meta.get(obj, "url") || angularFilter.Meta.get(obj); + if (url) { + if (angular.validator.email(url) === null) { + url = "mailto:" + url; + } + var html = '<a href="' + escapeHtml(url) + '">' + text + '</a>'; + return new angularFilter.Meta({text:text, url:url, html:html}); } - return new angular.filter.Meta({url:obj.url, text:obj.url, - html:'<img src="'+obj.url+'"' + style + '/>'}); - } - return null; -}; - -angular.filter.lowercase = function (obj) { - var text = angular.filter.Meta.get(obj); - return text ? ("" + text).toLowerCase() : text; -}; - -angular.filter.uppercase = function (obj) { - var text = angular.filter.Meta.get(obj); - return text ? ("" + text).toUpperCase() : text; -}; - -angular.filter.linecount = function (obj) { - var text = angular.filter.Meta.get(obj); - if (text==='' || !text) return 1; - return text.split(/\n|\f/).length; -}; - -angular.filter['if'] = function (result, expression) { - return expression ? result : undefined; -}; - -angular.filter.unless = function (result, expression) { - return expression ? undefined : result; -}; - -angular.filter.googleChartApi = function(type, data, width, height) { - data = data || {}; - var api = angular.filter.googleChartApi; - var chart = { - cht:type, - chco:api.collect(data, 'color'), - chtt:api.title(data), - chdl:api.collect(data, 'label'), - chd:api.values(data), - chf:'bg,s,FFFFFF00' + return obj; + }, + + + 'bytes': (function(){ + var SUFFIX = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; + return function(size) { + if(size === null) return ""; + + var suffix = 0; + while (size > 1000) { + size = size / 1024; + suffix++; + } + var txt = "" + size; + var dot = txt.indexOf('.'); + if (dot > -1 && dot + 2 < txt.length) { + txt = txt.substring(0, dot + 2); + } + return txt + " " + SUFFIX[suffix]; }; - if (_.isArray(data.xLabels)) { - chart.chxt='x'; - chart.chxl='0:|' + data.xLabels.join('|'); - } - return angular.filter.googleChartApi.encode(chart, width, height); -}; - -angular.filter.googleChartApi.values = function(data){ - var seriesValues = []; - _.each(data.series||[], function(serie){ - var values = []; - _.each(serie.values||[], function(value){ - values.push(value); - }); - seriesValues.push(values.join(',')); - }); - var values = seriesValues.join('|'); - return values === "" ? null : "t:" + values; -}; - -angular.filter.googleChartApi.title = function(data){ - var titles = []; - var title = data.title || []; - _.each(_.isArray(title)?title:[title], function(text){ - titles.push(encodeURIComponent(text)); - }); - return titles.join('|'); -}; - -angular.filter.googleChartApi.collect = function(data, key){ - var outterValues = []; - var count = 0; - _.each(data.series||[], function(serie){ - var innerValues = []; - var value = serie[key] || []; - _.each(_.isArray(value)?value:[value], function(color){ - innerValues.push(encodeURIComponent(color)); - count++; - }); - outterValues.push(innerValues.join('|')); - }); - return count?outterValues.join(','):null; -}; - -angular.filter.googleChartApi.encode= function(params, width, height) { - width = width || 200; - height = height || width; - var url = "http://chart.apis.google.com/chart?"; - var urlParam = []; - params.chs = width + "x" + height; - for ( var key in params) { - var value = params[key]; - if (value) { - urlParam.push(key + "=" + value); + })(), + + 'image': function(obj, width, height) { + if (obj && obj.url) { + var style = ""; + if (width) { + style = ' style="max-width: ' + width + + 'px; max-height: ' + (height || width) + 'px;"'; + } + return new angularFilter.Meta({url:obj.url, text:obj.url, + html:'<img src="'+obj.url+'"' + style + '/>'}); } - } - urlParam.sort(); - url += urlParam.join("&"); - return new angular.filter.Meta({url:url, text:value, - html:'<img width="' + width + '" height="' + height + '" src="'+url+'"/>'}); -}; - -angular.filter.qrcode = function(value, width, height) { - return angular.filter.googleChartApi.encode({cht:'qr', chl:encodeURIComponent(value)}, width, height); -}; -angular.filter.chart = { - pie:function(data, width, height) { - return angular.filter.googleChartApi('p', data, width, height); + return null; }, - pie3d:function(data, width, height) { - return angular.filter.googleChartApi('p3', data, width, height); + + 'lowercase': function (obj) { + var text = angularFilter.Meta.get(obj); + return text ? ("" + text).toLowerCase() : text; }, - pieConcentric:function(data, width, height) { - return angular.filter.googleChartApi('pc', data, width, height); + + 'uppercase': function (obj) { + var text = angularFilter.Meta.get(obj); + return text ? ("" + text).toUpperCase() : text; }, - barHorizontalStacked:function(data, width, height) { - return angular.filter.googleChartApi('bhs', data, width, height); + + 'linecount': function (obj) { + var text = angularFilter.Meta.get(obj); + if (text==='' || !text) return 1; + return text.split(/\n|\f/).length; }, - barHorizontalGrouped:function(data, width, height) { - return angular.filter.googleChartApi('bhg', data, width, height); + + 'if': function (result, expression) { + return expression ? result : undefined; }, - barVerticalStacked:function(data, width, height) { - return angular.filter.googleChartApi('bvs', data, width, height); + + 'unless': function (result, expression) { + return expression ? undefined : result; }, - barVerticalGrouped:function(data, width, height) { - return angular.filter.googleChartApi('bvg', data, width, height); + + 'googleChartApi': extend( + function(type, data, width, height) { + data = data || {}; + var chart = { + 'cht':type, + 'chco':angularFilterGoogleChartApi['collect'](data, 'color'), + 'chtt':angularFilterGoogleChartApi['title'](data), + 'chdl':angularFilterGoogleChartApi['collect'](data, 'label'), + 'chd':angularFilterGoogleChartApi['values'](data), + 'chf':'bg,s,FFFFFF00' + }; + if (_.isArray(data['xLabels'])) { + chart['chxt']='x'; + chart['chxl']='0:|' + data.xLabels.join('|'); + } + return angularFilterGoogleChartApi['encode'](chart, width, height); + }, + { + 'values': function(data){ + var seriesValues = []; + foreach(data['series']||[], function(serie){ + var values = []; + foreach(serie['values']||[], function(value){ + values.push(value); + }); + seriesValues.push(values.join(',')); + }); + var values = seriesValues.join('|'); + return values === "" ? null : "t:" + values; + }, + + 'title': function(data){ + var titles = []; + var title = data['title'] || []; + foreach(_.isArray(title)?title:[title], function(text){ + titles.push(encodeURIComponent(text)); + }); + return titles.join('|'); + }, + + 'collect': function(data, key){ + var outterValues = []; + var count = 0; + foreach(data['series']||[], function(serie){ + var innerValues = []; + var value = serie[key] || []; + foreach(_.isArray(value)?value:[value], function(color){ + innerValues.push(encodeURIComponent(color)); + count++; + }); + outterValues.push(innerValues.join('|')); + }); + return count?outterValues.join(','):null; + }, + + 'encode': function(params, width, height) { + width = width || 200; + height = height || width; + var url = "http://chart.apis.google.com/chart?"; + var urlParam = []; + params['chs'] = width + "x" + height; + foreach(params, function(value, key){ + if (value) { + urlParam.push(key + "=" + value); + } + }); + urlParam.sort(); + url += urlParam.join("&"); + return new angularFilter.Meta({url:url, + html:'<img width="' + width + '" height="' + height + '" src="'+url+'"/>'}); + } + } + ), + + + 'qrcode': function(value, width, height) { + return angularFilterGoogleChartApi['encode']({ + 'cht':'qr', 'chl':encodeURIComponent(value)}, width, height); }, - line:function(data, width, height) { - return angular.filter.googleChartApi('lc', data, width, height); + 'chart': { + 'pie':function(data, width, height) { + return angularFilterGoogleChartApi('p', data, width, height); + }, + 'pie3d':function(data, width, height) { + return angularFilterGoogleChartApi('p3', data, width, height); + }, + 'pieConcentric':function(data, width, height) { + return angularFilterGoogleChartApi('pc', data, width, height); + }, + 'barHorizontalStacked':function(data, width, height) { + return angularFilterGoogleChartApi('bhs', data, width, height); + }, + 'barHorizontalGrouped':function(data, width, height) { + return angularFilterGoogleChartApi('bhg', data, width, height); + }, + 'barVerticalStacked':function(data, width, height) { + return angularFilterGoogleChartApi('bvs', data, width, height); + }, + 'barVerticalGrouped':function(data, width, height) { + return angularFilterGoogleChartApi('bvg', data, width, height); + }, + 'line':function(data, width, height) { + return angularFilterGoogleChartApi('lc', data, width, height); + }, + 'sparkline':function(data, width, height) { + return angularFilterGoogleChartApi('ls', data, width, height); + }, + 'scatter':function(data, width, height) { + return angularFilterGoogleChartApi('s', data, width, height); + } }, - sparkline:function(data, width, height) { - return angular.filter.googleChartApi('ls', data, width, height); + + 'html': function(html){ + return new angularFilter.Meta({html:html}); }, - scatter:function(data, width, height) { - return angular.filter.googleChartApi('s', data, width, height); + + 'linky': function(text){ + if (!text) return text; + function regExpEscape(text) { + return text.replace(/([\/\.\*\+\?\|\(\)\[\]\{\}\\])/g, '\\$1'); + } + var URL = /(ftp|http|https|mailto):\/\/([^\(\)|\s]+)/; + var match; + var raw = text; + var html = []; + while (match=raw.match(URL)) { + var url = match[0].replace(/[\.\;\,\(\)\{\}\<\>]$/,''); + var i = raw.indexOf(url); + html.push(escapeHtml(raw.substr(0, i))); + html.push('<a href="' + url + '">'); + html.push(url); + html.push('</a>'); + raw = raw.substring(i + url.length); + } + html.push(escapeHtml(raw)); + return new angularFilter.Meta({text:text, html:html.join('')}); } -}; +}, function(v,k){angularFilter[k] = v;}); -angular.filter.html = function(html){ - return new angular.filter.Meta({html:html}); -}; +angularFilterGoogleChartApi = angularFilter['googleChartApi']; diff --git a/src/JSON.js b/src/JSON.js index 2b6393bf..98dfddd2 100644 --- a/src/JSON.js +++ b/src/JSON.js @@ -1,29 +1,38 @@ -nglr.array = [].constructor; +array = [].constructor; -nglr.toJson = function(obj, pretty){ +function toJson(obj, pretty){ var buf = []; - nglr.toJsonArray(buf, obj, pretty ? "\n " : null); + toJsonArray(buf, obj, pretty ? "\n " : null, _([])); return buf.join(''); }; -nglr.toPrettyJson = function(obj) { - return nglr.toJson(obj, true); +function toPrettyJson(obj) { + return toJson(obj, true); }; -nglr.fromJson = function(json) { +function fromJson(json) { try { - var parser = new nglr.Parser(json, true); + var parser = new Parser(json, true); var expression = parser.primary(); parser.assertAllConsumed(); return expression(); } catch (e) { - console.error("fromJson error: ", json, e); + error("fromJson error: ", json, e); throw e; } }; +angular['toJson'] = toJson; +angular['fromJson'] = fromJson; -nglr.toJsonArray = function(buf, obj, pretty){ +function toJsonArray(buf, obj, pretty, stack){ + if (typeof obj == "object") { + if (stack.include(obj)) { + buf.push("RECURSION"); + return; + } + stack.push(obj); + } var type = typeof obj; if (obj === null) { buf.push("null"); @@ -38,7 +47,7 @@ nglr.toJsonArray = function(buf, obj, pretty){ buf.push('' + obj); } } else if (type === 'string') { - return buf.push(angular.String.quoteUnicode(obj)); + return buf.push(angular['String']['quoteUnicode'](obj)); } else if (type === 'object') { if (obj instanceof Array) { buf.push("["); @@ -50,13 +59,13 @@ nglr.toJsonArray = function(buf, obj, pretty){ if (typeof item == 'function' || typeof item == 'undefined') { buf.push("null"); } else { - nglr.toJsonArray(buf, item, pretty); + toJsonArray(buf, item, pretty, stack); } sep = true; } buf.push("]"); } else if (obj instanceof Date) { - buf.push(angular.String.quoteUnicode(angular.Date.toString(obj))); + buf.push(angular['String']['quoteUnicode'](angular['Date']['toString'](obj))); } else { buf.push("{"); if (pretty) buf.push(pretty); @@ -78,9 +87,9 @@ nglr.toJsonArray = function(buf, obj, pretty){ buf.push(","); if (pretty) buf.push(pretty); } - buf.push(angular.String.quote(key)); + buf.push(angular['String']['quote'](key)); buf.push(":"); - nglr.toJsonArray(buf, value, childPretty); + toJsonArray(buf, value, childPretty, stack); comma = true; } } catch (e) { @@ -89,4 +98,7 @@ nglr.toJsonArray = function(buf, obj, pretty){ buf.push("}"); } } + if (typeof obj == "object") { + stack.pop(); + } }; diff --git a/src/Loader.js b/src/Loader.js deleted file mode 100644 index e4dec316..00000000 --- a/src/Loader.js +++ /dev/null @@ -1,389 +0,0 @@ -// Copyright (C) 2008,2009 BRAT Tech LLC - -// IE compatibility - -if (typeof document.getAttribute == 'undefined') - document.getAttribute = function() { - }; -if (typeof Node == 'undefined') { - Node = { - ELEMENT_NODE : 1, - ATTRIBUTE_NODE : 2, - TEXT_NODE : 3, - CDATA_SECTION_NODE : 4, - ENTITY_REFERENCE_NODE : 5, - ENTITY_NODE : 6, - PROCESSING_INSTRUCTION_NODE : 7, - COMMENT_NODE : 8, - DOCUMENT_NODE : 9, - DOCUMENT_TYPE_NODE : 10, - DOCUMENT_FRAGMENT_NODE : 11, - NOTATION_NODE : 12 - }; -} - -if (_.isUndefined(window.nglr)) nglr = {}; -if (_.isUndefined(window.angular)) angular = {}; -if (_.isUndefined(angular.validator)) angular.validator = {}; -if (_.isUndefined(angular.filter)) angular.filter = {}; -if (_.isUndefined(window.console)) - window.console = { - log:function() {}, - error:function() {} - }; -if (_.isUndefined(nglr.alert)) { - nglr.alert = function(){console.log(arguments); window.alert.apply(window, arguments); }; -} - -nglr.consoleLog = function(level, objs) { - var log = document.createElement("div"); - log.className = level; - var msg = ""; - var sep = ""; - for ( var i = 0; i < objs.length; i++) { - var obj = objs[i]; - msg += sep + (typeof obj == 'string' ? obj : nglr.toJson(obj)); - sep = " "; - } - log.appendChild(document.createTextNode(msg)); - nglr.consoleNode.appendChild(log); -}; - -nglr.isNode = function(inp) { - return inp && - inp.tagName && - inp.nodeName && - inp.ownerDocument && - inp.removeAttribute; -}; - -nglr.isLeafNode = function(node) { - switch (node.nodeName) { - case "OPTION": - case "PRE": - case "TITLE": - return true; - default: - return false; - } -}; - -nglr.noop = function() { -}; -nglr.setHtml = function(node, html) { - if (nglr.isLeafNode(node)) { - if (nglr.msie) { - node.innerText = html; - } else { - node.textContent = html; - } - } else { - node.innerHTML = html; - } -}; - -nglr.escapeHtml = function(html) { - if (!html || !html.replace) - return html; - return html. - replace(/&/g, '&'). - replace(/</g, '<'). - replace(/>/g, '>'); -}; - -nglr.escapeAttr = function(html) { - if (!html || !html.replace) - return html; - return html.replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, - '"'); -}; - -nglr.bind = function(_this, _function) { - if (!_this) - throw "Missing this"; - if (!_.isFunction(_function)) - throw "Missing function"; - return function() { - return _function.apply(_this, arguments); - }; -}; - -nglr.shiftBind = function(_this, _function) { - return function() { - var args = [ this ]; - for ( var i = 0; i < arguments.length; i++) { - args.push(arguments[i]); - } - return _function.apply(_this, args); - }; -}; - -nglr.outerHTML = function(node) { - var temp = document.createElement('div'); - temp.appendChild(node); - var outerHTML = temp.innerHTML; - temp.removeChild(node); - return outerHTML; -}; - -nglr.trim = function(str) { - return str.replace(/^ */, '').replace(/ *$/, ''); -}; - -nglr.toBoolean = function(value) { - var v = ("" + value).toLowerCase(); - if (v == 'f' || v == '0' || v == 'false' || v == 'no') - value = false; - return !!value; -}; - -nglr.merge = function(src, dst) { - for ( var key in src) { - var value = dst[key]; - var type = typeof value; - if (type == 'undefined') { - dst[key] = nglr.fromJson(nglr.toJson(src[key])); - } else if (type == 'object' && value.constructor != nglr.array && - key.substring(0, 1) != "$") { - nglr.merge(src[key], value); - } - } -}; - -// //////////////////////////// -// Loader -// //////////////////////////// - -nglr.Loader = function(document, head, config) { - this.document = jQuery(document); - this.head = jQuery(head); - this.config = config; - this.location = window.location; -}; - -nglr.Loader.prototype.load = function() { - this.configureLogging(); - this.loadCss('/stylesheets/jquery-ui/smoothness/jquery-ui-1.7.1.css'); - this.loadCss('/stylesheets/nglr.css'); - console.log("Server: " + this.config.server); - jQuery.noConflict(); - nglr.msie = jQuery.browser.msie; - this.configureJQueryPlugins(); - this.computeConfiguration(); - this.bindHtml(); -}; - -nglr.Loader.prototype.configureJQueryPlugins = function() { - console.log('Loader.configureJQueryPlugins()'); - jQuery.fn.removeNode = function() { - var node = this.get(0); - node.parentNode.removeChild(node); - }; - jQuery.fn.scope = function() { - var element = this; - while (element && element.get(0)) { - var scope = element.data("scope"); - if (scope) - return scope; - element = element.parent(); - } - return null; - }; - jQuery.fn.controller = function() { - return this.data('controller') || nglr.NullController.instance; - }; -}; - -nglr.Loader.prototype.uid = function() { - return "" + new Date().getTime(); -}; - -nglr.Loader.prototype.computeConfiguration = function() { - var config = this.config; - if (!config.database) { - var match = config.server.match(/https?:\/\/([\w]*)/) - config.database = match ? match[1] : "$MEMORY"; - } -}; - -nglr.Loader.prototype.bindHtml = function() { - console.log('Loader.bindHtml()'); - var watcher = new nglr.UrlWatcher(this.location); - var document = this.document; - var widgetFactory = new nglr.WidgetFactory(this.config.server, this.config.database); - var binder = new nglr.Binder(document[0], widgetFactory, watcher, this.config); - widgetFactory.onChangeListener = nglr.shiftBind(binder, binder.updateModel); - var controlBar = new nglr.ControlBar(document.find('body'), this.config.server, this.config.database); - var onUpdate = function(){binder.updateView();}; - var server = this.config.database=="$MEMORY" ? - new nglr.FrameServer(this.window) : - new nglr.Server(this.config.server, jQuery.getScript); - server = new nglr.VisualServer(server, new nglr.Status(jQuery(document.body)), onUpdate); - var users = new nglr.Users(server, controlBar); - var databasePath = '/data/' + this.config.database; - var post = function(request, callback){ - server.request("POST", databasePath, request, callback); - }; - var datastore = new nglr.DataStore(post, users, binder.anchor); - binder.updateListeners.push(function(){datastore.flush();}); - var scope = new nglr.Scope( { - $anchor : binder.anchor, - $binder : binder, - $config : this.config, - $console : window.console, - $datastore : datastore, - $save : function(callback) { - datastore.saveScope(scope.state, callback, binder.anchor); - }, - $window : window, - $uid : this.uid, - $users : users - }, "ROOT"); - - jQuery.each(["get", "set", "eval", "addWatchListener", "updateView"], - function(i, method){ - angular[method] = nglr.bind(scope, scope[method]); - }); - - document.data('scope', scope); - console.log('$binder.entity()'); - binder.entity(scope); - - console.log('$binder.compile()'); - binder.compile(); - - console.log('ControlBar.bind()'); - controlBar.bind(); - - console.log('$users.fetchCurrentUser()'); - function fetchCurrentUser() { - users.fetchCurrentUser(function(u) { - if (!u && document.find("[ng-auth=eager]").length) { - users.login(); - } - }); - } - fetchCurrentUser(); - - console.log('PopUp.bind()'); - new nglr.PopUp(document).bind(); - - console.log('$binder.parseAnchor()'); - binder.parseAnchor(); - - console.log('$binder.executeInit()'); - binder.executeInit(); - - console.log('$binder.updateView()'); - binder.updateView(); - - watcher.listener = nglr.bind(binder, binder.onUrlChange, watcher); - watcher.onUpdate = function(){nglr.alert("update");}; - watcher.watch(); - document.find("body").show(); - console.log('ready()'); - -}; - -nglr.Loader.prototype.visualPost = function(delegate) { - var status = new nglr.Status(jQuery(document.body)); - return function(request, delegateCallback) { - status.beginRequest(request); - var callback = function() { - status.endRequest(); - try { - delegateCallback.apply(this, arguments); - } catch (e) { - nglr.alert(nglr.toJson(e)); - } - }; - delegate(request, callback); - }; -}; - -nglr.Loader.prototype.configureLogging = function() { - var url = window.location.href + '#'; - url = url.split('#')[1]; - var config = { - debug : null - }; - var configs = url.split('&'); - for ( var i = 0; i < configs.length; i++) { - var part = (configs[i] + '=').split('='); - config[part[0]] = part[1]; - } - if (config.debug == 'console') { - nglr.consoleNode = document.createElement("div"); - nglr.consoleNode.id = 'ng-console'; - document.getElementsByTagName('body')[0].appendChild(nglr.consoleNode); - console.log = function() { - nglr.consoleLog('ng-console-info', arguments); - }; - console.error = function() { - nglr.consoleLog('ng-console-error', arguments); - }; - } -}; - -nglr.Loader.prototype.loadCss = function(css) { - var cssTag = document.createElement('link'); - cssTag.rel = "stylesheet"; - cssTag.type = "text/css"; - if (!css.match(/^http:/)) - css = this.config.server + css; - cssTag.href = css; - this.head[0].appendChild(cssTag); -}; - -nglr.UrlWatcher = function(location) { - this.location = location; - this.delay = 25; - this.setTimeout = function(fn, delay) { - window.setTimeout(fn, delay); - }; - this.listener = function(url) { - return url; - }; - this.expectedUrl = location.href; -}; - -nglr.UrlWatcher.prototype.watch = function() { - var self = this; - var pull = function() { - if (self.expectedUrl !== self.location.href) { - var notify = self.location.hash.match(/^#\$iframe_notify=(.*)$/); - if (notify) { - if (!self.expectedUrl.match(/#/)) { - self.expectedUrl += "#"; - } - self.location.href = self.expectedUrl; - var id = '_iframe_notify_' + notify[1]; - var notifyFn = nglr[id]; - delete nglr[id]; - try { - (notifyFn||nglr.noop)(); - } catch (e) { - nglr.alert(e); - } - } else { - self.listener(self.location.href); - self.expectedUrl = self.location.href; - } - } - self.setTimeout(pull, self.delay); - }; - pull(); -}; - -nglr.UrlWatcher.prototype.setUrl = function(url) { - var existingURL = window.location.href; - if (!existingURL.match(/#/)) - existingURL += '#'; - if (existingURL != url) - window.location.href = url; - self.existingURL = url; -}; - -nglr.UrlWatcher.prototype.getUrl = function() { - return window.location.href; -}; diff --git a/src/Model.js b/src/Model.js index 5e48251f..b09efd0e 100644 --- a/src/Model.js +++ b/src/Model.js @@ -1,16 +1,14 @@ -// Copyright (C) 2009 BRAT Tech LLC - // Single $ is special and does not get searched // Double $$ is special an is client only (does not get sent to server) -nglr.Model = function(entity, initial) { - this.$$entity = entity; - this.$loadFrom(initial||{}); - this.$entity = entity.title; - this.$migrate(); +function Model(entity, initial) { + this['$$entity'] = entity; + this['$loadFrom'](initial||{}); + this['$entity'] = entity['title']; + this['$migrate'](); }; -nglr.Model.copyDirectFields = function(src, dst) { +Model.copyDirectFields = function(src, dst) { if (src === dst || !src || !dst) return; var isDataField = function(src, dst, field) { return (field.substring(0,2) !== '$$') && @@ -27,39 +25,41 @@ nglr.Model.copyDirectFields = function(src, dst) { } }; -nglr.Model.prototype.$migrate = function() { - nglr.merge(this.$$entity.defaults, this); - return this; -}; - -nglr.Model.prototype.$merge = function(other) { - nglr.merge(other, this); - return this; -}; - -nglr.Model.prototype.$save = function(callback) { - this.$$entity.datastore.save(this, callback === true ? undefined : callback); - if (callback === true) this.$$entity.datastore.flush(); - return this; -}; - -nglr.Model.prototype.$delete = function(callback) { - this.$$entity.datastore.remove(this, callback === true ? undefined : callback); - if (callback === true) this.$$entity.datastore.flush(); - return this; -}; - -nglr.Model.prototype.$loadById = function(id, callback) { - this.$$entity.datastore.load(this, id, callback); - return this; -}; - -nglr.Model.prototype.$loadFrom = function(other) { - nglr.Model.copyDirectFields(other, this); - return this; -}; - -nglr.Model.prototype.$saveTo = function(other) { - nglr.Model.copyDirectFields(this, other); - return this; -}; +extend(Model.prototype, { + '$migrate': function() { + merge(this['$$entity']['defaults'], this); + return this; + }, + + '$merge': function(other) { + merge(other, this); + return this; + }, + + '$save': function(callback) { + this['$$entity'].datastore.save(this, callback === true ? undefined : callback); + if (callback === true) this['$$entity'].datastore.flush(); + return this; + }, + + '$delete': function(callback) { + this['$$entity'].datastore.remove(this, callback === true ? undefined : callback); + if (callback === true) this['$$entity'].datastore.flush(); + return this; + }, + + '$loadById': function(id, callback) { + this['$$entity'].datastore.load(this, id, callback); + return this; + }, + + '$loadFrom': function(other) { + Model.copyDirectFields(other, this); + return this; + }, + + '$saveTo': function(other) { + Model.copyDirectFields(this, other); + return this; + } +});
\ No newline at end of file diff --git a/src/Parser.js b/src/Parser.js index 3d72bebf..fe9671af 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -1,4 +1,4 @@ -nglr.Lexer = function(text, parsStrings){ +function Lexer(text, parsStrings){ this.text = text; // UTC dates have 20 characters, we send them through parser this.dateParseLength = parsStrings ? 20 : -1; @@ -6,7 +6,7 @@ nglr.Lexer = function(text, parsStrings){ this.index = 0; }; -nglr.Lexer.OPERATORS = { +Lexer.OPERATORS = { 'null':function(self){return null;}, 'true':function(self){return true;}, 'false':function(self){return false;}, @@ -30,712 +30,715 @@ nglr.Lexer.OPERATORS = { '|':function(self, a,b){return b(self, a);}, '!':function(self, a){return !a;} }; +Lexer.ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; -nglr.Lexer.prototype.peek = function() { - if (this.index + 1 < this.text.length) { - return this.text.charAt(this.index + 1); - } else { - return false; - } -}; - -nglr.Lexer.prototype.parse = function() { - var tokens = this.tokens; - var OPERATORS = nglr.Lexer.OPERATORS; - var canStartRegExp = true; - while (this.index < this.text.length) { - var ch = this.text.charAt(this.index); - if (ch == '"' || ch == "'") { - this.readString(ch); - canStartRegExp = true; - } else if (ch == '(' || ch == '[') { - tokens.push({index:this.index, text:ch}); - this.index++; - } else if (ch == '{' ) { - var peekCh = this.peek(); - if (peekCh == ':' || peekCh == '(') { - tokens.push({index:this.index, text:ch + peekCh}); +Lexer.prototype = { + peek: function() { + if (this.index + 1 < this.text.length) { + return this.text.charAt(this.index + 1); + } else { + return false; + } + }, + + parse: function() { + var tokens = this.tokens; + var OPERATORS = Lexer.OPERATORS; + var canStartRegExp = true; + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + if (ch == '"' || ch == "'") { + this.readString(ch); + canStartRegExp = true; + } else if (ch == '(' || ch == '[') { + tokens.push({index:this.index, text:ch}); this.index++; - } else { + } else if (ch == '{' ) { + var peekCh = this.peek(); + if (peekCh == ':' || peekCh == '(') { + tokens.push({index:this.index, text:ch + peekCh}); + this.index++; + } else { + tokens.push({index:this.index, text:ch}); + } + this.index++; + canStartRegExp = true; + } else if (ch == ')' || ch == ']' || ch == '}' ) { tokens.push({index:this.index, text:ch}); + this.index++; + canStartRegExp = false; + } else if ( ch == ':' || ch == '.' || ch == ',' || ch == ';') { + tokens.push({index:this.index, text:ch}); + this.index++; + canStartRegExp = true; + } else if ( canStartRegExp && ch == '/' ) { + this.readRegexp(); + canStartRegExp = false; + } else if ( this.isNumber(ch) ) { + this.readNumber(); + canStartRegExp = false; + } else if (this.isIdent(ch)) { + this.readIdent(); + canStartRegExp = false; + } else if (this.isWhitespace(ch)) { + this.index++; + } else { + var ch2 = ch + this.peek(); + var fn = OPERATORS[ch]; + var fn2 = OPERATORS[ch2]; + if (fn2) { + tokens.push({index:this.index, text:ch2, fn:fn2}); + this.index += 2; + } else if (fn) { + tokens.push({index:this.index, text:ch, fn:fn}); + this.index += 1; + } else { + throw "Lexer Error: Unexpected next character [" + + this.text.substring(this.index) + + "] in expression '" + this.text + + "' at column '" + (this.index+1) + "'."; + } + canStartRegExp = true; } - this.index++; - canStartRegExp = true; - } else if (ch == ')' || ch == ']' || ch == '}' ) { - tokens.push({index:this.index, text:ch}); - this.index++; - canStartRegExp = false; - } else if ( ch == ':' || ch == '.' || ch == ',' || ch == ';') { - tokens.push({index:this.index, text:ch}); - this.index++; - canStartRegExp = true; - } else if ( canStartRegExp && ch == '/' ) { - this.readRegexp(); - canStartRegExp = false; - } else if ( this.isNumber(ch) ) { - this.readNumber(); - canStartRegExp = false; - } else if (this.isIdent(ch)) { - this.readIdent(); - canStartRegExp = false; - } else if (this.isWhitespace(ch)) { - this.index++; - } else { - var ch2 = ch + this.peek(); - var fn = OPERATORS[ch]; - var fn2 = OPERATORS[ch2]; - if (fn2) { - tokens.push({index:this.index, text:ch2, fn:fn2}); - this.index += 2; - } else if (fn) { - tokens.push({index:this.index, text:ch, fn:fn}); - this.index += 1; + } + return tokens; + }, + + isNumber: function(ch) { + return '0' <= ch && ch <= '9'; + }, + + isWhitespace: function(ch) { + return ch == ' ' || ch == '\r' || ch == '\t' || + ch == '\n' || ch == '\v'; + }, + + isIdent: function(ch) { + return 'a' <= ch && ch <= 'z' || + 'A' <= ch && ch <= 'Z' || + '_' == ch || ch == '$'; + }, + + readNumber: function() { + var number = ""; + var start = this.index; + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + if (ch == '.' || this.isNumber(ch)) { + number += ch; } else { - throw "Lexer Error: Unexpected next character [" + - this.text.substring(this.index) + - "] in expression '" + this.text + - "' at column '" + (this.index+1) + "'."; + break; } - canStartRegExp = true; + this.index++; } - } - return tokens; -}; - -nglr.Lexer.prototype.isNumber = function(ch) { - return '0' <= ch && ch <= '9'; -}; - -nglr.Lexer.prototype.isWhitespace = function(ch) { - return ch == ' ' || ch == '\r' || ch == '\t' || - ch == '\n' || ch == '\v'; -}; - -nglr.Lexer.prototype.isIdent = function(ch) { - return 'a' <= ch && ch <= 'z' || - 'A' <= ch && ch <= 'Z' || - '_' == ch || ch == '$'; -}; - -nglr.Lexer.prototype.readNumber = function() { - var number = ""; - var start = this.index; - while (this.index < this.text.length) { - var ch = this.text.charAt(this.index); - if (ch == '.' || this.isNumber(ch)) { - number += ch; - } else { - break; + number = 1 * number; + this.tokens.push({index:start, text:number, + fn:function(){return number;}}); + }, + + readIdent: function() { + var ident = ""; + var start = this.index; + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + if (ch == '.' || this.isIdent(ch) || this.isNumber(ch)) { + ident += ch; + } else { + break; + } + this.index++; } - this.index++; - } - number = 1 * number; - this.tokens.push({index:start, text:number, - fn:function(){return number;}}); -}; - -nglr.Lexer.prototype.readIdent = function() { - var ident = ""; - var start = this.index; - while (this.index < this.text.length) { - var ch = this.text.charAt(this.index); - if (ch == '.' || this.isIdent(ch) || this.isNumber(ch)) { - ident += ch; - } else { - break; + var fn = Lexer.OPERATORS[ident]; + if (!fn) { + fn = function(self){ + return self.scope.get(ident); + }; + fn.isAssignable = ident; } + this.tokens.push({index:start, text:ident, fn:fn}); + }, + + readString: function(quote) { + var start = this.index; + var dateParseLength = this.dateParseLength; this.index++; - } - var fn = nglr.Lexer.OPERATORS[ident]; - if (!fn) { - fn = function(self){ - return self.scope.get(ident); - }; - fn.isAssignable = ident; - } - this.tokens.push({index:start, text:ident, fn:fn}); -}; -nglr.Lexer.ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; -nglr.Lexer.prototype.readString = function(quote) { - var start = this.index; - var dateParseLength = this.dateParseLength; - this.index++; - var string = ""; - var escape = false; - while (this.index < this.text.length) { - var ch = this.text.charAt(this.index); - if (escape) { - if (ch == 'u') { - var hex = this.text.substring(this.index + 1, this.index + 5); - this.index += 4; - string += String.fromCharCode(parseInt(hex, 16)); - } else { - var rep = nglr.Lexer.ESCAPE[ch]; - if (rep) { - string += rep; + var string = ""; + var escape = false; + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + if (escape) { + if (ch == 'u') { + var hex = this.text.substring(this.index + 1, this.index + 5); + this.index += 4; + string += String.fromCharCode(parseInt(hex, 16)); } else { - string += ch; + var rep = Lexer.ESCAPE[ch]; + if (rep) { + string += rep; + } else { + string += ch; + } } + escape = false; + } else if (ch == '\\') { + escape = true; + } else if (ch == quote) { + this.index++; + this.tokens.push({index:start, text:string, + fn:function(){ + return (string.length == dateParseLength) ? + angular['String']['toDate'](string) : string; + }}); + return; + } else { + string += ch; } - escape = false; - } else if (ch == '\\') { - escape = true; - } else if (ch == quote) { this.index++; - this.tokens.push({index:start, text:string, - fn:function(){ - return (string.length == dateParseLength) ? - angular.String.toDate(string) : string; - }}); - return; - } else { - string += ch; } + throw "Lexer Error: Unterminated quote [" + + this.text.substring(start) + "] starting at column '" + + (start+1) + "' in expression '" + this.text + "'."; + }, + + readRegexp: function(quote) { + var start = this.index; this.index++; - } - throw "Lexer Error: Unterminated quote [" + - this.text.substring(start) + "] starting at column '" + - (start+1) + "' in expression '" + this.text + "'."; -}; - -nglr.Lexer.prototype.readRegexp = function(quote) { - var start = this.index; - this.index++; - var regexp = ""; - var escape = false; - while (this.index < this.text.length) { - var ch = this.text.charAt(this.index); - if (escape) { - regexp += ch; - escape = false; - } else if (ch === '\\') { - regexp += ch; - escape = true; - } else if (ch === '/') { - this.index++; - var flags = ""; - if (this.isIdent(this.text.charAt(this.index))) { - this.readIdent(); - flags = this.tokens.pop().text; + var regexp = ""; + var escape = false; + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + if (escape) { + regexp += ch; + escape = false; + } else if (ch === '\\') { + regexp += ch; + escape = true; + } else if (ch === '/') { + this.index++; + var flags = ""; + if (this.isIdent(this.text.charAt(this.index))) { + this.readIdent(); + flags = this.tokens.pop().text; + } + var compiledRegexp = new RegExp(regexp, flags); + this.tokens.push({index:start, text:regexp, flags:flags, + fn:function(){return compiledRegexp;}}); + return; + } else { + regexp += ch; } - var compiledRegexp = new RegExp(regexp, flags); - this.tokens.push({index:start, text:regexp, flags:flags, - fn:function(){return compiledRegexp;}}); - return; - } else { - regexp += ch; + this.index++; } - this.index++; + throw "Lexer Error: Unterminated RegExp [" + + this.text.substring(start) + "] starting at column '" + + (start+1) + "' in expression '" + this.text + "'."; } - throw "Lexer Error: Unterminated RegExp [" + - this.text.substring(start) + "] starting at column '" + - (start+1) + "' in expression '" + this.text + "'."; }; +///////////////////////////////////////// -nglr.Parser = function(text, parseStrings){ +function Parser(text, parseStrings){ this.text = text; - this.tokens = new nglr.Lexer(text, parseStrings).parse(); + this.tokens = new Lexer(text, parseStrings).parse(); this.index = 0; }; -nglr.Parser.ZERO = function(){ +Parser.ZERO = function(){ return 0; }; -nglr.Parser.prototype.error = function(msg, token) { - throw "Token '" + token.text + - "' is " + msg + " at column='" + - (token.index + 1) + "' of expression '" + - this.text + "' starting at '" + this.text.substring(token.index) + "'."; -}; - -nglr.Parser.prototype.peekToken = function() { - if (this.tokens.length === 0) - throw "Unexpected end of expression: " + this.text; - return this.tokens[0]; -}; - -nglr.Parser.prototype.peek = function(e1, e2, e3, e4) { - var tokens = this.tokens; - if (tokens.length > 0) { - var token = tokens[0]; - var t = token.text; - if (t==e1 || t==e2 || t==e3 || t==e4 || - (!e1 && !e2 && !e3 && !e4)) { +Parser.prototype = { + error: function(msg, token) { + throw "Token '" + token.text + + "' is " + msg + " at column='" + + (token.index + 1) + "' of expression '" + + this.text + "' starting at '" + this.text.substring(token.index) + "'."; + }, + + peekToken: function() { + if (this.tokens.length === 0) + throw "Unexpected end of expression: " + this.text; + return this.tokens[0]; + }, + + peek: function(e1, e2, e3, e4) { + var tokens = this.tokens; + if (tokens.length > 0) { + var token = tokens[0]; + var t = token.text; + if (t==e1 || t==e2 || t==e3 || t==e4 || + (!e1 && !e2 && !e3 && !e4)) { + return token; + } + } + return false; + }, + + expect: function(e1, e2, e3, e4){ + var token = this.peek(e1, e2, e3, e4); + if (token) { + this.tokens.shift(); + this.currentToken = token; return token; } - } - return false; -}; - -nglr.Parser.prototype.expect = function(e1, e2, e3, e4){ - var token = this.peek(e1, e2, e3, e4); - if (token) { - this.tokens.shift(); - this.currentToken = token; - return token; - } - return false; -}; - -nglr.Parser.prototype.consume = function(e1){ - if (!this.expect(e1)) { - var token = this.peek(); - throw "Expecting '" + e1 + "' at column '" + - (token.index+1) + "' in '" + - this.text + "' got '" + - this.text.substring(token.index) + "'."; - } -}; - -nglr.Parser.prototype._unary = function(fn, parse) { - var right = parse.apply(this); - return function(self) { - return fn(self, right(self)); - }; -}; - -nglr.Parser.prototype._binary = function(left, fn, parse) { - var right = parse.apply(this); - return function(self) { - return fn(self, left(self), right(self)); - }; -}; - -nglr.Parser.prototype.hasTokens = function () { - return this.tokens.length > 0; -}; - -nglr.Parser.prototype.assertAllConsumed = function(){ - if (this.tokens.length !== 0) { - throw "Did not understand '" + this.text.substring(this.tokens[0].index) + - "' while evaluating '" + this.text + "'."; - } -}; - -nglr.Parser.prototype.statements = function(){ - var statements = []; - while(true) { - if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']')) - statements.push(this.filterChain()); - if (!this.expect(';')) { - return function (self){ - var value; - for ( var i = 0; i < statements.length; i++) { - var statement = statements[i]; - if (statement) - value = statement(self); - } - return value; - }; + return false; + }, + + consume: function(e1){ + if (!this.expect(e1)) { + var token = this.peek(); + throw "Expecting '" + e1 + "' at column '" + + (token.index+1) + "' in '" + + this.text + "' got '" + + this.text.substring(token.index) + "'."; } - } -}; - -nglr.Parser.prototype.filterChain = function(){ - var left = this.expression(); - var token; - while(true) { - if ((token = this.expect('|'))) { - left = this._binary(left, token.fn, this.filter); - } else { - return left; + }, + + _unary: function(fn, right) { + return function(self) { + return fn(self, right(self)); + }; + }, + + _binary: function(left, fn, right) { + return function(self) { + return fn(self, left(self), right(self)); + }; + }, + + hasTokens: function () { + return this.tokens.length > 0; + }, + + assertAllConsumed: function(){ + if (this.tokens.length !== 0) { + throw "Did not understand '" + this.text.substring(this.tokens[0].index) + + "' while evaluating '" + this.text + "'."; } - } -}; - -nglr.Parser.prototype.filter = function(){ - return this._pipeFunction(angular.filter); -}; - -nglr.Parser.prototype.validator = function(){ - return this._pipeFunction(angular.validator); -}; - -nglr.Parser.prototype._pipeFunction = function(fnScope){ - var fn = this.functionIdent(fnScope); - var argsFn = []; - var token; - while(true) { - if ((token = this.expect(':'))) { - argsFn.push(this.expression()); - } else { - var fnInvoke = function(self, input){ - var args = [input]; - for ( var i = 0; i < argsFn.length; i++) { - args.push(argsFn[i](self)); - } - return fn.apply(self, args); - }; - return function(){ - return fnInvoke; - }; + }, + + statements: function(){ + var statements = []; + while(true) { + if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']')) + statements.push(this.filterChain()); + if (!this.expect(';')) { + return function (self){ + var value; + for ( var i = 0; i < statements.length; i++) { + var statement = statements[i]; + if (statement) + value = statement(self); + } + return value; + }; + } } - } -}; - -nglr.Parser.prototype.expression = function(){ - return this.throwStmt(); -}; - -nglr.Parser.prototype.throwStmt = function(){ - if (this.expect('throw')) { - var throwExp = this.assignment(); - return function (self) { - throw throwExp(self); - }; - } else { - return this.assignment(); - } -}; - -nglr.Parser.prototype.assignment = function(){ - var left = this.logicalOR(); - var token; - if (token = this.expect('=')) { - if (!left.isAssignable) { - throw "Left hand side '" + - this.text.substring(0, token.index) + "' of assignment '" + - this.text.substring(token.index) + "' is not assignable."; - } - var ident = function(){return left.isAssignable;}; - return this._binary(ident, token.fn, this.logicalOR); - } else { - return left; - } -}; - -nglr.Parser.prototype.logicalOR = function(){ - var left = this.logicalAND(); - var token; - while(true) { - if ((token = this.expect('||'))) { - left = this._binary(left, token.fn, this.logicalAND); + }, + + filterChain: function(){ + var left = this.expression(); + var token; + while(true) { + if ((token = this.expect('|'))) { + left = this._binary(left, token.fn, this.filter()); + } else { + return left; + } + } + }, + + filter: function(){ + return this._pipeFunction(angular['filter']); + }, + + validator: function(){ + return this._pipeFunction(angular['validator']); + }, + + _pipeFunction: function(fnScope){ + var fn = this.functionIdent(fnScope); + var argsFn = []; + var token; + while(true) { + if ((token = this.expect(':'))) { + argsFn.push(this.expression()); + } else { + var fnInvoke = function(self, input){ + var args = [input]; + for ( var i = 0; i < argsFn.length; i++) { + args.push(argsFn[i](self)); + } + return fn.apply(self, args); + }; + return function(){ + return fnInvoke; + }; + } + } + }, + + expression: function(){ + return this.throwStmt(); + }, + + throwStmt: function(){ + if (this.expect('throw')) { + var throwExp = this.assignment(); + return function (self) { + throw throwExp(self); + }; } else { - return left; + return this.assignment(); } - } -}; - -nglr.Parser.prototype.logicalAND = function(){ - var left = this.negated(); - var token; - while(true) { - if ((token = this.expect('&&'))) { - left = this._binary(left, token.fn, this.negated); + }, + + assignment: function(){ + var left = this.logicalOR(); + var token; + if (token = this.expect('=')) { + if (!left.isAssignable) { + throw "Left hand side '" + + this.text.substring(0, token.index) + "' of assignment '" + + this.text.substring(token.index) + "' is not assignable."; + } + var ident = function(){return left.isAssignable;}; + return this._binary(ident, token.fn, this.logicalOR()); } else { - return left; + return left; } - } -}; - -nglr.Parser.prototype.negated = function(){ - var token; - if (token = this.expect('!')) { - return this._unary(token.fn, this.equality); - } else { - return this.equality(); - } -}; - -nglr.Parser.prototype.equality = function(){ - var left = this.relational(); - var token; - while(true) { - if ((token = this.expect('==','!='))) { - left = this._binary(left, token.fn, this.relational); + }, + + logicalOR: function(){ + var left = this.logicalAND(); + var token; + while(true) { + if ((token = this.expect('||'))) { + left = this._binary(left, token.fn, this.logicalAND()); + } else { + return left; + } + } + }, + + logicalAND: function(){ + var left = this.negated(); + var token; + while(true) { + if ((token = this.expect('&&'))) { + left = this._binary(left, token.fn, this.negated()); + } else { + return left; + } + } + }, + + negated: function(){ + var token; + if (token = this.expect('!')) { + return this._unary(token.fn, this.assignment()); } else { - return left; + return this.equality(); } - } -}; - -nglr.Parser.prototype.relational = function(){ - var left = this.additive(); - var token; - while(true) { - if ((token = this.expect('<', '>', '<=', '>='))) { - left = this._binary(left, token.fn, this.additive); + }, + + equality: function(){ + var left = this.relational(); + var token; + while(true) { + if ((token = this.expect('==','!='))) { + left = this._binary(left, token.fn, this.relational()); + } else { + return left; + } + } + }, + + relational: function(){ + var left = this.additive(); + var token; + while(true) { + if ((token = this.expect('<', '>', '<=', '>='))) { + left = this._binary(left, token.fn, this.additive()); + } else { + return left; + } + } + }, + + additive: function(){ + var left = this.multiplicative(); + var token; + while(token = this.expect('+','-')) { + left = this._binary(left, token.fn, this.multiplicative()); + } + return left; + }, + + multiplicative: function(){ + var left = this.unary(); + var token; + while(token = this.expect('*','/','%')) { + left = this._binary(left, token.fn, this.unary()); + } + return left; + }, + + unary: function(){ + var token; + if (this.expect('+')) { + return this.primary(); + } else if (token = this.expect('-')) { + return this._binary(Parser.ZERO, token.fn, this.multiplicative()); } else { - return left; + return this.primary(); } - } -}; - -nglr.Parser.prototype.additive = function(){ - var left = this.multiplicative(); - var token; - while(token = this.expect('+','-')) { - left = this._binary(left, token.fn, this.multiplicative); - } - return left; -}; - -nglr.Parser.prototype.multiplicative = function(){ - var left = this.unary(); - var token; - while(token = this.expect('*','/','%')) { - left = this._binary(left, token.fn, this.unary); - } - return left; -}; - -nglr.Parser.prototype.unary = function(){ - var token; - if (this.expect('+')) { - return this.primary(); - } else if (token = this.expect('-')) { - return this._binary(nglr.Parser.ZERO, token.fn, this.multiplicative); - } else { - return this.primary(); - } -}; - -nglr.Parser.prototype.functionIdent = function(fnScope) { - var token = this.expect(); - var element = token.text.split('.'); - var instance = fnScope; - var key; - for ( var i = 0; i < element.length; i++) { - key = element[i]; - if (instance) - instance = instance[key]; - } - if (typeof instance != 'function') { - throw "Function '" + token.text + "' at column '" + - (token.index+1) + "' in '" + this.text + "' is not defined."; - } - return instance; -}; - -nglr.Parser.prototype.primary = function() { - var primary; - if (this.expect('(')) { - var expression = this.filterChain(); - this.consume(')'); - primary = expression; - } else if (this.expect('[')) { - primary = this.arrayDeclaration(); - } else if (this.expect('{')) { - primary = this.object(); - } else if (this.expect('{:')) { - primary = this.closure(false); - } else if (this.expect('{(')) { - primary = this.closure(true); - } else { + }, + + functionIdent: function(fnScope) { var token = this.expect(); - primary = token.fn; - if (!primary) { - this.error("not a primary expression", token); + var element = token.text.split('.'); + var instance = fnScope; + var key; + for ( var i = 0; i < element.length; i++) { + key = element[i]; + if (instance) + instance = instance[key]; } - } - var next; - while (next = this.expect('(', '[', '.')) { - if (next.text === '(') { - primary = this.functionCall(primary); - } else if (next.text === '[') { - primary = this.objectIndex(primary); - } else if (next.text === '.') { - primary = this.fieldAccess(primary); + if (typeof instance != 'function') { + throw "Function '" + token.text + "' at column '" + + (token.index+1) + "' in '" + this.text + "' is not defined."; + } + return instance; + }, + + primary: function() { + var primary; + if (this.expect('(')) { + var expression = this.filterChain(); + this.consume(')'); + primary = expression; + } else if (this.expect('[')) { + primary = this.arrayDeclaration(); + } else if (this.expect('{')) { + primary = this.object(); + } else if (this.expect('{:')) { + primary = this.closure(false); + } else if (this.expect('{(')) { + primary = this.closure(true); } else { - throw "IMPOSSIBLE"; + var token = this.expect(); + primary = token.fn; + if (!primary) { + this.error("not a primary expression", token); + } } - } - return primary; -}; - -nglr.Parser.prototype.closure = function(hasArgs) { - var args = []; - if (hasArgs) { - if (!this.expect(')')) { - args.push(this.expect().text); - while(this.expect(',')) { + var next; + while (next = this.expect('(', '[', '.')) { + if (next.text === '(') { + primary = this.functionCall(primary); + } else if (next.text === '[') { + primary = this.objectIndex(primary); + } else if (next.text === '.') { + primary = this.fieldAccess(primary); + } else { + throw "IMPOSSIBLE"; + } + } + return primary; + }, + + closure: function(hasArgs) { + var args = []; + if (hasArgs) { + if (!this.expect(')')) { args.push(this.expect().text); + while(this.expect(',')) { + args.push(this.expect().text); + } + this.consume(')'); } - this.consume(')'); + this.consume(":"); } - this.consume(":"); - } - var statements = this.statements(); - this.consume("}"); - return function(self){ - return function($){ - var scope = new nglr.Scope(self.scope.state); - scope.set('$', $); - for ( var i = 0; i < args.length; i++) { - scope.set(args[i], arguments[i]); + var statements = this.statements(); + this.consume("}"); + return function(self){ + return function($){ + var scope = new Scope(self.scope.state); + scope.set('$', $); + for ( var i = 0; i < args.length; i++) { + scope.set(args[i], arguments[i]); + } + return statements({scope:scope}); + }; + }; + }, + + fieldAccess: function(object) { + var field = this.expect().text; + var fn = function (self){ + return Scope.getter(object(self), field); + }; + fn.isAssignable = field; + return fn; + }, + + objectIndex: function(obj) { + var indexFn = this.expression(); + this.consume(']'); + if (this.expect('=')) { + var rhs = this.expression(); + return function (self){ + return obj(self)[indexFn(self)] = rhs(self); + }; + } else { + return function (self){ + var o = obj(self); + var i = indexFn(self); + return (o) ? o[i] : undefined; + }; + } + }, + + functionCall: function(fn) { + var argsFn = []; + if (this.peekToken().text != ')') { + do { + argsFn.push(this.expression()); + } while (this.expect(',')); + } + this.consume(')'); + return function (self){ + var args = []; + for ( var i = 0; i < argsFn.length; i++) { + args.push(argsFn[i](self)); + } + var fnPtr = fn(self); + if (typeof fnPtr === 'function') { + return fnPtr.apply(self, args); + } else { + throw "Expression '" + fn.isAssignable + "' is not a function."; } - return statements({scope:scope}); }; - }; -}; - -nglr.Parser.prototype.fieldAccess = function(object) { - var field = this.expect().text; - var fn = function (self){ - return nglr.Scope.getter(object(self), field); - }; - fn.isAssignable = field; - return fn; -}; - -nglr.Parser.prototype.objectIndex = function(obj) { - var indexFn = this.expression(); - this.consume(']'); - if (this.expect('=')) { - var rhs = this.expression(); + }, + + // This is used with json array declaration + arrayDeclaration: function () { + var elementFns = []; + if (this.peekToken().text != ']') { + do { + elementFns.push(this.expression()); + } while (this.expect(',')); + } + this.consume(']'); return function (self){ - return obj(self)[indexFn(self)] = rhs(self); + var array = []; + for ( var i = 0; i < elementFns.length; i++) { + array.push(elementFns[i](self)); + } + return array; }; - } else { + }, + + object: function () { + var keyValues = []; + if (this.peekToken().text != '}') { + do { + var key = this.expect().text; + this.consume(":"); + var value = this.expression(); + keyValues.push({key:key, value:value}); + } while (this.expect(',')); + } + this.consume('}'); return function (self){ - var o = obj(self); - var i = indexFn(self); - return (o) ? o[i] : undefined; + var object = {}; + for ( var i = 0; i < keyValues.length; i++) { + var keyValue = keyValues[i]; + var value = keyValue.value(self); + object[keyValue.key] = value; + } + return object; }; - } -}; - -nglr.Parser.prototype.functionCall = function(fn) { - var argsFn = []; - if (this.peekToken().text != ')') { - do { - argsFn.push(this.expression()); - } while (this.expect(',')); - } - this.consume(')'); - return function (self){ - var args = []; - for ( var i = 0; i < argsFn.length; i++) { - args.push(argsFn[i](self)); + }, + + entityDeclaration: function () { + var decl = []; + while(this.hasTokens()) { + decl.push(this.entityDecl()); + if (!this.expect(';')) { + this.assertAllConsumed(); + } } - var fnPtr = fn(self); - if (typeof fnPtr === 'function') { - return fnPtr.apply(self, args); - } else { - throw "Expression '" + fn.isAssignable + "' is not a function."; + return function (self){ + var code = ""; + for ( var i = 0; i < decl.length; i++) { + code += decl[i](self); + } + return code; + }; + }, + + entityDecl: function () { + var entity = this.expect().text; + var instance; + var defaults; + if (this.expect('=')) { + instance = entity; + entity = this.expect().text; } - }; -}; - -// This is used with json array declaration -nglr.Parser.prototype.arrayDeclaration = function () { - var elementFns = []; - if (this.peekToken().text != ']') { - do { - elementFns.push(this.expression()); - } while (this.expect(',')); - } - this.consume(']'); - return function (self){ - var array = []; - for ( var i = 0; i < elementFns.length; i++) { - array.push(elementFns[i](self)); - } - return array; - }; -}; - -nglr.Parser.prototype.object = function () { - var keyValues = []; - if (this.peekToken().text != '}') { - do { - var key = this.expect().text; - this.consume(":"); - var value = this.expression(); - keyValues.push({key:key, value:value}); - } while (this.expect(',')); - } - this.consume('}'); - return function (self){ - var object = {}; - for ( var i = 0; i < keyValues.length; i++) { - var keyValue = keyValues[i]; - var value = keyValue.value(self); - object[keyValue.key] = value; - } - return object; - }; -}; - -nglr.Parser.prototype.entityDeclaration = function () { - var decl = []; - while(this.hasTokens()) { - decl.push(this.entityDecl()); - if (!this.expect(';')) { - this.assertAllConsumed(); + if (this.expect(':')) { + defaults = this.primary()(null); } - } - return function (self){ - var code = ""; - for ( var i = 0; i < decl.length; i++) { - code += decl[i](self); + return function(self) { + var Entity = self.datastore.entity(entity, defaults); + self.scope.set(entity, Entity); + if (instance) { + var document = Entity(); + document['$$anchor'] = instance; + self.scope.set(instance, document); + return "$anchor." + instance + ":{" + + instance + "=" + entity + ".load($anchor." + instance + ");" + + instance + ".$$anchor=" + angular['String']['quote'](instance) + ";" + + "};"; + } else { + return ""; + } + }; + }, + + watch: function () { + var decl = []; + while(this.hasTokens()) { + decl.push(this.watchDecl()); + if (!this.expect(';')) { + this.assertAllConsumed(); + } } - return code; - }; -}; - -nglr.Parser.prototype.entityDecl = function () { - var entity = this.expect().text; - var instance; - var defaults; - if (this.expect('=')) { - instance = entity; - entity = this.expect().text; - } - if (this.expect(':')) { - defaults = this.primary()(null); - } - return function(self) { - var datastore = self.scope.get('$datastore'); - var Entity = datastore.entity(entity, defaults); - self.scope.set(entity, Entity); - if (instance) { - var document = Entity(); - document.$$anchor = instance; - self.scope.set(instance, document); - return "$anchor." + instance + ":{" + - instance + "=" + entity + ".load($anchor." + instance + ");" + - instance + ".$$anchor=" + angular.String.quote(instance) + ";" + - "};"; + this.assertAllConsumed(); + return function (self){ + for ( var i = 0; i < decl.length; i++) { + var d = decl[i](self); + self.addListener(d.name, d.fn); + } + }; + }, + + watchDecl: function () { + var anchorName = this.expect().text; + this.consume(":"); + var expression; + if (this.peekToken().text == '{') { + this.consume("{"); + expression = this.statements(); + this.consume("}"); } else { - return ""; - } - }; -}; - -nglr.Parser.prototype.watch = function () { - var decl = []; - while(this.hasTokens()) { - decl.push(this.watchDecl()); - if (!this.expect(';')) { - this.assertAllConsumed(); - } - } - this.assertAllConsumed(); - return function (self){ - for ( var i = 0; i < decl.length; i++) { - var d = decl[i](self); - self.addListener(d.name, d.fn); + expression = this.expression(); } - }; -}; - -nglr.Parser.prototype.watchDecl = function () { - var anchorName = this.expect().text; - this.consume(":"); - var expression; - if (this.peekToken().text == '{') { - this.consume("{"); - expression = this.statements(); - this.consume("}"); - } else { - expression = this.expression(); + return function(self) { + return {name:anchorName, fn:expression}; + }; } - return function(self) { - return {name:anchorName, fn:expression}; - }; }; diff --git a/src/Scope.js b/src/Scope.js index 45dd15a4..7e477ec5 100644 --- a/src/Scope.js +++ b/src/Scope.js @@ -1,6 +1,4 @@ -// Copyright (C) 2009 BRAT Tech LLC - -nglr.Scope = function(initialState, name) { +function Scope(initialState, name) { this.widgets = []; this.watchListeners = {}; this.name = name; @@ -8,39 +6,14 @@ nglr.Scope = function(initialState, name) { var State = function(){}; State.prototype = initialState; this.state = new State(); - this.state.$parent = initialState; + this.state['$parent'] = initialState; if (name == "ROOT") { - this.state.$root = this.state; + this.state['$root'] = this.state; } }; -nglr.Scope.expressionCache = {}; - -nglr.Scope.prototype.updateView = function() { - var self = this; - this.fireWatchers(); - _.each(this.widgets, function(widget){ - self.evalWidget(widget, "", {}, function(){ - this.updateView(self); - }); - }); -}; - -nglr.Scope.prototype.addWidget = function(controller) { - if (controller) this.widgets.push(controller); -}; - -nglr.Scope.prototype.isProperty = function(exp) { - for ( var i = 0; i < exp.length; i++) { - var ch = exp.charAt(i); - if (ch!='.' && !nglr.Lexer.prototype.isIdent(ch)) { - return false; - } - } - return true; -}; - -nglr.Scope.getter = function(instance, path) { +Scope.expressionCache = {}; +Scope.getter = function(instance, path) { if (!path) return instance; var element = path.split('.'); var key; @@ -55,7 +28,7 @@ nglr.Scope.getter = function(instance, path) { instance = instance[key]; } if (_.isUndefined(instance) && key.charAt(0) == '$') { - var type = angular.Global.typeOf(lastInstance); + var type = angular['Global']['typeOf'](lastInstance); type = angular[type.charAt(0).toUpperCase()+type.substring(1)]; var fn = type ? type[[key.substring(1)]] : undefined; if (fn) { @@ -64,135 +37,164 @@ nglr.Scope.getter = function(instance, path) { } } } - if (typeof instance === 'function' && !instance.$$factory) { - return nglr.bind(lastInstance, instance); + if (typeof instance === 'function' && !instance['$$factory']) { + return bind(lastInstance, instance); } return instance; }; -nglr.Scope.prototype.get = function(path) { - return nglr.Scope.getter(this.state, path); -}; - -nglr.Scope.prototype.set = function(path, value) { - var element = path.split('.'); - var instance = this.state; - for ( var i = 0; element.length > 1; i++) { - var key = element.shift(); - var newInstance = instance[key]; - if (!newInstance) { - newInstance = {}; - instance[key] = newInstance; +Scope.prototype = { + updateView: function() { + var self = this; + this.fireWatchers(); + _.each(this.widgets, function(widget){ + self.evalWidget(widget, "", {}, function(){ + this.updateView(self); + }); + }); + }, + + addWidget: function(controller) { + if (controller) this.widgets.push(controller); + }, + + isProperty: function(exp) { + for ( var i = 0; i < exp.length; i++) { + var ch = exp.charAt(i); + if (ch!='.' && !Lexer.prototype.isIdent(ch)) { + return false; + } } - instance = newInstance; - } - instance[element.shift()] = value; - return value; -}; - -nglr.Scope.prototype.setEval = function(expressionText, value) { - this.eval(expressionText + "=" + nglr.toJson(value)); -}; - -nglr.Scope.prototype.eval = function(expressionText, context) { - var expression = nglr.Scope.expressionCache[expressionText]; - if (!expression) { - var parser = new nglr.Parser(expressionText); - expression = parser.statements(); - parser.assertAllConsumed(); - nglr.Scope.expressionCache[expressionText] = expression; - } - context = context || {}; - context.scope = this; - return expression(context); -}; - -//TODO: Refactor. This function needs to be an execution closure for widgets -// move to widgets -// remove expression, just have inner closure. -nglr.Scope.prototype.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'); + 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 element = path.split('.'); + var instance = this.state; + for ( var i = 0; element.length > 1; i++) { + var key = element.shift(); + var newInstance = instance[key]; + if (!newInstance) { + newInstance = {}; + instance[key] = newInstance; + } + instance = newInstance; } - if (onSuccess) { - value = onSuccess.apply(widget, [value]); + instance[element.shift()] = value; + return value; + }, + + setEval: function(expressionText, value) { + this.eval(expressionText + "=" + toJson(value)); + }, + + eval: function(expressionText, context) { + log('Scope.eval', expressionText); + var expression = Scope.expressionCache[expressionText]; + if (!expression) { + var parser = new Parser(expressionText); + expression = parser.statements(); + parser.assertAllConsumed(); + Scope.expressionCache[expressionText] = expression; } - return true; - } catch (e){ - console.error('Eval Widget Error:', e); - var jsonError = nglr.toJson(e, true); - widget.hasError = true; - jQuery(widget.view). - addClass('ng-exception'). - attr('ng-error', jsonError); - if (onFailure) { - onFailure.apply(widget, [e, jsonError]); + context = context || {}; + context.scope = this; + return expression(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){ + error('Eval Widget Error:', e); + var jsonError = toJson(e, true); + widget.hasError = true; + jQuery(widget.view). + addClass('ng-exception'). + attr('ng-error', jsonError); + if (onFailure) { + onFailure.apply(widget, [e, jsonError]); + } + return false; } - return false; - } -}; - -nglr.Scope.prototype.validate = function(expressionText, value) { - var expression = nglr.Scope.expressionCache[expressionText]; - if (!expression) { - expression = new nglr.Parser(expressionText).validator(); - nglr.Scope.expressionCache[expressionText] = expression; - } - var self = {scope:this}; - return expression(self)(self, value); -}; - -nglr.Scope.prototype.entity = function(entityDeclaration) { - var expression = new nglr.Parser(entityDeclaration).entityDeclaration(); - return expression({scope:this}); -}; - -nglr.Scope.prototype.markInvalid = function(widget) { - this.state.$invalidWidgets.push(widget); -}; - -nglr.Scope.prototype.watch = function(declaration) { - var self = this; - new nglr.Parser(declaration).watch()({ - scope:this, - addListener:function(watch, exp){ - self.addWatchListener(watch, function(n,o){ - try { - return exp({scope:self}, n, o); - } catch(e) { - nglr.alert(e); - } - }); + }, + + validate: function(expressionText, value) { + var expression = Scope.expressionCache[expressionText]; + if (!expression) { + expression = new Parser(expressionText).validator(); + Scope.expressionCache[expressionText] = expression; } - }); -}; - -nglr.Scope.prototype.addWatchListener = function(watchExpression, listener) { - var watcher = this.watchListeners[watchExpression]; - if (!watcher) { - watcher = {listeners:[], expression:watchExpression}; - this.watchListeners[watchExpression] = watcher; - } - watcher.listeners.push(listener); -}; - -nglr.Scope.prototype.fireWatchers = function() { - var self = this; - var fired = false; - jQuery.each(this.watchListeners, function(name, watcher) { - var value = self.eval(watcher.expression); - if (value !== watcher.lastValue) { - jQuery.each(watcher.listeners, function(i, listener){ - listener(value, watcher.lastValue); - fired = true; - }); - watcher.lastValue = value; + var self = {scope:this}; + return expression(self)(self, value); + }, + + entity: function(entityDeclaration, datastore) { + var expression = new Parser(entityDeclaration).entityDeclaration(); + return expression({scope:this, datastore:datastore}); + }, + + 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) { + var watcher = this.watchListeners[watchExpression]; + if (!watcher) { + watcher = {listeners:[], expression:watchExpression}; + this.watchListeners[watchExpression] = watcher; } - }); - return fired; -}; + watcher.listeners.push(listener); + }, + + fireWatchers: function() { + var self = this; + var 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; + } +};
\ No newline at end of file diff --git a/src/Server.js b/src/Server.js index 94b0cc10..5c4ec3c6 100644 --- a/src/Server.js +++ b/src/Server.js @@ -1,6 +1,4 @@ -// Copyright (C) 2008,2009 BRAT Tech LLC - -nglr.Server = function(url, getScript) { +function Server(url, getScript) { this.url = url; this.nextId = 0; this.getScript = getScript; @@ -8,51 +6,52 @@ nglr.Server = function(url, getScript) { this.maxSize = 1800; }; -nglr.Server.prototype.base64url = function(txt) { - return Base64.encode(txt); -}; - -nglr.Server.prototype.request = function(method, url, request, callback) { - var requestId = this.uuid + (this.nextId++); - nglr[requestId] = function(response) { - delete nglr[requestId]; - callback(200, response); - }; - var payload = {u:url, m:method, p:request}; - payload = this.base64url(nglr.toJson(payload)); - var totalPockets = Math.ceil(payload.length / this.maxSize); - var baseUrl = this.url + "/$/" + requestId + "/" + totalPockets + "/"; - for ( var pocketNo = 0; pocketNo < totalPockets; pocketNo++) { - var pocket = payload.substr(pocketNo * this.maxSize, this.maxSize); - this.getScript(baseUrl + (pocketNo+1) + "?h=" + pocket, nglr.noop); +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); + } } }; -nglr.FrameServer = function(frame) { +function FrameServer(frame) { this.frame = frame; }; -nglr.FrameServer.PREFIX = "$DATASET:"; +FrameServer.PREFIX = "$DATASET:"; -nglr.FrameServer.prototype = { +FrameServer.prototype = { read:function(){ - this.data = nglr.fromJson(this.frame.name.substr(nglr.FrameServer.PREFIX.length)); + this.data = fromJson(this.frame.name.substr(FrameServer.PREFIX.length)); }, write:function(){ - this.frame.name = nglr.FrameServer.PREFIX + nglr.toJson(this.data); + this.frame.name = FrameServer.PREFIX + toJson(this.data); }, request: function(method, url, request, callback) { - //alert(method + " " + url + " " + nglr.toJson(request) + " " + nglr.toJson(callback)); + //alert(method + " " + url + " " + toJson(request) + " " + toJson(callback)); } }; -nglr.VisualServer = function(delegate, status, update) { +function VisualServer(delegate, status, update) { this.delegate = delegate; this.update = update; this.status = status; }; -nglr.VisualServer.prototype = { +VisualServer.prototype = { request:function(method, url, request, callback) { var self = this; this.status.beginRequest(request); @@ -61,7 +60,7 @@ nglr.VisualServer.prototype = { try { callback.apply(this, arguments); } catch (e) { - nglr.alert(nglr.toJson(e)); + alert(toJson(e)); } self.update(); }); diff --git a/src/Users.js b/src/Users.js index c0c15848..f81507f4 100644 --- a/src/Users.js +++ b/src/Users.js @@ -1,36 +1,35 @@ -// Copyright (C) 2008,2009 BRAT Tech LLC -nglr.Users = function(server, controlBar) { +function Users(server, controlBar) { this.server = server; this.controlBar = controlBar; }; -nglr.Users.prototype = { - fetchCurrentUser:function(callback) { +extend(Users.prototype, { + 'fetchCurrentUser':function(callback) { var self = this; this.server.request("GET", "/account.json", {}, function(code, response){ - self.current = response.user; + self['current'] = response['user']; callback(response.user); }); }, - logout: function(callback) { + 'logout': function(callback) { var self = this; this.controlBar.logout(function(){ - delete self.current; - (callback||nglr.noop)(); + delete self['current']; + (callback||noop)(); }); }, - login: function(callback) { + 'login': function(callback) { var self = this; this.controlBar.login(function(){ - self.fetchCurrentUser(function(){ - (callback||nglr.noop)(); + self['fetchCurrentUser'](function(){ + (callback||noop)(); }); }); }, - notAuthorized: function(){ + 'notAuthorized': function(){ this.controlBar.notAuthorized(); } -}; +}); diff --git a/src/Validators.js b/src/Validators.js index 94cb1d52..5549ee39 100644 --- a/src/Validators.js +++ b/src/Validators.js @@ -1,80 +1,80 @@ -// Copyright (C) 2009 BRAT Tech LLC - -angular.validator.regexp = function(value, regexp, msg) { - if (!value.match(regexp)) { - return msg || - "Value does not match expected format " + regexp + "."; - } else { - return null; - } -}; - -angular.validator.number = function(value, min, max) { - var num = 1 * value; - if (num == value) { - if (typeof min != 'undefined' && num < min) { - return "Value can not be less than " + min + "."; +foreach({ + 'regexp': function(value, regexp, msg) { + if (!value.match(regexp)) { + return msg || + "Value does not match expected format " + regexp + "."; + } else { + return null; } - if (typeof min != 'undefined' && num > max) { - return "Value can not be greater than " + max + "."; + }, + + 'number': function(value, min, max) { + var num = 1 * value; + if (num == value) { + if (typeof min != 'undefined' && num < min) { + return "Value can not be less than " + min + "."; + } + if (typeof min != 'undefined' && num > max) { + return "Value can not be greater than " + max + "."; + } + return null; + } else { + return "Value is not a number."; + } + }, + + 'integer': function(value, min, max) { + var number = angularValidator['number'](value, min, max); + if (number === null && value != Math.round(value)) { + return "Value is not a whole number."; + } + return number; + }, + + 'date': function(value, min, max) { + if (value.match(/^\d\d?\/\d\d?\/\d\d\d\d$/)) { + return null; + } + return "Value is not a date. (Expecting format: 12/31/2009)."; + }, + + 'ssn': function(value) { + if (value.match(/^\d\d\d-\d\d-\d\d\d\d$/)) { + return null; + } + return "SSN needs to be in 999-99-9999 format."; + }, + + 'email': function(value) { + if (value.match(/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/)) { + return null; + } + return "Email needs to be in username@host.com format."; + }, + + 'phone': function(value) { + if (value.match(/^1\(\d\d\d\)\d\d\d-\d\d\d\d$/)) { + return null; + } + if (value.match(/^\+\d{2,3} (\(\d{1,5}\))?[\d ]+\d$/)) { + return null; + } + return "Phone number needs to be in 1(987)654-3210 format in North America or +999 (123) 45678 906 internationaly."; + }, + + 'url': function(value) { + if (value.match(/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/)) { + return null; + } + return "URL needs to be in http://server[:port]/path format."; + }, + + 'json': function(value) { + try { + fromJson(value); + return null; + } catch (e) { + return e.toString(); } - return null; - } else { - return "Value is not a number."; - } -}; - -angular.validator.integer = function(value, min, max) { - var number = angular.validator.number(value, min, max); - if (number === null && value != Math.round(value)) { - return "Value is not a whole number."; - } - return number; -}; - -angular.validator.date = function(value, min, max) { - if (value.match(/^\d\d?\/\d\d?\/\d\d\d\d$/)) { - return null; - } - return "Value is not a date. (Expecting format: 12/31/2009)."; -}; - -angular.validator.ssn = function(value) { - if (value.match(/^\d\d\d-\d\d-\d\d\d\d$/)) { - return null; - } - return "SSN needs to be in 999-99-9999 format."; -}; - -angular.validator.email = function(value) { - if (value.match(/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/)) { - return null; - } - return "Email needs to be in username@host.com format."; -}; - -angular.validator.phone = function(value) { - if (value.match(/^1\(\d\d\d\)\d\d\d-\d\d\d\d$/)) { - return null; - } - if (value.match(/^\+\d{2,3} (\(\d{1,5}\))?[\d ]+\d$/)) { - return null; - } - return "Phone number needs to be in 1(987)654-3210 format in North America or +999 (123) 45678 906 internationaly."; -}; - -angular.validator.url = function(value) { - if (value.match(/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/)) { - return null; - } - return "URL needs to be in http://server[:port]/path format."; -}; - -angular.validator.json = function(value) { - try { - nglr.fromJson(value); - return null; - } catch (e) { - return e.toString(); } -}; +}, function(v,k) {angularValidator[k] = v;}); diff --git a/src/Widgets.js b/src/Widgets.js index de74533a..d85c0ddc 100644 --- a/src/Widgets.js +++ b/src/Widgets.js @@ -1,88 +1,85 @@ -// Copyright (C) 2009 BRAT Tech LLC - - -nglr.WidgetFactory = function(serverUrl, database) { +function WidgetFactory(serverUrl, database) { this.nextUploadId = 0; this.serverUrl = serverUrl; this.database = database; - this.createSWF = swfobject.createSWF; - this.onChangeListener = function(){}; -}; - -nglr.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; - if (type == 'button' || type == 'submit' || type == 'reset' || type == 'image') { - controller = new nglr.ButtonController(input[0], exp); - event = "click"; - bubbleEvent = false; - } else if (type == 'text' || type == 'textarea' || type == 'hidden' || type == 'password') { - controller = new nglr.TextController(input[0], exp); - event = "keyup change"; - } else if (type == 'checkbox') { - controller = new nglr.CheckboxController(input[0], exp); - event = "click"; - } else if (type == 'radio') { - controller = new nglr.RadioController(input[0], exp); - event="click"; - } else if (type == 'select-one') { - controller = new nglr.SelectController(input[0], exp); - } else if (type == 'select-multiple') { - controller = new nglr.MultiSelectController(input[0], exp); - } else if (type == 'file') { - controller = this.createFileController(input, exp); + if (window['swfobject']) { + this.createSWF = window['swfobject']['createSWF']; } else { - throw 'Unknown type: ' + type; - } - input.data('controller', controller); - var binder = scope.get('$binder'); - var action = function() { - if (controller.updateModel(scope)) { - var action = jQuery(controller.view).attr('ng-action') || ""; - if (scope.evalWidget(controller, action)) { - binder.updateView(scope); - } + 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; + if (type == 'button' || type == 'submit' || type == 'reset' || type == 'image') { + controller = new ButtonController(input[0], exp); + event = "click"; + bubbleEvent = false; + } else if (type == 'text' || type == 'textarea' || type == 'hidden' || type == 'password') { + controller = new TextController(input[0], exp); + event = "keyup change"; + } else if (type == 'checkbox') { + controller = new CheckboxController(input[0], exp); + event = "click"; + } else if (type == 'radio') { + controller = new RadioController(input[0], exp); + event="click"; + } else if (type == 'select-one') { + controller = new SelectController(input[0], exp); + } else if (type == 'select-multiple') { + controller = new MultiSelectController(input[0], exp); + } else if (type == 'file') { + controller = this.createFileController(input, exp); + } else { + throw 'Unknown type: ' + type; } - return bubbleEvent; - }; - jQuery(controller.view, ":input"). - bind(event, action); - return controller; -}; - -nglr.WidgetFactory.prototype.createFileController = function(fileInput) { - var uploadId = '__uploadWidget_' + (this.nextUploadId++); - var view = nglr.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 nglr.FileController(view, fileInput[0].name, swfNode, this.serverUrl + "/data/" + this.database); - jQuery(swfNode).data('controller', cntl); - return cntl; -}; - -nglr.WidgetFactory.prototype.createTextWidget = function(textInput) { - var controller = new nglr.TextController(textInput); - controller.onChange(this.onChangeListener); - return controller; + 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).data('controller', cntl); + return cntl; + } }; - ///////////////////// // FileController /////////////////////// -nglr.FileController = function(view, scopeName, uploader, databaseUrl) { +function FileController(view, scopeName, uploader, databaseUrl) { this.view = view; this.uploader = uploader; this.scopeName = scopeName; @@ -91,114 +88,105 @@ nglr.FileController = function(view, scopeName, uploader, databaseUrl) { this.lastValue = undefined; }; -nglr.FileController.dispatchEvent = function(id, event, args) { +angularCallbacks['flashEvent'] = function(id, event, args) { var object = document.getElementById(id); - var controller = jQuery(object).data("controller"); - nglr.FileController.prototype['_on_' + event].apply(controller, args); + var jobject = jQuery(object); + var controller = jobject.data("controller"); + FileController.prototype[event].apply(controller, args); + _.defer(jobject.scope().get('$updateView')); }; -nglr.FileController.template = function(id) { +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>'); -}; - -nglr.FileController.prototype._on_cancel = function() { -}; - -nglr.FileController.prototype._on_complete = function() { -}; - -nglr.FileController.prototype._on_httpStatus = function(status) { - nglr.alert("httpStatus:" + this.scopeName + " status:" + status); -}; - -nglr.FileController.prototype._on_ioError = function() { - nglr.alert("ioError:" + this.scopeName); -}; - -nglr.FileController.prototype._on_open = function() { - nglr.alert("open:" + this.scopeName); -}; - -nglr.FileController.prototype._on_progress = function(bytesLoaded, bytesTotal) { -}; - -nglr.FileController.prototype._on_securityError = function() { - nglr.alert("securityError:" + this.scopeName); -}; - -nglr.FileController.prototype._on_uploadCompleteData = function(data) { - var value = nglr.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; - scope.get('$binder').updateView(); -}; - -nglr.FileController.prototype._on_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(); -}; - -nglr.FileController.prototype.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; - } -}; - -nglr.FileController.prototype.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); -}; - -nglr.FileController.prototype.upload = function() { - if (this.name) { - this.uploader.uploadFile(this.attachmentsPath); + '</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 /////////////////////// -nglr.NullController = function(view) {this.view = view;}; -nglr.NullController.prototype.updateModel = function() { return true; }; -nglr.NullController.prototype.updateView = function() { }; -nglr.NullController.instance = new nglr.NullController(); +function NullController(view) {this.view = view;}; +NullController.prototype = { + updateModel: function() { return true; }, + updateView: noop +}; +NullController.instance = new NullController(); /////////////////////// // ButtonController /////////////////////// -nglr.ButtonController = function(view) {this.view = view;}; -nglr.ButtonController.prototype.updateModel = function(scope) { return true; }; -nglr.ButtonController.prototype.updateView = function(scope) {}; +var ButtonController = NullController; /////////////////////// // TextController /////////////////////// -nglr.TextController = function(view, exp) { +function TextController(view, exp) { this.view = view; this.exp = exp; this.validator = view.getAttribute('ng-validate'); @@ -212,175 +200,183 @@ nglr.TextController = function(view, exp) { } }; -nglr.TextController.prototype.updateModel = function(scope) { - var value = this.view.value; - if (this.lastValue === value) { - return false; - } else { - scope.setEval(this.exp, value); - this.lastValue = value; - return true; - } -}; - -nglr.TextController.prototype.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 != value) { - view.value = value; - this.lastValue = value; - } - var isValidationError = false; - view.removeAttribute('ng-error'); - if (this.required) { - isValidationError = !(value && value.length > 0); - } - var errorText = isValidationError ? "Required Value" : null; - if (!isValidationError && this.validator && value) { - errorText = scope.validate(this.validator, value); - isValidationError = !!errorText; - } - if (this.lastErrorText !== errorText) { - this.lastErrorText = isValidationError; - if (errorText !== null) { - view.setAttribute('ng-error', errorText); - scope.markInvalid(this); +TextController.prototype = { + updateModel: function(scope) { + 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 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 != value) { + view.value = value; + this.lastValue = value; + } + var isValidationError = false; + view.removeAttribute('ng-error'); + if (this.required) { + isValidationError = !(value && value.length > 0); + } + var errorText = isValidationError ? "Required Value" : null; + if (!isValidationError && this.validator && value) { + errorText = scope.validate(this.validator, value); + isValidationError = !!errorText; + } + if (this.lastErrorText !== errorText) { + this.lastErrorText = isValidationError; + if (errorText !== null) { + view.setAttribute('ng-error', errorText); + scope.markInvalid(this); + } + jQuery(view).toggleClass('ng-validation-error', isValidationError); } - jQuery(view).toggleClass('ng-validation-error', isValidationError); } }; /////////////////////// // CheckboxController /////////////////////// -nglr.CheckboxController = function(view, exp) { +function CheckboxController(view, exp) { this.view = view; this.exp = exp; this.lastValue = undefined; this.initialValue = view.checked ? view.value : ""; }; -nglr.CheckboxController.prototype.updateModel = function(scope) { - var input = this.view; - var value = input.checked ? input.value : ''; - if (this.lastValue === value) { - return false; - } else { - scope.setEval(this.exp, value); - this.lastValue = value; - return true; - } -}; - -nglr.CheckboxController.prototype.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); +CheckboxController.prototype = { + updateModel: function(scope) { + var input = this.view; + var value = input.checked ? input.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.eval(this.exp); + if (typeof value === "undefined") { + value = this.initialValue; + scope.setEval(this.exp, value); + } + input.checked = input.value == (''+value); } - input.checked = input.value == (''+value); }; /////////////////////// // SelectController /////////////////////// -nglr.SelectController = function(view, exp) { +function SelectController(view, exp) { this.view = view; this.exp = exp; this.lastValue = undefined; this.initialValue = view.value; }; -nglr.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; +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; - return true; } } }; -nglr.SelectController.prototype.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 /////////////////////// -nglr.MultiSelectController = function(view, exp) { +function MultiSelectController(view, exp) { this.view = view; this.exp = exp; this.lastValue = undefined; this.initialValue = this.selected(); }; -nglr.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; -}; - -nglr.MultiSelectController.prototype.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; - } -}; - -nglr.MultiSelectController.prototype.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; +MultiSelectController.prototype = { + selected: function () { + var value = []; + var options = this.view.options; for ( var i = 0; i < options.length; i++) { var option = options[i]; - option.selected = _.include(selected, option.value); + 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; } - this.lastValue = selected; } }; /////////////////////// // RadioController /////////////////////// -nglr.RadioController = function(view, exp) { +function RadioController(view, exp) { this.view = view; this.exp = exp; this.lastChecked = undefined; @@ -389,53 +385,55 @@ nglr.RadioController = function(view, exp) { this.initialValue = view.checked ? view.value : null; }; -nglr.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; - } -}; - -nglr.RadioController.prototype.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; +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 /////////////////////// -nglr.BindUpdater = function(view, exp) { +function BindUpdater(view, exp) { this.view = view; - this.exp = nglr.Binder.parseBindings(exp); + this.exp = Binder.parseBindings(exp); this.hasError = false; this.scopeSelf = {element:view}; }; -nglr.BindUpdater.toText = function(obj) { - var e = nglr.escapeHtml; +BindUpdater.toText = function(obj) { + var e = escapeHtml; switch(typeof obj) { case "string": case "boolean": case "number": return e(obj); case "function": - return nglr.BindUpdater.toText(obj()); + return BindUpdater.toText(obj()); case "object": - if (nglr.isNode(obj)) { - return nglr.outerHTML(obj); + if (isNode(obj)) { + return outerHTML(obj); } else if (obj instanceof angular.filter.Meta) { switch(typeof obj.html) { case "string": @@ -444,8 +442,8 @@ nglr.BindUpdater.toText = function(obj) { case "function": return obj.html(); case "object": - if (nglr.isNode(obj.html)) - return nglr.outerHTML(obj.html); + if (isNode(obj.html)) + return outerHTML(obj.html); default: break; } @@ -461,158 +459,176 @@ nglr.BindUpdater.toText = function(obj) { } if (obj === null) return ""; - return e(nglr.toJson(obj, true)); + return e(toJson(obj, true)); default: return ""; } }; -nglr.BindUpdater.prototype.updateModel = function(scope) {}; -nglr.BindUpdater.prototype.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 = nglr.Binder.binding(part); - if (binding) { - scope.evalWidget(this, binding, this.scopeSelf, function(value){ - html.push(nglr.BindUpdater.toText(value)); - }, function(e, text){ - nglr.setHtml(this.view, text); - }); - if (this.hasError) { - 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, this.scopeSelf, function(value){ + html.push(BindUpdater.toText(value)); + }, function(e, text){ + setHtml(this.view, text); + }); + if (this.hasError) { + return; + } + } else { + html.push(escapeHtml(part)); } - } else { - html.push(nglr.escapeHtml(part)); } + setHtml(this.view, html.join('')); } - nglr.setHtml(this.view, html.join('')); }; -nglr.BindAttrUpdater = function(view, attrs) { +function BindAttrUpdater(view, attrs) { this.view = view; this.attrs = attrs; }; -nglr.BindAttrUpdater.prototype.updateModel = function(scope) {}; -nglr.BindAttrUpdater.prototype.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 = nglr.Binder.parseBindings(attributeTemplates[attrName]); - var attrValues = []; - for ( var i = 0; i < attributeTemplate.length; i++) { - var binding = nglr.Binder.binding(attributeTemplate[i]); - if (binding) { - try { - var value = scope.eval(binding, {element:jNode[0], attrName:attrName}); - if (value && (value.constructor !== nglr.array || value.length !== 0)) - attrValues.push(value); - } catch (e) { - this.hasError = true; - console.error('BindAttrUpdater', e); - var jsonError = nglr.toJson(e, true); - attrValues.push('[' + jsonError + ']'); - jNode. - addClass('ng-exception'). - attr('ng-error', jsonError); +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]); } - } else { - attrValues.push(attributeTemplate[i]); } - } - var attrValue = attrValues.length ? attrValues.join('') : null; - if(isImage && attrName == 'src' && !attrValue) - attrValue = scope.get('config.server') + '/images/blank.gif'; - jNode.attr(attrName, attrValue); + var attrValue = attrValues.length ? attrValues.join('') : null; + if(isImage && attrName == 'src' && !attrValue) + attrValue = scope.get('$config.blankImage'); + jNode.attr(attrName, attrValue); + } } }; -nglr.EvalUpdater = function(view, exp) { +function EvalUpdater(view, exp) { this.view = view; this.exp = exp; this.hasError = false; }; -nglr.EvalUpdater.prototype.updateModel = function(scope) {}; -nglr.EvalUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp); +EvalUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp); + } }; -nglr.HideUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -nglr.HideUpdater.prototype.updateModel = function(scope) {}; -nglr.HideUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(hideValue){ - var view = jQuery(this.view); - if (nglr.toBoolean(hideValue)) { - view.hide(); - } else { - view.show(); - } - }); +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(); + } + }); + } }; -nglr.ShowUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -nglr.ShowUpdater.prototype.updateModel = function(scope) {}; -nglr.ShowUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(hideValue){ - var view = jQuery(this.view); - if (nglr.toBoolean(hideValue)) { - view.show(); - } else { - view.hide(); - } - }); +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(); + } + }); + } }; -nglr.ClassUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -nglr.ClassUpdater.prototype.updateModel = function(scope) {}; -nglr.ClassUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(classValue){ - if (classValue !== null && classValue !== undefined) { - this.view.className = classValue; - } - }); +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; + } + }); + } }; -nglr.ClassEvenUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -nglr.ClassEvenUpdater.prototype.updateModel = function(scope) {}; -nglr.ClassEvenUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(classValue){ - var index = scope.get('$index'); - jQuery(this.view).toggleClass(classValue, index % 2 === 1); - }); +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); + }); + } }; -nglr.ClassOddUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -nglr.ClassOddUpdater.prototype.updateModel = function(scope) {}; -nglr.ClassOddUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(classValue){ - var index = scope.get('$index'); - jQuery(this.view).toggleClass(classValue, index % 2 === 0); - }); +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); + }); + } }; -nglr.StyleUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -nglr.StyleUpdater.prototype.updateModel = function(scope) {}; -nglr.StyleUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(styleValue){ - jQuery(this.view).attr('style', "").css(styleValue); - }); +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 /////////////////////// -nglr.RepeaterUpdater = function(view, repeaterExpression, template, prefix) { +function RepeaterUpdater(view, repeaterExpression, template, prefix) { this.view = view; this.template = template; this.prefix = prefix; @@ -633,85 +649,81 @@ nglr.RepeaterUpdater = function(view, repeaterExpression, template, prefix) { this.keyExp = match[2]; }; -nglr.RepeaterUpdater.prototype.updateModel = function(scope) {}; -nglr.RepeaterUpdater.prototype.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); +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 iteratorLength = iterator.length; - var childrenLength = this.children.length; - var cursor = this.view; - var time = 0; - var child = null; - var keyExp = this.keyExp; - var valueExp = this.valueExp; - var i = 0; - jQuery.each(iterator, function(key, value){ - if (i < childrenLength) { - // reuse children - child = self.children[i]; - child.scope.set(valueExp, value); - } else { - // grow children - var name = self.prefix + - valueExp + " in " + self.iteratorExp + "[" + i + "]"; - var childScope = new nglr.Scope(scope.state, name); - childScope.set('$index', i); - if (keyExp) - childScope.set(keyExp, key); - childScope.set(valueExp, value); - child = { scope:childScope, element:self.template(childScope, self.prefix, i) }; - cursor.after(child.element); - self.children.push(child); + var iteratorLength = iterator.length; + var childrenLength = this.children.length; + var cursor = this.view; + var time = 0; + var child = null; + var keyExp = this.keyExp; + var valueExp = this.valueExp; + var i = 0; + foreach(iterator, function(value, key){ + if (i < childrenLength) { + // reuse children + child = self.children[i]; + child.scope.set(valueExp, value); + } else { + // grow children + var name = self.prefix + + valueExp + " in " + self.iteratorExp + "[" + i + "]"; + var childScope = new Scope(scope.state, name); + childScope.set('$index', i); + if (keyExp) + childScope.set(keyExp, key); + childScope.set(valueExp, value); + child = { scope:childScope, element:self.template(childScope, self.prefix, i) }; + cursor.after(child.element); + self.children.push(child); + } + cursor = child.element; + var s = new Date().getTime(); + child.scope.updateView(); + time += new Date().getTime() - s; + i++; + }); + // shrink children + for ( var r = childrenLength; r > iteratorLength; --r) { + var unneeded = this.children.pop().element[0]; + unneeded.parentNode.removeChild(unneeded); } - cursor = child.element; - var s = new Date().getTime(); - child.scope.updateView(); - time += new Date().getTime() - s; - i++; - }); - // shrink children - for ( var r = childrenLength; r > iteratorLength; --r) { - var unneeded = this.children.pop(); - unneeded.element.removeNode(); - } - // 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); + // 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 ////////////////////////////////// -nglr.PopUp = function(doc) { +function PopUp(doc) { this.doc = doc; }; -nglr.PopUp.OUT_EVENT = "mouseleave mouseout click dblclick keypress keyup"; +PopUp.OUT_EVENT = "mouseleave mouseout click dblclick keypress keyup"; -nglr.PopUp.prototype.bind = function () { - var self = this; - this.doc.find('.ng-validation-error,.ng-exception'). - live("mouseover", nglr.PopUp.onOver); -}; - -nglr.PopUp.onOver = function(e) { - nglr.PopUp.onOut(); +PopUp.onOver = function(e) { + PopUp.onOut(); var jNode = jQuery(this); - jNode.bind(nglr.PopUp.OUT_EVENT, nglr.PopUp.onOut); + 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; @@ -740,35 +752,45 @@ nglr.PopUp.onOver = function(e) { return true; }; -nglr.PopUp.onOut = function() { +PopUp.onOut = function() { jQuery('#ng-callout'). - unbind(nglr.PopUp.OUT_EVENT, nglr.PopUp.onOut). + 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 ////////////////////////////////// -nglr.Status = function(body) { - this.loader = body.append(nglr.Status.DOM).find("#ng-loading"); +function Status(body) { + this.loader = body.append(Status.DOM).find("#ng-loading"); this.requestCount = 0; }; -nglr.Status.DOM ='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>'; +Status.DOM ='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>'; -nglr.Status.prototype.beginRequest = function () { - if (this.requestCount === 0) { - this.loader.show(); - } - this.requestCount++; -}; - -nglr.Status.prototype.endRequest = function () { - this.requestCount--; - if (this.requestCount === 0) { - this.loader.hide("fold"); +Status.prototype = { + beginRequest: function () { + if (this.requestCount === 0) { + this.loader.show(); + } + this.requestCount++; + }, + + endRequest: function () { + this.requestCount--; + if (this.requestCount === 0) { + this.loader.hide("fold"); + } } }; diff --git a/src/Widgets.js.orig b/src/Widgets.js.orig deleted file mode 100644 index df1d3e40..00000000 --- a/src/Widgets.js.orig +++ /dev/null @@ -1,764 +0,0 @@ - // Copyright (C) 2009 BRAT Tech LLC - - -nglr.WidgetFactory = function(serverUrl) { - this.nextUploadId = 0; - this.serverUrl = serverUrl; - this.createSWF = swfobject.createSWF; - this.onChangeListener = function(){}; -}; - -nglr.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; - if (type == 'button' || type == 'submit' || type == 'reset') { - controller = new nglr.ButtonController(input[0], exp); - event = "click"; - bubbleEvent = false; - } else if (type == 'text' || type == 'textarea') { - controller = new nglr.TextController(input[0], exp); - event = "keyup change"; - } else if (type == 'checkbox') { - controller = new nglr.CheckboxController(input[0], exp); - event = "click"; - } else if (type == 'radio') { - controller = new nglr.RadioController(input[0], exp); - event="click"; - } else if (type == 'select-one') { - controller = new nglr.SelectController(input[0], exp); - } else if (type == 'select-multiple') { - controller = new nglr.MultiSelectController(input[0], exp); - } else if (type == 'file') { - controller = this.createFileController(input, exp); - } else { - throw 'Unknown type: ' + type; - } - input.data('controller', controller); - var binder = scope.get('$binder'); - var action = function() { - if (controller.updateModel(scope)) { - var action = jQuery(controller.view).attr('ng-action') || ""; - if (scope.evalWidget(controller, action)) { - binder.updateView(scope); - } - } - return bubbleEvent; - }; - jQuery(controller.view, ":input"). - bind(event, action); - return controller; -}; - -nglr.WidgetFactory.prototype.createFileController = function(fileInput) { - var uploadId = '__uploadWidget_' + (this.nextUploadId++); - var view = nglr.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 nglr.FileController(view, fileInput[0].name, swfNode, this.serverUrl); - jQuery(swfNode).data('controller', cntl); - return cntl; -}; - -nglr.WidgetFactory.prototype.createTextWidget = function(textInput) { - var controller = new nglr.TextController(textInput); - controller.onChange(this.onChangeListener); - return controller; -}; - -///////////////////// -// FileController -/////////////////////// - -nglr.FileController = function(view, scopeName, uploader, serverUrl) { - this.view = view; - this.uploader = uploader; - this.scopeName = scopeName; - this.uploadUrl = serverUrl + '/upload'; - this.attachmentBase = serverUrl + '/attachments'; - this.value = null; - this.lastValue = undefined; -}; - -nglr.FileController.dispatchEvent = function(id, event, args) { - var object = document.getElementById(id); - var controller = jQuery(object).data("controller"); - nglr.FileController.prototype['_on_' + event].apply(controller, args); -}; - -nglr.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>'); -}; - -nglr.FileController.prototype._on_cancel = function() { -}; - -nglr.FileController.prototype._on_complete = function() { -}; - -nglr.FileController.prototype._on_httpStatus = function(status) { - nglr.alert("httpStatus:" + this.scopeName + " status:" + status); -}; - -nglr.FileController.prototype._on_ioError = function() { - nglr.alert("ioError:" + this.scopeName); -}; - -nglr.FileController.prototype._on_open = function() { - nglr.alert("open:" + this.scopeName); -}; - -nglr.FileController.prototype._on_progress = function(bytesLoaded, bytesTotal) { -}; - -nglr.FileController.prototype._on_securityError = function() { - nglr.alert("securityError:" + this.scopeName); -}; - -nglr.FileController.prototype._on_uploadCompleteData = function(data) { - this.value = nglr.fromJson(data); - this.value.url = this.attachmentBase + '/' + this.value.id + '/' + this.value.text; - this.view.find("input").attr('checked', true); - var scope = this.view.scope(); - this.updateModel(scope); - scope.get('$binder').updateView(); -}; - -nglr.FileController.prototype._on_select = function(name, size, type) { - this.name = name; - this.view.find("a").text(name).attr('href', name); - this.view.find("span").text(filters.bytes(size)); - this.upload(); -}; - -nglr.FileController.prototype.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; - } -}; - -nglr.FileController.prototype.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.name); - this.view.find("span").text(filters.bytes(this.value.size)); - } - this.view.find("input").attr('checked', !!modelValue); -}; - -nglr.FileController.prototype.upload = function() { - if (this.name) { - this.uploader.uploadFile(this.uploadUrl); - } -}; - - -/////////////////////// -// NullController -/////////////////////// -nglr.NullController = function(view) {this.view = view;}; -nglr.NullController.prototype.updateModel = function() { return true; }; -nglr.NullController.prototype.updateView = function() { }; -nglr.NullController.instance = new nglr.NullController(); - - -/////////////////////// -// ButtonController -/////////////////////// -nglr.ButtonController = function(view) {this.view = view;}; -nglr.ButtonController.prototype.updateModel = function(scope) { return true; }; -nglr.ButtonController.prototype.updateView = function(scope) {}; - -/////////////////////// -// TextController -/////////////////////// -nglr.TextController = function(view, exp) { - this.view = view; - 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 = view.value; - var widget = view.getAttribute('ng-widget'); - if (widget === 'datepicker') { - jQuery(view).datepicker(); - } -}; - -nglr.TextController.prototype.updateModel = function(scope) { - var value = this.view.value; - if (this.lastValue === value) { - return false; - } else { - scope.set(this.exp, value); - this.lastValue = value; - return true; - } -}; - -nglr.TextController.prototype.updateView = function(scope) { - var view = this.view; - var value = scope.get(this.exp); - if (typeof value === "undefined") { - value = this.initialValue; - scope.set(this.exp, value); - } - value = value ? value : ''; - if (this.lastValue != value) { - view.value = value; - this.lastValue = value; - } - var isValidationError = false; - view.removeAttribute('ng-error'); - if (this.required) { - isValidationError = !(value && value.length > 0); - } - var errorText = isValidationError ? "Required Value" : null; - if (!isValidationError && this.validator && value) { - errorText = scope.validate(this.validator, value); - isValidationError = !!errorText; - } - if (this.lastErrorText !== errorText) { - this.lastErrorText = isValidationError; - if (errorText !== null) { - view.setAttribute('ng-error', errorText); - scope.markInvalid(this); - } - jQuery(view).toggleClass('ng-validation-error', isValidationError); - } -}; - -/////////////////////// -// CheckboxController -/////////////////////// -nglr.CheckboxController = function(view, exp) { - this.view = view; - this.exp = exp; - this.lastValue = undefined; - this.initialValue = view.checked ? view.value : ""; -}; - -nglr.CheckboxController.prototype.updateModel = function(scope) { - var input = this.view; - var value = input.checked ? input.value : ''; - if (this.lastValue === value) { - return false; - } else { - scope.setEval(this.exp, value); - this.lastValue = value; - return true; - } -}; - -nglr.CheckboxController.prototype.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 = input.value == (''+value); -}; - -/////////////////////// -// SelectController -/////////////////////// -nglr.SelectController = function(view, exp) { - this.view = view; - this.exp = exp; - this.lastValue = undefined; - this.initialValue = view.value; -}; - -nglr.SelectController.prototype.updateModel = function(scope) { - var input = this.view; - if (input.selectedIndex < 0) { - scope.set(this.exp, null); - } else { - var value = this.view.value; - if (this.lastValue === value) { - return false; - } else { - scope.set(this.exp, value); - this.lastValue = value; - return true; - } - } -}; - -nglr.SelectController.prototype.updateView = function(scope) { - var input = this.view; - var value = scope.get(this.exp); - if (typeof value === 'undefined') { - value = this.initialValue; - scope.set(this.exp, value); - } - if (value !== this.lastValue) { - input.value = value ? value : ""; - this.lastValue = value; - } -}; - -/////////////////////// -// MultiSelectController -/////////////////////// -nglr.MultiSelectController = function(view, exp) { - this.view = view; - this.exp = exp; - this.lastValue = undefined; - this.initialValue = this.selected(); -}; - -nglr.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; -}; - -nglr.MultiSelectController.prototype.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.set(this.exp, value); - this.lastValue = value; - return true; - } -}; - -nglr.MultiSelectController.prototype.updateView = function(scope) { - var input = this.view; - var selected = scope.get(this.exp); - if (typeof selected === "undefined") { - selected = this.initialValue; - scope.set(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 = selected.contains(option.value); - } - this.lastValue = selected; - } -}; - -/////////////////////// -// RadioController -/////////////////////// -nglr.RadioController = function(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; -}; - -nglr.RadioController.prototype.updateModel = function(scope) { - var input = this.view; - if (this.lastChecked) { - return false; - } else { - input.checked = true; - this.lastValue = scope.set(this.exp, this.inputValue); - this.lastChecked = true; - return true; - } -}; - -nglr.RadioController.prototype.updateView = function(scope) { - var input = this.view; - var value = scope.get(this.exp); - if (this.initialValue && typeof value === "undefined") { - value = this.initialValue; - scope.set(this.exp, value); - } - if (this.lastValue != value) { - this.lastChecked = input.checked = this.inputValue == (''+value); - this.lastValue = value; - } -}; - -/////////////////////// -//ElementController -/////////////////////// -nglr.BindUpdater = function(view, exp) { - this.view = view; - this.exp = exp.parseBindings(); - this.hasError = false; - this.scopeSelf = {element:view}; -}; - -nglr.BindUpdater.toText = function(obj) { - var e = nglr.escapeHtml; - switch(typeof obj) { - case "string": - case "boolean": - case "number": - return e(obj); - case "function": - return nglr.BindUpdater.toText(obj()); - case "object": - if (nglr.isNode(obj)) { - return nglr.outerHTML(obj); - } else if (obj && obj.TAG === filters.Meta.TAG) { - switch(typeof obj.html) { - case "string": - case "number": - return obj.html; - case "function": - return 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(nglr.toJson(obj, true)); - default: - return ""; - } -}; - -nglr.BindUpdater.prototype.updateModel = function(scope) {}; -nglr.BindUpdater.prototype.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 = part.binding(); - if (binding) { - scope.evalWidget(this, binding, this.scopeSelf, function(value){ - html.push(nglr.BindUpdater.toText(value)); - }, function(e, text){ - nglr.setHtml(this.view, text); - }); - if (this.hasError) { - return; - } - } else { - html.push(nglr.escapeHtml(part)); - } - } - nglr.setHtml(this.view, html.join('')); -}; - -nglr.BindAttrUpdater = function(view, attrs) { - this.view = view; - this.attrs = attrs; -}; - -nglr.BindAttrUpdater.prototype.updateModel = function(scope) {}; -nglr.BindAttrUpdater.prototype.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 = attributeTemplates[attrName].parseBindings(); - var attrValues = []; - for ( var i = 0; i < attributeTemplate.length; i++) { - var binding = attributeTemplate[i].binding(); - if (binding) { - try { - var value = scope.eval(binding, {element:jNode[0], attrName:attrName}); - if (value && (value.constructor !== nglr.array || value.length !== 0)) - attrValues.push(value); - } catch (e) { - this.hasError = true; - console.error('BindAttrUpdater', e); - var jsonError = nglr.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.server') + '/images/blank.gif'; - jNode.attr(attrName, attrValue); - } -}; - -nglr.EvalUpdater = function(view, exp) { - this.view = view; - this.exp = exp; - this.hasError = false; -}; -nglr.EvalUpdater.prototype.updateModel = function(scope) {}; -nglr.EvalUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp); -}; - -nglr.HideUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -nglr.HideUpdater.prototype.updateModel = function(scope) {}; -nglr.HideUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(hideValue){ - var view = jQuery(this.view); - if (nglr.toBoolean(hideValue)) { - view.hide(); - } else { - view.show(); - } - }); -}; - -nglr.ShowUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -nglr.ShowUpdater.prototype.updateModel = function(scope) {}; -nglr.ShowUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(hideValue){ - var view = jQuery(this.view); - if (nglr.toBoolean(hideValue)) { - view.show(); - } else { - view.hide(); - } - }); -}; - -nglr.ClassUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -nglr.ClassUpdater.prototype.updateModel = function(scope) {}; -nglr.ClassUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(classValue){ - if (classValue !== null && classValue !== undefined) { - this.view.className = classValue; - } - }); -}; - -nglr.ClassEvenUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -nglr.ClassEvenUpdater.prototype.updateModel = function(scope) {}; -nglr.ClassEvenUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(classValue){ - var index = scope.get('$index'); - jQuery(this.view).toggleClass(classValue, index % 2 === 1); - }); -}; - -nglr.ClassOddUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -nglr.ClassOddUpdater.prototype.updateModel = function(scope) {}; -nglr.ClassOddUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(classValue){ - var index = scope.get('$index'); - jQuery(this.view).toggleClass(classValue, index % 2 === 0); - }); -}; - -nglr.StyleUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -nglr.StyleUpdater.prototype.updateModel = function(scope) {}; -nglr.StyleUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(styleValue){ - jQuery(this.view).attr('style', "").css(styleValue); - }); -}; - -/////////////////////// -// RepeaterUpdater -/////////////////////// -nglr.RepeaterUpdater = function(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 + "'."; - } - this.itemExp = match[1]; - this.iteratorExp = match[2]; -}; - -nglr.RepeaterUpdater.prototype.updateModel = function(scope) {}; -nglr.RepeaterUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.iteratorExp, {}, function(iterator){ - if (!iterator) { - iterator = []; - if (scope.isProperty(this.iteratorExp)) { - scope.set(this.iteratorExp, iterator); - } - } - var iteratorLength = iterator.length; - var childrenLength = this.children.length; - var cursor = this.view; - var time = 0; - var child = null; - var itemExp = this.itemExp; - for ( var i = 0; i < iteratorLength; i++) { - if (i < iteratorLength) { - if (i < childrenLength) { // reuse children - child = this.children[i]; - child.scope.set(itemExp, iterator[i]); - } else { // grow children - var name = this.prefix + - itemExp + " in " + this.iteratorExp + "[" + i + "]"; - var childScope = new nglr.Scope(scope.state, name); - childScope.set('$index', i); - childScope.set(itemExp, iterator[i]); - child = { scope:childScope, element:this.template(childScope, this.prefix, i) }; - cursor.after(child.element); - this.children.push(child); - } - cursor = child.element; - var s = new Date().getTime(); - child.scope.updateView(); - time += new Date().getTime() - s; - } - } - // shrink children - for ( var r = childrenLength; r > iteratorLength; --r) { - var unneeded = this.children.pop(); - unneeded.element.removeNode(); - } - // 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 -////////////////////////////////// - -nglr.PopUp = function(doc) { - this.doc = doc; -}; - -nglr.PopUp.OUT_EVENT = "mouseleave mouseout click dblclick keypress keyup"; - -nglr.PopUp.prototype.bind = function () { - var self = this; - this.doc.find('.ng-validation-error,.ng-exception'). - live("mouseover", nglr.PopUp.onOver); -}; - -nglr.PopUp.onOver = function(e) { - nglr.PopUp.onOut(); - var jNode = jQuery(this); - jNode.bind(nglr.PopUp.OUT_EVENT, nglr.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; -}; - -nglr.PopUp.onOut = function() { - jQuery('#ng-callout'). - unbind(nglr.PopUp.OUT_EVENT, nglr.PopUp.onOut). - remove(); - return true; -}; - -////////////////////////////////// -// Status -////////////////////////////////// - -nglr.Status = function (body) { - this.body = body; - this.requestCount = 0; -}; -nglr.Status.ANGULAR = "<a class='ng-angular-logo' href='http://www.getangular.com'>&lt;angular/&gt;</a>™"; - -nglr.Status.prototype.beginRequest = function () { - if (this.requestCount === 0) { -<<<<<<< HEAD:public/javascripts/nglr/Widgets.js - this.dialogView = jQuery('<div class="ng-dialog" title="'+nglr.ControlBar.ANGULAR+' Server Communication:">Please Wait...<div/><div class="loader"></div></div>'); -======= - this.dialogView = jQuery('<div title="'+nglr.Status.ANGULAR+' Server Communication:">Please Wait...<div/></div>'); - this.progressWidget = this.dialogView.find("div"); - this.progressWidget.progressbar({value:0}); ->>>>>>> master:public/javascripts/nglr/Widgets.js - this.dialogView.dialog({bgiframe:true, minHeight:50, modal:true}); - this.maxRequestCount = 0; - } - this.requestCount++; - this.maxRequestCount++; -}; - -nglr.Status.prototype.endRequest = function () { - this.requestCount--; - if (this.requestCount === 0) { - this.dialogView.dialog("destroy"); - this.dialogView.remove(); - this.dialogView = null; - } -}; diff --git a/src/XSitePost.js b/src/XSitePost.js deleted file mode 100644 index 7d81e207..00000000 --- a/src/XSitePost.js +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (C) 2008,2009 BRAT Tech LLC - -if (typeof nglr == 'undefined') nglr = {}; - -if (typeof console == 'undefined') console = {}; -if (typeof console.log == 'undefined') - console.log = function() {}; -if (typeof console.error == 'undefined') - console.error = function() {}; - -nglr.XSitePost = function(baseUrl, window, prefix) { - this.baseUrl = baseUrl; - this.post = jQuery.post; - this.window = window; - this.inQueue = {}; - this.outQueue = []; - this.maxMsgSize = 100000; - this.delay = 20; - this.prefix = prefix; - this.setTimeout=function(fn, delay){window.setTimeout(fn, delay);}; -}; - -nglr.XSitePost.prototype.init = function() { - this.window.name = ''; - this.response('ready', 'null'); -}; - -nglr.XSitePost.prototype.incomingFragment = function(fragment) { - var parts = fragment.split(";"); - this.incomingMsg(parts.shift(), 1*parts.shift(), 1*parts.shift(), parts.shift()); -}; - -nglr.XSitePost.prototype.incomingMsg = function(id, partNo, totalParts, msgPart) { - var msg = this.inQueue[id]; - if (!msg) { - msg = {id:id, parts:[], count:0}; - this.inQueue[id] = msg; - } - msg.parts[partNo] = msgPart; - msg.count++; - if (totalParts === msg.count) { - delete this.inQueue[id]; - var request = this.decodePost(msg.parts.join('')); - var self = this; - this.post(this.baseUrl + request.url, request.params, function(response, status){ - self.response(id, response, status); - }); - } -}; - -nglr.XSitePost.prototype.response = function(id, response, status) { - var start = 0; - var end; - var msg = Base64.encode(response); - var msgLen = msg.length; - var total = Math.ceil(msgLen / this.maxMsgSize); - var part = 0; - while (start < msgLen) { - end = Math.min(msgLen, start + this.maxMsgSize); - this.outQueue.push(id + ':'+part+':'+total+':' + msg.substring(start, end)); - start = end; - part++; - } -}; - -nglr.XSitePost.prototype.decodePost = function(post) { - var parts = post.split(':'); - var url = Base64.decode(parts.shift()); - var params = {}; - while(parts.length !== 0) { - var key = parts.shift(); - var value = Base64.decode(parts.shift()); - params[key] = value; - } - return {url:url, params:params}; -}; - -nglr.XSitePost.prototype.listen = function() { - console.log("listen()"); - var self = this; - var window = this.window; - var outQueue = this.outQueue; - var setTimeout = this.setTimeout; - var prefix = this.prefix; - var prefixLen = prefix.length; - var prefixRec = prefix + '>'; - var prefixRecLen = prefixRec.length; - window.name = prefix; - var pull = function(){ - var value = window.name; - if (value == prefix && outQueue.length > 0) { - window.name = prefix + '<' + outQueue.shift(); - } else if (value.substr(0, prefixRecLen) == prefixRec) { - self.incomingFragment(value.substr(prefixRecLen)); - window.name = prefix; - } - setTimeout(pull, self.delay); - }; - pull(); -}; diff --git a/src/angular-bootstrap.js b/src/angular-bootstrap.js deleted file mode 100644 index b7ae6a38..00000000 --- a/src/angular-bootstrap.js +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (C) 2008,2009 BRAT Tech LLC - -(function(previousOnLoad){ - var filename = /(.*)\/angular-(.*).js(#(.*))?/; - var scripts = document.getElementsByTagName("script"); - var scriptConfig = { - autoSubmit:true, - autoBind:true, - autoLoadDependencies:false - }; - for(var j = 0; j < scripts.length; j++) { - var src = scripts[j].src; - if (src && src.match(filename)) { - var parts = src.match(filename); - if (parts[2] == 'bootstrap') { - scriptConfig.autoLoadDependencies = true; - } - scriptConfig.server = parts[1] || ''; - if (!scriptConfig.server) { - scriptConfig.server = window.location.toString().split(window.location.pathname)[0]; - } - if (parts[4]) { - var directive = parts[4].split('&'); - for ( var i = 0; i < directive.length; i++) { - var keyValue = directive[i].split('='); - var key = keyValue[0]; - var value = keyValue.length == 1 ? true : keyValue[1]; - if (value == 'false') value = false; - if (value == 'true') value = true; - scriptConfig[key] = value; - } - } - } - } - - var addScript = function(path, server){ - server = server || scriptConfig.server; - document.write('<script type="text/javascript" src="' + server + path +'"></script>'); - }; - - if (scriptConfig.autoLoadDependencies) { - addScript("/javascripts/webtoolkit.base64.js"); - addScript("/javascripts/swfobject.js"); - addScript("/javascripts/jQuery/jquery-1.3.2.js"); - addScript("/javascripts/jQuery/jquery-ui-1.7.1.custom.min.js"); - addScript("/javascripts/underscore/underscore.js"); - addScript("/javascripts/nglr/Loader.js"); - addScript("/javascripts/nglr/API.js"); - addScript("/javascripts/nglr/Binder.js"); - addScript("/javascripts/nglr/ControlBar.js"); - addScript("/javascripts/nglr/DataStore.js"); - addScript("/javascripts/nglr/Filters.js"); - addScript("/javascripts/nglr/JSON.js"); - addScript("/javascripts/nglr/Model.js"); - addScript("/javascripts/nglr/Parser.js"); - addScript("/javascripts/nglr/Scope.js"); - addScript("/javascripts/nglr/Server.js"); - addScript("/javascripts/nglr/Users.js"); - addScript("/javascripts/nglr/Validators.js"); - addScript("/javascripts/nglr/Widgets.js"); - } else { - addScript("/ajax/libs/swfobject/2.2/swfobject.js", "http://ajax.googleapis.com"); - addScript("/ajax/libs/jquery/1.3.2/jquery.min.js", "http://ajax.googleapis.com"); - addScript("/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js", "http://ajax.googleapis.com"); - } - - window.onload = function() { - window.angular.init = function(root, config){ - var cnfgMerged = _.clone(scriptConfig||{}); - _.extend(cnfgMerged, config); - new nglr.Loader(root, jQuery("head"), cnfgMerged).load(); - }; - - var doc = window.document; - if (scriptConfig.bindRootId) { - doc = null; - var ids = scriptConfig.bindRootId.split('|'); - for ( var i = 0; i < ids.length && !doc; i++) { - var idCond = ids[i].split('?'); - var id = idCond[0]; - if (idCond.length > 1) { - if (!window.document.getElementById(idCond[1])) { - continue; - } - } - doc = window.document.getElementById(id); - } - } - if (scriptConfig.autoBind && doc) { - window.angular.init(doc); - } - if (typeof previousOnLoad === 'function') { - try { - previousOnLoad.apply(this, arguments); - } catch (e) {} - } - }; -})(window.onload); - - diff --git a/src/angular.prefix b/src/angular.prefix new file mode 100644 index 00000000..26a8429f --- /dev/null +++ b/src/angular.prefix @@ -0,0 +1,24 @@ +/** + * The MIT License + * + * Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +(function(window, document){ diff --git a/src/angular.suffix b/src/angular.suffix new file mode 100644 index 00000000..4b3cc37b --- /dev/null +++ b/src/angular.suffix @@ -0,0 +1 @@ +})(window, document);
\ No newline at end of file diff --git a/src/test/Runner.js b/src/test/Runner.js index 478ef73e..c6684951 100644 --- a/src/test/Runner.js +++ b/src/test/Runner.js @@ -1,9 +1,11 @@ -nglr.test.ScenarioRunner = function(scenarios, body) { +if (typeof test == 'undefined') test = {}; + +test.ScenarioRunner = function(scenarios, body) { this.scenarios = scenarios; this.body = body; }; -nglr.test.ScenarioRunner.prototype = { +test.ScenarioRunner.prototype = { run:function(){ this.setUpUI(); this.runScenarios(); @@ -23,22 +25,22 @@ nglr.test.ScenarioRunner.prototype = { }); }, runScenarios:function(){ - var runner = new nglr.test.Runner(this.console, this.testFrame); + var runner = new test.Runner(this.console, this.testFrame); _.stepper(this.scenarios, function(next, scenario, name){ - new nglr.test.Scenario(name, scenario).run(runner, next); + new test.Scenario(name, scenario).run(runner, next); }, function(){ } ); } }; -nglr.test.Runner = function(console, frame){ +test.Runner = function(console, frame){ this.console = console; this.current = null; this.tests = []; this.frame = frame; }; -nglr.test.Runner.prototype = { +test.Runner.prototype = { start:function(name){ var current = this.current = { name:name, @@ -46,10 +48,10 @@ nglr.test.Runner.prototype = { scenario:jQuery('<div class="scenario"></div>') }; current.run = current.scenario.append( - '<div class="run">' + - '<span class="name">.</span>' + - '<span class="time">.</span>' + - '<span class="state">.</span>' + + '<div class="run">' + + '<span class="name">.</span>' + + '<span class="time">.</span>' + + '<span class="state">.</span>' + '</run>').find(".run"); current.log = current.scenario.append('<div class="log"></div>').find(".log"); current.run.find(".name").text(name); @@ -73,22 +75,22 @@ nglr.test.Runner.prototype = { var buf = []; for ( var i = 1; i < arguments.length; i++) { var arg = arguments[i]; - buf.push(typeof arg == "string" ?arg:nglr.toJson(arg)); + buf.push(typeof arg == "string" ?arg:toJson(arg)); } var log = jQuery('<div class="' + level + '"></div>'); log.text(buf.join(" ")); this.current.log.append(log); this.console.scrollTop(this.console[0].scrollHeight); - if (level == "error") + if (level == "error") this.current.error = buf.join(" "); } }; -nglr.test.Scenario = function(name, scenario){ +test.Scenario = function(name, scenario){ this.name = name; this.scenario = scenario; }; -nglr.test.Scenario.prototype = { +test.Scenario.prototype = { run:function(runner, callback) { var self = this; _.stepper(this.scenario, function(next, steps, name){ @@ -108,22 +110,22 @@ nglr.test.Scenario.prototype = { }, verb:function(step){ var fn = null; - if (!step) fn = function (){ throw "Step is null!"; } + if (!step) fn = function (){ throw "Step is null!"; }; else if (step.Given) fn = angular.test.GIVEN[step.Given]; else if (step.When) fn = angular.test.WHEN[step.When]; else if (step.Then) fn = angular.test.THEN[step.Then]; return fn || function (){ - throw "ERROR: Need Given/When/Then got: " + nglr.toJson(step); - }; + throw "ERROR: Need Given/When/Then got: " + toJson(step); + }; }, context: function(runner) { var frame = runner.frame; var window = frame[0].contentWindow; var document; - if (window.jQuery) + if (window.jQuery) document = window.jQuery(window.document); var context = { - frame:frame, + frame:frame, window:window, log:_.bind(runner.log, runner, "info"), document:document, @@ -147,14 +149,14 @@ nglr.test.Scenario.prototype = { callback(); return; } - runner.log("info", nglr.toJson(step)); + runner.log("info", toJson(step)); var fn = this.verb(step); var context = this.context(runner); _.extend(context, step); try { (fn.call(context)||function(c){c();})(callback); } catch (e) { - runner.log("error", "ERROR: " + nglr.toJson(e)); + runner.log("error", "ERROR: " + toJson(e)); } } }; diff --git a/src/test/Steps.js b/src/test/Steps.js index af4b84d6..cc9ff549 100644 --- a/src/test/Steps.js +++ b/src/test/Steps.js @@ -14,7 +14,7 @@ angular.test.GIVEN = { }; }, dataset:function(){ - this.frame.name="$DATASET:" + nglr.toJson({dataset:this.dataset}); + this.frame.name="$DATASET:" + toJson({dataset:this.dataset}); } }; angular.test.WHEN = { diff --git a/src/test/_namespace.js b/src/test/_namespace.js index 78f430f1..e29ae72a 100644 --- a/src/test/_namespace.js +++ b/src/test/_namespace.js @@ -1,5 +1,5 @@ -if (!angular) angular = {}; -if (!angular.test) angular.test = {}; -if (!angular.test.GIVEN) angular.test.GIVEN = {}; -if (!angular.test.WHEN) angular.test.WHEN = {}; -if (!angular.test.THEN) angular.test.THEN = {}; +if (!angular) var angular = window['angular'] = {}; +if (!angular['test']) var angularTest = angular['test'] = {}; +if (!angular['test']['GIVEN']) angularTest['GIVEN'] = {}; +if (!angular['test']['WHEN']) angularTest['WHEN'] = {}; +if (!angular['test']['THEN']) angularTest['THEN'] = {}; |
