1 if (!("MBX" in window)) { 2 /** @namespace 3 @ignore 4 */ 5 MBX = {}; 6 } 7 8 /** 9 Create and extend controllers 10 @class 11 */ 12 MBX.JsController = (function () { 13 /** 14 public methods of JsController 15 @memberof MBX.JsController 16 @namespace 17 */ 18 var publicObj = {}; 19 20 /** 21 used to cache instances of controllers and stop name collisions 22 @memberof MBX.JsController 23 @private 24 */ 25 var controllerCache = {}; 26 27 28 /** @private */ 29 var jsElementClass = '.js_updateable'; 30 31 /** 32 The instance that gets created from calling 33 MBX.JsController.create(name, opts) 34 @see MBX.JsController 35 @name JsController 36 @class 37 */ 38 JsController = function (name, opts) { 39 opts = opts || {}; 40 if (!name) { 41 throw new Error("A name must be specified"); 42 } 43 if (controllerCache[name]) { 44 throw new Error("A controller by that name exists"); 45 } 46 47 this.controllerName = name; 48 Object.extend(this, opts); 49 if (this.model) { 50 this._subscribeToEvents(); 51 } 52 controllerCache[name] = this; 53 54 MBX.EventHandler.fireCustom(MBX, publicObj.Event.newController, { 55 object: this 56 }); 57 }; 58 59 JsController.prototype = 60 /** @lends JsController */ 61 { 62 63 active: true, 64 65 /** reactivate event listening on this controller 66 */ 67 activate: function () { 68 this.active = true; 69 }, 70 71 /** quiets all events on this controller, your callbacks will 72 not get called 73 */ 74 deactivate: function () { 75 this.active = false; 76 }, 77 78 /** 79 If you have passed in a function for onInstanceChange 80 then this will pass the object and the key that changed to 81 your function 82 @requires MBX.JsModel 83 @requires this.model 84 @see JsModel#instance 85 @example 86 myController = MBX.JsController.create("myController", { 87 onInstanceChange: function (modelInstance, key) { 88 // where key will be the attribute (string) 89 // that has changed on the modelInstance (JsModel#instance) 90 } 91 }); 92 */ 93 _onInstanceChange: function (evt) { 94 if (this.onInstanceChange && this.active) { 95 this.onInstanceChange(evt.object, evt.key); 96 } 97 }, 98 99 /** 100 @private 101 @requires MBX.JsModel 102 @requires this.model 103 */ 104 _onInstanceCreate: function (evt) { 105 if (this.onInstanceCreate && this.active) { 106 this.onInstanceCreate(evt.object); 107 } 108 }, 109 110 fireAfterRender: function () { 111 MBX.EventHandler.fireCustom(MBX, this.controllerName + "_" + MBX.JsController.Event.afterRender); 112 }, 113 114 /** 115 @private 116 @requires MBX.JsModel 117 @requires this.model 118 */ 119 _onInstanceDestroy: function (evt) { 120 if (this.onInstanceDestroy && this.active) { 121 this.onInstanceDestroy(evt.object); 122 } 123 }, 124 125 /** 126 @private 127 @requires MBX.JsModel 128 @requires this.model 129 */ 130 _onAttributeChange: function (evt) { 131 if (this.active && typeof this.onAttributeChange == 'function') { 132 this.onAttributeChange(evt.key); 133 } 134 }, 135 136 /** 137 @private 138 @requires MBX.JsModel 139 @requires this.model 140 141 */ 142 _subscribeToEvents: function () { 143 var model = this.model; 144 if (model.constructor != Array) { 145 model = [model]; 146 } 147 148 model.each(function (model) { 149 var changeEvent = model.Event.changeInstance; 150 var newEvent = model.Event.newInstance; 151 var destroyEvent = model.Event.destroyInstance; 152 var attributeEvent = model.Event.changeAttribute; 153 var defer = this.looselyCoupled; 154 155 this.eventSubscriptions = []; 156 this.eventSubscriptions.push(MBX.EventHandler.subscribe(MBX, changeEvent, this._onInstanceChange.bind(this), {defer: defer})); 157 this.eventSubscriptions.push(MBX.EventHandler.subscribe(MBX, newEvent, this._onInstanceCreate.bind(this), {defer: defer})); 158 this.eventSubscriptions.push(MBX.EventHandler.subscribe(MBX, destroyEvent, this._onInstanceDestroy.bind(this), {defer: defer})); 159 this.eventSubscriptions.push(MBX.EventHandler.subscribe(MBX, attributeEvent, this._onAttributeChange.bind(this), {defer: defer})); 160 }.bind(this)); 161 }, 162 163 /** 164 @private 165 @requires MBX.JsModel 166 @requires this.model 167 */ 168 _unsubscribeToEvents: function () { 169 if (this.eventSubscriptions && this.eventSubscriptions[0]) { 170 this.eventSubscriptions.each(function (subscription) { 171 MBX.EventHandler.unsubscribe(subscription); 172 }); 173 } 174 } 175 }; 176 177 /** 178 This is mostly used internally and is fired on MBX everytime a controller is created 179 @memberOf MBX.JsController 180 @name MBX.JsController.Event 181 */ 182 publicObj.Event = { 183 newController: "new_controller", 184 afterRender: "render_finished" 185 }; 186 187 /** 188 call extend() to add methods and/or attributes to ALL controllers 189 @param {Object} methsAndAttrs 190 @name MBX.JsController.extend 191 @function 192 */ 193 publicObj.extend = function (methsAndAttrs) { 194 methsAndAttrs = methsAndAttrs || {}; 195 Object.extend(JsController.prototype, methsAndAttrs); 196 }; 197 198 /** 199 Controllers allow some decently powerful hooks. You can specify a model, and an 200 onInstanceChange, onInstanceDestroy, onInstanceCreate. 201 202 If your controller listens to a model, but you are not dependent on real-time updates, 203 you can add the option "looselyCoupled: true" and all updates will be done with 204 setTimeout, which will be a performance enhancement. 205 206 @name MBX.JsController.create 207 @param {String} name the name of the controller 208 @param {Object} opts used to extend the controller methods at instantiation 209 @see JsController 210 @function 211 @example 212 MBX.DesktopUploadController = MBX.JsController.create("DesktopUpload", { 213 looselyCoupled: false, // false is the default 214 ANewMethod: function (something) { 215 return something; 216 } 217 }) 218 MBX.DesktopUpload.ANewMethod("boo") == "boo"; 219 @example 220 MBX.DesktopUploadController = MBX.JsController.create("DesktopUpload", { 221 model: MBX.DesktopUpload, 222 onInstanceCreate: function (instance) { 223 alert(instance.get('greeting')); 224 } 225 }); 226 MBX.DesktopUpload.create({ greeting: 'hi' }); // will alert('hi'); 227 @example 228 MBX.DesktopUploadController = MBX.JsController.create("DesktopUpload", { 229 model: MBX.DesktopUpload, 230 onInstanceChange: function (instance) { 231 alert(instance.get('greeting')); 232 } 233 }); 234 var instance = MBX.DesktopUpload.create(); 235 instance.set('greeting', 'hi'); // will alert('hi') 236 */ 237 publicObj.create = function (name, opts) { 238 if (controllerCache[name]) { 239 throw new Error("A controller with the name of " + name + " is already in use"); 240 } 241 var controller = new JsController(name, opts); 242 if (typeof controller.initialize == "function") { 243 controller.initialize(); 244 } 245 return controller; 246 }; 247 248 /** 249 Destroy a controller and unsubscribe its event listeners 250 @param {String} name the name of the controller 251 @name MBX.JsController.destroyController 252 @function 253 */ 254 publicObj.destroyController = function (name) { 255 if (controllerCache[name]) { 256 controllerCache[name]._unsubscribeToEvents(); 257 delete controllerCache[name]; 258 } 259 }; 260 261 return publicObj; 262 })(); 263