Created
March 29, 2013 08:54
-
-
Save mklymyshyn/5269616 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function (g) { | |
var VERSION = "0.1.1"; | |
var PREFIX = "___scope_cleaner__"; | |
// check is some variable is array or not | |
var isArray = function (obj) { | |
return obj.constructor === Array; | |
}; | |
// wrapper on Array.indexOf for old browser | |
var inArray = function (arr, key) { | |
// use native implementation if possible | |
if (Array.indexOf) { | |
return arr.indexOf(key); | |
} | |
for (var i = 0, j = arr.length; i < j; i++) { | |
if (arr[i] === key) { | |
return i; | |
} | |
} | |
return -1; | |
}; | |
// make each shortcut | |
var each = function (arr, func) { | |
for (var i = 0, j = arr.length; i < j; i++) { | |
var result = func(arr[i], i); | |
if (typeof result === "boolean" && result === false) { | |
break; | |
} | |
} | |
}; | |
// build array hash | |
var hash = function (arr) { | |
return arr.join("_"); | |
}; | |
// anyone can override "undefined" in global scope | |
// here we use hack to set a "real" undefined to the variable | |
var reservedUndefined = (function (undef) { | |
return undef; | |
})(); | |
// Initialization of Scope class | |
var Scope = function () { | |
this._storage = null; | |
this.prefix = PREFIX; | |
this.VERSION = VERSION; | |
return this; | |
}; | |
// Defining methods for scope | |
Scope.prototype = { | |
// internal method to provide ID of current storage | |
storage: function () { | |
if (this._storage === null) { | |
this._storage = this.prefix + | |
Math.random().toString(36).substring(7); | |
g[this._storage] = []; | |
} | |
return this._storage; | |
}, | |
noConflict: function () { | |
if (typeof g.__ScopePrev !== "undefined") { | |
g.Scope = g.__ScopePrev; | |
} | |
return new Scope(); | |
}, | |
// save state into special variable. | |
// It's possible to specify explicitly what global variables | |
// should be restored on ``Scope.restore()`` call | |
freeze: function (_keys) { | |
var undef = []; | |
var scope = []; | |
var globals = []; | |
var key; | |
var i; | |
var j; | |
var keys = _keys || []; | |
if (!isArray(g[this.storage()])) { | |
g[this.storage()] = []; | |
} | |
if (!isArray(keys)) { | |
keys = Array.prototype.slice.call(arguments); | |
} | |
for (i = 0, j = keys.length; i < j; i++) { | |
key = keys[i]; | |
if (typeof g[key] === "undefined") { | |
undef.push(key); | |
continue; | |
} | |
scope.push([key, g[key]]); | |
} | |
// watcher | |
for (key in g) { | |
globals.push(key); | |
} | |
g[this.storage()].push([undef, scope, globals, []]); | |
}, | |
keep: function (_keys) { | |
var keys = _keys || []; | |
var lastIndex = isArray(g[this.storage()]) ? g[this.storage()].length - 1 : -1; | |
if (!isArray(keys)) { | |
keys = Array.prototype.slice.call(arguments); | |
} | |
if (typeof g[this.storage()][lastIndex] === "undefined") { | |
return; | |
} | |
g[this.storage()][lastIndex][3].concat(keys); | |
}, | |
// Restore state of the variables before .keep() call | |
restore: function (_keepKeys) { | |
var scopeList = g[this.storage()]; | |
var i = 0; | |
var j = 0; | |
var key; | |
var value; | |
var keepKeys = _keepKeys || []; | |
if (!isArray(keepKeys)) { | |
keepKeys = Array.prototype.slice.call(arguments); | |
} | |
if (typeof scopeList === "undefined") { | |
return false; | |
} | |
var scope = g[this.storage()].pop(); | |
if (typeof scope === "undefined") { | |
return false; | |
} | |
var data = scope[1], undef = scope[0], globals = scope[2]; | |
keepKeys = keepKeys.concat(scope[3]); | |
var keepVar = function (key) { | |
return inArray(keepKeys, key) !== -1; | |
}; | |
// restore state of keeped variables | |
for (i = 0, j = data.length; i < j; i++) { | |
key = data[i][0]; | |
value = data[i][1]; | |
if (keepVar(key)) continue; | |
g[key] = value; | |
} | |
// set scope variables to undefined | |
for (i = 0, j = undef.length; i < j; i++) { | |
if (keepVar(undef[i])) continue; | |
if (typeof g[undef[i]] !== "undefined") { | |
if (!delete(g[undef[i]])) { | |
g[undef[i]] = reservedUndefined; | |
} | |
} | |
} | |
// cleanup variables which appears in global scope | |
// after code execution | |
for (key in g) { | |
if (keepVar(key)) continue; | |
if (inArray(globals, key) === -1) { | |
try { | |
delete g[key]; | |
} catch(e) {} | |
g[key] = reservedUndefined; | |
} | |
} | |
return true; | |
}, | |
// Scope.keep() may be called several times. In this | |
// case it behave like Semaphore and you should | |
// call equal amount Scope.restore(). Or you can | |
// call Scope.restoreAll() and it will restore | |
// state which was before first Scope.keep() call | |
restoreAll: function (keepKeys) { | |
do {} while (this.restore(keepKeys) === true); | |
}, | |
// Display difference in variables before and after | |
// execution of some code (actually before Scope.keep() call | |
// and call of State.state()) | |
state: function () { | |
// Display current state of the scope | |
var globals = []; | |
var differences = []; | |
var key; | |
var part = []; | |
var i, j, n, m; | |
var report = ""; | |
var displayReport = function (_msg) { | |
var msg = "Scope state report: " + _msg; | |
if (console) { | |
console.log(msg); | |
return; | |
} | |
alert(msg); | |
}; | |
if (g[this.storage()].length === 0) { | |
displayReport("Scope not used yet or already restored."); | |
return; | |
} | |
// collect all variables from all globals | |
for (i = 0, j = g[this.storage()].length; i < j; i++) { | |
var gs = g[this.storage()][i][2]; | |
for (n = 0, m = gs.length; n < m; n++) { | |
if (inArray(globals, gs[n]) === -1) { | |
globals.push(gs[n]); | |
} | |
} | |
} | |
for (key in g) { | |
if (inArray(globals, key) === -1 && key.indexOf(PREFIX) === -1) { | |
differences.push(key); | |
} | |
} | |
displayReport("Variables which haven't exist before execution:\n" + | |
differences.join(", ")); | |
} | |
}; | |
// this one to have noConflict functionality | |
if (typeof g.Scope !== "undefined") { | |
g.__ScopePrev = g.Scope; | |
} | |
g.Scope = new Scope(); | |
})(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment