diff --git a/lib/js/kopi/app.js b/lib/js/kopi/app.js index 6a87511..ce88f23 100644 --- a/lib/js/kopi/app.js +++ b/lib/js/kopi/app.js @@ -3,7 +3,7 @@ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; define("kopi/app", function(require, exports, module) { - var $, App, appInstance, array, baseURL, events, hist, klass, loc, logger, logging, overlays, settings, support, text, uri, utils, viewport, win; + var $, App, array, baseURL, events, hist, klass, loc, logger, logging, overlays, settings, support, text, uri, utils, viewport, win; $ = require("jquery"); settings = require("kopi/settings"); @@ -22,7 +22,6 @@ loc = location; baseURL = uri.current(); logger = logging.logger(module.id); - appInstance = null; /* Application class @@ -63,6 +62,8 @@ App.UNLOCK_EVENT = "unlock"; + klass.singleton(App); + klass.configure(App); klass.accessor(App.prototype, "router"); @@ -73,10 +74,7 @@ if (options == null) { options = {}; } - if (appInstance) { - throw new Error("Only one app can be initialized in current page"); - } - appInstance = this; + this._isSingleton(); self = this; self.guid = utils.guid("app"); self.started = false; @@ -134,7 +132,7 @@ cls = this.constructor; self = this; if (self.started) { - logger.warn("App has already been launched."); + logger.warn("[app:start] App has already been launched."); return self; } self.container = $("body"); @@ -142,7 +140,7 @@ self.viewport.skeleton().render(); self._listenToURLChange(); self.emit(cls.START_EVENT); - logger.info("Start app: " + self.guid); + logger.info("[app:start] Start app: " + self.guid); self.started = true; if (!(support.history && self._options.usePushState)) { self.load(self._options.startURL || self.getCurrentURL()); @@ -156,7 +154,6 @@ self = this; self._stopListenToURLChange(); self.started = false; - appInstance = null; return self; }; @@ -178,7 +175,7 @@ App.prototype.load = function(url, options) { var cls, self, state; - logger.info("Load URL: " + url); + logger.info("[app:load] Load URL: " + url); cls = this.constructor; self = this; url = uri.parse(uri.absolute(url)); @@ -211,15 +208,15 @@ App.prototype.onrequest = function(e, url, options) { var cls, isUpdate, match, request, self, view; - logger.info("Receive request: " + url.path); + logger.info("[app:onrequest] Receive request: " + url.path); self = this; cls = this.constructor; match = self._match(url); if (!match) { - logger.info("No matching view found."); + logger.info("[app:onrequest] No matching view found."); if (self._options.redirectWhenNoRouteFound) { url = uri.unparse(url); - logger.info("Redirect to URL: " + url); + logger.info("[app:onrequest] Redirect to URL: " + url); uri.goto(url); } return; @@ -268,7 +265,7 @@ self._useHash = true; self._interval = setInterval(checkFn, self._options.interval); } else { - logger.warn("App will not repond to url change"); + logger.warn("[app:_listenToURLChange] App will not repond to url change"); } }; @@ -329,19 +326,20 @@ var guid, matches, param, path, request, route, self, view, _i, _len, _ref, _ref1; if (!this._router) { - return logger.warn("Router is not provided"); + return logger.warn("[app:_match] Router is not provided"); } self = this; path = uri.parse(url).path; request = this._router.match(path); if (!request) { - return logger.warn("Can not find proper route for path: " + path); + return logger.warn("[app:_match] Can not find proper route for path: " + path); } route = request.route; _ref = self._views; for (guid in _ref) { view = _ref[guid]; - if (route.group === true && view.name === route.view.name) { + if (route.group === true && view.constructor.viewName() === route.view.viewName()) { + logger.log("[app:_match] group: true"); return [view, request]; } else if (text.isString(route.group)) { if (view.params[route.group] === route.params[route.group]) { @@ -377,7 +375,7 @@ return { App: App, instance: function() { - return appInstance; + return App.instance(); } }; }); diff --git a/lib/js/kopi/utils/klass.js b/lib/js/kopi/utils/klass.js index ce9a037..86ae08e 100644 --- a/lib/js/kopi/utils/klass.js +++ b/lib/js/kopi/utils/klass.js @@ -4,6 +4,7 @@ define("kopi/utils/klass", function(require, exports, module) { var SingletonError, accessor, configure, create, include, object, reader, singleton; + object = require("kopi/utils/object"); SingletonError = require("kopi/exceptions").SingletonError; /* @@ -16,6 +17,7 @@ create = function(name, parent) { var ctor, key, klass, value; + if (parent) { klass = function() { return parent.constructor.apply(this, arguments); @@ -47,6 +49,7 @@ include = function(klass, mixin) { var method, name, _ref; + for (name in mixin) { if (!__hasProp.call(mixin, name)) continue; method = mixin[name]; @@ -64,6 +67,7 @@ }; configure = function(klass, options) { var _base; + klass._options || (klass._options = {}); if (options) { klass._options = object.extend({}, klass._options, options); @@ -92,6 +96,7 @@ accessor = function(klass, method, property) { var name; + if (property == null) { property = {}; } @@ -132,6 +137,7 @@ reader = function(klass, method, property) { var name; + if (property == null) { property = {}; } @@ -158,6 +164,7 @@ singleton = function(klass) { var instance; + instance = null; klass.instance = function() { return instance; diff --git a/src/coffee/kopi/app.coffee b/src/coffee/kopi/app.coffee index e36d2f4..9fb5acf 100644 --- a/src/coffee/kopi/app.coffee +++ b/src/coffee/kopi/app.coffee @@ -18,7 +18,6 @@ define "kopi/app", (require, exports, module) -> loc = location baseURL = uri.current() logger = logging.logger(module.id) - appInstance = null ### Application class @@ -55,15 +54,14 @@ define "kopi/app", (require, exports, module) -> @LOCK_EVENT = "lock" @UNLOCK_EVENT = "unlock" + klass.singleton this + klass.configure this klass.accessor @prototype, "router" constructor: (options={}) -> - # Make sure only one app is launched in current page - throw new Error("Only one app can be initialized in current page") if appInstance - # Set singleton of application - appInstance = this + @_isSingleton() self = this self.guid = utils.guid("app") @@ -107,7 +105,7 @@ define "kopi/app", (require, exports, module) -> cls = this.constructor self = this if self.started - logger.warn("App has already been launched.") + logger.warn("[app:start] App has already been launched.") return self # Ensure layout elements self.container = $("body") @@ -115,7 +113,7 @@ define "kopi/app", (require, exports, module) -> self.viewport.skeleton().render() self._listenToURLChange() self.emit(cls.START_EVENT) - logger.info "Start app: #{self.guid}" + logger.info "[app:start] Start app: #{self.guid}" self.started = true # Load current URL unless support.history and self._options.usePushState @@ -126,7 +124,6 @@ define "kopi/app", (require, exports, module) -> self = this self._stopListenToURLChange() self.started = false - appInstance = null self getCurrentURL: -> @@ -140,7 +137,7 @@ define "kopi/app", (require, exports, module) -> @param {Hash} options ### load: (url, options) -> - logger.info "Load URL: #{url}" + logger.info "[app:load] Load URL: #{url}" cls = this.constructor self = this url = uri.parse uri.absolute(url) @@ -169,16 +166,16 @@ define "kopi/app", (require, exports, module) -> @param {kopi.utils.uri.URI} url ### onrequest: (e, url, options) -> - logger.info "Receive request: #{url.path}" + logger.info "[app:onrequest] Receive request: #{url.path}" self = this cls = this.constructor match = self._match(url) if not match - logger.info "No matching view found." + logger.info "[app:onrequest] No matching view found." if self._options.redirectWhenNoRouteFound url = uri.unparse url - logger.info("Redirect to URL: #{url}") + logger.info("[app:onrequest] Redirect to URL: #{url}") uri.goto url return @@ -224,7 +221,7 @@ define "kopi/app", (require, exports, module) -> self._useHash = true self._interval = setInterval checkFn, self._options.interval else - logger.warn("App will not repond to url change") + logger.warn("[app:_listenToURLChange] App will not repond to url change") return ### @@ -269,20 +266,21 @@ define "kopi/app", (require, exports, module) -> @return {kopi.views.View} ### _match: (url) -> - return logger.warn "Router is not provided" unless @_router + return logger.warn "[app:_match] Router is not provided" unless @_router self = this path = uri.parse(url).path request = @_router.match(path) # If no proper router is found - return logger.warn("Can not find proper route for path: #{path}") unless request + return logger.warn("[app:_match] Can not find proper route for path: #{path}") unless request route = request.route for guid, view of self._views # If `group` is `true`, use same view for every URL matches route - if route.group is true and view.name == route.view.name - return [view, request] + if route.group is true and view.constructor.viewName() == route.view.viewName() + logger.log "[app:_match] group: true" + return [view, request] # If `group` is `string`, use same view for every URL matches route else if text.isString(route.group) if view.params[route.group] == route.params[route.group] @@ -307,4 +305,7 @@ define "kopi/app", (require, exports, module) -> [view, request] App: App - instance: -> appInstance + # DEPRECATED + # Use App.instance() instead + # -- Wu Yuntao, 2013-07-01 + instance: -> App.instance() diff --git a/src/coffee/kopi/utils/klass.coffee b/src/coffee/kopi/utils/klass.coffee index c7a0dfa..9d19b7f 100644 --- a/src/coffee/kopi/utils/klass.coffee +++ b/src/coffee/kopi/utils/klass.coffee @@ -112,8 +112,12 @@ define "kopi/utils/klass", (require, exports, module) -> ### singleton = (klass) -> instance = null + # Class method to get singleton klass.instance = -> instance + # Class method to remove singleton klass.removeInstance = -> instance = null + # A private instance method to check if instance + # can be initialized as a singleton klass.prototype._isSingleton = -> throw new SingletonError(klass) if instance instance = this