define([ "dojo/_base/array", // array.forEach array.map "dojo/aspect", "dojo/_base/declare" ], function(array, aspect, declare){ // module: // dijit/Destroyable return declare("dijit.Destroyable", null, { // summary: // Mixin to track handles and release them when instance is destroyed. // description: // Call this.own(...) on list of handles (returned from dojo/aspect, dojo/on, // dojo/Stateful::watch, or any class (including widgets) with a destroyRecursive() or destroy() method. // Then call destroy() later to destroy this instance and release the resources. destroy: function(/*Boolean*/ preserveDom){ // summary: // Destroy this class, releasing any resources registered via own(). this._destroyed = true; }, own: function(){ // summary: // Track specified handles and remove/destroy them when this instance is destroyed, unless they were // already removed/destroyed manually. // tags: // protected // returns: // The array of specified handles, so you can do for example: // | var handle = this.own(on(...))[0]; var cleanupMethods = [ "destroyRecursive", "destroy", "remove" ]; array.forEach(arguments, function(handle){ // When this.destroy() is called, destroy handle. Since I'm using aspect.before(), // the handle will be destroyed before a subclass's destroy() method starts running, before it calls // this.inherited() or even if it doesn't call this.inherited() at all. If that's an issue, make an // onDestroy() method and connect to that instead. var destroyMethodName; var odh = aspect.before(this, "destroy", function (preserveDom){ handle[destroyMethodName](preserveDom); }); // Callback for when handle is manually destroyed. var hdhs = []; function onManualDestroy(){ odh.remove(); array.forEach(hdhs, function(hdh){ hdh.remove(); }); } // Setup listeners for manual destroy of handle. // Also computes destroyMethodName, used in listener above. if(handle.then){ // Special path for Promises. Detect when Promise is resolved, rejected, or // canceled (nb: cancelling a Promise causes it to be rejected). destroyMethodName = "cancel"; handle.then(onManualDestroy, onManualDestroy); }else{ // Path for other handles. Just use AOP to detect when handle is manually destroyed. array.forEach(cleanupMethods, function(cleanupMethod){ if(typeof handle[cleanupMethod] === "function"){ if(!destroyMethodName){ // Use first matching method name in above listener (prefer destroyRecursive() to destroy()) destroyMethodName = cleanupMethod; } hdhs.push(aspect.after(handle, cleanupMethod, onManualDestroy, true)); } }); } }, this); return arguments; // handle } }); });