var inject = function () {
document.head.appendChild((function () {
var fn = function bootstrap (window) {
var angular = window.angular;
// Helper to determine if the root 'ng' module has been loaded
// window.angular may be available if the app is bootstrapped asynchronously, but 'ng' might
// finish loading later.
var ngLoaded = function () {
if (!window.angular) {
return false;
}
try {
window.angular.module('ng');
}
catch (e) {
return false;
}
return true;
};
if (!ngLoaded()) {
(function () {
// TODO: var name
var areWeThereYet = function (ev) {
if (ev.srcElement.tagName === 'SCRIPT') {
var oldOnload = ev.srcElement.onload;
ev.srcElement.onload = function () {
if (ngLoaded()) {
document.removeEventListener('DOMNodeInserted', areWeThereYet);
bootstrap(window);
}
if (oldOnload) {
oldOnload.apply(this, arguments);
}
};
}
};
document.addEventListener('DOMNodeInserted', areWeThereYet);
}());
return;
}
// do not patch twice
if (window.__ngDebug) {
return;
}
// Helpers
// =======
// polyfill for performance.now on older webkit
if (!performance.now) {
performance.now = performance.webkitNow;
}
// Based on cycle.js
// https://github.com/douglascrockford/JSON-js/blob/master/cycle.js
// Make a deep copy of an object or array, assuring that there is at most
// one instance of each object or array in the resulting structure. The
// duplicate references (which might be forming cycles) are replaced with
// an object of the form
// {$ref: PATH}
// where the PATH is a JSONPath string that locates the first occurrence.
var decycle = function (object) {
var objects = [], // Keep a reference to each unique object or array
paths = []; // Keep the path to each unique object or array
return (function derez(value, path) {
var i, // The loop counter
name, // Property name
nu; // The new object or array
switch (typeof value) {
case 'object':
if (value instanceof HTMLElement) {
return value.innerHTML.toString().trim();
}
if (!value) {
return null;
}
for (i = 0; i < objects.length; i += 1) {
if (objects[i] === value) {
return {$ref: paths[i]};
}
}
objects.push(value);
paths.push(path);
if (value instanceof Array) {
nu = [];
for (i = 0; i < value.length; i += 1) {
nu[i] = derez(value[i], path + '[' + i + ']');
}
} else {
nu = {};
for (name in value) {
if (name[0] !== '$' && Object.prototype.hasOwnProperty.call(value, name)) {
nu[name] = derez(value[name],
path + '[' + JSON.stringify(name) + ']');
}
}
}
return nu;
case 'number':
case 'string':
case 'boolean':
return value;
}
}(object, '$'));
};
// End
// ===
// given a scope object, return an object with deep clones
// of the models exposed on that scope
var getScopeLocals = function (scope) {
var scopeLocals = {}, prop;
for (prop in scope) {
if (scope.hasOwnProperty(prop) && prop !== 'this' && prop[0] !== '$') {
scopeLocals[prop] = decycle(scope[prop]);
}
}
return scopeLocals;
};
// helper to extract dependencies from function arguments
// not all versions of AngularJS expose annotate
var annotate = angular.injector().annotate;
if (!annotate) {
annotate = (function () {
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(.+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
// TODO: should I keep these assertions?
function assertArg(arg, name, reason) {
if (!arg) {
throw new Error("Argument '" + (name || '?') + "' is " + (reason || "required"));
}
return arg;
}
function assertArgFn(arg, name, acceptArrayAnnotation) {
if (acceptArrayAnnotation && angular.isArray(arg)) {
arg = arg[arg.length - 1];
}
assertArg(angular.isFunction(arg), name, 'not a function, got ' +
(arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
return arg;
}
return function (fn) {
var $inject,
fnText,
argDecl,
last;
if (typeof fn == 'function') {
if (!($inject = fn.$inject)) {
$inject = [];
fnText = fn.toString().replace(STRIP_COMMENTS, '');
argDecl = fnText.match(FN_ARGS);
argDecl[1].split(FN_ARG_SPLIT).forEach(function(arg) {
arg.replace(FN_ARG, function(all, underscore, name) {
$inject.push(name);
});
});
fn.$inject = $inject;
}
} else if (angular.isArray(fn)) {
last = fn.length - 1;
assertArgFn(fn[last], 'fn');
$inject = fn.slice(0, last);
} else {
assertArgFn(fn, 'fn', true);
}
return $inject;
};
}());
}
// throttle based on _.throttle from Lo-Dash
// https://github.com/bestiejs/lodash/blob/master/lodash.js#L4625
var throttle = function (func, wait) {
var args,
result,
thisArg,
timeoutId,
lastCalled = 0;
function trailingCall() {
lastCalled = new Date();
timeoutId = null;
result = func.apply(thisArg, args);
}
return function() {
var now = new Date(),
remaining = wait - (now - lastCalled);
args = arguments;
thisArg = this;
if (remaining <= 0) {
clearTimeout(timeoutId);
timeoutId = null;
lastCalled = now;
result = func.apply(thisArg, args);
}
else if (!timeoutId) {
timeoutId = setTimeout(trailingCall, remaining);
}
return result;
};
};
var debounce = function (func, wait, immediate) {
var args,
result,
thisArg,
timeoutId;
function delayed() {
timeoutId = null;
if (!immediate) {
result = func.apply(thisArg, args);
}
}
return function() {
var isImmediate = immediate && !timeoutId;
args = arguments;
thisArg = this;
clearTimeout(timeoutId);
timeoutId = setTimeout(delayed, wait);
if (isImmediate) {
result = func.apply(thisArg, args);
}
return result;
};
};
var updateScopeModelCache = function (scope) {
debug.models[scope.$id] = getScopeLocals(scope);
debug.scopeDirty[scope.$id] = false;
};
var popover = null;
// Public API
// ==========
var api = window.__ngDebug = {
getDeps: function () {
return debug.deps;
},
getRootScopeIds: function () {
var ids = [];
angular.forEach(debug.rootScopes, function (elt, id) {
ids.push(id);
});
return ids;
},
// returns null or cached scope
getModel: function (id) {
if (debug.scopeDirty[id]) {
updateScopeModelCache(debug.scopes[id]);
return debug.models[id];
}
},
getScopeTree: function (id) {
if (debug.scopeTreeDirty[id] === false) {
return;
}
var traverse = function (sc) {
var tree = {
id: sc.$id,
children: []
};
var child = sc.$$childHead;
if (child) {
do {
tree.children.push(traverse(child));
} while (child !== sc.$$childTail && (child = child.$$nextSibling));
}
return tree;
};
var root = debug.rootScopes[id];
var tree = traverse(root);
if (tree) {
debug.scopeTreeDirty[id] = false;
}
return tree;
},
getWatchPerf: function () {
var changes = [];
angular.forEach(debug.watchPerf, function (info, name) {
if (info.time > 0) {
changes.push({
name: name,
time: info.time
});
info.time = 0;
}
});
return changes;
},
getWatchTree: function (id) {
var traverse = function (sc) {
var tree = {
id: sc.$id,
watchers: debug.watchers[sc.$id],
children: []
};
var child = sc.$$childHead;
if (child) {
do {
tree.children.push(traverse(child));
} while (child !== sc.$$childTail && (child = child.$$nextSibling));
}
return tree;
};
var root = debug.rootScopes[id];
var tree = traverse(root);
return tree;
},
enable: function () {
if (popover) {
return;
}
var angular = window.angular;
popover = angular.element(
'
' +
'
' +
'
{ Please select a scope }
' +
'' +
'' +
'' +
'' +
'
' +
'
');
angular.element(window.document.body).append(popover);
var popoverContent = angular.element(angular.element(popover.children('div')[0]).children()[0]);
var dragElt = angular.element(angular.element(popover.children('div')[0]).children()[1]);
var selectElt = angular.element(angular.element(popover.children('div')[0]).children()[2]);
var closeElt = angular.element(angular.element(popover.children('div')[0]).children()[3]);
var currentScope = null,
currentElt = null;
function onMove (ev) {
var x = ev.clientX,
y = ev.clientY;
if (x > window.outerWidth - 100) {
x = window.outerWidth - 100;
} else if (x < 0) {
x = 0;
}
if (y > window.outerHeight - 100) {
y = window.outerHeight - 100;
} else if (y < 0) {
y = 0;
}
x += 5;
y += 5;
popover.css('left', x + 'px');
popover.css('top', y + 'px');
}
closeElt.bind('click', function () {
popover.remove();
popover = null;
});
selectElt.bind('click', bindSelectScope);
var selecting = false;
function bindSelectScope () {
if (selecting) {
return;
}
setTimeout(function () {
selecting = true;
selectElt.attr('disabled', true);
angular.element(document.body).css('cursor', 'crosshair');
angular.element(document.getElementsByClassName('ng-scope'))
.bind('click', onSelectScope)
.bind('mouseover', onHoverScope);
}, 30);
}
var hoverScopeElt = null;
function markHoverElt () {
if (hoverScopeElt) {
hoverScopeElt.addClass('bat-selected');
}
}
function unmarkHoverElt () {
if (hoverScopeElt) {
hoverScopeElt.removeClass('bat-selected');
}
}
function onSelectScope (ev) {
render(this);
angular.element(document.getElementsByClassName('ng-scope'))
.unbind('click', onSelectScope)
.unbind('mouseover', onHoverScope);
unmarkHoverElt();
selecting = false;
selectElt.attr('disabled', false);
angular.element(document.body).css('cursor', '');
hovering = false;
}
var hovering = false;
function onHoverScope (ev) {
if (hovering) {
return;
}
hovering = true;
var that = this;
setTimeout(function () {
unmarkHoverElt();
hoverScopeElt = angular.element(that);
markHoverElt();
hovering = false;
render(that);
}, 100);
}
function onUnhoverScope (ev) {
angular.element(this).css('border', '');
}
dragElt.bind('mousedown', function (ev) {
ev.preventDefault();
rendering = true;
angular.element(document).bind('mousemove', onMove);
});
angular.element(document).bind('mouseup', function () {
angular.element(document).unbind('mousemove', onMove);
setTimeout(function () {
rendering = false;
}, 120);
});
function renderTree (data) {
var tree = angular.element('');
angular.forEach(data, function (val, key) {
var toAppend;
if (val === undefined) {
toAppend = 'undefined';
} else if (val === null) {
toAppend = 'null';
} else if (val instanceof Array) {
toAppend = '[ ... ]';
} else if (val instanceof Object) {
toAppend = '{ ... }';
} else {
toAppend = val.toString();
}
if (data instanceof Array) {
toAppend = '