lots of cleanup to get it ready for OS

This commit is contained in:
Misko Hevery
2010-01-23 15:54:58 -08:00
parent e41ee88ef8
commit 4460328bc1
18 changed files with 572 additions and 635 deletions

View File

@@ -134,16 +134,6 @@ function bind(_this, _function) {
};
}
function shiftBind(_this, _function) {
return function() {
var args = [ this ];
for ( var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
return _function.apply(_this, args);
};
}
function outerHTML(node) {
var temp = document.createElement('div');
temp.appendChild(node);
@@ -177,159 +167,9 @@ function merge(src, dst) {
}
// ////////////////////////////
// Angular
// UrlWatcher
// ////////////////////////////
function Angular(document, head, config) {
this.document = jQuery(document);
this.head = jQuery(head);
this.config = config;
this.location = window.location;
}
Angular.prototype = {
load: function() {
this.configureLogging();
log("Server: " + this.config.server);
this.configureJQueryPlugins();
this.computeConfiguration();
this.bindHtml();
},
configureJQueryPlugins: function() {
log('Angular.configureJQueryPlugins()');
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') || NullController.instance;
};
},
uid: function() {
return "" + new Date().getTime();
},
computeConfiguration: function() {
var config = this.config;
if (!config.database) {
var match = config.server.match(/https?:\/\/([\w]*)/);
config.database = match ? match[1] : "$MEMORY";
}
},
bindHtml: function() {
log('Angular.bindHtml()');
var watcher = this.watcher = new UrlWatcher(this.location);
var document = this.document;
var widgetFactory = new WidgetFactory(this.config.server, this.config.database);
var binder = new Binder(document[0], widgetFactory, watcher, this.config);
widgetFactory.onChangeListener = shiftBind(binder, binder.updateModel);
var controlBar = new ControlBar(document.find('body'), this.config.server);
var onUpdate = function(){binder.updateView();};
var server = this.config.database=="$MEMORY" ?
new FrameServer(this.window) :
new Server(this.config.server, jQuery.getScript);
server = new VisualServer(server, new Status(jQuery(document.body)), onUpdate);
var users = new Users(server, controlBar);
var databasePath = '/data/' + this.config.database;
var post = function(request, callback){
server.request("POST", databasePath, request, callback);
};
var datastore = new DataStore(post, users, binder.anchor);
binder.updateListeners.push(function(){datastore.flush();});
var scope = new 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");
document.data('scope', scope);
log('$binder.entity()');
binder.entity(scope);
log('$binder.compile()');
binder.compile();
log('ControlBar.bind()');
controlBar.bind();
log('$users.fetchCurrentUser()');
function fetchCurrentUser() {
users.fetchCurrentUser(function(u) {
if (!u && document.find("[ng-auth=eager]").length) {
users.login();
}
});
}
fetchCurrentUser();
log('PopUp.bind()');
new PopUp(document).bind();
log('$binder.parseAnchor()');
binder.parseAnchor();
document.find("body").show();
log('ready()');
},
visualPost: function(delegate) {
var status = new Status(jQuery(document.body));
return function(request, delegateCallback) {
status.beginRequest(request);
var callback = function() {
status.endRequest();
try {
delegateCallback.apply(this, arguments);
} catch (e) {
alert(toJson(e));
}
};
delegate(request, callback);
};
},
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') {
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 UrlWatcher(location) {
this.location = location;
this.delay = 25;
@@ -343,6 +183,9 @@ function UrlWatcher(location) {
}
UrlWatcher.prototype = {
listen: function(fn){
this.listener = fn;
},
watch: function() {
var self = this;
var pull = function() {
@@ -369,48 +212,149 @@ UrlWatcher.prototype = {
self.setTimeout(pull, self.delay);
};
pull();
return this;
},
setUrl: function(url) {
//TODO: conditionaly?
var existingURL = window.location.href;
set: function(url) {
var existingURL = this.location.href;
if (!existingURL.match(/#/))
existingURL += '#';
if (existingURL != url)
window.location.href = url;
this.location.href = url;
this.existingURL = url;
},
getUrl: function() {
get: function() {
return window.location.href;
}
};
angular['compile'] = function(root, config) {
config = config || {};
var defaults = {
'server': "",
'addUrlChangeListener': noop
};
//todo: don't start watcher
var angular = new Angular(root, jQuery("head"), _(defaults).extend(config));
//todo: don't load stylesheet by default
// loader.loadCss('/stylesheets/jquery-ui/smoothness/jquery-ui-1.7.1.css');
// loader.loadCss('/stylesheets/css');
angular.load();
var scope = jQuery(root).scope();
//TODO: cleanup
return {
'updateView':function(){return scope.updateView();},
'set':function(){return scope.set.apply(scope, arguments);},
'get':function(){return scope.get.apply(scope, arguments);},
'init':function(){scope.get('$binder.executeInit')(); scope.updateView();},
'watchUrl':function(){
var binder = scope.get('$binder');
var watcher = angular.watcher;
watcher.listener = bind(binder, binder.onUrlChange, watcher);
watcher.onUpdate = function(){alert("update");};
watcher.watch();
/////////////////////////////////////////////////
function configureJQueryPlugins() {
log('Angular.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, config['location'], config);
var controlBar = new ControlBar(element.find('body'), config.server);
var onUpdate = function(){binder.updateView();};
var server = config.database=="$MEMORY" ?
new FrameServer(this.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.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();
scope.updateView();
return self;
},
'element':element[0],
'config':config
});
return self;
}
angular['startUrlWatcher'] = function(){
return new UrlWatcher(window['location']).watch();
};
angular['compile'] = function(element, config) {
config = _({
'server': "",
'location': {'get':noop, 'set':noop, 'listen':noop}
}).extend(config||{});
configureLogging(config);
configureJQueryPlugins();
return wireAngular(jQuery(element), config);
};

View File

@@ -1,6 +1,6 @@
function Binder(doc, widgetFactory, urlWatcher, config) {
function Binder(doc, widgetFactory, location, config) {
this.doc = doc;
this.urlWatcher = urlWatcher;
this.location = location;
this.anchor = {};
this.widgetFactory = widgetFactory;
this.config = config || {};
@@ -48,9 +48,8 @@ Binder.prototype = {
return params;
},
parseAnchor: function(url) {
var self = this;
url = url || this.urlWatcher.getUrl();
parseAnchor: function() {
var self = this, url = this.location.get() || "";
var anchorIndex = url.indexOf('#');
if (anchorIndex < 0) return;
@@ -65,13 +64,13 @@ Binder.prototype = {
});
},
onUrlChange: function (url) {
this.parseAnchor(url);
onUrlChange: function() {
this.parseAnchor();
this.updateView();
},
updateAnchor: function() {
var url = this.urlWatcher.getUrl();
var url = this.location.get();
var anchorIndex = url.indexOf('#');
if (anchorIndex > -1)
url = url.substring(0, anchorIndex);
@@ -88,7 +87,7 @@ Binder.prototype = {
sep = '&';
}
}
this.urlWatcher.setUrl(url);
this.location.set(url);
return url;
},
@@ -154,7 +153,7 @@ Binder.prototype = {
jNode.addClass("ng-exception");
jNode.attr('ng-error', toJson(e, true));
}
scope.eval('$binder.updateView()');
scope.get('$updateView')();
return false;
});
},

View File

@@ -2,7 +2,7 @@ array = [].constructor;
function toJson(obj, pretty){
var buf = [];
toJsonArray(buf, obj, pretty ? "\n " : null);
toJsonArray(buf, obj, pretty ? "\n " : null, _([]));
return buf.join('');
};
@@ -25,7 +25,14 @@ function fromJson(json) {
angular['toJson'] = toJson;
angular['fromJson'] = fromJson;
function toJsonArray(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");
@@ -52,7 +59,7 @@ function toJsonArray(buf, obj, pretty){
if (typeof item == 'function' || typeof item == 'undefined') {
buf.push("null");
} else {
toJsonArray(buf, item, pretty);
toJsonArray(buf, item, pretty, stack);
}
sep = true;
}
@@ -82,7 +89,7 @@ function toJsonArray(buf, obj, pretty){
}
buf.push(angular['String']['quote'](key));
buf.push(":");
toJsonArray(buf, value, childPretty);
toJsonArray(buf, value, childPretty, stack);
comma = true;
}
} catch (e) {
@@ -91,4 +98,7 @@ function toJsonArray(buf, obj, pretty){
buf.push("}");
}
}
if (typeof obj == "object") {
stack.pop();
}
};

View File

@@ -7,7 +7,7 @@ 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);
});
},
@@ -15,7 +15,7 @@ Users.prototype = {
'logout': function(callback) {
var self = this;
this.controlBar.logout(function(){
delete self.current;
delete self['current'];
(callback||noop)();
});
},

View File

@@ -9,7 +9,6 @@ function WidgetFactory(serverUrl, database) {
alert("ERROR: swfobject not loaded!");
};
}
this.onChangeListener = function(){};
};
WidgetFactory.prototype = {
@@ -43,12 +42,12 @@ WidgetFactory.prototype = {
throw 'Unknown type: ' + type;
}
input.data('controller', controller);
var binder = scope.get('$binder');
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)) {
binder.updateView(scope);
updateView(scope);
}
}
return bubbleEvent;
@@ -74,12 +73,6 @@ WidgetFactory.prototype = {
var cntl = new FileController(view, fileInput[0].name, swfNode, this.serverUrl + "/data/" + this.database);
jQuery(swfNode).data('controller', cntl);
return cntl;
},
createTextWidget: function(textInput) {
var controller = new TextController(textInput);
controller.onChange(this.onChangeListener);
return controller;
}
};
/////////////////////