$location = {{$location}}
 */
angularServiceInject("$location", function($browser) {
  var scope = this,
      location = {update:update, updateHash: updateHash},
      lastLocation = {};
  $browser.onHashChange(function() { //register
    update($browser.getUrl());
    copy(location, lastLocation);
    scope.$eval();
  })(); //initialize
  this.$onEval(PRIORITY_FIRST, sync);
  this.$onEval(PRIORITY_LAST, updateBrowser);
  return location;
  // PUBLIC METHODS
  /**
   * @workInProgress
   * @ngdoc method
   * @name angular.service.$location#update
   * @methodOf angular.service.$location
   * 
   * @description
   * Update location object
   * Does not immediately update the browser
   * Browser is updated at the end of $eval()
   *
   * @example
   * scope.$location.update('http://www.angularjs.org/path#hash?search=x');
   * scope.$location.update({host: 'www.google.com', protocol: 'https'});
   * scope.$location.update({hashPath: '/path', hashSearch: {a: 'b', x: true}});
   *
   * @param {(string|Object)} href Full href as a string or object with properties
   */
  function update(href) {
    if (isString(href)) {
      extend(location, parseHref(href));
    } else {
      if (isDefined(href.hash)) {
        extend(href, parseHash(href.hash));
      }
      extend(location, href);
      if (isDefined(href.hashPath || href.hashSearch)) {
        location.hash = composeHash(location);
      }
      location.href = composeHref(location);
    }
  }
  /**
   * @workInProgress
   * @ngdoc method
   * @name angular.service.$location#updateHash
   * @methodOf angular.service.$location
   * 
   * @description
   * Update location hash part
   * @see update()
   *
   * @example
   * scope.$location.updateHash('/hp')
   *   ==> update({hashPath: '/hp'})
   *
   * scope.$location.updateHash({a: true, b: 'val'})
   *   ==> update({hashSearch: {a: true, b: 'val'}})
   *
   * scope.$location.updateHash('/hp', {a: true})
   *   ==> update({hashPath: '/hp', hashSearch: {a: true}})
   *
   * @param {(string|Object)} path A hashPath or hashSearch object
   * @param {Object=} search A hashSearch object
   */
  function updateHash(path, search) {
    var hash = {};
    if (isString(path)) {
      hash.hashPath = path;
      if (isDefined(search))
        hash.hashSearch = search;
    } else
      hash.hashSearch = path;
    update(hash);
  }
  // INNER METHODS
  /**
   * Synchronizes all location object properties.
   *
   * User is allowed to change properties, so after property change,
   * location object is not in consistent state.
   *
   * Properties are synced with the following precedence order:
   *
   * - `$location.href`
   * - `$location.hash`
   * - everything else
   *
   * @example
   * scope.$location.href = 'http://www.angularjs.org/path#a/b'
   * immediately after this call, other properties are still the old ones...
   *
   * This method checks the changes and update location to the consistent state
   */
  function sync() {
    if (!equals(location, lastLocation)) {
      if (location.href != lastLocation.href) {
        update(location.href);
        return;
      }
      if (location.hash != lastLocation.hash) {
        var hash = parseHash(location.hash);
        updateHash(hash.path, hash.search);
      } else {
        location.hash = composeHash(location);
        location.href = composeHref(location);
      }
      update(location.href);
    }
  }
  /**
   * If location has changed, update the browser
   * This method is called at the end of $eval() phase
   */
  function updateBrowser() {
    sync();
    if ($browser.getUrl() != location.href) {
      $browser.setUrl(location.href);
      copy(location, lastLocation);
    }
  }
  /**
   * Compose href string from a location object
   *
   * @param {Object} loc The location object with all properties
   * @return {string} Composed href
   */
  function composeHref(loc) {
    var url = toKeyValue(loc.search);
    var port = (loc.port == DEFAULT_PORTS[loc.protocol] ? _null : loc.port);
    return loc.protocol  + '://' + loc.host +
          (port ? ':' + port : '') + loc.path +
          (url ? '?' + url : '') + (loc.hash ? '#' + loc.hash : '');
  }
  /**
   * Compose hash string from location object
   *
   * @param {Object} loc Object with hashPath and hashSearch properties
   * @return {string} Hash string
   */
  function composeHash(loc) {
    var hashSearch = toKeyValue(loc.hashSearch);
    //TODO: temporary fix for issue #158
    return escape(loc.hashPath).replace(/%21/gi, '!').replace(/%3A/gi, ':').replace(/%24/gi, '$') +
          (hashSearch ? '?' + hashSearch : '');
  }
  /**
   * Parse href string into location object
   *
   * @param {string} href
   * @return {Object} The location object
   */
  function parseHref(href) {
    var loc = {};
    var match = URL_MATCH.exec(href);
    if (match) {
      loc.href = href.replace(/#$/, '');
      loc.protocol = match[1];
      loc.host = match[3] || '';
      loc.port = match[5] || DEFAULT_PORTS[loc.protocol] || _null;
      loc.path = match[6] || '';
      loc.search = parseKeyValue(match[8]);
      loc.hash = match[10] || '';
      extend(loc, parseHash(loc.hash));
    }
    return loc;
  }
  /**
   * Parse hash string into object
   *
   * @param {string} hash
   */
  function parseHash(hash) {
    var h = {};
    var match = HASH_MATCH.exec(hash);
    if (match) {
      h.hash = hash;
      h.hashPath = unescape(match[1] || '');
      h.hashSearch = parseKeyValue(match[3]);
    }
    return h;
  }
}, ['$browser']);
/**
 * @workInProgress
 * @ngdoc service
 * @name angular.service.$log
 * @requires $window
 * 
 * @description
 * Is simple service for logging. Default implementation writes the message
 * into the browser's console (if present).
 * 
 * This is useful for debugging.
 * 
 * @example
   Reload this page with open console, enter text and hit the log button...
Message: */ angularServiceInject("$log", function($window){ return { /** * @workInProgress * @ngdoc method * @name angular.service.$log#log * @methodOf angular.service.$log * * @description * Write a log message */ log: consoleLog('log'), /** * @workInProgress * @ngdoc method * @name angular.service.$log#warn * @methodOf angular.service.$log * * @description * Write a warning message */ warn: consoleLog('warn'), /** * @workInProgress * @ngdoc method * @name angular.service.$log#info * @methodOf angular.service.$log * * @description * Write an information message */ info: consoleLog('info'), /** * @workInProgress * @ngdoc method * @name angular.service.$log#error * @methodOf angular.service.$log * * @description * Write an error message */ error: consoleLog('error') }; function consoleLog(type) { var console = $window.console || {}; var logFn = console[type] || console.log || noop; if (logFn.apply) { return function(){ var args = []; forEach(arguments, function(arg){ args.push(formatError(arg)); }); return logFn.apply(console, args); }; } else { // we are IE, in which case there is nothing we can do return logFn; } } }, ['$window'], EAGER); /** * @workInProgress * @ngdoc service * @name angular.service.$exceptionHandler * @requires $log * * @description * Any uncaught exception inangular.service('$updateView').delay = 10
 *
 * The delay is there so that multiple updates to the model which occur sufficiently close
 * together can be merged into a single update.
 *
 * You don't usually call '$updateView' directly since angular does it for you in most cases,
 * but there are some cases when you need to call it.
 *
 *  - `$updateView()` called automatically by angular:
 *    - Your Application Controllers: Your controller code is called by angular and hence
 *      angular is aware that you may have changed the model.
 *    - Your Services: Your service is usually called by your controller code, hence same rules
 *      apply.
 *  - May need to call `$updateView()` manually:
 *    - Widgets / Directives: If you listen to any DOM events or events on any third party
 *      libraries, then angular is not aware that you may have changed state state of the
 *      model, and hence you need to call '$updateView()' manually.
 *    - 'setTimeout'/'XHR':  If you call 'setTimeout' (instead of {@link angular.service.$defer})
 *      or 'XHR' (instead of {@link angular.service.$xhr}) then you may be changing the model
 *      without angular knowledge and you may need to call '$updateView()' directly.
 *
 * NOTE: if you wish to update the view immediately (without delay), you can do so by calling
 * {@link scope.$eval} at any time from your code:
 * scope.$root.$eval()* * In unit-test mode the update is instantaneous and synchronous to simplify writing tests. * */ function serviceUpdateViewFactory($browser){ var rootScope = this; var scheduled; function update(){ scheduled = false; rootScope.$eval(); } return $browser.isMock ? update : function(){ if (!scheduled) { scheduled = true; $browser.defer(update, serviceUpdateViewFactory.delay); } }; } serviceUpdateViewFactory.delay = 25; angularServiceInject('$updateView', serviceUpdateViewFactory, ['$browser']); /** * @workInProgress * @ngdoc service * @name angular.service.$hover * @requires $browser * @requires $document * * @description * * @example */ angularServiceInject("$hover", function(browser, document) { var tooltip, self = this, error, width = 300, arrowWidth = 10, body = jqLite(document[0].body); browser.hover(function(element, show){ if (show && (error = element.attr(NG_EXCEPTION) || element.attr(NG_VALIDATION_ERROR))) { if (!tooltip) { tooltip = { callout: jqLite(''), arrow: jqLite(''), title: jqLite(''), content: jqLite('') }; tooltip.callout.append(tooltip.arrow); tooltip.callout.append(tooltip.title); tooltip.callout.append(tooltip.content); body.append(tooltip.callout); } var docRect = body[0].getBoundingClientRect(), elementRect = element[0].getBoundingClientRect(), leftSpace = docRect.right - elementRect.right - arrowWidth; tooltip.title.text(element.hasClass("ng-exception") ? "EXCEPTION:" : "Validation error..."); tooltip.content.text(error); if (leftSpace < width) { tooltip.arrow.addClass('ng-arrow-right'); tooltip.arrow.css({left: (width + 1)+'px'}); tooltip.callout.css({ position: 'fixed', left: (elementRect.left - arrowWidth - width - 4) + "px", top: (elementRect.top - 3) + "px", width: width + "px" }); } else { tooltip.arrow.addClass('ng-arrow-left'); tooltip.callout.css({ position: 'fixed', left: (elementRect.right + arrowWidth) + "px", top: (elementRect.top - 3) + "px", width: width + "px" }); } } else if (tooltip) { tooltip.callout.remove(); tooltip = _null; } }); }, ['$browser', '$document'], EAGER); /** * @workInProgress * @ngdoc service * @name angular.service.$invalidWidgets * * @description * Keeps references to all invalid widgets found during validation. * Can be queried to find whether there are any invalid widgets currently displayed. * * @example */ angularServiceInject("$invalidWidgets", function(){ var invalidWidgets = []; /** Remove an element from the array of invalid widgets */ invalidWidgets.markValid = function(element){ var index = indexOf(invalidWidgets, element); if (index != -1) invalidWidgets.splice(index, 1); }; /** Add an element to the array of invalid widgets */ invalidWidgets.markInvalid = function(element){ var index = indexOf(invalidWidgets, element); if (index === -1) invalidWidgets.push(element); }; /** Return count of all invalid widgets that are currently visible */ invalidWidgets.visible = function() { var count = 0; forEach(invalidWidgets, function(widget){ count = count + (isVisible(widget) ? 1 : 0); }); return count; }; /* At the end of each eval removes all invalid widgets that are not part of the current DOM. */ this.$onEval(PRIORITY_LAST, function() { for(var i = 0; i < invalidWidgets.length;) { var widget = invalidWidgets[i]; if (isOrphan(widget[0])) { invalidWidgets.splice(i, 1); if (widget.dealoc) widget.dealoc(); } else { i++; } } }); /** * Traverses DOM element's (widget's) parents and considers the element to be an orphant if one of * it's parents isn't the current window.document. */ function isOrphan(widget) { if (widget == window.document) return false; var parent = widget.parentNode; return !parent || isOrphan(parent); } return invalidWidgets; }, [], EAGER); function switchRouteMatcher(on, when, dstName) { var regex = '^' + when.replace(/[\.\\\(\)\^\$]/g, "\$1") + '$', params = [], dst = {}; forEach(when.split(/\W/), function(param){ if (param) { var paramRegExp = new RegExp(":" + param + "([\\W])"); if (regex.match(paramRegExp)) { regex = regex.replace(paramRegExp, "([^\/]*)$1"); params.push(param); } } }); var match = on.match(new RegExp(regex)); if (match) { forEach(params, function(name, index){ dst[name] = match[index + 1]; }); if (dstName) this.$set(dstName, dst); } return match ? dst : _null; } /** * @workInProgress * @ngdoc service * @name angular.service.$route * @requires $location * * @property {Object} current Name of the current route * @property {Array.