Permalink
Browse files

Initial entry of PureMVC JavaScript (old style) implementation.

  • Loading branch information...
1 parent f67607f commit 5192b872d4ba8a6a7368843ce56e00fb0137e8a9 @guilhermeblanco committed Jan 8, 2012
Showing with 3,050 additions and 0 deletions.
  1. +3 −0 .gitmodules
  2. +129 −0 src/org/puremvc/js/Init.js
  3. +207 −0 src/org/puremvc/js/core/Controller.js
  4. +153 −0 src/org/puremvc/js/core/Model.js
  5. +299 −0 src/org/puremvc/js/core/View.js
  6. +128 −0 src/org/puremvc/js/patterns/command/MacroCommand.js
  7. +40 −0 src/org/puremvc/js/patterns/command/SimpleCommand.js
  8. +479 −0 src/org/puremvc/js/patterns/facade/Facade.js
  9. +129 −0 src/org/puremvc/js/patterns/mediator/Mediator.js
  10. +127 −0 src/org/puremvc/js/patterns/observer/Notification.js
  11. +80 −0 src/org/puremvc/js/patterns/observer/Notifier.js
  12. +119 −0 src/org/puremvc/js/patterns/observer/Observer.js
  13. +103 −0 src/org/puremvc/js/patterns/proxy/Proxy.js
  14. +45 −0 tests/index.html
  15. +25 −0 tests/mocks/CommandMock.js
  16. +32 −0 tests/mocks/MediatorMock.js
  17. +32 −0 tests/mocks/ProxyMock.js
  18. +70 −0 tests/org/puremvc/js/Init.js
  19. +70 −0 tests/org/puremvc/js/core/Controller.js
  20. +112 −0 tests/org/puremvc/js/core/Model.js
  21. +142 −0 tests/org/puremvc/js/core/View.js
  22. +24 −0 tests/org/puremvc/js/patterns/command/MacroCommand.js
  23. +14 −0 tests/org/puremvc/js/patterns/command/SimpleCommand.js
  24. +285 −0 tests/org/puremvc/js/patterns/facade/Facade.js
  25. +60 −0 tests/org/puremvc/js/patterns/mediator/Mediator.js
  26. +28 −0 tests/org/puremvc/js/patterns/observer/Notification.js
  27. +27 −0 tests/org/puremvc/js/patterns/observer/Notifier.js
  28. +41 −0 tests/org/puremvc/js/patterns/observer/Observer.js
  29. +46 −0 tests/org/puremvc/js/patterns/proxy/Proxy.js
  30. +1 −0 tests/vendors/qunit
View
@@ -0,0 +1,3 @@
+[submodule "tests/vendors/qunit"]
+ path = tests/vendors/qunit
+ url = https://github.com/jquery/qunit.git
View
@@ -0,0 +1,129 @@
+var org = {
+ puremvc: {
+ js: {
+ core: {},
+ patterns: {}
+ }
+ }
+ },
+ Relegate = {};
+
+Object.extend = function (child, parent)
+{
+ var tmp = function () {};
+
+ tmp.prototype = parent.prototype;
+
+ child.superClass = parent.prototype;
+ child.prototype = new tmp();
+
+ child.prototype.constructor = child;
+};
+
+Object.declare = function (fqcn, obj, scope)
+{
+ var parts = fqcn.split('.'),
+ curPart,
+ curScope,
+ curObj;
+
+ scope = scope || window;
+ obj = obj || {};
+
+ curScope = scope;
+
+ while ((curPart = parts.shift()) !== undefined) {
+ curObj = (parts.length > 0)
+ ? ((typeof curScope[curPart] !== 'undefined') ? curScope[curPart] : {})
+ : obj;
+
+ curScope[curPart] = curObj;
+
+ curScope = curScope[curPart];
+ }
+
+ return curScope;
+};
+
+Relegate.create = function (scope, func)
+{
+ var ctorParams = [],
+ i, iLength;
+
+ for (i = 2, iLength = arguments.length; i < iLength; i += 1) {
+ ctorParams.push(arguments[i]);
+ }
+
+ return function ()
+ {
+ var funcParams = [],
+ j, jLength;
+
+ for (j = 0, jLength = arguments.length; j < jLength; j += 1) {
+ funcParams.push(arguments[j]);
+ }
+
+ return func.apply(scope, funcParams.concat(ctorParams));
+ };
+};
+
+Date.fromISO8601 = function (str)
+{
+ var regexp = /(\d\d\d\d)(-)?(\d\d)(-)?(\d\d)(T)?(\d\d)(:)?(\d\d)(:)?(\d\d)(\.\d+)?(Z|([+-])(\d\d)(:)?(\d\d))/,
+ date = new Date(),
+ offset,
+ match;
+
+ if ( ! str.toString().match(new RegExp(regexp))) {
+ date.setTime(Date.parse(str));
+
+ return date;
+ }
+
+ match = str.match(new RegExp(regexp));
+ offset = 0;
+
+ date.setUTCDate(1);
+ date.setUTCFullYear(parseInt(match[1],10));
+ date.setUTCMonth(parseInt(match[3],10) - 1);
+ date.setUTCDate(parseInt(match[5],10));
+ date.setUTCHours(parseInt(match[7],10));
+ date.setUTCMinutes(parseInt(match[9],10));
+ date.setUTCSeconds(parseInt(match[11],10));
+ date.setUTCMilliseconds((match[12]) ? parseFloat(match[12]) * 1000 : 0);
+
+ if (match[13] != 'Z') {
+ offset = (match[15] * 60) + parseInt(match[17],10);
+ offset *= ((match[14] == '-') ? -1 : 1);
+
+ date.setTime(date.getTime() - offset * 60 * 1000);
+ }
+
+ return date;
+};
+
+Object.declare('org.puremvc.js.Init', function (d, path) {
+ var lastPathChar = path.substr(path.length - 1),
+ scriptOpenTag = '<script type="text/javascript" src="',
+ scriptCloseTag = '"></script>',
+ puremvcPath = ((lastPathChar === '/' || lastPathChar === '\\') ? path : path + '/'),
+ puremvcFiles = [
+ 'org/puremvc/js/patterns/observer/Notifier.js',
+ 'org/puremvc/js/patterns/observer/Notification.js',
+ 'org/puremvc/js/patterns/observer/Observer.js',
+ 'org/puremvc/js/patterns/command/SimpleCommand.js',
+ 'org/puremvc/js/patterns/command/MacroCommand.js',
+ 'org/puremvc/js/patterns/mediator/Mediator.js',
+ 'org/puremvc/js/patterns/proxy/Proxy.js',
+ 'org/puremvc/js/patterns/facade/Facade.js',
+ 'org/puremvc/js/core/Model.js',
+ 'org/puremvc/js/core/View.js',
+ 'org/puremvc/js/core/Controller.js',
+ ],
+ filesLength = puremvcFiles.length,
+ i;
+
+ for (i = 0; i < filesLength; i = i + 1) {
+ d.write(scriptOpenTag + puremvcPath + puremvcFiles[i] + scriptCloseTag);
+ }
+});
@@ -0,0 +1,207 @@
+/**
+ * A Singleton <code>Controller</code> implementation.
+ *
+ * <P>
+ * In PureMVC, the <code>Controller</code> class follows the 'Command and Controller' strategy, and assumes
+ * these responsibilities:
+ *
+ * <UL>
+ * <LI>
+ * Remembering which <code>Command</code>s are intended to handle which <code>Notifications</code>.
+ * </LI>
+ *
+ * <LI>
+ * Registering itself as an <code>Observer</code> with the <code>View</code> for each
+ * <code>Notification</code> that it has an <code>Command</code> mapping for.
+ * </LI>
+ *
+ * <LI>
+ * Creating a new instance of the proper <code>Command</code> to handle a given <code>Notification</code>
+ * when notified by the <code>View</code>.
+ * </LI>
+ *
+ * <LI>
+ * Calling the <code>Command</code>'s <code>execute</code> method, passing in the <code>Notification</code>.
+ * </LI>
+ * </UL>
+ * </P>
+ *
+ * <P>
+ * Your application must register <code>Command</code>s with the Controller.
+ * </P>
+ *
+ * <P>
+ * The simplest way is to subclass <code>Facade</code>, and use its <code>_initializeController</code> method
+ * to add your registrations.
+ * </P>
+ *
+ * @see org.puremvc.js.core.View
+ * @see org.puremvc.js.patterns.observer.Observer
+ * @see org.puremvc.js.patterns.observer.Notification
+ * @see org.puremvc.js.patterns.command.SimpleCommand
+ * @see org.puremvc.js.patterns.command.MacroCommand
+ */
+Object.declare('org.puremvc.js.core.Controller');
+
+/**
+ * Constructor.
+ *
+ * <P>
+ * This <code>Controller</code> implementation is a Singleton, so you should not call the constructor
+ * directly, but instead call the static Singleton Factory method <code>Controller.getInstance()</code>
+ * </P>
+ *
+ * @throws Error Error if Singleton instance has already been constructed
+ */
+org.puremvc.js.core.Controller = function ()
+{
+ if (org.puremvc.js.core.Controller.instance !== null) {
+ throw new Error(org.puremvc.js.core.Controller.SINGLETON_MESSAGE);
+ }
+
+ org.puremvc.js.core.Controller.instance = this;
+
+ this._commandMap = {};
+
+ this._initializeController();
+};
+
+// Singleton instance
+org.puremvc.js.core.Controller.instance = null;
+
+// Contant messages
+org.puremvc.js.core.Controller.SINGLETON_MESSAGE = 'Controller Singleton already constructed!';
+
+/**
+ * <code>Controller</code> Singleton Factory method.
+ *
+ * @return Controller the Singleton instance of <code>Controller</code>
+ */
+org.puremvc.js.core.Controller.getInstance = function ()
+{
+ if (org.puremvc.js.core.Controller.instance === null) {
+ org.puremvc.js.core.Controller.instance = new org.puremvc.js.core.Controller();
+ }
+
+ return org.puremvc.js.core.Controller.instance;
+};
+
+var _p = org.puremvc.js.core.Controller.prototype;
+
+// Mapping of Notification names to Command Class references
+_p._commandMap = null;
+
+// Local reference to View
+_p._view = null;
+
+/**
+ * Initialize the Singleton <code>Controller</code> instance.
+ *
+ * <P>Called automatically by the constructor.</P>
+ *
+ * <P>Note that if you are using a subclass of <code>View</code>
+ * in your application, you should <i>also</i> subclass <code>Controller</code>
+ * and override the <code>_initializeController</code> method in the
+ * following way:</P>
+ *
+ * <listing>
+ * // ensure that the Controller is talking to my View implementation
+ * _p._initializeController = function ()
+ * {
+ * this._view = MyView.getInstance();
+ * };
+ * </listing>
+ *
+ * @return void
+ */
+_p._initializeController = function ()
+{
+ this._view = org.puremvc.js.core.View.getInstance();
+};
+
+/**
+ * If an <code>Command</code> has previously been registered to handle a the given
+ * <code>Notification</code>, then it is executed.
+ *
+ * @param notification <code>Notification</code>
+ *
+ * @return void
+ */
+_p.executeCommand = function (notification)
+{
+ var notificationName = notification.getName(),
+ commandClassRef,
+ commandInstance;
+
+ if ( ! this.hasCommand(notificationName)) {
+ return;
+ }
+
+ commandClassRef = this._commandMap[notificationName],
+ commandInstance = new commandClassRef();
+
+ commandInstance.execute(notification);
+};
+
+/**
+ * Register a particular <code>Command</code> class as the handler for a particular
+ * <code>Notification</code>.
+ *
+ * <P>
+ * If an <code>Command</code> has already been registered to handle <code>Notification</code>s
+ * with this name, it is no longer used, the new <code>Command</code> is used instead.
+ * </P>
+ *
+ * <P>
+ * The Observer for the new ICommand is only created if this the first time an ICommand has been
+ * regisered for this Notification name.
+ * </P>
+ *
+ * @param notificationName the name of the <code>Notification</code>
+ * @param commandClassRef the <code>Class</code> of the <code>Command</code>
+ *
+ * @return void
+ */
+_p.registerCommand = function (notificationName, commandClassRef)
+{
+ if ( ! this.hasCommand(notificationName)) {
+ this._view.registerObserver(
+ notificationName,
+ new org.puremvc.js.patterns.observer.Observer(this.executeCommand, this)
+ );
+ }
+
+ this._commandMap[notificationName] = commandClassRef;
+};
+
+/**
+ * Check if a Command is registered for a given Notification
+ *
+ * @param notificationName
+ *
+ * @return boolean Whether a Command is currently registered for the given <code>notificationName</code>.
+ */
+_p.hasCommand = function (notificationName)
+{
+ return (typeof this._commandMap[notificationName] !== 'undefined');
+};
+
+/**
+ * Remove a previously registered <code>Command</code> to <code>Notification</code> mapping.
+ *
+ * @param notificationName The name of the <code>Notification</code> to remove the <code>Command</code> mapping for.
+ *
+ * @return void
+ */
+_p.removeCommand = function (notificationName)
+{
+ // If the Command is registered...
+ if (this.hasCommand(notificationName)) {
+ // Remove the observer
+ this._view.removeObserver(notificationName, this);
+
+ // Remove the command
+ this._commandMap[notificationName] = null;
+ delete this._commandMap[notificationName];
+ }
+};
Oops, something went wrong.

0 comments on commit 5192b87

Please sign in to comment.