//! ng-stats version 2.5.4 built with ♥ by Kent C. Dodds <kent@doddsfamily.us> (http://kent.doddsfamily.us), Viper Bailey <jinxidoru@gmail.com> (http://jinxidoru.blogspot.com), Daniel Lamb <dlamb.open.source@gmail.com> (http://daniellmb.com) (ó ì_í)=óò=(ì_í ò)

(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory(require("angular"));
	else if(typeof define === 'function' && define.amd)
		define(["angular"], factory);
	else if(typeof exports === 'object')
		exports["showAngularStats"] = factory(require("angular"));
	else
		root["showAngularStats"] = factory(root["angular"]);
})(this, function(__WEBPACK_EXTERNAL_MODULE_1__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};

/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {

/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId])
/******/ 			return installedModules[moduleId].exports;

/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			exports: {},
/******/ 			id: moduleId,
/******/ 			loaded: false
/******/ 		};

/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/ 		// Flag the module as loaded
/******/ 		module.loaded = true;

/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}


/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;

/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;

/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";

/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

	/* eslint no-console:0 */
	'use strict';

	Object.defineProperty(exports, '__esModule', {
	  value: true
	});

	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

	var _angular = __webpack_require__(1);

	var _angular2 = _interopRequireDefault(_angular);

	var angular = _angular2['default'];

	/* istanbul ignore next */
	if (!angular.version) {
	  // we're doing this because some versions
	  // of angular don't expose itself correctly
	  angular = window.angular;
	}

	exports['default'] = showAngularStats;

	var autoloadKey = 'showAngularStats_autoload';
	var current = null;
	// define the timer function to use based upon whether or not 'performance is available'
	var timerNow = window.self.performance && window.self.performance.now ? function () {
	  return window.self.performance.now();
	} : function () {
	  return Date.now();
	};

	var lastWatchCountRun = timerNow();
	var watchCountTimeout = null;
	var lastWatchCount = getWatcherCount() || 0;
	var lastDigestLength = 0;
	var scopeSelectors = '.ng-scope, .ng-isolate-scope';
	var $rootScope;

	var digestIsHijacked = false;

	var listeners = {
	  watchCount: {},
	  digestLength: {}
	};

	// Hijack $digest to time it and update data on every digest.
	function hijackDigest() {
	  if (digestIsHijacked) {
	    return;
	  }
	  digestIsHijacked = true;
	  var scopePrototype = Object.getPrototypeOf(getRootScope());
	  var oldDigest = scopePrototype.$digest;
	  scopePrototype.$digest = function $digest() {
	    var start = timerNow();
	    oldDigest.apply(this, arguments);
	    var diff = timerNow() - start;
	    updateData(getWatcherCount(), diff);
	  };
	}

	// used to prevent localstorage error in chrome packaged apps
	function isChromeApp() {
	  return typeof chrome !== 'undefined' && typeof chrome.storage !== 'undefined' && typeof chrome.storage.local !== 'undefined';
	}

	// check for autoload
	var autoloadOptions = sessionStorage[autoloadKey] || !isChromeApp() && localStorage[autoloadKey];
	if (autoloadOptions) {
	  autoload(JSON.parse(autoloadOptions));
	}

	function autoload(options) {
	  if (window.self.angular && getRootScope()) {
	    showAngularStats(options);
	  } else {
	    // wait for angular to load...
	    setTimeout(function () {
	      autoload(options);
	    }, 200);
	  }
	}

	function initOptions(opts) {

	  // Remove autoload if they did not specifically request it
	  if (opts === false || !opts.autoload) {
	    sessionStorage.removeItem(autoloadKey);
	    localStorage.removeItem(autoloadKey);
	    // do nothing if the argument is false
	    if (opts === false) {
	      return;
	    }
	  }

	  opts.position = opts.position || 'top-left';
	  opts = angular.extend({
	    htmlId: null,
	    rootScope: undefined,
	    digestTimeThreshold: 16,
	    watchCountThreshold: 2000,
	    autoload: false,
	    trackDigest: false,
	    trackWatches: false,
	    logDigest: false,
	    logWatches: false,
	    styles: {
	      position: 'fixed',
	      background: 'black',
	      borderBottom: '1px solid #666',
	      borderRight: '1px solid #666',
	      color: '#666',
	      fontFamily: 'Courier',
	      width: 130,
	      zIndex: 9999,
	      textAlign: 'right',
	      top: opts.position.indexOf('top') === -1 ? null : 0,
	      bottom: opts.position.indexOf('bottom') === -1 ? null : 0,
	      right: opts.position.indexOf('right') === -1 ? null : 0,
	      left: opts.position.indexOf('left') === -1 ? null : 0
	    }
	  }, opts || {});

	  // for ionic support
	  if (opts.rootScope) {
	    $rootScope = opts.rootScope;
	  }

	  return opts;
	}

	function showAngularStats(opts) {
	  /* eslint max-statements:[2, 45] */
	  /* eslint complexity:[2, 18] */
	  /* eslint consistent-return:0 */
	  // TODO ^^ fix these things...
	  opts = opts !== undefined ? opts : {};
	  var returnData = {
	    listeners: listeners
	  };
	  // delete the previous one
	  if (current) {
	    current.$el && current.$el.remove();
	    current.active = false;
	    current = null;
	  }

	  // Implemented in separate function due to webpack's statement count limit
	  opts = initOptions(opts);

	  if (!opts) {
	    return;
	  }

	  hijackDigest();

	  // setup the state
	  var state = current = { active: true };

	  // auto-load on startup
	  if (opts.autoload) {
	    if (opts.autoload === 'localStorage') {
	      localStorage.setItem(autoloadKey, JSON.stringify(opts));
	    } else if (opts.autoload === 'sessionStorage' || typeof opts.autoload === 'boolean') {
	      sessionStorage.setItem(autoloadKey, JSON.stringify(opts));
	    } else {
	      throw new Error('Invalid value for autoload: ' + opts.autoload + ' can only be "localStorage" "sessionStorage" or boolean.');
	    }
	  }

	  // general variables
	  var bodyEl = angular.element(document.body);
	  var noDigestSteps = 0;

	  // add the DOM element
	  var htmlId = opts.htmlId ? ' id="' + opts.htmlId + '"' : '';
	  state.$el = angular.element('<div' + htmlId + '><canvas></canvas><div><span></span> | <span></span></div></div>').css(opts.styles);
	  bodyEl.append(state.$el);
	  var $watchCount = state.$el.find('span');
	  var $digestTime = $watchCount.next();

	  // initialize the canvas
	  var graphSz = { width: 130, height: 40 };
	  var cvs = state.$el.find('canvas').attr(graphSz)[0];

	  // add listeners
	  listeners.digestLength.ngStatsAddToCanvas = function (digestLength) {
	    addDataToCanvas(null, digestLength);
	  };

	  listeners.watchCount.ngStatsAddToCanvas = function (watchCount) {
	    addDataToCanvas(watchCount);
	  };

	  track('digest', listeners.digestLength);
	  track('watches', listeners.watchCount, true);

	  log('digest', listeners.digestLength);
	  log('watches', listeners.watchCount, true);

	  function track(thingToTrack, listenerCollection, diffOnly) {
	    var capThingToTrack = thingToTrack.charAt(0).toUpperCase() + thingToTrack.slice(1);
	    if (opts['track' + capThingToTrack]) {
	      returnData[thingToTrack] = [];
	      listenerCollection['track + capThingToTrack'] = function (tracked) {
	        if (!diffOnly || returnData[thingToTrack][returnData.length - 1] !== tracked) {
	          returnData[thingToTrack][returnData.length - 1] = tracked;
	          returnData[thingToTrack].push(tracked);
	        }
	      };
	    }
	  }

	  function log(thingToLog, listenerCollection, diffOnly) {
	    var capThingToLog = thingToLog.charAt(0).toUpperCase() + thingToLog.slice(1);
	    if (opts['log' + capThingToLog]) {
	      var last;
	      listenerCollection['log' + capThingToLog] = function (tracked) {
	        if (!diffOnly || last !== tracked) {
	          last = tracked;
	          var color = colorLog(thingToLog, tracked);
	          if (color) {
	            console.log('%c' + thingToLog + ':', color, tracked);
	          } else {
	            console.log(thingToLog + ':', tracked);
	          }
	        }
	      };
	    }
	  }

	  function getColor(metric, threshold) {
	    if (metric > threshold) {
	      return 'red';
	    } else if (metric > 0.7 * threshold) {
	      return 'orange';
	    }
	    return 'green';
	  }

	  function colorLog(thingToLog, tracked) {
	    var color;
	    if (thingToLog === 'digest') {
	      color = 'color:' + getColor(tracked, opts.digestTimeThreshold);
	    } else if (thingToLog === 'watches') {
	      color = 'color:' + getColor(tracked, opts.watchCountThreshold);
	    }
	    return color;
	  }

	  function addDataToCanvas(watchCount, digestLength) {
	    var averageDigest = digestLength || lastDigestLength;
	    var digestColor = getColor(averageDigest, opts.digestTimeThreshold);
	    lastWatchCount = nullOrUndef(watchCount) ? lastWatchCount : watchCount;
	    var watchColor = getColor(lastWatchCount, opts.watchCountThreshold);
	    lastDigestLength = nullOrUndef(digestLength) ? lastDigestLength : digestLength;
	    $watchCount.text(lastWatchCount).css({ color: watchColor });
	    $digestTime.text(lastDigestLength.toFixed(2)).css({ color: digestColor });

	    if (!digestLength) {
	      return;
	    }

	    // color the sliver if this is the first step
	    var ctx = cvs.getContext('2d');
	    if (noDigestSteps > 0) {
	      noDigestSteps = 0;
	      ctx.fillStyle = '#333';
	      ctx.fillRect(graphSz.width - 1, 0, 1, graphSz.height);
	    }

	    // mark the point on the graph
	    ctx.fillStyle = digestColor;
	    ctx.fillRect(graphSz.width - 1, Math.max(0, graphSz.height - averageDigest), 2, 2);
	  }

	  // Shift the canvas to the left.
	  function shiftLeft() {
	    if (state.active) {
	      setTimeout(shiftLeft, 250);
	      var ctx = cvs.getContext('2d');
	      var imageData = ctx.getImageData(1, 0, graphSz.width - 1, graphSz.height);
	      ctx.putImageData(imageData, 0, 0);
	      ctx.fillStyle = noDigestSteps++ > 2 ? 'black' : '#333';
	      ctx.fillRect(graphSz.width - 1, 0, 1, graphSz.height);
	    }
	  }

	  // start everything
	  shiftLeft();
	  if (!$rootScope.$$phase) {
	    $rootScope.$digest();
	  }

	  return returnData;
	}

	angular.module('angularStats', []).directive('angularStats', function () {
	  var index = 1;
	  return {
	    scope: {
	      digestLength: '@',
	      watchCount: '@',
	      watchCountRoot: '@',
	      onDigestLengthUpdate: '&?',
	      onWatchCountUpdate: '&?'
	    },
	    link: function link(scope, el, attrs) {
	      hijackDigest();
	      var directiveIndex = index++;

	      setupDigestLengthElement();
	      setupWatchCountElement();
	      addWatchCountListener();
	      addDigestLengthListener();
	      scope.$on('$destroy', destroyListeners);

	      function setupDigestLengthElement() {
	        if (attrs.hasOwnProperty('digestLength')) {
	          var digestEl = el;
	          if (attrs.digestLength) {
	            digestEl = angular.element(el[0].querySelector(attrs.digestLength));
	          }
	          listeners.digestLength['ngStatsDirective' + directiveIndex] = function (length) {
	            window.dirDigestNode = digestEl[0];
	            digestEl.text((length || 0).toFixed(2));
	          };
	        }
	      }

	      function setupWatchCountElement() {
	        if (attrs.hasOwnProperty('watchCount')) {
	          var watchCountRoot;
	          var watchCountEl = el;
	          if (scope.watchCount) {
	            watchCountEl = angular.element(el[0].querySelector(attrs.watchCount));
	          }

	          if (scope.watchCountRoot) {
	            if (scope.watchCountRoot === 'this') {
	              watchCountRoot = el;
	            } else {
	              // In the case this directive is being compiled and it's not in the dom,
	              // we're going to do the find from the root of what we have...
	              var rootParent;
	              if (attrs.hasOwnProperty('watchCountOfChild')) {
	                rootParent = el[0];
	              } else {
	                rootParent = findRootOfElement(el);
	              }
	              watchCountRoot = angular.element(rootParent.querySelector(scope.watchCountRoot));
	              if (!watchCountRoot.length) {
	                throw new Error('no element at selector: ' + scope.watchCountRoot);
	              }
	            }
	          }

	          listeners.watchCount['ngStatsDirective' + directiveIndex] = function (count) {
	            var watchCount = count;
	            if (watchCountRoot) {
	              watchCount = getWatcherCountForElement(watchCountRoot);
	            }
	            watchCountEl.text(watchCount);
	          };
	        }
	      }

	      function addWatchCountListener() {
	        if (attrs.hasOwnProperty('onWatchCountUpdate')) {
	          listeners.watchCount['ngStatsDirectiveUpdate' + directiveIndex] = function (count) {
	            scope.onWatchCountUpdate({ watchCount: count });
	          };
	        }
	      }

	      function addDigestLengthListener() {
	        if (attrs.hasOwnProperty('onDigestLengthUpdate')) {
	          listeners.digestLength['ngStatsDirectiveUpdate' + directiveIndex] = function (length) {
	            scope.onDigestLengthUpdate({ digestLength: length });
	          };
	        }
	      }

	      function destroyListeners() {
	        delete listeners.digestLength['ngStatsDirectiveUpdate' + directiveIndex];
	        delete listeners.watchCount['ngStatsDirectiveUpdate' + directiveIndex];
	        delete listeners.digestLength['ngStatsDirective' + directiveIndex];
	        delete listeners.watchCount['ngStatsDirective' + directiveIndex];
	      }
	    }
	  };

	  function findRootOfElement(el) {
	    var parent = el[0];
	    while (parent.parentElement) {
	      parent = parent.parentElement;
	    }
	    return parent;
	  }
	});

	// UTILITY FUNCTIONS

	function getRootScope() {
	  if ($rootScope) {
	    return $rootScope;
	  }
	  var scopeEl = document.querySelector(scopeSelectors);
	  if (!scopeEl) {
	    return null;
	  }
	  $rootScope = angular.element(scopeEl).scope().$root;
	  return $rootScope;
	}

	// Uses timeouts to ensure that this is only run every 300ms (it's a perf bottleneck)
	function getWatcherCount() {
	  clearTimeout(watchCountTimeout);
	  var now = timerNow();
	  if (now - lastWatchCountRun > 300) {
	    lastWatchCountRun = now;
	    lastWatchCount = getWatcherCountForScope();
	  } else {
	    watchCountTimeout = setTimeout(function () {
	      updateData(getWatcherCount());
	    }, 350);
	  }
	  return lastWatchCount;
	}

	function getWatcherCountForElement(element) {
	  var startingScope = getClosestChildScope(element);
	  return getWatcherCountForScope(startingScope);
	}

	function getClosestChildScope(element) {
	  element = angular.element(element);
	  var scope = element.scope();
	  if (!scope) {
	    element = angular.element(element.querySelector(scopeSelectors));
	    scope = element.scope();
	  }
	  return scope;
	}

	function getWatchersFromScope(scope, arrs) {
	  var result = scope && scope.$$watchers ? scope.$$watchers : [];
	  angular.forEach(result, function(item, index) {
	  	if (angular.isString(item.exp)) {
		  	var _tmp = _.findWhere(arrs, {'id': item.exp});
		  	if (_tmp) {
		  		_tmp.count++;
		  	} else {
		  		_tmp = {'id': item.exp, 'count': 1};
		  		arrs.push(_tmp);
		  	}
		  }
	  });

	  return result;
	}

	// iterate through listeners to call them with the watchCount and digestLength
	function updateData(watchCount, digestLength) {
	  // update the listeners
	  if (!nullOrUndef(watchCount)) {
	    angular.forEach(listeners.watchCount, function (listener) {
	      listener(watchCount);
	    });
	  }
	  if (!nullOrUndef(digestLength)) {
	    angular.forEach(listeners.digestLength, function (listener) {
	      listener(digestLength);
	    });
	  }
	}

	function nullOrUndef(item) {
	  return item === null || item === undefined;
	}

	function getWatcherCountForScope(scope) {
	  var count = 0;
	  var arrs = []
	  iterateScopes(scope, function (childScope) {
	    count += getWatchersFromScope(childScope, arrs).length;
	  });

	  return count;
	}

	function iterateScopes(currentScope, fn) {
	  if (typeof currentScope === 'function') {
	    fn = currentScope;
	    currentScope = null;
	  }
	  currentScope = currentScope || getRootScope();
	  currentScope = _makeScopeReference(currentScope);
	  if (!currentScope) {
	    return;
	  }
	  var ret = fn(currentScope);
	  if (ret === false) {
	    return ret;
	  }
	  return iterateChildren(currentScope, fn);
	}

	function iterateSiblings(start, fn) {
	  var ret;
	  /* eslint no-extra-boolean-cast:0 */
	  while (!!(start = start.$$nextSibling)) {
	    ret = fn(start);
	    if (ret === false) {
	      break;
	    }

	    ret = iterateChildren(start, fn);
	    if (ret === false) {
	      break;
	    }
	  }
	  return ret;
	}

	function iterateChildren(start, fn) {
	  var ret;
	  while (!!(start = start.$$childHead)) {
	    ret = fn(start);
	    if (ret === false) {
	      break;
	    }

	    ret = iterateSiblings(start, fn);
	    if (ret === false) {
	      break;
	    }
	  }
	  return ret;
	}

	function getScopeById(id) {
	  var myScope = null;
	  iterateScopes(function (scope) {
	    if (scope.$id === id) {
	      myScope = scope;
	      return false;
	    }
	  });
	  return myScope;
	}

	function _makeScopeReference(scope) {
	  if (_isScopeId(scope)) {
	    scope = getScopeById(scope);
	  }
	  return scope;
	}

	function _isScopeId(scope) {
	  return typeof scope === 'string' || typeof scope === 'number';
	}
	module.exports = exports['default'];

/***/ },
/* 1 */
/***/ function(module, exports) {

	module.exports = __WEBPACK_EXTERNAL_MODULE_1__;

/***/ }
/******/ ])
});
;
