Path: blob/trunk/third_party/closure/goog/disposable/disposable.js
2868 views
// Copyright 2005 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Implements the disposable interface. The dispose method is used16* to clean up references and resources.17* @author [email protected] (Erik Arvidsson)18*/192021goog.provide('goog.Disposable');22goog.provide('goog.dispose');23goog.provide('goog.disposeAll');2425goog.require('goog.disposable.IDisposable');26272829/**30* Class that provides the basic implementation for disposable objects. If your31* class holds one or more references to COM objects, DOM nodes, or other32* disposable objects, it should extend this class or implement the disposable33* interface (defined in goog.disposable.IDisposable).34* @constructor35* @implements {goog.disposable.IDisposable}36*/37goog.Disposable = function() {38/**39* If monitoring the goog.Disposable instances is enabled, stores the creation40* stack trace of the Disposable instance.41* @type {string|undefined}42*/43this.creationStack;4445if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {46if (goog.Disposable.INCLUDE_STACK_ON_CREATION) {47this.creationStack = new Error().stack;48}49goog.Disposable.instances_[goog.getUid(this)] = this;50}51// Support sealing52this.disposed_ = this.disposed_;53this.onDisposeCallbacks_ = this.onDisposeCallbacks_;54};555657/**58* @enum {number} Different monitoring modes for Disposable.59*/60goog.Disposable.MonitoringMode = {61/**62* No monitoring.63*/64OFF: 0,65/**66* Creating and disposing the goog.Disposable instances is monitored. All67* disposable objects need to call the {@code goog.Disposable} base68* constructor. The PERMANENT mode must be switched on before creating any69* goog.Disposable instances.70*/71PERMANENT: 1,72/**73* INTERACTIVE mode can be switched on and off on the fly without producing74* errors. It also doesn't warn if the disposable objects don't call the75* {@code goog.Disposable} base constructor.76*/77INTERACTIVE: 278};798081/**82* @define {number} The monitoring mode of the goog.Disposable83* instances. Default is OFF. Switching on the monitoring is only84* recommended for debugging because it has a significant impact on85* performance and memory usage. If switched off, the monitoring code86* compiles down to 0 bytes.87*/88goog.define('goog.Disposable.MONITORING_MODE', 0);899091/**92* @define {boolean} Whether to attach creation stack to each created disposable93* instance; This is only relevant for when MonitoringMode != OFF.94*/95goog.define('goog.Disposable.INCLUDE_STACK_ON_CREATION', true);969798/**99* Maps the unique ID of every undisposed {@code goog.Disposable} object to100* the object itself.101* @type {!Object<number, !goog.Disposable>}102* @private103*/104goog.Disposable.instances_ = {};105106107/**108* @return {!Array<!goog.Disposable>} All {@code goog.Disposable} objects that109* haven't been disposed of.110*/111goog.Disposable.getUndisposedObjects = function() {112var ret = [];113for (var id in goog.Disposable.instances_) {114if (goog.Disposable.instances_.hasOwnProperty(id)) {115ret.push(goog.Disposable.instances_[Number(id)]);116}117}118return ret;119};120121122/**123* Clears the registry of undisposed objects but doesn't dispose of them.124*/125goog.Disposable.clearUndisposedObjects = function() {126goog.Disposable.instances_ = {};127};128129130/**131* Whether the object has been disposed of.132* @type {boolean}133* @private134*/135goog.Disposable.prototype.disposed_ = false;136137138/**139* Callbacks to invoke when this object is disposed.140* @type {Array<!Function>}141* @private142*/143goog.Disposable.prototype.onDisposeCallbacks_;144145146/**147* @return {boolean} Whether the object has been disposed of.148* @override149*/150goog.Disposable.prototype.isDisposed = function() {151return this.disposed_;152};153154155/**156* @return {boolean} Whether the object has been disposed of.157* @deprecated Use {@link #isDisposed} instead.158*/159goog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed;160161162/**163* Disposes of the object. If the object hasn't already been disposed of, calls164* {@link #disposeInternal}. Classes that extend {@code goog.Disposable} should165* override {@link #disposeInternal} in order to delete references to COM166* objects, DOM nodes, and other disposable objects. Reentrant.167*168* @return {void} Nothing.169* @override170*/171goog.Disposable.prototype.dispose = function() {172if (!this.disposed_) {173// Set disposed_ to true first, in case during the chain of disposal this174// gets disposed recursively.175this.disposed_ = true;176this.disposeInternal();177if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {178var uid = goog.getUid(this);179if (goog.Disposable.MONITORING_MODE ==180goog.Disposable.MonitoringMode.PERMANENT &&181!goog.Disposable.instances_.hasOwnProperty(uid)) {182throw Error(183this + ' did not call the goog.Disposable base ' +184'constructor or was disposed of after a clearUndisposedObjects ' +185'call');186}187delete goog.Disposable.instances_[uid];188}189}190};191192193/**194* Associates a disposable object with this object so that they will be disposed195* together.196* @param {goog.disposable.IDisposable} disposable that will be disposed when197* this object is disposed.198*/199goog.Disposable.prototype.registerDisposable = function(disposable) {200this.addOnDisposeCallback(goog.partial(goog.dispose, disposable));201};202203204/**205* Invokes a callback function when this object is disposed. Callbacks are206* invoked in the order in which they were added. If a callback is added to207* an already disposed Disposable, it will be called immediately.208* @param {function(this:T):?} callback The callback function.209* @param {T=} opt_scope An optional scope to call the callback in.210* @template T211*/212goog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) {213if (this.disposed_) {214goog.isDef(opt_scope) ? callback.call(opt_scope) : callback();215return;216}217if (!this.onDisposeCallbacks_) {218this.onDisposeCallbacks_ = [];219}220221this.onDisposeCallbacks_.push(222goog.isDef(opt_scope) ? goog.bind(callback, opt_scope) : callback);223};224225226/**227* Deletes or nulls out any references to COM objects, DOM nodes, or other228* disposable objects. Classes that extend {@code goog.Disposable} should229* override this method.230* Not reentrant. To avoid calling it twice, it must only be called from the231* subclass' {@code disposeInternal} method. Everywhere else the public232* {@code dispose} method must be used.233* For example:234* <pre>235* mypackage.MyClass = function() {236* mypackage.MyClass.base(this, 'constructor');237* // Constructor logic specific to MyClass.238* ...239* };240* goog.inherits(mypackage.MyClass, goog.Disposable);241*242* mypackage.MyClass.prototype.disposeInternal = function() {243* // Dispose logic specific to MyClass.244* ...245* // Call superclass's disposeInternal at the end of the subclass's, like246* // in C++, to avoid hard-to-catch issues.247* mypackage.MyClass.base(this, 'disposeInternal');248* };249* </pre>250* @protected251*/252goog.Disposable.prototype.disposeInternal = function() {253if (this.onDisposeCallbacks_) {254while (this.onDisposeCallbacks_.length) {255this.onDisposeCallbacks_.shift()();256}257}258};259260261/**262* Returns True if we can verify the object is disposed.263* Calls {@code isDisposed} on the argument if it supports it. If obj264* is not an object with an isDisposed() method, return false.265* @param {*} obj The object to investigate.266* @return {boolean} True if we can verify the object is disposed.267*/268goog.Disposable.isDisposed = function(obj) {269if (obj && typeof obj.isDisposed == 'function') {270return obj.isDisposed();271}272return false;273};274275276/**277* Calls {@code dispose} on the argument if it supports it. If obj is not an278* object with a dispose() method, this is a no-op.279* @param {*} obj The object to dispose of.280*/281goog.dispose = function(obj) {282if (obj && typeof obj.dispose == 'function') {283obj.dispose();284}285};286287288/**289* Calls {@code dispose} on each member of the list that supports it. (If the290* member is an ArrayLike, then {@code goog.disposeAll()} will be called291* recursively on each of its members.) If the member is not an object with a292* {@code dispose()} method, then it is ignored.293* @param {...*} var_args The list.294*/295goog.disposeAll = function(var_args) {296for (var i = 0, len = arguments.length; i < len; ++i) {297var disposable = arguments[i];298if (goog.isArrayLike(disposable)) {299goog.disposeAll.apply(null, disposable);300} else {301goog.dispose(disposable);302}303}304};305306307