diff --git a/Gruntfile.js b/Gruntfile.js
index 5e3266cfa..f14b7db69 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -151,7 +151,8 @@ module.exports = function (grunt) {
                 src: ['dist/angular-google-maps.js'],
                 options: {
                     keepRunner: true,
-                    vendor: ['http://maps.googleapis.com/maps/api/js?sensor=false&language=en', 'lib/*.js', 'dist/angular-google-maps.js'],
+                    vendor: ['http://maps.googleapis.com/maps/api/js?sensor=false&language=en','lib/jquery.js',
+                        'lib/angular.js','lib/angular-mock.js','lib/lodash.js','dist/angular-google-maps.js'],
                     specs: ['spec/*.spec.js', 'spec/**/*.spec.js', 'spec/**/**/*-spec.js', 'spec/**/**/**/*.spec.js',
                         'tmp/spec/js/*/spec.js', 'tmp/spec/**/*.spec.js', 'tmp/spec/**/**/*-spec.js', 'tmp/spec/**/**/**/*.spec.js'
                     ],
diff --git a/component.json b/component.json
index b0d8bac8b..50761443b 100644
--- a/component.json
+++ b/component.json
@@ -2,6 +2,7 @@
   "name": "angular-google-maps",
   "version": "0.0.0",
   "dependencies": {
-    "angular": "~1.0.7"
+    "angular": "~1.2.2",
+    "angular-mocks": "~1.2.2"
   }
 }
diff --git a/dist/angular-google-maps.js b/dist/angular-google-maps.js
index b78dc9b31..ed36b9f76 100644
--- a/dist/angular-google-maps.js
+++ b/dist/angular-google-maps.js
@@ -1701,7 +1701,6 @@
         this.$log = directives.api.utils.Logger;
         this.$timeout = $timeout;
         this.restrict = 'ECMA';
-        this.replace = true;
         this.require = '^googleMap';
         this.priority = -1;
         this.transclude = true;
@@ -1892,8 +1891,10 @@
 
       Marker.prototype.controller = [
         '$scope', '$element', function($scope, $element) {
-          return this.getMarker = function() {
-            return $element.data('instance');
+          return {
+            getMarker: function() {
+              return $element.data('instance');
+            }
           };
         }
       ];
@@ -1949,8 +1950,10 @@ not 1:1 in this setting.
 
       Markers.prototype.controller = [
         '$scope', '$element', function($scope, $element) {
-          return this.getMarkersScope = function() {
-            return $scope;
+          return {
+            getMarkersScope: function() {
+              return $scope;
+            }
           };
         }
       ];
diff --git a/dist/angular-google-maps.min.js b/dist/angular-google-maps.min.js
index adb720eb3..3b90a3c36 100644
--- a/dist/angular-google-maps.min.js
+++ b/dist/angular-google-maps.min.js
@@ -1,7 +1,7 @@
-/*! angular-google-maps 0.0.0 2013-11-18
+/*! angular-google-maps 0.0.0 2013-11-27
  *  AngularJS directives for Google Maps
  *  git: https://github.com/nlaplante/angular-google-maps.git
  */
 function ClusterIcon(a,b){a.getMarkerClusterer().extend(ClusterIcon,google.maps.OverlayView),this.cluster_=a,this.className_=a.getMarkerClusterer().getClusterClass(),this.styles_=b,this.center_=null,this.div_=null,this.sums_=null,this.visible_=!1,this.setMap(a.getMap())}function Cluster(a){this.markerClusterer_=a,this.map_=a.getMap(),this.gridSize_=a.getGridSize(),this.minClusterSize_=a.getMinimumClusterSize(),this.averageCenter_=a.getAverageCenter(),this.printable_=a.getPrintable(),this.markers_=[],this.center_=null,this.bounds_=null,this.clusterIcon_=new ClusterIcon(this,a.getStyles())}function MarkerClusterer(a,b,c){this.extend(MarkerClusterer,google.maps.OverlayView),b=b||[],c=c||{},this.markers_=[],this.clusters_=[],this.listeners_=[],this.activeMap_=null,this.ready_=!1,this.gridSize_=c.gridSize||60,this.minClusterSize_=c.minimumClusterSize||2,this.maxZoom_=c.maxZoom||null,this.styles_=c.styles||[],this.title_=c.title||"",this.zoomOnClick_=!0,void 0!==c.zoomOnClick&&(this.zoomOnClick_=c.zoomOnClick),this.averageCenter_=!1,void 0!==c.averageCenter&&(this.averageCenter_=c.averageCenter),this.ignoreHidden_=!1,void 0!==c.ignoreHidden&&(this.ignoreHidden_=c.ignoreHidden),this.printable_=!1,void 0!==c.printable&&(this.printable_=c.printable),this.imagePath_=c.imagePath||MarkerClusterer.IMAGE_PATH,this.imageExtension_=c.imageExtension||MarkerClusterer.IMAGE_EXTENSION,this.imageSizes_=c.imageSizes||MarkerClusterer.IMAGE_SIZES,this.calculator_=c.calculator||MarkerClusterer.CALCULATOR,this.batchSize_=c.batchSize||MarkerClusterer.BATCH_SIZE,this.batchSizeIE_=c.batchSizeIE||MarkerClusterer.BATCH_SIZE_IE,this.clusterClass_=c.clusterClass||"cluster",-1!==navigator.userAgent.toLowerCase().indexOf("msie")&&(this.batchSize_=this.batchSizeIE_),this.setupStyles_(),this.addMarkers(b,!0),this.setMap(a)}function inherits(a,b){function c(){}c.prototype=b.prototype,a.superClass_=b.prototype,a.prototype=new c,a.prototype.constructor=a}function MarkerLabel_(a,b){this.marker_=a,this.handCursorURL_=a.handCursorURL,this.labelDiv_=document.createElement("div"),this.labelDiv_.style.cssText="position: absolute; overflow: hidden;",this.eventDiv_=document.createElement("div"),this.eventDiv_.style.cssText=this.labelDiv_.style.cssText,this.eventDiv_.setAttribute("onselectstart","return false;"),this.eventDiv_.setAttribute("ondragstart","return false;"),this.crossDiv_=MarkerLabel_.getSharedCross(b)}function MarkerWithLabel(a){a=a||{},a.labelContent=a.labelContent||"",a.labelAnchor=a.labelAnchor||new google.maps.Point(0,0),a.labelClass=a.labelClass||"markerLabels",a.labelStyle=a.labelStyle||{},a.labelInBackground=a.labelInBackground||!1,"undefined"==typeof a.labelVisible&&(a.labelVisible=!0),"undefined"==typeof a.raiseOnDrag&&(a.raiseOnDrag=!0),"undefined"==typeof a.clickable&&(a.clickable=!0),"undefined"==typeof a.draggable&&(a.draggable=!1),"undefined"==typeof a.optimized&&(a.optimized=!1),a.crossImage=a.crossImage||"http"+("https:"===document.location.protocol?"s":"")+"://maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png",a.handCursor=a.handCursor||"http"+("https:"===document.location.protocol?"s":"")+"://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur",a.optimized=!1,this.label=new MarkerLabel_(this,a.crossImage,a.handCursor),google.maps.Marker.apply(this,arguments)}!function(){var a=angular.module("google-maps",[]);a.factory("debounce",["$timeout",function(a){return function(b){var c=0;return function(){var d=this,e=arguments;c++;var f=function(a){return function(){return a===c?b.apply(d,e):void 0}}(c);return a(f,0,!0)}}}])}(),function(){this.ngGmapModule=function(a,b){var c,d;return null==b&&(b=function(){}),"string"==typeof a&&(a=a.split(".")),c=this[d=a.shift()]||(this[d]={}),c.ngGmapModule||(c.ngGmapModule=this.ngGmapModule),a.length?c.ngGmapModule(a,b):b.call(c)}}.call(this),function(){var a=[].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1};this.ngGmapModule("oo",function(){var b;return b=["extended","included"],this.BaseObject=function(){function c(){}return c.extend=function(c){var d,e,f;for(d in c)e=c[d],a.call(b,d)<0&&(this[d]=e);return null!=(f=c.extended)&&f.apply(0),this},c.include=function(c){var d,e,f;for(d in c)e=c[d],a.call(b,d)<0&&(this.prototype[d]=e);return null!=(f=c.included)&&f.apply(0),this},c}()})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api.managers",function(){return this.ClustererMarkerManager=function(b){function d(b,c,e){this.clear=a(this.clear,this),this.draw=a(this.draw,this),this.removeMany=a(this.removeMany,this),this.remove=a(this.remove,this),this.addMany=a(this.addMany,this),this.add=a(this.add,this);var f;d.__super__.constructor.call(this),f=this,this.opt_options=e,this.clusterer=null!=e&&void 0===c?new MarkerClusterer(b,void 0,e):null!=e&&null!=c?new MarkerClusterer(b,c,e):new MarkerClusterer(b),this.clusterer.setIgnoreHidden(!0),this.$log=directives.api.utils.Logger,this.noDrawOnSingleAddRemoves=!0,this.$log.info(this)}return c(d,b),d.prototype.add=function(a){return this.clusterer.addMarker(a,this.noDrawOnSingleAddRemoves)},d.prototype.addMany=function(a){return this.clusterer.addMarkers(a)},d.prototype.remove=function(a){return this.clusterer.removeMarker(a,this.noDrawOnSingleAddRemoves)},d.prototype.removeMany=function(a){return this.clusterer.addMarkers(a)},d.prototype.draw=function(){return this.clusterer.repaint()},d.prototype.clear=function(){return this.clusterer.clearMarkers(),this.clusterer.repaint()},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api.managers",function(){return this.MarkerManager=function(b){function d(b){this.handleOptDraw=a(this.handleOptDraw,this),this.clear=a(this.clear,this),this.draw=a(this.draw,this),this.removeMany=a(this.removeMany,this),this.remove=a(this.remove,this),this.addMany=a(this.addMany,this),this.add=a(this.add,this);var c;d.__super__.constructor.call(this),c=this,this.gMap=b,this.gMarkers=[],this.$log=directives.api.utils.Logger,this.$log.info(this)}return c(d,b),d.prototype.add=function(a,b){return this.handleOptDraw(a,b,!0),this.gMarkers.push(a)},d.prototype.addMany=function(a){var b,c,d,e;for(e=[],c=0,d=a.length;d>c;c++)b=a[c],e.push(this.add(b));return e},d.prototype.remove=function(a,b){var c,d;return this.handleOptDraw(a,b,!1),b?(c=void 0,null!=this.gMarkers.indexOf?c=this.gMarkers.indexOf(a):(d=0,_.find(this.gMarkers,function(b){d+=1,b===a&&(c=d)})),null!=c?this.gMarkers.splice(c,1):void 0):void 0},d.prototype.removeMany=function(){var a,b,c,d,e;for(d=this.gMarkers,e=[],b=0,c=d.length;c>b;b++)a=d[b],e.push(this.remove(a));return e},d.prototype.draw=function(){var a,b,c,d,e,f,g,h,i,j=this;for(a=[],h=this.gMarkers,c=function(b){return b.isDrawn?void 0:b.doAdd?b.setMap(j.gMap):a.push(b)},d=0,f=h.length;f>d;d++)b=h[d],c(b);for(i=[],e=0,g=a.length;g>e;e++)b=a[e],i.push(this.remove(b,!0));return i},d.prototype.clear=function(){var a,b,c,d;for(d=this.gMarkers,b=0,c=d.length;c>b;b++)a=d[b],a.setMap(null);return delete this.gMarkers,this.gMarkers=[]},d.prototype.handleOptDraw=function(a,b,c){return b===!0?(c?a.setMap(this.gMap):a.setMap(null),a.isDrawn=!0):(a.isDrawn=!1,a.doAdd=c)},d}(oo.BaseObject)})}.call(this),function(){this.ngGmapModule("directives.api.utils",function(){return this.AsyncProcessor={handleLargeArray:function(a,b,c,d,e,f){var g;return null==e&&(e=100),null==f&&(f=0),void 0===a||a.length<=0?(d(),void 0):(g=function(){var h,i;for(h=e,i=f;h--&&i<a.length;)b(a[i]),++i;return i<a.length?(f=i,null!=c&&c(),setTimeout(g,1)):d()},g())}}})}.call(this),function(){this.ngGmapModule("directives.api.utils",function(){return this.GmapUtil={getLabelPositionPoint:function(a){var b,c;return void 0===a?void 0:(a=/^([\d\.]+)\s([\d\.]+)$/.exec(a),b=a[1],c=a[2],b&&c?new google.maps.Point(b,c):void 0)},createMarkerOptions:function(a,b,c,d){var e;return null==d&&(d=void 0),e=angular.extend({},c,{position:new google.maps.LatLng(a.latitude,a.longitude),icon:b,visible:null!=a.latitude&&null!=a.longitude}),null!=d&&(e.map=d),e},createWindowOptions:function(a,b,c,d){return null!=c&&null!=d?angular.extend({},d,{content:c,position:angular.isObject(a)?a.getPosition():new google.maps.LatLng(b.coords.latitude,b.coords.longitude)}):void 0},defaultDelay:50}})}.call(this),function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};this.ngGmapModule("directives.api.utils",function(){return this.Linked=function(a){function c(a,b,c,d){this.scope=a,this.element=b,this.attrs=c,this.ctrls=d}return b(c,a),c}(oo.BaseObject)})}.call(this),function(){this.ngGmapModule("directives.api.utils",function(){var a;return this.Logger={logger:void 0,doLog:!1,info:function(b){return a.doLog?null!=a.logger?a.logger.info(b):console.info(b):void 0},error:function(b){return a.doLog?null!=a.logger?a.logger.error(b):console.error(b):void 0}},a=this.Logger})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api.models.child",function(){return this.MarkerLabelChildModel=function(b){function d(b,c){this.destroy=a(this.destroy,this),this.draw=a(this.draw,this),this.setPosition=a(this.setPosition,this),this.setZIndex=a(this.setZIndex,this),this.setVisible=a(this.setVisible,this),this.setAnchor=a(this.setAnchor,this),this.setMandatoryStyles=a(this.setMandatoryStyles,this),this.setStyles=a(this.setStyles,this),this.setContent=a(this.setContent,this),this.setTitle=a(this.setTitle,this),this.getSharedCross=a(this.getSharedCross,this);var e,f,g;d.__super__.constructor.call(this),e=this,this.marker=b,this.marker.set("labelContent",c.labelContent),this.marker.set("labelAnchor",this.getLabelPositionPoint(c.labelAnchor)),this.marker.set("labelClass",c.labelClass||"labels"),this.marker.set("labelStyle",c.labelStyle||{opacity:100}),this.marker.set("labelInBackground",c.labelInBackground||!1),c.labelVisible||this.marker.set("labelVisible",!0),c.raiseOnDrag||this.marker.set("raiseOnDrag",!0),c.clickable||this.marker.set("clickable",!0),c.draggable||this.marker.set("draggable",!1),c.optimized||this.marker.set("optimized",!1),c.crossImage=null!=(f=c.crossImage)?f:document.location.protocol+"//maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png",c.handCursor=null!=(g=c.handCursor)?g:document.location.protocol+"//maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur",this.markerLabel=new MarkerLabel_(this.marker,c.crossImage,c.handCursor),this.marker.set("setMap",function(a){return google.maps.Marker.prototype.setMap.apply(this,arguments),e.markerLabel.setMap(a)}),this.marker.setMap(this.marker.getMap())}return c(d,b),d.include(directives.api.utils.GmapUtil),d.prototype.getSharedCross=function(a){return this.markerLabel.getSharedCross(a)},d.prototype.setTitle=function(){return this.markerLabel.setTitle()},d.prototype.setContent=function(){return this.markerLabel.setContent()},d.prototype.setStyles=function(){return this.markerLabel.setStyles()},d.prototype.setMandatoryStyles=function(){return this.markerLabel.setMandatoryStyles()},d.prototype.setAnchor=function(){return this.markerLabel.setAnchor()},d.prototype.setVisible=function(){return this.markerLabel.setVisible()},d.prototype.setZIndex=function(){return this.markerLabel.setZIndex()},d.prototype.setPosition=function(){return this.markerLabel.setPosition()},d.prototype.draw=function(){return this.markerLabel.draw()},d.prototype.destroy=function(){return null!=this.markerLabel.labelDiv_.parentNode&&null!=this.markerLabel.eventDiv_.parentNode?this.markerLabel.onRemove():void 0},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api.models.child",function(){return this.MarkerChildModel=function(b){function d(b,c,d,e,f,g,h,i){var j,k=this;this.index=b,this.model=c,this.parentScope=d,this.gMap=e,this.defaults=g,this.doClick=h,this.gMarkerManager=i,this.watchDestroy=a(this.watchDestroy,this),this.setLabelOptions=a(this.setLabelOptions,this),this.isLabelDefined=a(this.isLabelDefined,this),this.setOptions=a(this.setOptions,this),this.setIcon=a(this.setIcon,this),this.setCoords=a(this.setCoords,this),this.destroy=a(this.destroy,this),this.maybeSetScopeValue=a(this.maybeSetScopeValue,this),this.createMarker=a(this.createMarker,this),this.setMyScope=a(this.setMyScope,this),j=this,this.iconKey=this.parentScope.icon,this.coordsKey=this.parentScope.coords,this.clickKey=this.parentScope.click(),this.labelContentKey=this.parentScope.labelContent,this.optionsKey=this.parentScope.options,this.labelOptionsKey=this.parentScope.labelOptions,this.myScope=this.parentScope.$new(!1),this.myScope.model=this.model,this.setMyScope(this.model,void 0,!0),this.createMarker(this.model),this.myScope.$watch("model",function(a,b){return a!==b?k.setMyScope(a,b):void 0},!0),this.$log=directives.api.utils.Logger,this.$log.info(j),this.watchDestroy(this.myScope)}return c(d,b),d.include(directives.api.utils.GmapUtil),d.prototype.setMyScope=function(a,b,c){return null==b&&(b=void 0),null==c&&(c=!1),this.maybeSetScopeValue("icon",a,b,this.iconKey,this.evalModelHandle,c,this.setIcon),this.maybeSetScopeValue("coords",a,b,this.coordsKey,this.evalModelHandle,c,this.setCoords),this.maybeSetScopeValue("labelContent",a,b,this.labelContentKey,this.evalModelHandle,c),this.maybeSetScopeValue("click",a,b,this.clickKey,this.evalModelHandle,c),this.createMarker(a,b,c)},d.prototype.createMarker=function(a,b,c){var d=this;return null==b&&(b=void 0),null==c&&(c=!1),this.maybeSetScopeValue("options",a,b,this.optionsKey,function(a,b){var c;return void 0===a?void 0:(c="self"===b?a:a[b],void 0===c?c=void 0===b?d.defaults:d.myScope.options:c)},c,this.setOptions)},d.prototype.evalModelHandle=function(a,b){return void 0===a?void 0:"self"===b?a:a[b]},d.prototype.maybeSetScopeValue=function(a,b,c,d,e,f,g){var h,i;return null==g&&(g=void 0),void 0===c?(this.myScope[a]=e(b,d),f||null!=g&&g(this.myScope),void 0):(i=e(c,d),h=e(b,d),h===i||this.myScope[a]===h||(this.myScope[a]=h,f)?void 0:(null!=g&&g(this.myScope),this.gMarkerManager.draw()))},d.prototype.destroy=function(){return this.myScope.$destroy()},d.prototype.setCoords=function(a){return a.$id===this.myScope.$id&&void 0!==this.gMarker?null!=a.coords?(this.gMarker.setPosition(new google.maps.LatLng(a.coords.latitude,a.coords.longitude)),this.gMarker.setVisible(null!=a.coords.latitude&&null!=a.coords.longitude),this.gMarkerManager.remove(this.gMarker),this.gMarkerManager.add(this.gMarker)):this.gMarkerManager.remove(this.gMarker):void 0},d.prototype.setIcon=function(a){return a.$id===this.myScope.$id&&void 0!==this.gMarker?(this.gMarkerManager.remove(this.gMarker),this.gMarker.setIcon(a.icon),this.gMarkerManager.add(this.gMarker),this.gMarker.setPosition(new google.maps.LatLng(a.coords.latitude,a.coords.longitude)),this.gMarker.setVisible(a.coords.latitude&&null!=a.coords.longitude)):void 0},d.prototype.setOptions=function(a){var b,c=this;if(a.$id===this.myScope.$id&&(null!=this.gMarker&&(this.gMarkerManager.remove(this.gMarker),delete this.gMarker),null!=(b=a.coords)?b:"function"==typeof a.icon?a.icon(null!=a.options):void 0))return this.opts=this.createMarkerOptions(a.coords,a.icon,a.options),delete this.gMarker,this.gMarker=this.isLabelDefined(a)?new MarkerWithLabel(this.setLabelOptions(this.opts,a)):new google.maps.Marker(this.opts),this.gMarkerManager.add(this.gMarker),google.maps.event.addListener(this.gMarker,"click",function(){return c.doClick&&null!=c.myScope.click?c.myScope.click():void 0})},d.prototype.isLabelDefined=function(a){return null!=a.labelContent},d.prototype.setLabelOptions=function(a,b){return a.labelAnchor=this.getLabelPositionPoint(b.labelAnchor),a.labelClass=b.labelClass,a.labelContent=b.labelContent,a},d.prototype.watchDestroy=function(a){var b=this;return a.$on("$destroy",function(){var a;return null!=b.gMarker&&(b.gMarkerManager.remove(b.gMarker),delete b.gMarker),a=void 0})},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api.models.child",function(){return this.WindowChildModel=function(b){function d(b,c,d,e,f,g,h,i,j,k){this.element=j,null==k&&(k=!1),this.destroy=a(this.destroy,this),this.hideWindow=a(this.hideWindow,this),this.showWindow=a(this.showWindow,this),this.handleClick=a(this.handleClick,this),this.watchCoords=a(this.watchCoords,this),this.watchShow=a(this.watchShow,this),this.createGWin=a(this.createGWin,this),this.scope=b,this.opts=c,this.mapCtrl=e,this.markerCtrl=f,this.isIconVisibleOnClick=d,this.initialMarkerVisibility=null!=this.markerCtrl?this.markerCtrl.getVisible():!1,this.$log=directives.api.utils.Logger,this.$http=g,this.$templateCache=h,this.$compile=i,this.createGWin(),null!=this.markerCtrl&&this.markerCtrl.setClickable(!0),this.handleClick(),this.watchShow(),this.watchCoords(),this.needToManualDestroy=k,this.$log.info(this)}return c(d,b),d.include(directives.api.utils.GmapUtil),d.prototype.createGWin=function(a){var b=this;return null==a&&(a=!1),null==this.gWin&&a&&(this.opts=null!=this.markerCtrl?this.createWindowOptions(this.markerCtrl,this.scope,this.element.html(),{}):{}),null!=this.opts&&void 0===this.gWin?(this.gWin=new google.maps.InfoWindow(this.opts),google.maps.event.addListener(this.gWin,"closeclick",function(){return null!=b.markerCtrl&&b.markerCtrl.setVisible(b.initialMarkerVisibility),null!=b.scope.closeClick?b.scope.closeClick():void 0})):void 0},d.prototype.watchShow=function(){var a=this;return this.scope.$watch("show",function(b,c){return b!==c?b?a.showWindow():a.hideWindow():null!=a.gWin&&b&&!a.gWin.getMap()?a.showWindow():void 0},!0)},d.prototype.watchCoords=function(){var a=this;return this.scope.$watch("coords",function(b,c){return b!==c?a.gWin.setPosition(new google.maps.LatLng(b.latitude,b.longitude)):void 0},!0)},d.prototype.handleClick=function(){var a=this;return null!=this.markerCtrl?google.maps.event.addListener(this.markerCtrl,"click",function(){var b;return a.createGWin(!0),b=a.markerCtrl.getPosition(),null!=a.gWin&&(a.gWin.setPosition(b),a.gWin.open(a.mapCtrl)),a.markerCtrl.setVisible(a.isIconVisibleOnClick)}):void 0},d.prototype.showWindow=function(){var a=this;if(this.scope.templateUrl){if(this.gWin)return this.$http.get(this.scope.templateUrl,{cache:this.$templateCache}).then(function(b){var c,d;return d=a.scope.$new(),angular.isDefined(a.scope.templateParameter)&&(d.parameter=a.scope.templateParameter),c=a.$compile(b.data)(d),a.gWin.setContent(c.get(0)),a.gWin.open(a.mapCtrl)})}else if(null!=this.gWin)return this.gWin.open(this.mapCtrl)},d.prototype.hideWindow=function(){return null!=this.gWin?this.gWin.close():void 0},d.prototype.destroy=function(){var a;return this.hideWindow(this.gWin),null!=this.scope&&this.needToManualDestroy&&this.scope.$destroy(),delete this.gWin,a=void 0},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api.models.parent",function(){return this.IMarkerParentModel=function(b){function d(b,c,d,e,f){var g,h=this;this.scope=b,this.element=c,this.attrs=d,this.mapCtrl=e,this.$timeout=f,this.linkInit=a(this.linkInit,this),this.onDestroy=a(this.onDestroy,this),this.onWatch=a(this.onWatch,this),this.watch=a(this.watch,this),this.validateScope=a(this.validateScope,this),this.onTimeOut=a(this.onTimeOut,this),g=this,this.$log=directives.api.utils.Logger,this.validateScope(b)&&(this.doClick=angular.isDefined(d.click),null!=b.options&&(this.DEFAULTS=b.options),this.$timeout(function(){return h.watch("coords",b),h.watch("icon",b),h.watch("options",b),h.onTimeOut(b),b.$on("$destroy",function(){return h.onDestroy(b)})}))}return c(d,b),d.prototype.DEFAULTS={},d.prototype.isFalse=function(a){return-1!==["false","FALSE",0,"n","N","no","NO"].indexOf(a)},d.prototype.onTimeOut=function(){},d.prototype.validateScope=function(a){var b;return null==a?!1:(b=null!=a.coords,b||this.$log.error(this.constructor.name+": no valid coords attribute found"),b)},d.prototype.watch=function(a,b){var c=this;return b.$watch(a,function(d,e){return d!==e?c.onWatch(a,b,d,e):void 0},!0)},d.prototype.onWatch=function(){throw new Exception("Not Implemented!!")},d.prototype.onDestroy=function(){throw new Exception("Not Implemented!!")},d.prototype.linkInit=function(){throw new Exception("Not Implemented!!")},d}(oo.BaseObject)})}.call(this),function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};this.ngGmapModule("directives.api.models.parent",function(){return this.IWindowParentModel=function(a){function c(a,b,c,d,e,f,g,h){var i;i=this,this.$log=directives.api.utils.Logger,this.$timeout=e,this.$compile=f,this.$http=g,this.$templateCache=h,null!=a.options&&(this.DEFAULTS=a.options)}return b(c,a),c.include(directives.api.utils.GmapUtil),c.prototype.DEFAULTS={},c}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api.models.parent",function(){return this.LayerParentModel=function(b){function d(b,c,d,e,f,g){var h=this;return this.scope=b,this.element=c,this.attrs=d,this.mapCtrl=e,this.$timeout=f,this.$log=null!=g?g:directives.api.utils.Logger,this.createGoogleLayer=a(this.createGoogleLayer,this),null==this.attrs.type?(this.$log.info("type attribute for the layer directive is mandatory. Layer creation aborted!!"),void 0):(this.createGoogleLayer(),this.gMap=void 0,this.doShow=!0,this.$timeout(function(){return h.gMap=e.getMap(),angular.isDefined(h.attrs.show)&&(h.doShow=h.scope.show),null!==h.doShow&&h.doShow&&null!==h.Map&&h.layer.setMap(h.gMap),h.scope.$watch("show",function(a,b){return a!==b?(h.doShow=a,a?h.layer.setMap(h.gMap):h.layer.setMap(null)):void 0},!0),h.scope.$watch("options",function(a,b){return a!==b?(h.layer.setMap(null),h.layer=null,h.createGoogleLayer()):void 0},!0),h.scope.$on("$destroy",function(){return this.layer.setMap(null)})}),void 0)}return c(d,b),d.prototype.createGoogleLayer=function(){return this.layer=null!=this.attrs.options?void 0===this.attrs.namespace?new google.maps[this.attrs.type]:new google.maps[this.attrs.namespace][this.attrs.type]:void 0===this.attrs.namespace?new google.maps[this.attrs.type](this.scope.options):new google.maps[this.attrs.namespace][this.attrs.type](this.scope.options)},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api.models.parent",function(){return this.MarkerParentModel=function(b){function d(b,c,e,f,g){this.onDestroy=a(this.onDestroy,this),this.onWatch=a(this.onWatch,this),this.onTimeOut=a(this.onTimeOut,this);var h;d.__super__.constructor.call(this,b,c,e,f,g),h=this}return c(d,b),d.include(directives.api.utils.GmapUtil),d.prototype.onTimeOut=function(a){var b,c=this;return b=this.createMarkerOptions(a.coords,a.icon,a.options,this.mapCtrl.getMap()),this.gMarker=new google.maps.Marker(b),this.element.data("instance",this.gMarker),google.maps.event.addListener(this.gMarker,"click",function(){return c.doClick&&null!=a.click?c.$timeout(function(){return c.scope.click()}):void 0}),this.$log.info(this)},d.prototype.onWatch=function(a,b){switch(a){case"coords":return null!=b.coords&&null!=this.gMarker?(this.gMarker.setMap(this.mapCtrl.getMap()),this.gMarker.setPosition(new google.maps.LatLng(b.coords.latitude,b.coords.longitude)),this.gMarker.setVisible(null!=b.coords.latitude&&null!=b.coords.longitude),this.gMarker.setOptions(b.options)):this.gMarker.setMap(null);case"icon":if(null!=b.icon&&null!=b.coords&&null!=this.gMarker)return this.gMarker.setOptions(b.options),this.gMarker.setIcon(b.icon),this.gMarker.setMap(null),this.gMarker.setMap(this.mapCtrl.getMap()),this.gMarker.setPosition(new google.maps.LatLng(b.coords.latitude,b.coords.longitude)),this.gMarker.setVisible(b.coords.latitude&&null!=b.coords.longitude);break;case"options":if(null!=b.coords&&null!=b.icon&&b.options)return this.gMarker.setMap(null),delete this.gMarker,this.gMarker=new google.maps.Marker(this.createMarkerOptions(b.coords,b.icon,b.options,this.mapCtrl.getMap()))}},d.prototype.onDestroy=function(){var a;return void 0===this.gMarker?(a=void 0,void 0):(this.gMarker.setMap(null),delete this.gMarker,a=void 0)},d}(directives.api.models.parent.IMarkerParentModel)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api.models.parent",function(){return this.MarkersParentModel=function(b){function d(b,c,e,f,g){this.fit=a(this.fit,this),this.onDestroy=a(this.onDestroy,this),this.onWatch=a(this.onWatch,this),this.reBuildMarkers=a(this.reBuildMarkers,this),this.createMarkers=a(this.createMarkers,this),this.validateScope=a(this.validateScope,this),this.onTimeOut=a(this.onTimeOut,this);var h;d.__super__.constructor.call(this,b,c,e,f,g),h=this,this.markers=[],this.markersIndex=0,this.gMarkerManager=void 0,this.scope=b,this.bigGulp=directives.api.utils.AsyncProcessor,this.$timeout=g,this.$log.info(this)}return c(d,b),d.prototype.onTimeOut=function(a){return this.watch("models",a),this.watch("doCluster",a),this.watch("clusterOptions",a),this.watch("fit",a),this.createMarkers(a)},d.prototype.validateScope=function(a){var b;return b=angular.isUndefined(a.models)||void 0===a.models,b&&this.$log.error(this.constructor.name+": no valid models attribute found"),d.__super__.validateScope.call(this,a)||b},d.prototype.createMarkers=function(a){var b=this;return null!=a.doCluster&&a.doCluster===!0?null!=a.clusterOptions?void 0===this.gMarkerManager?this.gMarkerManager=new directives.api.managers.ClustererMarkerManager(this.mapCtrl.getMap(),void 0,a.clusterOptions):this.gMarkerManager.opt_options!==a.clusterOptions&&(this.gMarkerManager=new directives.api.managers.ClustererMarkerManager(this.mapCtrl.getMap(),void 0,a.clusterOptions)):this.gMarkerManager=new directives.api.managers.ClustererMarkerManager(this.mapCtrl.getMap()):this.gMarkerManager=new directives.api.managers.MarkerManager(this.mapCtrl.getMap()),this.bigGulp.handleLargeArray(a.models,function(c){var d;return a.doRebuild=!0,d=new directives.api.models.child.MarkerChildModel(b.markersIndex,c,a,b.mapCtrl,b.$timeout,b.DEFAULTS,b.doClick,b.gMarkerManager),b.markers.push(d),b.markersIndex++},function(){},function(){return b.gMarkerManager.draw(),angular.isDefined(b.attrs.fit)&&null!=a.fit&&a.fit&&b.fit(),a.markerModels=b.markers})},d.prototype.reBuildMarkers=function(a){var b,c,d,e,f;if(a.doRebuild||void 0===a.doRebuild){for(f=this.markers,c=function(a){return a.destroy()},d=0,e=f.length;e>d;d++)b=f[d],c(b);return delete this.markers,this.markers=[],this.markersIndex=0,null!=this.gMarkerManager&&this.gMarkerManager.clear(),this.createMarkers(a)}},d.prototype.onWatch=function(a,b,c,d){return"models"!==a||c.length!==d.length?"options"===a&&null!=c?(this.DEFAULTS=c,void 0):this.reBuildMarkers(b):void 0},d.prototype.onDestroy=function(){var a,b,c,d;for(d=this.markers,b=0,c=d.length;c>b;b++)a=d[b],a.destroy();return null!=this.gMarkerManager?this.gMarkerManager.clear():void 0},d.prototype.fit=function(){var a,b;return this.mapCtrl&&null!=this.markers&&this.markers.length&&(a=new google.maps.LatLngBounds,b=!1,_.each(this.markers,function(c){return null!=c.gMarker?(b||(b=!0),a.extend(c.gMarker.getPosition())):void 0}),b)?this.mapCtrl.getMap().fitBounds(a):void 0},d}(directives.api.models.parent.IMarkerParentModel)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api.models.parent",function(){return this.WindowsParentModel=function(b){function d(b,c,e,f,g,h,i,j,k){this.interpolateContent=a(this.interpolateContent,this),this.setChildScope=a(this.setChildScope,this),this.createWindow=a(this.createWindow,this),this.setContentKeys=a(this.setContentKeys,this),this.createChildScopesWindows=a(this.createChildScopesWindows,this),this.watchOurScope=a(this.watchOurScope,this),this.watchDestroy=a(this.watchDestroy,this),this.watchModels=a(this.watchModels,this),this.watch=a(this.watch,this);var l,m,n,o,p,q=this;for(d.__super__.constructor.call(this,b,c,e,f,g,h,i,j,k),m=this,this.$interpolate=k,this.windows=[],this.windwsIndex=0,this.scopePropNames=["show","coords","templateUrl","templateParameter","isIconVisibleOnClick","closeClick"],p=this.scopePropNames,n=0,o=p.length;o>n;n++)l=p[n],this[l+"Key"]=void 0;this.linked=new directives.api.utils.Linked(b,c,e,f),this.models=void 0,this.contentKeys=void 0,this.isIconVisibleOnClick=void 0,this.firstTime=!0,this.bigGulp=directives.api.utils.AsyncProcessor,this.$log.info(m),this.$timeout(function(){return q.watchOurScope(b),q.createChildScopesWindows()},50)}return c(d,b),d.prototype.watch=function(a,b,c){var d=this;return a.$watch(b,function(a,e){var f,g,h,i,j;if(a!==e){for(d[c]="function"==typeof a?a():a,i=d.windows,j=[],g=0,h=i.length;h>g;g++)f=i[g],j.push(function(a){return a.scope[b]="self"===d[c]?a:a[d[c]]}(f));return j}},!0)},d.prototype.watchModels=function(a){var b=this;return a.$watch("models",function(a,c){return a!==c&&a.length!==c.length?b.bigGulp.handleLargeArray(b.windows,function(a){return a.destroy()},function(){},function(){return b.windows=[],b.windowsIndex=0,b.createChildScopesWindows()}):void 0},!0)},d.prototype.watchDestroy=function(a){var b=this;return a.$on("$destroy",function(){return b.bigGulp.handleLargeArray(b.windows,function(a){return a.destroy()},function(){},function(){return delete b.windows,b.windows=[],b.windowsIndex=0})})},d.prototype.watchOurScope=function(a){var b,c,d,e,f,g=this;for(e=this.scopePropNames,f=[],c=0,d=e.length;d>c;c++)b=e[c],f.push(function(b){var c;return c=b+"Key",g[c]="function"==typeof a[b]?a[b]():a[b],g.watch(a,b,c)}(b));return f},d.prototype.createChildScopesWindows=function(){var a,b,c,d=this;
-return this.isIconVisibleOnClick=!0,angular.isDefined(this.linked.attrs.isiconvisibleonclick)&&(this.isIconVisibleOnClick=this.linked.scope.isIconVisibleOnClick),a=this.linked.ctrls[0].getMap(),b=this.linked.ctrls.length>1&&null!=this.linked.ctrls[1]?this.linked.ctrls[1].getMarkersScope():void 0,c=angular.isUndefined(this.linked.scope.models),c&&(void 0===b||void 0===b.markerModels&&void 0===b.models)?(this.$log.info("No models to create windows from! Need direct models or models derrived from markers!"),void 0):null!=a?null!=this.linked.scope.models?(this.models=this.linked.scope.models,this.firstTime&&(this.watchModels(this.linked.scope),this.watchDestroy(this.linked.scope)),this.setContentKeys(this.linked.scope.models),this.bigGulp.handleLargeArray(this.linked.scope.models,function(b){return d.createWindow(b,void 0,a)},function(){},function(){return d.firstTime=!1})):(this.models=b.models,this.firstTime&&(this.watchModels(b),this.watchDestroy(b)),this.setContentKeys(b.models),this.bigGulp.handleLargeArray(b.markerModels,function(b){return d.createWindow(b.model,b.gMarker,a)},function(){},function(){return d.firstTime=!1})):void 0},d.prototype.setContentKeys=function(a){return a.length>0?this.contentKeys=Object.keys(a[0]):void 0},d.prototype.createWindow=function(a,b,c){var d,e,f,g=this;return d=this.linked.scope.$new(!1),this.setChildScope(d,a),d.$watch("model",function(a,b){return a!==b?g.setChildScope(d,a):void 0},!0),f=this.interpolateContent(this.linked.element.html(),a),e=this.createWindowOptions(b,d,f,this.DEFAULTS),this.windows.push(new directives.api.models.child.WindowChildModel(d,e,this.isIconVisibleOnClick,c,b,this.$http,this.$templateCache,this.$compile,!0))},d.prototype.setChildScope=function(a,b){var c,d,e,f,g,h=this;for(g=this.scopePropNames,d=function(c){var d,e;return d=c+"Key",e="self"===h[d]?b:b[h[d]],e!==a[c]?a[c]=e:void 0},e=0,f=g.length;f>e;e++)c=g[e],d(c);return a.model=b},d.prototype.interpolateContent=function(a,b){var c,d,e,f,g,h;if(void 0!==this.contentKeys&&0!==this.contentKeys.length){for(c=this.$interpolate(a),d={},h=this.contentKeys,f=0,g=h.length;g>f;f++)e=h[f],d[e]=b[e];return c(d)}},d}(directives.api.models.parent.IWindowParentModel)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.ILabel=function(b){function d(b){this.link=a(this.link,this);var c;c=this,this.restrict="ECMA",this.replace=!0,this.template=void 0,this.require=void 0,this.transclude=!0,this.priority=-100,this.scope={labelContent:"=content",labelAnchor:"@anchor",labelClass:"@class",labelStyle:"=style"},this.$log=directives.api.utils.Logger,this.$timeout=b}return c(d,b),d.prototype.link=function(){throw new Exception("Not Implemented!!")},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.IMarker=function(b){function d(b){this.link=a(this.link,this);var c;c=this,this.$log=directives.api.utils.Logger,this.$timeout=b,this.restrict="ECMA",this.replace=!0,this.require="^googleMap",this.priority=-1,this.transclude=!0,this.replace=!0,this.scope={coords:"=coords",icon:"=icon",click:"&click",options:"=options"}}return c(d,b),d.prototype.controller=["$scope","$element",function(){throw new Exception("Not Implemented!!")}],d.prototype.link=function(){throw new Exception("Not implemented!!")},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.IWindow=function(b){function d(b,c,d,e){this.link=a(this.link,this);var f;f=this,this.restrict="ECMA",this.template=void 0,this.transclude=!0,this.priority=-100,this.require=void 0,this.replace=!0,this.scope={coords:"=coords",show:"=show",templateUrl:"=templateurl",templateParameter:"=templateparameter",isIconVisibleOnClick:"=isiconvisibleonclick",closeClick:"&closeclick",options:"=options"},this.$log=directives.api.utils.Logger,this.$timeout=b,this.$compile=c,this.$http=d,this.$templateCache=e}return c(d,b),d.prototype.link=function(){throw new Exception("Not Implemented!!")},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Label=function(b){function d(b){this.link=a(this.link,this);var c;d.__super__.constructor.call(this,b),c=this,this.require="^marker",this.template='<span class="angular-google-maps-marker-label" ng-transclude></span>',this.$log.info(this)}return c(d,b),d.prototype.link=function(a,b,c,d){return this.$timeout(function(){var b,c;return c=d.getMarker(),null!=c&&(b=new directives.api.models.child.MarkerLabelChildModel(c,a)),a.$on("$destroy",function(){return b.destroy()})},directives.api.utils.GmapUtil.defaultDelay+25)},d}(directives.api.ILabel)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Layer=function(b){function d(b){this.link=a(this.link,this),this.$log=directives.api.utils.Logger,this.$timeout=b,this.restrict="ECMA",this.require="^googleMap",this.priority=-1,this.transclude=!0,this.template='<span class="angular-google-map-layer" ng-transclude></span>',this.replace=!0,this.scope={show:"=show",type:"=type",namespace:"=namespace",options:"=options"}}return c(d,b),d.prototype.link=function(a,b,c,d){return new directives.api.models.parent.LayerParentModel(a,b,c,d,this.$timeout)},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Marker=function(b){function d(b){this.link=a(this.link,this);var c;d.__super__.constructor.call(this,b),c=this,this.template='<span class="angular-google-map-marker" ng-transclude></span>',this.$log.info(this)}return c(d,b),d.prototype.controller=["$scope","$element",function(a,b){return this.getMarker=function(){return b.data("instance")}}],d.prototype.link=function(a,b,c,d){return new directives.api.models.parent.MarkerParentModel(a,b,c,d,this.$timeout)},d}(directives.api.IMarker)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Markers=function(b){function d(b){this.link=a(this.link,this);var c;d.__super__.constructor.call(this,b),c=this,this.template='<span class="angular-google-map-markers" ng-transclude></span>',this.scope.models="=models",this.scope.doCluster="=docluster",this.scope.clusterOptions="=clusteroptions",this.scope.fit="=fit",this.scope.labelContent="=labelcontent",this.scope.labelAnchor="@labelanchor",this.scope.labelClass="@labelclass",this.$timeout=b,this.$log.info(this)}return c(d,b),d.prototype.controller=["$scope","$element",function(a){return this.getMarkersScope=function(){return a}}],d.prototype.link=function(a,b,c,d){return new directives.api.models.parent.MarkersParentModel(a,b,c,d,this.$timeout)},d}(directives.api.IMarker)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Window=function(b){function d(b,c,e,f){this.link=a(this.link,this);var g;d.__super__.constructor.call(this,b,c,e,f),g=this,this.require=["^googleMap","^?marker"],this.template='<span class="angular-google-maps-window" ng-transclude></span>',this.$log.info(g)}return c(d,b),d.include(directives.api.utils.GmapUtil),d.prototype.link=function(a,b,c,d){var e=this;return this.$timeout(function(){var f,g,h,i,j,k,l;return h=!0,angular.isDefined(c.isiconvisibleonclick)&&(h=a.isIconVisibleOnClick),i=d[0].getMap(),j=d.length>1&&null!=d[1]?d[1].getMarker():void 0,f=null!=a.options?a.options:{},g=null!=a&&null!=a.coords&&null!=a.coords.latitude&&null!=a.coords.longitude,k=null!=j&&g?e.createWindowOptions(j,a,b.html(),f):void 0,null!=i&&(l=new directives.api.models.child.WindowChildModel(a,k,h,i,j,e.$http,e.$templateCache,e.$compile,b)),a.$on("$destroy",function(){return l.destroy()})},directives.api.utils.GmapUtil.defaultDelay+25)},d}(directives.api.IWindow)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Windows=function(b){function d(b,c,e,f,g){this.link=a(this.link,this);var h;d.__super__.constructor.call(this,b,c,e,f),h=this,this.$interpolate=g,this.require=["^googleMap","^?markers"],this.template='<span class="angular-google-maps-windows" ng-transclude></span>',this.scope.models="=models",this.$log.info(h)}return c(d,b),d.prototype.link=function(a,b,c,d){return new directives.api.models.parent.WindowsParentModel(a,b,c,d,this.$timeout,this.$compile,this.$http,this.$templateCache,this.$interpolate)},d}(directives.api.IWindow)})}.call(this),angular.module("google-maps").controller("PolylineDisplayController",["$scope",function(a){a.toggleStrokeColor=function(){a.stroke.color="#6060FB"==a.stroke.color?"red":"#6060FB"}}]),ClusterIcon.prototype.onAdd=function(){var a,b,c=this;this.div_=document.createElement("div"),this.div_.className=this.className_,this.visible_&&this.show(),this.getPanes().overlayMouseTarget.appendChild(this.div_),this.boundsChangedListener_=google.maps.event.addListener(this.getMap(),"bounds_changed",function(){b=a}),google.maps.event.addDomListener(this.div_,"mousedown",function(){a=!0,b=!1}),google.maps.event.addDomListener(this.div_,"click",function(d){if(a=!1,!b){var e,f,g=c.cluster_.getMarkerClusterer();google.maps.event.trigger(g,"click",c.cluster_),google.maps.event.trigger(g,"clusterclick",c.cluster_),g.getZoomOnClick()&&(f=g.getMaxZoom(),e=c.cluster_.getBounds(),g.getMap().fitBounds(e),setTimeout(function(){g.getMap().fitBounds(e),null!==f&&g.getMap().getZoom()>f&&g.getMap().setZoom(f+1)},100)),d.cancelBubble=!0,d.stopPropagation&&d.stopPropagation()}}),google.maps.event.addDomListener(this.div_,"mouseover",function(){var a=c.cluster_.getMarkerClusterer();google.maps.event.trigger(a,"mouseover",c.cluster_)}),google.maps.event.addDomListener(this.div_,"mouseout",function(){var a=c.cluster_.getMarkerClusterer();google.maps.event.trigger(a,"mouseout",c.cluster_)})},ClusterIcon.prototype.onRemove=function(){this.div_&&this.div_.parentNode&&(this.hide(),google.maps.event.removeListener(this.boundsChangedListener_),google.maps.event.clearInstanceListeners(this.div_),this.div_.parentNode.removeChild(this.div_),this.div_=null)},ClusterIcon.prototype.draw=function(){if(this.visible_){var a=this.getPosFromLatLng_(this.center_);this.div_.style.top=a.y+"px",this.div_.style.left=a.x+"px"}},ClusterIcon.prototype.hide=function(){this.div_&&(this.div_.style.display="none"),this.visible_=!1},ClusterIcon.prototype.show=function(){if(this.div_){var a=this.getPosFromLatLng_(this.center_);this.div_.style.cssText=this.createCss(a),this.div_.innerHTML=this.cluster_.printable_?"<img src='"+this.url_+"'><div style='position: absolute; top: 0px; left: 0px; width: "+this.width_+"px;'>"+this.sums_.text+"</div>":this.sums_.text,this.div_.title="undefined"==typeof this.sums_.title||""===this.sums_.title?this.cluster_.getMarkerClusterer().getTitle():this.sums_.title,this.div_.style.display=""}this.visible_=!0},ClusterIcon.prototype.useStyle=function(a){this.sums_=a;var b=Math.max(0,a.index-1);b=Math.min(this.styles_.length-1,b);var c=this.styles_[b];this.url_=c.url,this.height_=c.height,this.width_=c.width,this.anchor_=c.anchor,this.anchorIcon_=c.anchorIcon||[parseInt(this.height_/2,10),parseInt(this.width_/2,10)],this.textColor_=c.textColor||"black",this.textSize_=c.textSize||11,this.textDecoration_=c.textDecoration||"none",this.fontWeight_=c.fontWeight||"bold",this.fontStyle_=c.fontStyle||"normal",this.fontFamily_=c.fontFamily||"Arial,sans-serif",this.backgroundPosition_=c.backgroundPosition||"0 0"},ClusterIcon.prototype.setCenter=function(a){this.center_=a},ClusterIcon.prototype.createCss=function(a){var b=[];return this.cluster_.printable_||(b.push("background-image:url("+this.url_+");"),b.push("background-position:"+this.backgroundPosition_+";")),"object"==typeof this.anchor_?("number"==typeof this.anchor_[0]&&this.anchor_[0]>0&&this.anchor_[0]<this.height_?b.push("height:"+(this.height_-this.anchor_[0])+"px; padding-top:"+this.anchor_[0]+"px;"):b.push("height:"+this.height_+"px; line-height:"+this.height_+"px;"),"number"==typeof this.anchor_[1]&&this.anchor_[1]>0&&this.anchor_[1]<this.width_?b.push("width:"+(this.width_-this.anchor_[1])+"px; padding-left:"+this.anchor_[1]+"px;"):b.push("width:"+this.width_+"px; text-align:center;")):b.push("height:"+this.height_+"px; line-height:"+this.height_+"px; width:"+this.width_+"px; text-align:center;"),b.push("cursor:pointer; top:"+a.y+"px; left:"+a.x+"px; color:"+this.textColor_+"; position:absolute; font-size:"+this.textSize_+"px; font-family:"+this.fontFamily_+"; font-weight:"+this.fontWeight_+"; font-style:"+this.fontStyle_+"; text-decoration:"+this.textDecoration_+";"),b.join("")},ClusterIcon.prototype.getPosFromLatLng_=function(a){var b=this.getProjection().fromLatLngToDivPixel(a);return b.x-=this.anchorIcon_[1],b.y-=this.anchorIcon_[0],b},Cluster.prototype.getSize=function(){return this.markers_.length},Cluster.prototype.getMarkers=function(){return this.markers_},Cluster.prototype.getCenter=function(){return this.center_},Cluster.prototype.getMap=function(){return this.map_},Cluster.prototype.getMarkerClusterer=function(){return this.markerClusterer_},Cluster.prototype.getBounds=function(){var a,b=new google.maps.LatLngBounds(this.center_,this.center_),c=this.getMarkers();for(a=0;a<c.length;a++)b.extend(c[a].getPosition());return b},Cluster.prototype.remove=function(){this.clusterIcon_.setMap(null),this.markers_=[],delete this.markers_},Cluster.prototype.addMarker=function(a){var b,c,d;if(this.isMarkerAlreadyAdded_(a))return!1;if(this.center_){if(this.averageCenter_){var e=this.markers_.length+1,f=(this.center_.lat()*(e-1)+a.getPosition().lat())/e,g=(this.center_.lng()*(e-1)+a.getPosition().lng())/e;this.center_=new google.maps.LatLng(f,g),this.calculateBounds_()}}else this.center_=a.getPosition(),this.calculateBounds_();if(a.isAdded=!0,this.markers_.push(a),c=this.markers_.length,d=this.markerClusterer_.getMaxZoom(),null!==d&&this.map_.getZoom()>d)a.getMap()!==this.map_&&a.setMap(this.map_);else if(c<this.minClusterSize_)a.getMap()!==this.map_&&a.setMap(this.map_);else if(c===this.minClusterSize_)for(b=0;c>b;b++)this.markers_[b].setMap(null);else a.setMap(null);return this.updateIcon_(),!0},Cluster.prototype.isMarkerInClusterBounds=function(a){return this.bounds_.contains(a.getPosition())},Cluster.prototype.calculateBounds_=function(){var a=new google.maps.LatLngBounds(this.center_,this.center_);this.bounds_=this.markerClusterer_.getExtendedBounds(a)},Cluster.prototype.updateIcon_=function(){var a=this.markers_.length,b=this.markerClusterer_.getMaxZoom();if(null!==b&&this.map_.getZoom()>b)return this.clusterIcon_.hide(),void 0;if(a<this.minClusterSize_)return this.clusterIcon_.hide(),void 0;var c=this.markerClusterer_.getStyles().length,d=this.markerClusterer_.getCalculator()(this.markers_,c);this.clusterIcon_.setCenter(this.center_),this.clusterIcon_.useStyle(d),this.clusterIcon_.show()},Cluster.prototype.isMarkerAlreadyAdded_=function(a){var b;if(this.markers_.indexOf)return-1!==this.markers_.indexOf(a);for(b=0;b<this.markers_.length;b++)if(a===this.markers_[b])return!0;return!1},MarkerClusterer.prototype.onAdd=function(){var a=this;this.activeMap_=this.getMap(),this.ready_=!0,this.repaint(),this.listeners_=[google.maps.event.addListener(this.getMap(),"zoom_changed",function(){a.resetViewport_(!1),(this.getZoom()===(this.get("minZoom")||0)||this.getZoom()===this.get("maxZoom"))&&google.maps.event.trigger(this,"idle")}),google.maps.event.addListener(this.getMap(),"idle",function(){a.redraw_()})]},MarkerClusterer.prototype.onRemove=function(){var a;for(a=0;a<this.markers_.length;a++)this.markers_[a].getMap()!==this.activeMap_&&this.markers_[a].setMap(this.activeMap_);for(a=0;a<this.clusters_.length;a++)this.clusters_[a].remove();for(this.clusters_=[],a=0;a<this.listeners_.length;a++)google.maps.event.removeListener(this.listeners_[a]);this.listeners_=[],this.activeMap_=null,this.ready_=!1},MarkerClusterer.prototype.draw=function(){},MarkerClusterer.prototype.setupStyles_=function(){var a,b;if(!(this.styles_.length>0))for(a=0;a<this.imageSizes_.length;a++)b=this.imageSizes_[a],this.styles_.push({url:this.imagePath_+(a+1)+"."+this.imageExtension_,height:b,width:b})},MarkerClusterer.prototype.fitMapToMarkers=function(){var a,b=this.getMarkers(),c=new google.maps.LatLngBounds;for(a=0;a<b.length;a++)c.extend(b[a].getPosition());this.getMap().fitBounds(c)},MarkerClusterer.prototype.getGridSize=function(){return this.gridSize_},MarkerClusterer.prototype.setGridSize=function(a){this.gridSize_=a},MarkerClusterer.prototype.getMinimumClusterSize=function(){return this.minClusterSize_},MarkerClusterer.prototype.setMinimumClusterSize=function(a){this.minClusterSize_=a},MarkerClusterer.prototype.getMaxZoom=function(){return this.maxZoom_},MarkerClusterer.prototype.setMaxZoom=function(a){this.maxZoom_=a},MarkerClusterer.prototype.getStyles=function(){return this.styles_},MarkerClusterer.prototype.setStyles=function(a){this.styles_=a},MarkerClusterer.prototype.getTitle=function(){return this.title_},MarkerClusterer.prototype.setTitle=function(a){this.title_=a},MarkerClusterer.prototype.getZoomOnClick=function(){return this.zoomOnClick_},MarkerClusterer.prototype.setZoomOnClick=function(a){this.zoomOnClick_=a},MarkerClusterer.prototype.getAverageCenter=function(){return this.averageCenter_},MarkerClusterer.prototype.setAverageCenter=function(a){this.averageCenter_=a},MarkerClusterer.prototype.getIgnoreHidden=function(){return this.ignoreHidden_},MarkerClusterer.prototype.setIgnoreHidden=function(a){this.ignoreHidden_=a},MarkerClusterer.prototype.getImageExtension=function(){return this.imageExtension_},MarkerClusterer.prototype.setImageExtension=function(a){this.imageExtension_=a},MarkerClusterer.prototype.getImagePath=function(){return this.imagePath_},MarkerClusterer.prototype.setImagePath=function(a){this.imagePath_=a},MarkerClusterer.prototype.getImageSizes=function(){return this.imageSizes_},MarkerClusterer.prototype.setImageSizes=function(a){this.imageSizes_=a},MarkerClusterer.prototype.getCalculator=function(){return this.calculator_},MarkerClusterer.prototype.setCalculator=function(a){this.calculator_=a},MarkerClusterer.prototype.getPrintable=function(){return this.printable_},MarkerClusterer.prototype.setPrintable=function(a){this.printable_=a},MarkerClusterer.prototype.getBatchSizeIE=function(){return this.batchSizeIE_},MarkerClusterer.prototype.setBatchSizeIE=function(a){this.batchSizeIE_=a},MarkerClusterer.prototype.getClusterClass=function(){return this.clusterClass_},MarkerClusterer.prototype.setClusterClass=function(a){this.clusterClass_=a},MarkerClusterer.prototype.getMarkers=function(){return this.markers_},MarkerClusterer.prototype.getTotalMarkers=function(){return this.markers_.length},MarkerClusterer.prototype.getClusters=function(){return this.clusters_},MarkerClusterer.prototype.getTotalClusters=function(){return this.clusters_.length},MarkerClusterer.prototype.addMarker=function(a,b){this.pushMarkerTo_(a),b||this.redraw_()},MarkerClusterer.prototype.addMarkers=function(a,b){var c;for(c=0;c<a.length;c++)this.pushMarkerTo_(a[c]);b||this.redraw_()},MarkerClusterer.prototype.pushMarkerTo_=function(a){if(a.getDraggable()){var b=this;google.maps.event.addListener(a,"dragend",function(){b.ready_&&(this.isAdded=!1,b.repaint())})}a.isAdded=!1,this.markers_.push(a)},MarkerClusterer.prototype.removeMarker=function(a,b){var c=this.removeMarker_(a);return!b&&c&&this.repaint(),c},MarkerClusterer.prototype.removeMarkers=function(a,b){var c,d,e=!1;for(c=0;c<a.length;c++)d=this.removeMarker_(a[c]),e=e||d;return!b&&e&&this.repaint(),e},MarkerClusterer.prototype.removeMarker_=function(a){var b,c=-1;if(this.markers_.indexOf)c=this.markers_.indexOf(a);else for(b=0;b<this.markers_.length;b++)if(a===this.markers_[b]){c=b;break}return-1===c?!1:(a.setMap(null),this.markers_.splice(c,1),!0)},MarkerClusterer.prototype.clearMarkers=function(){this.resetViewport_(!0),this.markers_=[]},MarkerClusterer.prototype.repaint=function(){var a=this.clusters_.slice();this.clusters_=[],this.resetViewport_(!1),this.redraw_(),setTimeout(function(){var b;for(b=0;b<a.length;b++)a[b].remove()},0)},MarkerClusterer.prototype.getExtendedBounds=function(a){var b=this.getProjection(),c=new google.maps.LatLng(a.getNorthEast().lat(),a.getNorthEast().lng()),d=new google.maps.LatLng(a.getSouthWest().lat(),a.getSouthWest().lng()),e=b.fromLatLngToDivPixel(c);e.x+=this.gridSize_,e.y-=this.gridSize_;var f=b.fromLatLngToDivPixel(d);f.x-=this.gridSize_,f.y+=this.gridSize_;var g=b.fromDivPixelToLatLng(e),h=b.fromDivPixelToLatLng(f);return a.extend(g),a.extend(h),a},MarkerClusterer.prototype.redraw_=function(){this.createClusters_(0)},MarkerClusterer.prototype.resetViewport_=function(a){var b,c;for(b=0;b<this.clusters_.length;b++)this.clusters_[b].remove();for(this.clusters_=[],b=0;b<this.markers_.length;b++)c=this.markers_[b],c.isAdded=!1,a&&c.setMap(null)},MarkerClusterer.prototype.distanceBetweenPoints_=function(a,b){var c=6371,d=(b.lat()-a.lat())*Math.PI/180,e=(b.lng()-a.lng())*Math.PI/180,f=Math.sin(d/2)*Math.sin(d/2)+Math.cos(a.lat()*Math.PI/180)*Math.cos(b.lat()*Math.PI/180)*Math.sin(e/2)*Math.sin(e/2),g=2*Math.atan2(Math.sqrt(f),Math.sqrt(1-f)),h=c*g;return h},MarkerClusterer.prototype.isMarkerInBounds_=function(a,b){return b.contains(a.getPosition())},MarkerClusterer.prototype.addToClosestCluster_=function(a){var b,c,d,e,f=4e4,g=null;for(b=0;b<this.clusters_.length;b++)d=this.clusters_[b],e=d.getCenter(),e&&(c=this.distanceBetweenPoints_(e,a.getPosition()),f>c&&(f=c,g=d));g&&g.isMarkerInClusterBounds(a)?g.addMarker(a):(d=new Cluster(this),d.addMarker(a),this.clusters_.push(d))},MarkerClusterer.prototype.createClusters_=function(a){var b,c,d,e=this;if(this.ready_){0===a&&(google.maps.event.trigger(this,"clusteringbegin",this),"undefined"!=typeof this.timerRefStatic&&(clearTimeout(this.timerRefStatic),delete this.timerRefStatic)),d=this.getMap().getZoom()>3?new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest(),this.getMap().getBounds().getNorthEast()):new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472,-178.48388434375),new google.maps.LatLng(-85.08136444384544,178.00048865625));var f=this.getExtendedBounds(d),g=Math.min(a+this.batchSize_,this.markers_.length);for(b=a;g>b;b++)c=this.markers_[b],!c.isAdded&&this.isMarkerInBounds_(c,f)&&(!this.ignoreHidden_||this.ignoreHidden_&&c.getVisible())&&this.addToClosestCluster_(c);g<this.markers_.length?this.timerRefStatic=setTimeout(function(){e.createClusters_(g)},0):(delete this.timerRefStatic,google.maps.event.trigger(this,"clusteringend",this))}},MarkerClusterer.prototype.extend=function(a,b){return function(a){var b;for(b in a.prototype)this.prototype[b]=a.prototype[b];return this}.apply(a,[b])},MarkerClusterer.CALCULATOR=function(a,b){for(var c=0,d="",e=a.length.toString(),f=e;0!==f;)f=parseInt(f/10,10),c++;return c=Math.min(c,b),{text:e,index:c,title:d}},MarkerClusterer.BATCH_SIZE=2e3,MarkerClusterer.BATCH_SIZE_IE=500,MarkerClusterer.IMAGE_PATH="http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclustererplus/images/m",MarkerClusterer.IMAGE_EXTENSION="png",MarkerClusterer.IMAGE_SIZES=[53,56,66,78,90],inherits(MarkerLabel_,google.maps.OverlayView),MarkerLabel_.getSharedCross=function(a){var b;return"undefined"==typeof MarkerLabel_.getSharedCross.crossDiv&&(b=document.createElement("img"),b.style.cssText="position: absolute; z-index: 1000002; display: none;",b.style.marginLeft="-8px",b.style.marginTop="-9px",b.src=a,MarkerLabel_.getSharedCross.crossDiv=b),MarkerLabel_.getSharedCross.crossDiv},MarkerLabel_.prototype.onAdd=function(){var a,b,c,d,e,f,g,h=this,i=!1,j=!1,k=20,l="url("+this.handCursorURL_+")",m=function(a){a.preventDefault&&a.preventDefault(),a.cancelBubble=!0,a.stopPropagation&&a.stopPropagation()},n=function(){h.marker_.setAnimation(null)};this.getPanes().overlayImage.appendChild(this.labelDiv_),this.getPanes().overlayMouseTarget.appendChild(this.eventDiv_),"undefined"==typeof MarkerLabel_.getSharedCross.processed&&(this.getPanes().overlayImage.appendChild(this.crossDiv_),MarkerLabel_.getSharedCross.processed=!0),this.listeners_=[google.maps.event.addDomListener(this.eventDiv_,"mouseover",function(a){(h.marker_.getDraggable()||h.marker_.getClickable())&&(this.style.cursor="pointer",google.maps.event.trigger(h.marker_,"mouseover",a))}),google.maps.event.addDomListener(this.eventDiv_,"mouseout",function(a){!h.marker_.getDraggable()&&!h.marker_.getClickable()||j||(this.style.cursor=h.marker_.getCursor(),google.maps.event.trigger(h.marker_,"mouseout",a))}),google.maps.event.addDomListener(this.eventDiv_,"mousedown",function(a){j=!1,h.marker_.getDraggable()&&(i=!0,this.style.cursor=l),(h.marker_.getDraggable()||h.marker_.getClickable())&&(google.maps.event.trigger(h.marker_,"mousedown",a),m(a))}),google.maps.event.addDomListener(document,"mouseup",function(b){var c;if(i&&(i=!1,h.eventDiv_.style.cursor="pointer",google.maps.event.trigger(h.marker_,"mouseup",b)),j){if(e){c=h.getProjection().fromLatLngToDivPixel(h.marker_.getPosition()),c.y+=k,h.marker_.setPosition(h.getProjection().fromDivPixelToLatLng(c));try{h.marker_.setAnimation(google.maps.Animation.BOUNCE),setTimeout(n,1406)}catch(f){}}h.crossDiv_.style.display="none",h.marker_.setZIndex(a),d=!0,j=!1,b.latLng=h.marker_.getPosition(),google.maps.event.trigger(h.marker_,"dragend",b)}}),google.maps.event.addListener(h.marker_.getMap(),"mousemove",function(d){var l;i&&(j?(d.latLng=new google.maps.LatLng(d.latLng.lat()-b,d.latLng.lng()-c),l=h.getProjection().fromLatLngToDivPixel(d.latLng),e&&(h.crossDiv_.style.left=l.x+"px",h.crossDiv_.style.top=l.y+"px",h.crossDiv_.style.display="",l.y-=k),h.marker_.setPosition(h.getProjection().fromDivPixelToLatLng(l)),e&&(h.eventDiv_.style.top=l.y+k+"px"),google.maps.event.trigger(h.marker_,"drag",d)):(b=d.latLng.lat()-h.marker_.getPosition().lat(),c=d.latLng.lng()-h.marker_.getPosition().lng(),a=h.marker_.getZIndex(),f=h.marker_.getPosition(),g=h.marker_.getMap().getCenter(),e=h.marker_.get("raiseOnDrag"),j=!0,h.marker_.setZIndex(1e6),d.latLng=h.marker_.getPosition(),google.maps.event.trigger(h.marker_,"dragstart",d)))}),google.maps.event.addDomListener(document,"keydown",function(a){j&&27===a.keyCode&&(e=!1,h.marker_.setPosition(f),h.marker_.getMap().setCenter(g),google.maps.event.trigger(document,"mouseup",a))}),google.maps.event.addDomListener(this.eventDiv_,"click",function(a){(h.marker_.getDraggable()||h.marker_.getClickable())&&(d?d=!1:(google.maps.event.trigger(h.marker_,"click",a),m(a)))}),google.maps.event.addDomListener(this.eventDiv_,"dblclick",function(a){(h.marker_.getDraggable()||h.marker_.getClickable())&&(google.maps.event.trigger(h.marker_,"dblclick",a),m(a))}),google.maps.event.addListener(this.marker_,"dragstart",function(){j||(e=this.get("raiseOnDrag"))}),google.maps.event.addListener(this.marker_,"drag",function(){j||e&&(h.setPosition(k),h.labelDiv_.style.zIndex=1e6+(this.get("labelInBackground")?-1:1))}),google.maps.event.addListener(this.marker_,"dragend",function(){j||e&&h.setPosition(0)}),google.maps.event.addListener(this.marker_,"position_changed",function(){h.setPosition()}),google.maps.event.addListener(this.marker_,"zindex_changed",function(){h.setZIndex()}),google.maps.event.addListener(this.marker_,"visible_changed",function(){h.setVisible()}),google.maps.event.addListener(this.marker_,"labelvisible_changed",function(){h.setVisible()}),google.maps.event.addListener(this.marker_,"title_changed",function(){h.setTitle()}),google.maps.event.addListener(this.marker_,"labelcontent_changed",function(){h.setContent()}),google.maps.event.addListener(this.marker_,"labelanchor_changed",function(){h.setAnchor()}),google.maps.event.addListener(this.marker_,"labelclass_changed",function(){h.setStyles()}),google.maps.event.addListener(this.marker_,"labelstyle_changed",function(){h.setStyles()})]},MarkerLabel_.prototype.onRemove=function(){var a;for(null!==this.labelDiv_.parentNode&&this.labelDiv_.parentNode.removeChild(this.labelDiv_),null!==this.eventDiv_.parentNode&&this.eventDiv_.parentNode.removeChild(this.eventDiv_),a=0;a<this.listeners_.length;a++)google.maps.event.removeListener(this.listeners_[a])},MarkerLabel_.prototype.draw=function(){this.setContent(),this.setTitle(),this.setStyles()},MarkerLabel_.prototype.setContent=function(){var a=this.marker_.get("labelContent");"undefined"==typeof a.nodeType?(this.labelDiv_.innerHTML=a,this.eventDiv_.innerHTML=this.labelDiv_.innerHTML):(this.labelDiv_.innerHTML="",this.labelDiv_.appendChild(a),a=a.cloneNode(!0),this.eventDiv_.appendChild(a))},MarkerLabel_.prototype.setTitle=function(){this.eventDiv_.title=this.marker_.getTitle()||""},MarkerLabel_.prototype.setStyles=function(){var a,b;this.labelDiv_.className=this.marker_.get("labelClass"),this.eventDiv_.className=this.labelDiv_.className,this.labelDiv_.style.cssText="",this.eventDiv_.style.cssText="",b=this.marker_.get("labelStyle");for(a in b)b.hasOwnProperty(a)&&(this.labelDiv_.style[a]=b[a],this.eventDiv_.style[a]=b[a]);this.setMandatoryStyles()},MarkerLabel_.prototype.setMandatoryStyles=function(){this.labelDiv_.style.position="absolute",this.labelDiv_.style.overflow="hidden","undefined"!=typeof this.labelDiv_.style.opacity&&""!==this.labelDiv_.style.opacity&&(this.labelDiv_.style.MsFilter='"progid:DXImageTransform.Microsoft.Alpha(opacity='+100*this.labelDiv_.style.opacity+')"',this.labelDiv_.style.filter="alpha(opacity="+100*this.labelDiv_.style.opacity+")"),this.eventDiv_.style.position=this.labelDiv_.style.position,this.eventDiv_.style.overflow=this.labelDiv_.style.overflow,this.eventDiv_.style.opacity=.01,this.eventDiv_.style.MsFilter='"progid:DXImageTransform.Microsoft.Alpha(opacity=1)"',this.eventDiv_.style.filter="alpha(opacity=1)",this.setAnchor(),this.setPosition(),this.setVisible()},MarkerLabel_.prototype.setAnchor=function(){var a=this.marker_.get("labelAnchor");
+return this.isIconVisibleOnClick=!0,angular.isDefined(this.linked.attrs.isiconvisibleonclick)&&(this.isIconVisibleOnClick=this.linked.scope.isIconVisibleOnClick),a=this.linked.ctrls[0].getMap(),b=this.linked.ctrls.length>1&&null!=this.linked.ctrls[1]?this.linked.ctrls[1].getMarkersScope():void 0,c=angular.isUndefined(this.linked.scope.models),c&&(void 0===b||void 0===b.markerModels&&void 0===b.models)?(this.$log.info("No models to create windows from! Need direct models or models derrived from markers!"),void 0):null!=a?null!=this.linked.scope.models?(this.models=this.linked.scope.models,this.firstTime&&(this.watchModels(this.linked.scope),this.watchDestroy(this.linked.scope)),this.setContentKeys(this.linked.scope.models),this.bigGulp.handleLargeArray(this.linked.scope.models,function(b){return d.createWindow(b,void 0,a)},function(){},function(){return d.firstTime=!1})):(this.models=b.models,this.firstTime&&(this.watchModels(b),this.watchDestroy(b)),this.setContentKeys(b.models),this.bigGulp.handleLargeArray(b.markerModels,function(b){return d.createWindow(b.model,b.gMarker,a)},function(){},function(){return d.firstTime=!1})):void 0},d.prototype.setContentKeys=function(a){return a.length>0?this.contentKeys=Object.keys(a[0]):void 0},d.prototype.createWindow=function(a,b,c){var d,e,f,g=this;return d=this.linked.scope.$new(!1),this.setChildScope(d,a),d.$watch("model",function(a,b){return a!==b?g.setChildScope(d,a):void 0},!0),f=this.interpolateContent(this.linked.element.html(),a),e=this.createWindowOptions(b,d,f,this.DEFAULTS),this.windows.push(new directives.api.models.child.WindowChildModel(d,e,this.isIconVisibleOnClick,c,b,this.$http,this.$templateCache,this.$compile,!0))},d.prototype.setChildScope=function(a,b){var c,d,e,f,g,h=this;for(g=this.scopePropNames,d=function(c){var d,e;return d=c+"Key",e="self"===h[d]?b:b[h[d]],e!==a[c]?a[c]=e:void 0},e=0,f=g.length;f>e;e++)c=g[e],d(c);return a.model=b},d.prototype.interpolateContent=function(a,b){var c,d,e,f,g,h;if(void 0!==this.contentKeys&&0!==this.contentKeys.length){for(c=this.$interpolate(a),d={},h=this.contentKeys,f=0,g=h.length;g>f;f++)e=h[f],d[e]=b[e];return c(d)}},d}(directives.api.models.parent.IWindowParentModel)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.ILabel=function(b){function d(b){this.link=a(this.link,this);var c;c=this,this.restrict="ECMA",this.replace=!0,this.template=void 0,this.require=void 0,this.transclude=!0,this.priority=-100,this.scope={labelContent:"=content",labelAnchor:"@anchor",labelClass:"@class",labelStyle:"=style"},this.$log=directives.api.utils.Logger,this.$timeout=b}return c(d,b),d.prototype.link=function(){throw new Exception("Not Implemented!!")},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.IMarker=function(b){function d(b){this.link=a(this.link,this);var c;c=this,this.$log=directives.api.utils.Logger,this.$timeout=b,this.restrict="ECMA",this.require="^googleMap",this.priority=-1,this.transclude=!0,this.replace=!0,this.scope={coords:"=coords",icon:"=icon",click:"&click",options:"=options"}}return c(d,b),d.prototype.controller=["$scope","$element",function(){throw new Exception("Not Implemented!!")}],d.prototype.link=function(){throw new Exception("Not implemented!!")},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.IWindow=function(b){function d(b,c,d,e){this.link=a(this.link,this);var f;f=this,this.restrict="ECMA",this.template=void 0,this.transclude=!0,this.priority=-100,this.require=void 0,this.replace=!0,this.scope={coords:"=coords",show:"=show",templateUrl:"=templateurl",templateParameter:"=templateparameter",isIconVisibleOnClick:"=isiconvisibleonclick",closeClick:"&closeclick",options:"=options"},this.$log=directives.api.utils.Logger,this.$timeout=b,this.$compile=c,this.$http=d,this.$templateCache=e}return c(d,b),d.prototype.link=function(){throw new Exception("Not Implemented!!")},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Label=function(b){function d(b){this.link=a(this.link,this);var c;d.__super__.constructor.call(this,b),c=this,this.require="^marker",this.template='<span class="angular-google-maps-marker-label" ng-transclude></span>',this.$log.info(this)}return c(d,b),d.prototype.link=function(a,b,c,d){return this.$timeout(function(){var b,c;return c=d.getMarker(),null!=c&&(b=new directives.api.models.child.MarkerLabelChildModel(c,a)),a.$on("$destroy",function(){return b.destroy()})},directives.api.utils.GmapUtil.defaultDelay+25)},d}(directives.api.ILabel)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Layer=function(b){function d(b){this.link=a(this.link,this),this.$log=directives.api.utils.Logger,this.$timeout=b,this.restrict="ECMA",this.require="^googleMap",this.priority=-1,this.transclude=!0,this.template='<span class="angular-google-map-layer" ng-transclude></span>',this.replace=!0,this.scope={show:"=show",type:"=type",namespace:"=namespace",options:"=options"}}return c(d,b),d.prototype.link=function(a,b,c,d){return new directives.api.models.parent.LayerParentModel(a,b,c,d,this.$timeout)},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Marker=function(b){function d(b){this.link=a(this.link,this);var c;d.__super__.constructor.call(this,b),c=this,this.template='<span class="angular-google-map-marker" ng-transclude></span>',this.$log.info(this)}return c(d,b),d.prototype.controller=["$scope","$element",function(a,b){return{getMarker:function(){return b.data("instance")}}}],d.prototype.link=function(a,b,c,d){return new directives.api.models.parent.MarkerParentModel(a,b,c,d,this.$timeout)},d}(directives.api.IMarker)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Markers=function(b){function d(b){this.link=a(this.link,this);var c;d.__super__.constructor.call(this,b),c=this,this.template='<span class="angular-google-map-markers" ng-transclude></span>',this.scope.models="=models",this.scope.doCluster="=docluster",this.scope.clusterOptions="=clusteroptions",this.scope.fit="=fit",this.scope.labelContent="=labelcontent",this.scope.labelAnchor="@labelanchor",this.scope.labelClass="@labelclass",this.$timeout=b,this.$log.info(this)}return c(d,b),d.prototype.controller=["$scope","$element",function(a){return{getMarkersScope:function(){return a}}}],d.prototype.link=function(a,b,c,d){return new directives.api.models.parent.MarkersParentModel(a,b,c,d,this.$timeout)},d}(directives.api.IMarker)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Window=function(b){function d(b,c,e,f){this.link=a(this.link,this);var g;d.__super__.constructor.call(this,b,c,e,f),g=this,this.require=["^googleMap","^?marker"],this.template='<span class="angular-google-maps-window" ng-transclude></span>',this.$log.info(g)}return c(d,b),d.include(directives.api.utils.GmapUtil),d.prototype.link=function(a,b,c,d){var e=this;return this.$timeout(function(){var f,g,h,i,j,k,l;return h=!0,angular.isDefined(c.isiconvisibleonclick)&&(h=a.isIconVisibleOnClick),i=d[0].getMap(),j=d.length>1&&null!=d[1]?d[1].getMarker():void 0,f=null!=a.options?a.options:{},g=null!=a&&null!=a.coords&&null!=a.coords.latitude&&null!=a.coords.longitude,k=null!=j&&g?e.createWindowOptions(j,a,b.html(),f):void 0,null!=i&&(l=new directives.api.models.child.WindowChildModel(a,k,h,i,j,e.$http,e.$templateCache,e.$compile,b)),a.$on("$destroy",function(){return l.destroy()})},directives.api.utils.GmapUtil.defaultDelay+25)},d}(directives.api.IWindow)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Windows=function(b){function d(b,c,e,f,g){this.link=a(this.link,this);var h;d.__super__.constructor.call(this,b,c,e,f),h=this,this.$interpolate=g,this.require=["^googleMap","^?markers"],this.template='<span class="angular-google-maps-windows" ng-transclude></span>',this.scope.models="=models",this.$log.info(h)}return c(d,b),d.prototype.link=function(a,b,c,d){return new directives.api.models.parent.WindowsParentModel(a,b,c,d,this.$timeout,this.$compile,this.$http,this.$templateCache,this.$interpolate)},d}(directives.api.IWindow)})}.call(this),angular.module("google-maps").controller("PolylineDisplayController",["$scope",function(a){a.toggleStrokeColor=function(){a.stroke.color="#6060FB"==a.stroke.color?"red":"#6060FB"}}]),ClusterIcon.prototype.onAdd=function(){var a,b,c=this;this.div_=document.createElement("div"),this.div_.className=this.className_,this.visible_&&this.show(),this.getPanes().overlayMouseTarget.appendChild(this.div_),this.boundsChangedListener_=google.maps.event.addListener(this.getMap(),"bounds_changed",function(){b=a}),google.maps.event.addDomListener(this.div_,"mousedown",function(){a=!0,b=!1}),google.maps.event.addDomListener(this.div_,"click",function(d){if(a=!1,!b){var e,f,g=c.cluster_.getMarkerClusterer();google.maps.event.trigger(g,"click",c.cluster_),google.maps.event.trigger(g,"clusterclick",c.cluster_),g.getZoomOnClick()&&(f=g.getMaxZoom(),e=c.cluster_.getBounds(),g.getMap().fitBounds(e),setTimeout(function(){g.getMap().fitBounds(e),null!==f&&g.getMap().getZoom()>f&&g.getMap().setZoom(f+1)},100)),d.cancelBubble=!0,d.stopPropagation&&d.stopPropagation()}}),google.maps.event.addDomListener(this.div_,"mouseover",function(){var a=c.cluster_.getMarkerClusterer();google.maps.event.trigger(a,"mouseover",c.cluster_)}),google.maps.event.addDomListener(this.div_,"mouseout",function(){var a=c.cluster_.getMarkerClusterer();google.maps.event.trigger(a,"mouseout",c.cluster_)})},ClusterIcon.prototype.onRemove=function(){this.div_&&this.div_.parentNode&&(this.hide(),google.maps.event.removeListener(this.boundsChangedListener_),google.maps.event.clearInstanceListeners(this.div_),this.div_.parentNode.removeChild(this.div_),this.div_=null)},ClusterIcon.prototype.draw=function(){if(this.visible_){var a=this.getPosFromLatLng_(this.center_);this.div_.style.top=a.y+"px",this.div_.style.left=a.x+"px"}},ClusterIcon.prototype.hide=function(){this.div_&&(this.div_.style.display="none"),this.visible_=!1},ClusterIcon.prototype.show=function(){if(this.div_){var a=this.getPosFromLatLng_(this.center_);this.div_.style.cssText=this.createCss(a),this.div_.innerHTML=this.cluster_.printable_?"<img src='"+this.url_+"'><div style='position: absolute; top: 0px; left: 0px; width: "+this.width_+"px;'>"+this.sums_.text+"</div>":this.sums_.text,this.div_.title="undefined"==typeof this.sums_.title||""===this.sums_.title?this.cluster_.getMarkerClusterer().getTitle():this.sums_.title,this.div_.style.display=""}this.visible_=!0},ClusterIcon.prototype.useStyle=function(a){this.sums_=a;var b=Math.max(0,a.index-1);b=Math.min(this.styles_.length-1,b);var c=this.styles_[b];this.url_=c.url,this.height_=c.height,this.width_=c.width,this.anchor_=c.anchor,this.anchorIcon_=c.anchorIcon||[parseInt(this.height_/2,10),parseInt(this.width_/2,10)],this.textColor_=c.textColor||"black",this.textSize_=c.textSize||11,this.textDecoration_=c.textDecoration||"none",this.fontWeight_=c.fontWeight||"bold",this.fontStyle_=c.fontStyle||"normal",this.fontFamily_=c.fontFamily||"Arial,sans-serif",this.backgroundPosition_=c.backgroundPosition||"0 0"},ClusterIcon.prototype.setCenter=function(a){this.center_=a},ClusterIcon.prototype.createCss=function(a){var b=[];return this.cluster_.printable_||(b.push("background-image:url("+this.url_+");"),b.push("background-position:"+this.backgroundPosition_+";")),"object"==typeof this.anchor_?("number"==typeof this.anchor_[0]&&this.anchor_[0]>0&&this.anchor_[0]<this.height_?b.push("height:"+(this.height_-this.anchor_[0])+"px; padding-top:"+this.anchor_[0]+"px;"):b.push("height:"+this.height_+"px; line-height:"+this.height_+"px;"),"number"==typeof this.anchor_[1]&&this.anchor_[1]>0&&this.anchor_[1]<this.width_?b.push("width:"+(this.width_-this.anchor_[1])+"px; padding-left:"+this.anchor_[1]+"px;"):b.push("width:"+this.width_+"px; text-align:center;")):b.push("height:"+this.height_+"px; line-height:"+this.height_+"px; width:"+this.width_+"px; text-align:center;"),b.push("cursor:pointer; top:"+a.y+"px; left:"+a.x+"px; color:"+this.textColor_+"; position:absolute; font-size:"+this.textSize_+"px; font-family:"+this.fontFamily_+"; font-weight:"+this.fontWeight_+"; font-style:"+this.fontStyle_+"; text-decoration:"+this.textDecoration_+";"),b.join("")},ClusterIcon.prototype.getPosFromLatLng_=function(a){var b=this.getProjection().fromLatLngToDivPixel(a);return b.x-=this.anchorIcon_[1],b.y-=this.anchorIcon_[0],b},Cluster.prototype.getSize=function(){return this.markers_.length},Cluster.prototype.getMarkers=function(){return this.markers_},Cluster.prototype.getCenter=function(){return this.center_},Cluster.prototype.getMap=function(){return this.map_},Cluster.prototype.getMarkerClusterer=function(){return this.markerClusterer_},Cluster.prototype.getBounds=function(){var a,b=new google.maps.LatLngBounds(this.center_,this.center_),c=this.getMarkers();for(a=0;a<c.length;a++)b.extend(c[a].getPosition());return b},Cluster.prototype.remove=function(){this.clusterIcon_.setMap(null),this.markers_=[],delete this.markers_},Cluster.prototype.addMarker=function(a){var b,c,d;if(this.isMarkerAlreadyAdded_(a))return!1;if(this.center_){if(this.averageCenter_){var e=this.markers_.length+1,f=(this.center_.lat()*(e-1)+a.getPosition().lat())/e,g=(this.center_.lng()*(e-1)+a.getPosition().lng())/e;this.center_=new google.maps.LatLng(f,g),this.calculateBounds_()}}else this.center_=a.getPosition(),this.calculateBounds_();if(a.isAdded=!0,this.markers_.push(a),c=this.markers_.length,d=this.markerClusterer_.getMaxZoom(),null!==d&&this.map_.getZoom()>d)a.getMap()!==this.map_&&a.setMap(this.map_);else if(c<this.minClusterSize_)a.getMap()!==this.map_&&a.setMap(this.map_);else if(c===this.minClusterSize_)for(b=0;c>b;b++)this.markers_[b].setMap(null);else a.setMap(null);return this.updateIcon_(),!0},Cluster.prototype.isMarkerInClusterBounds=function(a){return this.bounds_.contains(a.getPosition())},Cluster.prototype.calculateBounds_=function(){var a=new google.maps.LatLngBounds(this.center_,this.center_);this.bounds_=this.markerClusterer_.getExtendedBounds(a)},Cluster.prototype.updateIcon_=function(){var a=this.markers_.length,b=this.markerClusterer_.getMaxZoom();if(null!==b&&this.map_.getZoom()>b)return this.clusterIcon_.hide(),void 0;if(a<this.minClusterSize_)return this.clusterIcon_.hide(),void 0;var c=this.markerClusterer_.getStyles().length,d=this.markerClusterer_.getCalculator()(this.markers_,c);this.clusterIcon_.setCenter(this.center_),this.clusterIcon_.useStyle(d),this.clusterIcon_.show()},Cluster.prototype.isMarkerAlreadyAdded_=function(a){var b;if(this.markers_.indexOf)return-1!==this.markers_.indexOf(a);for(b=0;b<this.markers_.length;b++)if(a===this.markers_[b])return!0;return!1},MarkerClusterer.prototype.onAdd=function(){var a=this;this.activeMap_=this.getMap(),this.ready_=!0,this.repaint(),this.listeners_=[google.maps.event.addListener(this.getMap(),"zoom_changed",function(){a.resetViewport_(!1),(this.getZoom()===(this.get("minZoom")||0)||this.getZoom()===this.get("maxZoom"))&&google.maps.event.trigger(this,"idle")}),google.maps.event.addListener(this.getMap(),"idle",function(){a.redraw_()})]},MarkerClusterer.prototype.onRemove=function(){var a;for(a=0;a<this.markers_.length;a++)this.markers_[a].getMap()!==this.activeMap_&&this.markers_[a].setMap(this.activeMap_);for(a=0;a<this.clusters_.length;a++)this.clusters_[a].remove();for(this.clusters_=[],a=0;a<this.listeners_.length;a++)google.maps.event.removeListener(this.listeners_[a]);this.listeners_=[],this.activeMap_=null,this.ready_=!1},MarkerClusterer.prototype.draw=function(){},MarkerClusterer.prototype.setupStyles_=function(){var a,b;if(!(this.styles_.length>0))for(a=0;a<this.imageSizes_.length;a++)b=this.imageSizes_[a],this.styles_.push({url:this.imagePath_+(a+1)+"."+this.imageExtension_,height:b,width:b})},MarkerClusterer.prototype.fitMapToMarkers=function(){var a,b=this.getMarkers(),c=new google.maps.LatLngBounds;for(a=0;a<b.length;a++)c.extend(b[a].getPosition());this.getMap().fitBounds(c)},MarkerClusterer.prototype.getGridSize=function(){return this.gridSize_},MarkerClusterer.prototype.setGridSize=function(a){this.gridSize_=a},MarkerClusterer.prototype.getMinimumClusterSize=function(){return this.minClusterSize_},MarkerClusterer.prototype.setMinimumClusterSize=function(a){this.minClusterSize_=a},MarkerClusterer.prototype.getMaxZoom=function(){return this.maxZoom_},MarkerClusterer.prototype.setMaxZoom=function(a){this.maxZoom_=a},MarkerClusterer.prototype.getStyles=function(){return this.styles_},MarkerClusterer.prototype.setStyles=function(a){this.styles_=a},MarkerClusterer.prototype.getTitle=function(){return this.title_},MarkerClusterer.prototype.setTitle=function(a){this.title_=a},MarkerClusterer.prototype.getZoomOnClick=function(){return this.zoomOnClick_},MarkerClusterer.prototype.setZoomOnClick=function(a){this.zoomOnClick_=a},MarkerClusterer.prototype.getAverageCenter=function(){return this.averageCenter_},MarkerClusterer.prototype.setAverageCenter=function(a){this.averageCenter_=a},MarkerClusterer.prototype.getIgnoreHidden=function(){return this.ignoreHidden_},MarkerClusterer.prototype.setIgnoreHidden=function(a){this.ignoreHidden_=a},MarkerClusterer.prototype.getImageExtension=function(){return this.imageExtension_},MarkerClusterer.prototype.setImageExtension=function(a){this.imageExtension_=a},MarkerClusterer.prototype.getImagePath=function(){return this.imagePath_},MarkerClusterer.prototype.setImagePath=function(a){this.imagePath_=a},MarkerClusterer.prototype.getImageSizes=function(){return this.imageSizes_},MarkerClusterer.prototype.setImageSizes=function(a){this.imageSizes_=a},MarkerClusterer.prototype.getCalculator=function(){return this.calculator_},MarkerClusterer.prototype.setCalculator=function(a){this.calculator_=a},MarkerClusterer.prototype.getPrintable=function(){return this.printable_},MarkerClusterer.prototype.setPrintable=function(a){this.printable_=a},MarkerClusterer.prototype.getBatchSizeIE=function(){return this.batchSizeIE_},MarkerClusterer.prototype.setBatchSizeIE=function(a){this.batchSizeIE_=a},MarkerClusterer.prototype.getClusterClass=function(){return this.clusterClass_},MarkerClusterer.prototype.setClusterClass=function(a){this.clusterClass_=a},MarkerClusterer.prototype.getMarkers=function(){return this.markers_},MarkerClusterer.prototype.getTotalMarkers=function(){return this.markers_.length},MarkerClusterer.prototype.getClusters=function(){return this.clusters_},MarkerClusterer.prototype.getTotalClusters=function(){return this.clusters_.length},MarkerClusterer.prototype.addMarker=function(a,b){this.pushMarkerTo_(a),b||this.redraw_()},MarkerClusterer.prototype.addMarkers=function(a,b){var c;for(c=0;c<a.length;c++)this.pushMarkerTo_(a[c]);b||this.redraw_()},MarkerClusterer.prototype.pushMarkerTo_=function(a){if(a.getDraggable()){var b=this;google.maps.event.addListener(a,"dragend",function(){b.ready_&&(this.isAdded=!1,b.repaint())})}a.isAdded=!1,this.markers_.push(a)},MarkerClusterer.prototype.removeMarker=function(a,b){var c=this.removeMarker_(a);return!b&&c&&this.repaint(),c},MarkerClusterer.prototype.removeMarkers=function(a,b){var c,d,e=!1;for(c=0;c<a.length;c++)d=this.removeMarker_(a[c]),e=e||d;return!b&&e&&this.repaint(),e},MarkerClusterer.prototype.removeMarker_=function(a){var b,c=-1;if(this.markers_.indexOf)c=this.markers_.indexOf(a);else for(b=0;b<this.markers_.length;b++)if(a===this.markers_[b]){c=b;break}return-1===c?!1:(a.setMap(null),this.markers_.splice(c,1),!0)},MarkerClusterer.prototype.clearMarkers=function(){this.resetViewport_(!0),this.markers_=[]},MarkerClusterer.prototype.repaint=function(){var a=this.clusters_.slice();this.clusters_=[],this.resetViewport_(!1),this.redraw_(),setTimeout(function(){var b;for(b=0;b<a.length;b++)a[b].remove()},0)},MarkerClusterer.prototype.getExtendedBounds=function(a){var b=this.getProjection(),c=new google.maps.LatLng(a.getNorthEast().lat(),a.getNorthEast().lng()),d=new google.maps.LatLng(a.getSouthWest().lat(),a.getSouthWest().lng()),e=b.fromLatLngToDivPixel(c);e.x+=this.gridSize_,e.y-=this.gridSize_;var f=b.fromLatLngToDivPixel(d);f.x-=this.gridSize_,f.y+=this.gridSize_;var g=b.fromDivPixelToLatLng(e),h=b.fromDivPixelToLatLng(f);return a.extend(g),a.extend(h),a},MarkerClusterer.prototype.redraw_=function(){this.createClusters_(0)},MarkerClusterer.prototype.resetViewport_=function(a){var b,c;for(b=0;b<this.clusters_.length;b++)this.clusters_[b].remove();for(this.clusters_=[],b=0;b<this.markers_.length;b++)c=this.markers_[b],c.isAdded=!1,a&&c.setMap(null)},MarkerClusterer.prototype.distanceBetweenPoints_=function(a,b){var c=6371,d=(b.lat()-a.lat())*Math.PI/180,e=(b.lng()-a.lng())*Math.PI/180,f=Math.sin(d/2)*Math.sin(d/2)+Math.cos(a.lat()*Math.PI/180)*Math.cos(b.lat()*Math.PI/180)*Math.sin(e/2)*Math.sin(e/2),g=2*Math.atan2(Math.sqrt(f),Math.sqrt(1-f)),h=c*g;return h},MarkerClusterer.prototype.isMarkerInBounds_=function(a,b){return b.contains(a.getPosition())},MarkerClusterer.prototype.addToClosestCluster_=function(a){var b,c,d,e,f=4e4,g=null;for(b=0;b<this.clusters_.length;b++)d=this.clusters_[b],e=d.getCenter(),e&&(c=this.distanceBetweenPoints_(e,a.getPosition()),f>c&&(f=c,g=d));g&&g.isMarkerInClusterBounds(a)?g.addMarker(a):(d=new Cluster(this),d.addMarker(a),this.clusters_.push(d))},MarkerClusterer.prototype.createClusters_=function(a){var b,c,d,e=this;if(this.ready_){0===a&&(google.maps.event.trigger(this,"clusteringbegin",this),"undefined"!=typeof this.timerRefStatic&&(clearTimeout(this.timerRefStatic),delete this.timerRefStatic)),d=this.getMap().getZoom()>3?new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest(),this.getMap().getBounds().getNorthEast()):new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472,-178.48388434375),new google.maps.LatLng(-85.08136444384544,178.00048865625));var f=this.getExtendedBounds(d),g=Math.min(a+this.batchSize_,this.markers_.length);for(b=a;g>b;b++)c=this.markers_[b],!c.isAdded&&this.isMarkerInBounds_(c,f)&&(!this.ignoreHidden_||this.ignoreHidden_&&c.getVisible())&&this.addToClosestCluster_(c);g<this.markers_.length?this.timerRefStatic=setTimeout(function(){e.createClusters_(g)},0):(delete this.timerRefStatic,google.maps.event.trigger(this,"clusteringend",this))}},MarkerClusterer.prototype.extend=function(a,b){return function(a){var b;for(b in a.prototype)this.prototype[b]=a.prototype[b];return this}.apply(a,[b])},MarkerClusterer.CALCULATOR=function(a,b){for(var c=0,d="",e=a.length.toString(),f=e;0!==f;)f=parseInt(f/10,10),c++;return c=Math.min(c,b),{text:e,index:c,title:d}},MarkerClusterer.BATCH_SIZE=2e3,MarkerClusterer.BATCH_SIZE_IE=500,MarkerClusterer.IMAGE_PATH="http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclustererplus/images/m",MarkerClusterer.IMAGE_EXTENSION="png",MarkerClusterer.IMAGE_SIZES=[53,56,66,78,90],inherits(MarkerLabel_,google.maps.OverlayView),MarkerLabel_.getSharedCross=function(a){var b;return"undefined"==typeof MarkerLabel_.getSharedCross.crossDiv&&(b=document.createElement("img"),b.style.cssText="position: absolute; z-index: 1000002; display: none;",b.style.marginLeft="-8px",b.style.marginTop="-9px",b.src=a,MarkerLabel_.getSharedCross.crossDiv=b),MarkerLabel_.getSharedCross.crossDiv},MarkerLabel_.prototype.onAdd=function(){var a,b,c,d,e,f,g,h=this,i=!1,j=!1,k=20,l="url("+this.handCursorURL_+")",m=function(a){a.preventDefault&&a.preventDefault(),a.cancelBubble=!0,a.stopPropagation&&a.stopPropagation()},n=function(){h.marker_.setAnimation(null)};this.getPanes().overlayImage.appendChild(this.labelDiv_),this.getPanes().overlayMouseTarget.appendChild(this.eventDiv_),"undefined"==typeof MarkerLabel_.getSharedCross.processed&&(this.getPanes().overlayImage.appendChild(this.crossDiv_),MarkerLabel_.getSharedCross.processed=!0),this.listeners_=[google.maps.event.addDomListener(this.eventDiv_,"mouseover",function(a){(h.marker_.getDraggable()||h.marker_.getClickable())&&(this.style.cursor="pointer",google.maps.event.trigger(h.marker_,"mouseover",a))}),google.maps.event.addDomListener(this.eventDiv_,"mouseout",function(a){!h.marker_.getDraggable()&&!h.marker_.getClickable()||j||(this.style.cursor=h.marker_.getCursor(),google.maps.event.trigger(h.marker_,"mouseout",a))}),google.maps.event.addDomListener(this.eventDiv_,"mousedown",function(a){j=!1,h.marker_.getDraggable()&&(i=!0,this.style.cursor=l),(h.marker_.getDraggable()||h.marker_.getClickable())&&(google.maps.event.trigger(h.marker_,"mousedown",a),m(a))}),google.maps.event.addDomListener(document,"mouseup",function(b){var c;if(i&&(i=!1,h.eventDiv_.style.cursor="pointer",google.maps.event.trigger(h.marker_,"mouseup",b)),j){if(e){c=h.getProjection().fromLatLngToDivPixel(h.marker_.getPosition()),c.y+=k,h.marker_.setPosition(h.getProjection().fromDivPixelToLatLng(c));try{h.marker_.setAnimation(google.maps.Animation.BOUNCE),setTimeout(n,1406)}catch(f){}}h.crossDiv_.style.display="none",h.marker_.setZIndex(a),d=!0,j=!1,b.latLng=h.marker_.getPosition(),google.maps.event.trigger(h.marker_,"dragend",b)}}),google.maps.event.addListener(h.marker_.getMap(),"mousemove",function(d){var l;i&&(j?(d.latLng=new google.maps.LatLng(d.latLng.lat()-b,d.latLng.lng()-c),l=h.getProjection().fromLatLngToDivPixel(d.latLng),e&&(h.crossDiv_.style.left=l.x+"px",h.crossDiv_.style.top=l.y+"px",h.crossDiv_.style.display="",l.y-=k),h.marker_.setPosition(h.getProjection().fromDivPixelToLatLng(l)),e&&(h.eventDiv_.style.top=l.y+k+"px"),google.maps.event.trigger(h.marker_,"drag",d)):(b=d.latLng.lat()-h.marker_.getPosition().lat(),c=d.latLng.lng()-h.marker_.getPosition().lng(),a=h.marker_.getZIndex(),f=h.marker_.getPosition(),g=h.marker_.getMap().getCenter(),e=h.marker_.get("raiseOnDrag"),j=!0,h.marker_.setZIndex(1e6),d.latLng=h.marker_.getPosition(),google.maps.event.trigger(h.marker_,"dragstart",d)))}),google.maps.event.addDomListener(document,"keydown",function(a){j&&27===a.keyCode&&(e=!1,h.marker_.setPosition(f),h.marker_.getMap().setCenter(g),google.maps.event.trigger(document,"mouseup",a))}),google.maps.event.addDomListener(this.eventDiv_,"click",function(a){(h.marker_.getDraggable()||h.marker_.getClickable())&&(d?d=!1:(google.maps.event.trigger(h.marker_,"click",a),m(a)))}),google.maps.event.addDomListener(this.eventDiv_,"dblclick",function(a){(h.marker_.getDraggable()||h.marker_.getClickable())&&(google.maps.event.trigger(h.marker_,"dblclick",a),m(a))}),google.maps.event.addListener(this.marker_,"dragstart",function(){j||(e=this.get("raiseOnDrag"))}),google.maps.event.addListener(this.marker_,"drag",function(){j||e&&(h.setPosition(k),h.labelDiv_.style.zIndex=1e6+(this.get("labelInBackground")?-1:1))}),google.maps.event.addListener(this.marker_,"dragend",function(){j||e&&h.setPosition(0)}),google.maps.event.addListener(this.marker_,"position_changed",function(){h.setPosition()}),google.maps.event.addListener(this.marker_,"zindex_changed",function(){h.setZIndex()}),google.maps.event.addListener(this.marker_,"visible_changed",function(){h.setVisible()}),google.maps.event.addListener(this.marker_,"labelvisible_changed",function(){h.setVisible()}),google.maps.event.addListener(this.marker_,"title_changed",function(){h.setTitle()}),google.maps.event.addListener(this.marker_,"labelcontent_changed",function(){h.setContent()}),google.maps.event.addListener(this.marker_,"labelanchor_changed",function(){h.setAnchor()}),google.maps.event.addListener(this.marker_,"labelclass_changed",function(){h.setStyles()}),google.maps.event.addListener(this.marker_,"labelstyle_changed",function(){h.setStyles()})]},MarkerLabel_.prototype.onRemove=function(){var a;for(null!==this.labelDiv_.parentNode&&this.labelDiv_.parentNode.removeChild(this.labelDiv_),null!==this.eventDiv_.parentNode&&this.eventDiv_.parentNode.removeChild(this.eventDiv_),a=0;a<this.listeners_.length;a++)google.maps.event.removeListener(this.listeners_[a])},MarkerLabel_.prototype.draw=function(){this.setContent(),this.setTitle(),this.setStyles()},MarkerLabel_.prototype.setContent=function(){var a=this.marker_.get("labelContent");"undefined"==typeof a.nodeType?(this.labelDiv_.innerHTML=a,this.eventDiv_.innerHTML=this.labelDiv_.innerHTML):(this.labelDiv_.innerHTML="",this.labelDiv_.appendChild(a),a=a.cloneNode(!0),this.eventDiv_.appendChild(a))},MarkerLabel_.prototype.setTitle=function(){this.eventDiv_.title=this.marker_.getTitle()||""},MarkerLabel_.prototype.setStyles=function(){var a,b;this.labelDiv_.className=this.marker_.get("labelClass"),this.eventDiv_.className=this.labelDiv_.className,this.labelDiv_.style.cssText="",this.eventDiv_.style.cssText="",b=this.marker_.get("labelStyle");for(a in b)b.hasOwnProperty(a)&&(this.labelDiv_.style[a]=b[a],this.eventDiv_.style[a]=b[a]);this.setMandatoryStyles()},MarkerLabel_.prototype.setMandatoryStyles=function(){this.labelDiv_.style.position="absolute",this.labelDiv_.style.overflow="hidden","undefined"!=typeof this.labelDiv_.style.opacity&&""!==this.labelDiv_.style.opacity&&(this.labelDiv_.style.MsFilter='"progid:DXImageTransform.Microsoft.Alpha(opacity='+100*this.labelDiv_.style.opacity+')"',this.labelDiv_.style.filter="alpha(opacity="+100*this.labelDiv_.style.opacity+")"),this.eventDiv_.style.position=this.labelDiv_.style.position,this.eventDiv_.style.overflow=this.labelDiv_.style.overflow,this.eventDiv_.style.opacity=.01,this.eventDiv_.style.MsFilter='"progid:DXImageTransform.Microsoft.Alpha(opacity=1)"',this.eventDiv_.style.filter="alpha(opacity=1)",this.setAnchor(),this.setPosition(),this.setVisible()},MarkerLabel_.prototype.setAnchor=function(){var a=this.marker_.get("labelAnchor");
 this.labelDiv_.style.marginLeft=-a.x+"px",this.labelDiv_.style.marginTop=-a.y+"px",this.eventDiv_.style.marginLeft=-a.x+"px",this.eventDiv_.style.marginTop=-a.y+"px"},MarkerLabel_.prototype.setPosition=function(a){var b=this.getProjection().fromLatLngToDivPixel(this.marker_.getPosition());"undefined"==typeof a&&(a=0),this.labelDiv_.style.left=Math.round(b.x)+"px",this.labelDiv_.style.top=Math.round(b.y-a)+"px",this.eventDiv_.style.left=this.labelDiv_.style.left,this.eventDiv_.style.top=this.labelDiv_.style.top,this.setZIndex()},MarkerLabel_.prototype.setZIndex=function(){var a=this.marker_.get("labelInBackground")?-1:1;"undefined"==typeof this.marker_.getZIndex()?(this.labelDiv_.style.zIndex=parseInt(this.labelDiv_.style.top,10)+a,this.eventDiv_.style.zIndex=this.labelDiv_.style.zIndex):(this.labelDiv_.style.zIndex=this.marker_.getZIndex()+a,this.eventDiv_.style.zIndex=this.labelDiv_.style.zIndex)},MarkerLabel_.prototype.setVisible=function(){this.labelDiv_.style.display=this.marker_.get("labelVisible")?this.marker_.getVisible()?"block":"none":"none",this.eventDiv_.style.display=this.labelDiv_.style.display},inherits(MarkerWithLabel,google.maps.Marker),MarkerWithLabel.prototype.setMap=function(a){google.maps.Marker.prototype.setMap.apply(this,arguments),this.label.setMap(a)},angular.module("google-maps").factory("array-sync",["add-events",function(a){return function(b,c,d){var e=c.$eval(d),f=a(b,{set_at:function(a){var c=b.getAt(a);c&&c.lng&&c.lat&&(e[a].latitude=c.lat(),e[a].longitude=c.lng())},insert_at:function(a){var c=b.getAt(a);c&&c.lng&&c.lat&&e.splice(a,0,{latitude:c.lat(),longitude:c.lng()})},remove_at:function(a){e.splice(a,1)}}),g=c.$watch(d,function(a){var c=b;if(a){for(var d,e=0,f=c.getLength(),g=a.length,h=Math.min(f,g);h>e;e++){var i=c.getAt(e);d=a[e],(i.lat()!=d.latitude||i.lng()!=d.longitude)&&c.setAt(e,new google.maps.LatLng(d.latitude,d.longitude))}for(;g>e;e++)d=a[e],c.push(new google.maps.LatLng(d.latitude,d.longitude));for(;f>e;e++)c.pop()}},!0);return function(){f&&(f(),f=null),g&&(g(),g=null)}}}]),angular.module("google-maps").factory("add-events",["$timeout",function(a){function b(b,c,d){return google.maps.event.addListener(b,c,function(){d.apply(this,arguments),a(function(){},!0)})}function c(a,c,d){if(d)return b(a,c,d);var e=[];return angular.forEach(c,function(c,d){console.log("adding listener: "+d+": "+c.toString()+" to : "+a),e.push(b(a,d,c))}),function(){angular.forEach(e,function(a){_.isFunction(a)&&a(),null!==a.e&&_.isFunction(a.e)&&a.e()}),e=null}}return c}]),angular.module("google-maps").directive("googleMap",["$log","$timeout",function(a,b){"use strict";function c(a){return angular.isDefined(a)&&null!==a&&a===!0||"1"===a||"y"===a||"true"===a}directives.api.utils.Logger.logger=a;var d={mapTypeId:google.maps.MapTypeId.ROADMAP};return{restrict:"ECMA",transclude:!0,replace:!1,template:'<div class="angular-google-map"><div class="angular-google-map-container"></div><div ng-transclude style="display: none"></div></div>',scope:{center:"=center",zoom:"=zoom",dragging:"=dragging",markers:"=markers",refresh:"&refresh",windows:"=windows",options:"=options",events:"=events",bounds:"=bounds"},controller:["$scope",function(a){this.getMap=function(){return a.map}}],link:function(e,f,g){if(!angular.isDefined(e.center)||!angular.isDefined(e.center.latitude)||!angular.isDefined(e.center.longitude))return a.error("angular-google-maps: could not find a valid center property"),void 0;if(!angular.isDefined(e.zoom))return a.error("angular-google-maps: map zoom property not set"),void 0;var h=angular.element(f);h.addClass("angular-google-map");var i={options:{}};if(g.options&&(i.options=e.options),g.type){var j=g.type.toUpperCase();google.maps.MapTypeId.hasOwnProperty(j)?i.mapTypeId=google.maps.MapTypeId[g.type.toUpperCase()]:a.error('angular-google-maps: invalid map type "'+g.type+'"')}var k=new google.maps.Map(h.find("div")[1],angular.extend({},d,i,{center:new google.maps.LatLng(e.center.latitude,e.center.longitude),draggable:c(g.draggable),zoom:e.zoom,bounds:e.bounds})),l=!1;google.maps.event.addListener(k,"dragstart",function(){l=!0,b(function(){e.$apply(function(a){a.dragging=l})})}),google.maps.event.addListener(k,"dragend",function(){l=!1,b(function(){e.$apply(function(a){a.dragging=l})})}),google.maps.event.addListener(k,"drag",function(){var a=k.center;b(function(){e.$apply(function(b){b.center.latitude=a.lat(),b.center.longitude=a.lng()})})}),google.maps.event.addListener(k,"zoom_changed",function(){e.zoom!=k.zoom&&b(function(){e.$apply(function(a){a.zoom=k.zoom})})});var m=!1;if(google.maps.event.addListener(k,"center_changed",function(){var a=k.center;m||b(function(){e.$apply(function(b){k.dragging||(b.center.latitude!==a.lat()&&(b.center.latitude=a.lat()),b.center.longitude!==a.lng()&&(b.center.longitude=a.lng()))})})}),google.maps.event.addListener(k,"idle",function(){var a=k.getBounds(),c=a.getNorthEast(),d=a.getSouthWest();b(function(){e.$apply(function(a){null!==a.bounds&&void 0!==a.bounds&&void 0!==a.bounds&&(a.bounds.northeast={latitude:c.lat(),longitude:c.lng()},a.bounds.southwest={latitude:d.lat(),longitude:d.lng()})})})}),angular.isDefined(e.events)&&null!==e.events&&angular.isObject(e.events)){var n=function(a){return function(){e.events[a].apply(e,[k,a,arguments])}};for(var o in e.events)e.events.hasOwnProperty(o)&&angular.isFunction(e.events[o])&&google.maps.event.addListener(k,o,n(o))}e.map=k,google.maps.event.trigger(k,"resize"),angular.isUndefined(e.refresh())||e.$watch("refresh()",function(a,b){if(a&&!b){var d=new google.maps.LatLng(a.latitude,a.longitude);c(g.pan)?k.panTo(d):k.setCenter(d)}}),e.$watch("center",function(a,b){if(a!==b){if(m=!0,!l){var d=new google.maps.LatLng(a.latitude,a.longitude);c(g.pan)?k.panTo(d):k.setCenter(d)}m=!1}},!0),e.$watch("zoom",function(a,b){a!==b&&k.setZoom(a)}),e.$watch("bounds",function(a,b){if(a!==b){var c=new google.maps.LatLng(a.northeast.latitude,a.northeast.longitude),d=new google.maps.LatLng(a.southwest.latitude,a.southwest.longitude),e=new google.maps.LatLngBounds(d,c);k.fitBounds(e)}})}}}]),angular.module("google-maps").directive("marker",["$timeout",function(a){return new directives.api.Marker(a)}]),angular.module("google-maps").directive("markers",["$timeout",function(a){return new directives.api.Markers(a)}]),angular.module("google-maps").directive("markerLabel",["$log","$timeout",function(a,b){return new directives.api.Label(b)}]),angular.module("google-maps").directive("polygon",["$log","$timeout",function(a,b){"use strict";function c(a){for(var b=0;b<a.length;b++)if(angular.isUndefined(a[b].latitude)||angular.isUndefined(a[b].longitude))return!1;return!0}function d(a){for(var b=new google.maps.MVCArray,c=0;c<a.length;c++)b.push(new google.maps.LatLng(a[c].latitude,a[c].longitude));return b}function e(a,b){for(var c=new google.maps.LatLngBounds,d=0;d<b.length;d++)c.extend(b.getAt(d));a.fitBounds(c)}function f(a){return angular.isDefined(a)&&null!==a&&a===!0||"1"===a||"y"===a||"true"===a}var g={};return{restrict:"ECA",require:"^googleMap",replace:!0,scope:{path:"=path",stroke:"=stroke",clickable:"=",draggable:"=",editable:"=",geodesic:"=",icons:"=icons",visible:"="},link:function(h,i,j,k){return angular.isUndefined(h.path)||null===h.path||h.path.length<2||!c(h.path)?(a.error("polyline: no valid path attribute found"),void 0):(b(function(){var a=k.getMap(),b=d(h.path),c=angular.extend({},g,{map:a,path:b,strokeColor:h.stroke&&h.stroke.color,strokeOpacity:h.stroke&&h.stroke.opacity,strokeWeight:h.stroke&&h.stroke.weight});angular.forEach({clickable:!0,draggable:!1,editable:!1,geodesic:!1,visible:!0},function(a,b){c[b]=angular.isUndefined(h[b])||null===h[b]?a:h[b]});var i=new google.maps.Polyline(c);f(j.fit)&&e(a,b),angular.isDefined(h.editable)&&h.$watch("editable",function(a){i.setEditable(a)}),angular.isDefined(h.draggable)&&h.$watch("draggable",function(a){i.setDraggable(a)}),angular.isDefined(h.visible)&&h.$watch("visible",function(a){i.setVisible(a)});var l,m,n,o=i.getPath();l=google.maps.event.addListener(o,"set_at",function(a){var b=o.getAt(a);b&&b.lng&&b.lat&&(h.path[a].latitude=b.lat(),h.path[a].longitude=b.lng(),h.$apply())}),m=google.maps.event.addListener(o,"insert_at",function(a){var b=o.getAt(a);b&&b.lng&&b.lat&&(h.path.splice(a,0,{latitude:b.lat(),longitude:b.lng()}),h.$apply())}),n=google.maps.event.addListener(o,"remove_at",function(a){h.path.splice(a,1),h.$apply()}),h.$watch("path",function(b){var c=i.getPath();if(b!==c)if(b){i.setMap(a);for(var d=0,g=c.getLength(),h=b.length,k=Math.min(g,h);k>d;d++)oldValue=c.getAt(d),newValue=b[d],(oldValue.lat()!=newValue.latitude||oldValue.lng()!=newValue.longitude)&&c.setAt(d,new google.maps.LatLng(newValue.latitude,newValue.longitude));for(;h>d;d++)newValue=b[d],c.push(new google.maps.LatLng(newValue.latitude,newValue.longitude));for(;g>d;d++)c.pop();f(j.fit)&&e(a,c)}else i.setMap(null)},!0),h.$on("$destroy",function(){i.setMap(null),l(),l=null,m(),m=null,n(),n=null})}),void 0)}}}]),angular.module("google-maps").directive("polyline",["$log","$timeout","array-sync",function(a,b,c){"use strict";function d(a){for(var b=0;b<a.length;b++)if(angular.isUndefined(a[b].latitude)||angular.isUndefined(a[b].longitude))return!1;return!0}function e(a){for(var b=new google.maps.MVCArray,c=0;c<a.length;c++)b.push(new google.maps.LatLng(a[c].latitude,a[c].longitude));return b}function f(a,b){for(var c=new google.maps.LatLngBounds,d=0;d<b.length;d++)c.extend(b.getAt(d));a.fitBounds(c)}function g(a){return angular.isDefined(a)&&null!==a&&a===!0||"1"===a||"y"===a||"true"===a}var h={};return{restrict:"ECA",replace:!0,require:"^googleMap",scope:{path:"=path",stroke:"=stroke",clickable:"=",draggable:"=",editable:"=",geodesic:"=",icons:"=icons",visible:"="},link:function(i,j,k,l){return angular.isUndefined(i.path)||null===i.path||i.path.length<2||!d(i.path)?(a.error("polyline: no valid path attribute found"),void 0):(b(function(){function a(a){var c=angular.extend({},h,{map:b,path:a,strokeColor:i.stroke&&i.stroke.color,strokeOpacity:i.stroke&&i.stroke.opacity,strokeWeight:i.stroke&&i.stroke.weight});return angular.forEach({clickable:!0,draggable:!1,editable:!1,geodesic:!1,visible:!0},function(a,b){c[b]=angular.isUndefined(i[b])||null===i[b]?a:i[b]}),c}var b=l.getMap(),d=new google.maps.Polyline(a(e(i.path)));g(k.fit)&&f(b,pathPoints),angular.isDefined(i.editable)&&i.$watch("editable",function(a){d.setEditable(a)}),angular.isDefined(i.draggable)&&i.$watch("draggable",function(a){d.setDraggable(a)}),angular.isDefined(i.visible)&&i.$watch("visible",function(a){d.setVisible(a)}),angular.isDefined(i.geodesic)&&i.$watch("geodesic",function(){d.setOptions(a(d.getPath()))}),angular.isDefined(i.stroke)&&angular.isDefined(i.stroke.weight)&&i.$watch("stroke.weight",function(){d.setOptions(a(d.getPath()))}),angular.isDefined(i.stroke)&&angular.isDefined(i.stroke.color)&&i.$watch("stroke.color",function(){d.setOptions(a(d.getPath()))});var j=c(d.getPath(),i,"path");i.$on("$destroy",function(){d.setMap(null),j&&(j(),j=null)})}),void 0)}}}]),angular.module("google-maps").directive("window",["$timeout","$compile","$http","$templateCache",function(a,b,c,d){return new directives.api.Window(a,b,c,d)}]),angular.module("google-maps").directive("windows",["$timeout","$compile","$http","$templateCache","$interpolate",function(a,b,c,d,e){return new directives.api.Windows(a,b,c,d,e)}]),angular.module("google-maps").directive("layer",["$timeout",function(a){return new directives.api.Layer(a)}]);
\ No newline at end of file
diff --git a/example/example.html b/example/example.html
index 65247475b..fe15196f9 100644
--- a/example/example.html
+++ b/example/example.html
@@ -285,9 +285,9 @@ <h1>angular-google-maps example</h1>
 </div>
 <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
 <script src="http://maps.googleapis.com/maps/api/js?libraries=weather&sensor=false&language=en&v=3.13"></script>
-<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js"></script>
+<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular.js"></script>
 <script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>
-<script src="http://underscorejs.org/underscore-min.js"></script>
+<script src="//cdn.jsdelivr.net/lodash/2.4.0/lodash.backbone.js" type="text/javascript"></script>
 <script src="angular-google-maps.js"></script>
 <script src="example-controller.js"></script>
 <script src="templates/infoController.js"></script>
diff --git a/lib/angular-1.0.7.js b/lib/angular-1.0.7.js
deleted file mode 100755
index a860c8594..000000000
--- a/lib/angular-1.0.7.js
+++ /dev/null
@@ -1,14847 +0,0 @@
-/**
- * @license AngularJS v1.0.7
- * (c) 2010-2012 Google, Inc. http://angularjs.org
- * License: MIT
- */
-(function(window, document, undefined) {
-'use strict';
-
-////////////////////////////////////
-
-/**
- * @ngdoc function
- * @name angular.lowercase
- * @function
- *
- * @description Converts the specified string to lowercase.
- * @param {string} string String to be converted to lowercase.
- * @returns {string} Lowercased string.
- */
-var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;};
-
-
-/**
- * @ngdoc function
- * @name angular.uppercase
- * @function
- *
- * @description Converts the specified string to uppercase.
- * @param {string} string String to be converted to uppercase.
- * @returns {string} Uppercased string.
- */
-var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;};
-
-
-var manualLowercase = function(s) {
-  return isString(s)
-      ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
-      : s;
-};
-var manualUppercase = function(s) {
-  return isString(s)
-      ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
-      : s;
-};
-
-
-// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
-// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
-// with correct but slower alternatives.
-if ('i' !== 'I'.toLowerCase()) {
-  lowercase = manualLowercase;
-  uppercase = manualUppercase;
-}
-
-
-var /** holds major version number for IE or NaN for real browsers */
-    msie              = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
-    jqLite,           // delay binding since jQuery could be loaded after us.
-    jQuery,           // delay binding
-    slice             = [].slice,
-    push              = [].push,
-    toString          = Object.prototype.toString,
-
-    /** @name angular */
-    angular           = window.angular || (window.angular = {}),
-    angularModule,
-    nodeName_,
-    uid               = ['0', '0', '0'];
-
-
-/**
- * @private
- * @param {*} obj
- * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
- */
-function isArrayLike(obj) {
-  if (!obj || (typeof obj.length !== 'number')) return false;
-
-  // We have on object which has length property. Should we treat it as array?
-  if (typeof obj.hasOwnProperty != 'function' &&
-      typeof obj.constructor != 'function') {
-    // This is here for IE8: it is a bogus object treat it as array;
-    return true;
-  } else  {
-    return obj instanceof JQLite ||                      // JQLite
-           (jQuery && obj instanceof jQuery) ||          // jQuery
-           toString.call(obj) !== '[object Object]' ||   // some browser native object
-           typeof obj.callee === 'function';              // arguments (on IE8 looks like regular obj)
-  }
-}
-
-
-/**
- * @ngdoc function
- * @name angular.forEach
- * @function
- *
- * @description
- * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
- * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value`
- * is the value of an object property or an array element and `key` is the object property key or
- * array element index. Specifying a `context` for the function is optional.
- *
- * Note: this function was previously known as `angular.foreach`.
- *
-   <pre>
-     var values = {name: 'misko', gender: 'male'};
-     var log = [];
-     angular.forEach(values, function(value, key){
-       this.push(key + ': ' + value);
-     }, log);
-     expect(log).toEqual(['name: misko', 'gender:male']);
-   </pre>
- *
- * @param {Object|Array} obj Object to iterate over.
- * @param {Function} iterator Iterator function.
- * @param {Object=} context Object to become context (`this`) for the iterator function.
- * @returns {Object|Array} Reference to `obj`.
- */
-function forEach(obj, iterator, context) {
-  var key;
-  if (obj) {
-    if (isFunction(obj)){
-      for (key in obj) {
-        if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) {
-          iterator.call(context, obj[key], key);
-        }
-      }
-    } else if (obj.forEach && obj.forEach !== forEach) {
-      obj.forEach(iterator, context);
-    } else if (isArrayLike(obj)) {
-      for (key = 0; key < obj.length; key++)
-        iterator.call(context, obj[key], key);
-    } else {
-      for (key in obj) {
-        if (obj.hasOwnProperty(key)) {
-          iterator.call(context, obj[key], key);
-        }
-      }
-    }
-  }
-  return obj;
-}
-
-function sortedKeys(obj) {
-  var keys = [];
-  for (var key in obj) {
-    if (obj.hasOwnProperty(key)) {
-      keys.push(key);
-    }
-  }
-  return keys.sort();
-}
-
-function forEachSorted(obj, iterator, context) {
-  var keys = sortedKeys(obj);
-  for ( var i = 0; i < keys.length; i++) {
-    iterator.call(context, obj[keys[i]], keys[i]);
-  }
-  return keys;
-}
-
-
-/**
- * when using forEach the params are value, key, but it is often useful to have key, value.
- * @param {function(string, *)} iteratorFn
- * @returns {function(*, string)}
- */
-function reverseParams(iteratorFn) {
-  return function(value, key) { iteratorFn(key, value) };
-}
-
-/**
- * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
- * characters such as '012ABC'. The reason why we are not using simply a number counter is that
- * the number string gets longer over time, and it can also overflow, where as the nextId
- * will grow much slower, it is a string, and it will never overflow.
- *
- * @returns an unique alpha-numeric string
- */
-function nextUid() {
-  var index = uid.length;
-  var digit;
-
-  while(index) {
-    index--;
-    digit = uid[index].charCodeAt(0);
-    if (digit == 57 /*'9'*/) {
-      uid[index] = 'A';
-      return uid.join('');
-    }
-    if (digit == 90  /*'Z'*/) {
-      uid[index] = '0';
-    } else {
-      uid[index] = String.fromCharCode(digit + 1);
-      return uid.join('');
-    }
-  }
-  uid.unshift('0');
-  return uid.join('');
-}
-
-
-/**
- * Set or clear the hashkey for an object.
- * @param obj object 
- * @param h the hashkey (!truthy to delete the hashkey)
- */
-function setHashKey(obj, h) {
-  if (h) {
-    obj.$$hashKey = h;
-  }
-  else {
-    delete obj.$$hashKey;
-  }
-}
-
-/**
- * @ngdoc function
- * @name angular.extend
- * @function
- *
- * @description
- * Extends the destination object `dst` by copying all of the properties from the `src` object(s)
- * to `dst`. You can specify multiple `src` objects.
- *
- * @param {Object} dst Destination object.
- * @param {...Object} src Source object(s).
- * @returns {Object} Reference to `dst`.
- */
-function extend(dst) {
-  var h = dst.$$hashKey;
-  forEach(arguments, function(obj){
-    if (obj !== dst) {
-      forEach(obj, function(value, key){
-        dst[key] = value;
-      });
-    }
-  });
-
-  setHashKey(dst,h);
-  return dst;
-}
-
-function int(str) {
-  return parseInt(str, 10);
-}
-
-
-function inherit(parent, extra) {
-  return extend(new (extend(function() {}, {prototype:parent}))(), extra);
-}
-
-
-/**
- * @ngdoc function
- * @name angular.noop
- * @function
- *
- * @description
- * A function that performs no operations. This function can be useful when writing code in the
- * functional style.
-   <pre>
-     function foo(callback) {
-       var result = calculateResult();
-       (callback || angular.noop)(result);
-     }
-   </pre>
- */
-function noop() {}
-noop.$inject = [];
-
-
-/**
- * @ngdoc function
- * @name angular.identity
- * @function
- *
- * @description
- * A function that returns its first argument. This function is useful when writing code in the
- * functional style.
- *
-   <pre>
-     function transformer(transformationFn, value) {
-       return (transformationFn || identity)(value);
-     };
-   </pre>
- */
-function identity($) {return $;}
-identity.$inject = [];
-
-
-function valueFn(value) {return function() {return value;};}
-
-/**
- * @ngdoc function
- * @name angular.isUndefined
- * @function
- *
- * @description
- * Determines if a reference is undefined.
- *
- * @param {*} value Reference to check.
- * @returns {boolean} True if `value` is undefined.
- */
-function isUndefined(value){return typeof value == 'undefined';}
-
-
-/**
- * @ngdoc function
- * @name angular.isDefined
- * @function
- *
- * @description
- * Determines if a reference is defined.
- *
- * @param {*} value Reference to check.
- * @returns {boolean} True if `value` is defined.
- */
-function isDefined(value){return typeof value != 'undefined';}
-
-
-/**
- * @ngdoc function
- * @name angular.isObject
- * @function
- *
- * @description
- * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
- * considered to be objects.
- *
- * @param {*} value Reference to check.
- * @returns {boolean} True if `value` is an `Object` but not `null`.
- */
-function isObject(value){return value != null && typeof value == 'object';}
-
-
-/**
- * @ngdoc function
- * @name angular.isString
- * @function
- *
- * @description
- * Determines if a reference is a `String`.
- *
- * @param {*} value Reference to check.
- * @returns {boolean} True if `value` is a `String`.
- */
-function isString(value){return typeof value == 'string';}
-
-
-/**
- * @ngdoc function
- * @name angular.isNumber
- * @function
- *
- * @description
- * Determines if a reference is a `Number`.
- *
- * @param {*} value Reference to check.
- * @returns {boolean} True if `value` is a `Number`.
- */
-function isNumber(value){return typeof value == 'number';}
-
-
-/**
- * @ngdoc function
- * @name angular.isDate
- * @function
- *
- * @description
- * Determines if a value is a date.
- *
- * @param {*} value Reference to check.
- * @returns {boolean} True if `value` is a `Date`.
- */
-function isDate(value){
-  return toString.apply(value) == '[object Date]';
-}
-
-
-/**
- * @ngdoc function
- * @name angular.isArray
- * @function
- *
- * @description
- * Determines if a reference is an `Array`.
- *
- * @param {*} value Reference to check.
- * @returns {boolean} True if `value` is an `Array`.
- */
-function isArray(value) {
-  return toString.apply(value) == '[object Array]';
-}
-
-
-/**
- * @ngdoc function
- * @name angular.isFunction
- * @function
- *
- * @description
- * Determines if a reference is a `Function`.
- *
- * @param {*} value Reference to check.
- * @returns {boolean} True if `value` is a `Function`.
- */
-function isFunction(value){return typeof value == 'function';}
-
-
-/**
- * Checks if `obj` is a window object.
- *
- * @private
- * @param {*} obj Object to check
- * @returns {boolean} True if `obj` is a window obj.
- */
-function isWindow(obj) {
-  return obj && obj.document && obj.location && obj.alert && obj.setInterval;
-}
-
-
-function isScope(obj) {
-  return obj && obj.$evalAsync && obj.$watch;
-}
-
-
-function isFile(obj) {
-  return toString.apply(obj) === '[object File]';
-}
-
-
-function isBoolean(value) {
-  return typeof value == 'boolean';
-}
-
-
-function trim(value) {
-  return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
-}
-
-/**
- * @ngdoc function
- * @name angular.isElement
- * @function
- *
- * @description
- * Determines if a reference is a DOM element (or wrapped jQuery element).
- *
- * @param {*} value Reference to check.
- * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
- */
-function isElement(node) {
-  return node &&
-    (node.nodeName  // we are a direct element
-    || (node.bind && node.find));  // we have a bind and find method part of jQuery API
-}
-
-/**
- * @param str 'key1,key2,...'
- * @returns {object} in the form of {key1:true, key2:true, ...}
- */
-function makeMap(str){
-  var obj = {}, items = str.split(","), i;
-  for ( i = 0; i < items.length; i++ )
-    obj[ items[i] ] = true;
-  return obj;
-}
-
-
-if (msie < 9) {
-  nodeName_ = function(element) {
-    element = element.nodeName ? element : element[0];
-    return (element.scopeName && element.scopeName != 'HTML')
-      ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName;
-  };
-} else {
-  nodeName_ = function(element) {
-    return element.nodeName ? element.nodeName : element[0].nodeName;
-  };
-}
-
-
-function map(obj, iterator, context) {
-  var results = [];
-  forEach(obj, function(value, index, list) {
-    results.push(iterator.call(context, value, index, list));
-  });
-  return results;
-}
-
-
-/**
- * @description
- * Determines the number of elements in an array, the number of properties an object has, or
- * the length of a string.
- *
- * Note: This function is used to augment the Object type in Angular expressions. See
- * {@link angular.Object} for more information about Angular arrays.
- *
- * @param {Object|Array|string} obj Object, array, or string to inspect.
- * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object
- * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array.
- */
-function size(obj, ownPropsOnly) {
-  var size = 0, key;
-
-  if (isArray(obj) || isString(obj)) {
-    return obj.length;
-  } else if (isObject(obj)){
-    for (key in obj)
-      if (!ownPropsOnly || obj.hasOwnProperty(key))
-        size++;
-  }
-
-  return size;
-}
-
-
-function includes(array, obj) {
-  return indexOf(array, obj) != -1;
-}
-
-function indexOf(array, obj) {
-  if (array.indexOf) return array.indexOf(obj);
-
-  for ( var i = 0; i < array.length; i++) {
-    if (obj === array[i]) return i;
-  }
-  return -1;
-}
-
-function arrayRemove(array, value) {
-  var index = indexOf(array, value);
-  if (index >=0)
-    array.splice(index, 1);
-  return value;
-}
-
-function isLeafNode (node) {
-  if (node) {
-    switch (node.nodeName) {
-    case "OPTION":
-    case "PRE":
-    case "TITLE":
-      return true;
-    }
-  }
-  return false;
-}
-
-/**
- * @ngdoc function
- * @name angular.copy
- * @function
- *
- * @description
- * Creates a deep copy of `source`, which should be an object or an array.
- *
- * * If no destination is supplied, a copy of the object or array is created.
- * * If a destination is provided, all of its elements (for array) or properties (for objects)
- *   are deleted and then all elements/properties from the source are copied to it.
- * * If  `source` is not an object or array, `source` is returned.
- *
- * Note: this function is used to augment the Object type in Angular expressions. See
- * {@link ng.$filter} for more information about Angular arrays.
- *
- * @param {*} source The source that will be used to make a copy.
- *                   Can be any type, including primitives, `null`, and `undefined`.
- * @param {(Object|Array)=} destination Destination into which the source is copied. If
- *     provided, must be of the same type as `source`.
- * @returns {*} The copy or updated `destination`, if `destination` was specified.
- */
-function copy(source, destination){
-  if (isWindow(source) || isScope(source)) throw Error("Can't copy Window or Scope");
-  if (!destination) {
-    destination = source;
-    if (source) {
-      if (isArray(source)) {
-        destination = copy(source, []);
-      } else if (isDate(source)) {
-        destination = new Date(source.getTime());
-      } else if (isObject(source)) {
-        destination = copy(source, {});
-      }
-    }
-  } else {
-    if (source === destination) throw Error("Can't copy equivalent objects or arrays");
-    if (isArray(source)) {
-      destination.length = 0;
-      for ( var i = 0; i < source.length; i++) {
-        destination.push(copy(source[i]));
-      }
-    } else {
-      var h = destination.$$hashKey;
-      forEach(destination, function(value, key){
-        delete destination[key];
-      });
-      for ( var key in source) {
-        destination[key] = copy(source[key]);
-      }
-      setHashKey(destination,h);
-    }
-  }
-  return destination;
-}
-
-/**
- * Create a shallow copy of an object
- */
-function shallowCopy(src, dst) {
-  dst = dst || {};
-
-  for(var key in src) {
-    if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') {
-      dst[key] = src[key];
-    }
-  }
-
-  return dst;
-}
-
-
-/**
- * @ngdoc function
- * @name angular.equals
- * @function
- *
- * @description
- * Determines if two objects or two values are equivalent. Supports value types, arrays and
- * objects.
- *
- * Two objects or values are considered equivalent if at least one of the following is true:
- *
- * * Both objects or values pass `===` comparison.
- * * Both objects or values are of the same type and all of their properties pass `===` comparison.
- * * Both values are NaN. (In JavasScript, NaN == NaN => false. But we consider two NaN as equal)
- *
- * During a property comparision, properties of `function` type and properties with names
- * that begin with `$` are ignored.
- *
- * Scope and DOMWindow objects are being compared only by identify (`===`).
- *
- * @param {*} o1 Object or value to compare.
- * @param {*} o2 Object or value to compare.
- * @returns {boolean} True if arguments are equal.
- */
-function equals(o1, o2) {
-  if (o1 === o2) return true;
-  if (o1 === null || o2 === null) return false;
-  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
-  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
-  if (t1 == t2) {
-    if (t1 == 'object') {
-      if (isArray(o1)) {
-        if ((length = o1.length) == o2.length) {
-          for(key=0; key<length; key++) {
-            if (!equals(o1[key], o2[key])) return false;
-          }
-          return true;
-        }
-      } else if (isDate(o1)) {
-        return isDate(o2) && o1.getTime() == o2.getTime();
-      } else {
-        if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2)) return false;
-        keySet = {};
-        for(key in o1) {
-          if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
-          if (!equals(o1[key], o2[key])) return false;
-          keySet[key] = true;
-        }
-        for(key in o2) {
-          if (!keySet[key] &&
-              key.charAt(0) !== '$' &&
-              o2[key] !== undefined &&
-              !isFunction(o2[key])) return false;
-        }
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-
-function concat(array1, array2, index) {
-  return array1.concat(slice.call(array2, index));
-}
-
-function sliceArgs(args, startIndex) {
-  return slice.call(args, startIndex || 0);
-}
-
-
-/**
- * @ngdoc function
- * @name angular.bind
- * @function
- *
- * @description
- * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
- * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
- * known as [function currying](http://en.wikipedia.org/wiki/Currying).
- *
- * @param {Object} self Context which `fn` should be evaluated in.
- * @param {function()} fn Function to be bound.
- * @param {...*} args Optional arguments to be prebound to the `fn` function call.
- * @returns {function()} Function that wraps the `fn` with all the specified bindings.
- */
-function bind(self, fn) {
-  var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
-  if (isFunction(fn) && !(fn instanceof RegExp)) {
-    return curryArgs.length
-      ? function() {
-          return arguments.length
-            ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0)))
-            : fn.apply(self, curryArgs);
-        }
-      : function() {
-          return arguments.length
-            ? fn.apply(self, arguments)
-            : fn.call(self);
-        };
-  } else {
-    // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
-    return fn;
-  }
-}
-
-
-function toJsonReplacer(key, value) {
-  var val = value;
-
-  if (/^\$+/.test(key)) {
-    val = undefined;
-  } else if (isWindow(value)) {
-    val = '$WINDOW';
-  } else if (value &&  document === value) {
-    val = '$DOCUMENT';
-  } else if (isScope(value)) {
-    val = '$SCOPE';
-  }
-
-  return val;
-}
-
-
-/**
- * @ngdoc function
- * @name angular.toJson
- * @function
- *
- * @description
- * Serializes input into a JSON-formatted string.
- *
- * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
- * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
- * @returns {string} Jsonified string representing `obj`.
- */
-function toJson(obj, pretty) {
-  return JSON.stringify(obj, toJsonReplacer, pretty ? '  ' : null);
-}
-
-
-/**
- * @ngdoc function
- * @name angular.fromJson
- * @function
- *
- * @description
- * Deserializes a JSON string.
- *
- * @param {string} json JSON string to deserialize.
- * @returns {Object|Array|Date|string|number} Deserialized thingy.
- */
-function fromJson(json) {
-  return isString(json)
-      ? JSON.parse(json)
-      : json;
-}
-
-
-function toBoolean(value) {
-  if (value && value.length !== 0) {
-    var v = lowercase("" + value);
-    value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
-  } else {
-    value = false;
-  }
-  return value;
-}
-
-/**
- * @returns {string} Returns the string representation of the element.
- */
-function startingTag(element) {
-  element = jqLite(element).clone();
-  try {
-    // turns out IE does not let you set .html() on elements which
-    // are not allowed to have children. So we just ignore it.
-    element.html('');
-  } catch(e) {}
-  // As Per DOM Standards
-  var TEXT_NODE = 3;
-  var elemHtml = jqLite('<div>').append(element).html();
-  try {
-    return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
-        elemHtml.
-          match(/^(<[^>]+>)/)[1].
-          replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
-  } catch(e) {
-    return lowercase(elemHtml);
-  }
-
-}
-
-
-/////////////////////////////////////////////////
-
-/**
- * Parses an escaped url query string into key-value pairs.
- * @returns Object.<(string|boolean)>
- */
-function parseKeyValue(/**string*/keyValue) {
-  var obj = {}, key_value, key;
-  forEach((keyValue || "").split('&'), function(keyValue){
-    if (keyValue) {
-      key_value = keyValue.split('=');
-      key = decodeURIComponent(key_value[0]);
-      obj[key] = isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true;
-    }
-  });
-  return obj;
-}
-
-function toKeyValue(obj) {
-  var parts = [];
-  forEach(obj, function(value, key) {
-    parts.push(encodeUriQuery(key, true) + (value === true ? '' : '=' + encodeUriQuery(value, true)));
-  });
-  return parts.length ? parts.join('&') : '';
-}
-
-
-/**
- * We need our custom method because encodeURIComponent is too agressive and doesn't follow
- * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
- * segments:
- *    segment       = *pchar
- *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
- *    pct-encoded   = "%" HEXDIG HEXDIG
- *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
- *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
- *                     / "*" / "+" / "," / ";" / "="
- */
-function encodeUriSegment(val) {
-  return encodeUriQuery(val, true).
-             replace(/%26/gi, '&').
-             replace(/%3D/gi, '=').
-             replace(/%2B/gi, '+');
-}
-
-
-/**
- * This method is intended for encoding *key* or *value* parts of query component. We need a custom
- * method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be
- * encoded per http://tools.ietf.org/html/rfc3986:
- *    query       = *( pchar / "/" / "?" )
- *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
- *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
- *    pct-encoded   = "%" HEXDIG HEXDIG
- *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
- *                     / "*" / "+" / "," / ";" / "="
- */
-function encodeUriQuery(val, pctEncodeSpaces) {
-  return encodeURIComponent(val).
-             replace(/%40/gi, '@').
-             replace(/%3A/gi, ':').
-             replace(/%24/g, '$').
-             replace(/%2C/gi, ',').
-             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
-}
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngApp
- *
- * @element ANY
- * @param {angular.Module} ngApp an optional application
- *   {@link angular.module module} name to load.
- *
- * @description
- *
- * Use this directive to auto-bootstrap an application. Only
- * one directive can be used per HTML document. The directive
- * designates the root of the application and is typically placed
- * at the root of the page.
- *
- * In the example below if the `ngApp` directive would not be placed
- * on the `html` element then the document would not be compiled
- * and the `{{ 1+2 }}` would not be resolved to `3`.
- *
- * `ngApp` is the easiest way to bootstrap an application.
- *
- <doc:example>
-   <doc:source>
-    I can add: 1 + 2 =  {{ 1+2 }}
-   </doc:source>
- </doc:example>
- *
- */
-function angularInit(element, bootstrap) {
-  var elements = [element],
-      appElement,
-      module,
-      names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'],
-      NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;
-
-  function append(element) {
-    element && elements.push(element);
-  }
-
-  forEach(names, function(name) {
-    names[name] = true;
-    append(document.getElementById(name));
-    name = name.replace(':', '\\:');
-    if (element.querySelectorAll) {
-      forEach(element.querySelectorAll('.' + name), append);
-      forEach(element.querySelectorAll('.' + name + '\\:'), append);
-      forEach(element.querySelectorAll('[' + name + ']'), append);
-    }
-  });
-
-  forEach(elements, function(element) {
-    if (!appElement) {
-      var className = ' ' + element.className + ' ';
-      var match = NG_APP_CLASS_REGEXP.exec(className);
-      if (match) {
-        appElement = element;
-        module = (match[2] || '').replace(/\s+/g, ',');
-      } else {
-        forEach(element.attributes, function(attr) {
-          if (!appElement && names[attr.name]) {
-            appElement = element;
-            module = attr.value;
-          }
-        });
-      }
-    }
-  });
-  if (appElement) {
-    bootstrap(appElement, module ? [module] : []);
-  }
-}
-
-/**
- * @ngdoc function
- * @name angular.bootstrap
- * @description
- * Use this function to manually start up angular application.
- *
- * See: {@link guide/bootstrap Bootstrap}
- *
- * @param {Element} element DOM element which is the root of angular application.
- * @param {Array<String|Function>=} modules an array of module declarations. See: {@link angular.module modules}
- * @returns {AUTO.$injector} Returns the newly created injector for this app.
- */
-function bootstrap(element, modules) {
-  var resumeBootstrapInternal = function() {
-    element = jqLite(element);
-    modules = modules || [];
-    modules.unshift(['$provide', function($provide) {
-      $provide.value('$rootElement', element);
-    }]);
-    modules.unshift('ng');
-    var injector = createInjector(modules);
-    injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
-       function(scope, element, compile, injector) {
-        scope.$apply(function() {
-          element.data('$injector', injector);
-          compile(element)(scope);
-        });
-      }]
-    );
-    return injector;
-  };
-
-  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
-
-  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
-    return resumeBootstrapInternal();
-  }
-
-  window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
-  angular.resumeBootstrap = function(extraModules) {
-    forEach(extraModules, function(module) {
-      modules.push(module);
-    });
-    resumeBootstrapInternal();
-  };
-}
-
-var SNAKE_CASE_REGEXP = /[A-Z]/g;
-function snake_case(name, separator){
-  separator = separator || '_';
-  return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
-    return (pos ? separator : '') + letter.toLowerCase();
-  });
-}
-
-function bindJQuery() {
-  // bind to jQuery if present;
-  jQuery = window.jQuery;
-  // reset to jQuery or default to us.
-  if (jQuery) {
-    jqLite = jQuery;
-    extend(jQuery.fn, {
-      scope: JQLitePrototype.scope,
-      controller: JQLitePrototype.controller,
-      injector: JQLitePrototype.injector,
-      inheritedData: JQLitePrototype.inheritedData
-    });
-    JQLitePatchJQueryRemove('remove', true);
-    JQLitePatchJQueryRemove('empty');
-    JQLitePatchJQueryRemove('html');
-  } else {
-    jqLite = JQLite;
-  }
-  angular.element = jqLite;
-}
-
-/**
- * throw error if the argument is falsy.
- */
-function assertArg(arg, name, reason) {
-  if (!arg) {
-    throw new Error("Argument '" + (name || '?') + "' is " + (reason || "required"));
-  }
-  return arg;
-}
-
-function assertArgFn(arg, name, acceptArrayAnnotation) {
-  if (acceptArrayAnnotation && isArray(arg)) {
-      arg = arg[arg.length - 1];
-  }
-
-  assertArg(isFunction(arg), name, 'not a function, got ' +
-      (arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
-  return arg;
-}
-
-/**
- * @ngdoc interface
- * @name angular.Module
- * @description
- *
- * Interface for configuring angular {@link angular.module modules}.
- */
-
-function setupModuleLoader(window) {
-
-  function ensure(obj, name, factory) {
-    return obj[name] || (obj[name] = factory());
-  }
-
-  return ensure(ensure(window, 'angular', Object), 'module', function() {
-    /** @type {Object.<string, angular.Module>} */
-    var modules = {};
-
-    /**
-     * @ngdoc function
-     * @name angular.module
-     * @description
-     *
-     * The `angular.module` is a global place for creating and registering Angular modules. All
-     * modules (angular core or 3rd party) that should be available to an application must be
-     * registered using this mechanism.
-     *
-     *
-     * # Module
-     *
-     * A module is a collocation of services, directives, filters, and configuration information. Module
-     * is used to configure the {@link AUTO.$injector $injector}.
-     *
-     * <pre>
-     * // Create a new module
-     * var myModule = angular.module('myModule', []);
-     *
-     * // register a new service
-     * myModule.value('appName', 'MyCoolApp');
-     *
-     * // configure existing services inside initialization blocks.
-     * myModule.config(function($locationProvider) {
-     *   // Configure existing providers
-     *   $locationProvider.hashPrefix('!');
-     * });
-     * </pre>
-     *
-     * Then you can create an injector and load your modules like this:
-     *
-     * <pre>
-     * var injector = angular.injector(['ng', 'MyModule'])
-     * </pre>
-     *
-     * However it's more likely that you'll just use
-     * {@link ng.directive:ngApp ngApp} or
-     * {@link angular.bootstrap} to simplify this process for you.
-     *
-     * @param {!string} name The name of the module to create or retrieve.
-     * @param {Array.<string>=} requires If specified then new module is being created. If unspecified then the
-     *        the module is being retrieved for further configuration.
-     * @param {Function} configFn Optional configuration function for the module. Same as
-     *        {@link angular.Module#config Module#config()}.
-     * @returns {module} new module with the {@link angular.Module} api.
-     */
-    return function module(name, requires, configFn) {
-      if (requires && modules.hasOwnProperty(name)) {
-        modules[name] = null;
-      }
-      return ensure(modules, name, function() {
-        if (!requires) {
-          throw Error('No module: ' + name);
-        }
-
-        /** @type {!Array.<Array.<*>>} */
-        var invokeQueue = [];
-
-        /** @type {!Array.<Function>} */
-        var runBlocks = [];
-
-        var config = invokeLater('$injector', 'invoke');
-
-        /** @type {angular.Module} */
-        var moduleInstance = {
-          // Private state
-          _invokeQueue: invokeQueue,
-          _runBlocks: runBlocks,
-
-          /**
-           * @ngdoc property
-           * @name angular.Module#requires
-           * @propertyOf angular.Module
-           * @returns {Array.<string>} List of module names which must be loaded before this module.
-           * @description
-           * Holds the list of modules which the injector will load before the current module is loaded.
-           */
-          requires: requires,
-
-          /**
-           * @ngdoc property
-           * @name angular.Module#name
-           * @propertyOf angular.Module
-           * @returns {string} Name of the module.
-           * @description
-           */
-          name: name,
-
-
-          /**
-           * @ngdoc method
-           * @name angular.Module#provider
-           * @methodOf angular.Module
-           * @param {string} name service name
-           * @param {Function} providerType Construction function for creating new instance of the service.
-           * @description
-           * See {@link AUTO.$provide#provider $provide.provider()}.
-           */
-          provider: invokeLater('$provide', 'provider'),
-
-          /**
-           * @ngdoc method
-           * @name angular.Module#factory
-           * @methodOf angular.Module
-           * @param {string} name service name
-           * @param {Function} providerFunction Function for creating new instance of the service.
-           * @description
-           * See {@link AUTO.$provide#factory $provide.factory()}.
-           */
-          factory: invokeLater('$provide', 'factory'),
-
-          /**
-           * @ngdoc method
-           * @name angular.Module#service
-           * @methodOf angular.Module
-           * @param {string} name service name
-           * @param {Function} constructor A constructor function that will be instantiated.
-           * @description
-           * See {@link AUTO.$provide#service $provide.service()}.
-           */
-          service: invokeLater('$provide', 'service'),
-
-          /**
-           * @ngdoc method
-           * @name angular.Module#value
-           * @methodOf angular.Module
-           * @param {string} name service name
-           * @param {*} object Service instance object.
-           * @description
-           * See {@link AUTO.$provide#value $provide.value()}.
-           */
-          value: invokeLater('$provide', 'value'),
-
-          /**
-           * @ngdoc method
-           * @name angular.Module#constant
-           * @methodOf angular.Module
-           * @param {string} name constant name
-           * @param {*} object Constant value.
-           * @description
-           * Because the constant are fixed, they get applied before other provide methods.
-           * See {@link AUTO.$provide#constant $provide.constant()}.
-           */
-          constant: invokeLater('$provide', 'constant', 'unshift'),
-
-          /**
-           * @ngdoc method
-           * @name angular.Module#filter
-           * @methodOf angular.Module
-           * @param {string} name Filter name.
-           * @param {Function} filterFactory Factory function for creating new instance of filter.
-           * @description
-           * See {@link ng.$filterProvider#register $filterProvider.register()}.
-           */
-          filter: invokeLater('$filterProvider', 'register'),
-
-          /**
-           * @ngdoc method
-           * @name angular.Module#controller
-           * @methodOf angular.Module
-           * @param {string} name Controller name.
-           * @param {Function} constructor Controller constructor function.
-           * @description
-           * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
-           */
-          controller: invokeLater('$controllerProvider', 'register'),
-
-          /**
-           * @ngdoc method
-           * @name angular.Module#directive
-           * @methodOf angular.Module
-           * @param {string} name directive name
-           * @param {Function} directiveFactory Factory function for creating new instance of
-           * directives.
-           * @description
-           * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
-           */
-          directive: invokeLater('$compileProvider', 'directive'),
-
-          /**
-           * @ngdoc method
-           * @name angular.Module#config
-           * @methodOf angular.Module
-           * @param {Function} configFn Execute this function on module load. Useful for service
-           *    configuration.
-           * @description
-           * Use this method to register work which needs to be performed on module loading.
-           */
-          config: config,
-
-          /**
-           * @ngdoc method
-           * @name angular.Module#run
-           * @methodOf angular.Module
-           * @param {Function} initializationFn Execute this function after injector creation.
-           *    Useful for application initialization.
-           * @description
-           * Use this method to register work which should be performed when the injector is done
-           * loading all modules.
-           */
-          run: function(block) {
-            runBlocks.push(block);
-            return this;
-          }
-        };
-
-        if (configFn) {
-          config(configFn);
-        }
-
-        return  moduleInstance;
-
-        /**
-         * @param {string} provider
-         * @param {string} method
-         * @param {String=} insertMethod
-         * @returns {angular.Module}
-         */
-        function invokeLater(provider, method, insertMethod) {
-          return function() {
-            invokeQueue[insertMethod || 'push']([provider, method, arguments]);
-            return moduleInstance;
-          }
-        }
-      });
-    };
-  });
-
-}
-
-/**
- * @ngdoc property
- * @name angular.version
- * @description
- * An object that contains information about the current AngularJS version. This object has the
- * following properties:
- *
- * - `full` – `{string}` – Full version string, such as "0.9.18".
- * - `major` – `{number}` – Major version number, such as "0".
- * - `minor` – `{number}` – Minor version number, such as "9".
- * - `dot` – `{number}` – Dot version number, such as "18".
- * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
- */
-var version = {
-  full: '1.0.7',    // all of these placeholder strings will be replaced by grunt's
-  major: 1,    // package task
-  minor: 0,
-  dot: 7,
-  codeName: 'monochromatic-rainbow'
-};
-
-
-function publishExternalAPI(angular){
-  extend(angular, {
-    'bootstrap': bootstrap,
-    'copy': copy,
-    'extend': extend,
-    'equals': equals,
-    'element': jqLite,
-    'forEach': forEach,
-    'injector': createInjector,
-    'noop':noop,
-    'bind':bind,
-    'toJson': toJson,
-    'fromJson': fromJson,
-    'identity':identity,
-    'isUndefined': isUndefined,
-    'isDefined': isDefined,
-    'isString': isString,
-    'isFunction': isFunction,
-    'isObject': isObject,
-    'isNumber': isNumber,
-    'isElement': isElement,
-    'isArray': isArray,
-    'version': version,
-    'isDate': isDate,
-    'lowercase': lowercase,
-    'uppercase': uppercase,
-    'callbacks': {counter: 0}
-  });
-
-  angularModule = setupModuleLoader(window);
-  try {
-    angularModule('ngLocale');
-  } catch (e) {
-    angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
-  }
-
-  angularModule('ng', ['ngLocale'], ['$provide',
-    function ngModule($provide) {
-      $provide.provider('$compile', $CompileProvider).
-        directive({
-            a: htmlAnchorDirective,
-            input: inputDirective,
-            textarea: inputDirective,
-            form: formDirective,
-            script: scriptDirective,
-            select: selectDirective,
-            style: styleDirective,
-            option: optionDirective,
-            ngBind: ngBindDirective,
-            ngBindHtmlUnsafe: ngBindHtmlUnsafeDirective,
-            ngBindTemplate: ngBindTemplateDirective,
-            ngClass: ngClassDirective,
-            ngClassEven: ngClassEvenDirective,
-            ngClassOdd: ngClassOddDirective,
-            ngCsp: ngCspDirective,
-            ngCloak: ngCloakDirective,
-            ngController: ngControllerDirective,
-            ngForm: ngFormDirective,
-            ngHide: ngHideDirective,
-            ngInclude: ngIncludeDirective,
-            ngInit: ngInitDirective,
-            ngNonBindable: ngNonBindableDirective,
-            ngPluralize: ngPluralizeDirective,
-            ngRepeat: ngRepeatDirective,
-            ngShow: ngShowDirective,
-            ngSubmit: ngSubmitDirective,
-            ngStyle: ngStyleDirective,
-            ngSwitch: ngSwitchDirective,
-            ngSwitchWhen: ngSwitchWhenDirective,
-            ngSwitchDefault: ngSwitchDefaultDirective,
-            ngOptions: ngOptionsDirective,
-            ngView: ngViewDirective,
-            ngTransclude: ngTranscludeDirective,
-            ngModel: ngModelDirective,
-            ngList: ngListDirective,
-            ngChange: ngChangeDirective,
-            required: requiredDirective,
-            ngRequired: requiredDirective,
-            ngValue: ngValueDirective
-        }).
-        directive(ngAttributeAliasDirectives).
-        directive(ngEventDirectives);
-      $provide.provider({
-        $anchorScroll: $AnchorScrollProvider,
-        $browser: $BrowserProvider,
-        $cacheFactory: $CacheFactoryProvider,
-        $controller: $ControllerProvider,
-        $document: $DocumentProvider,
-        $exceptionHandler: $ExceptionHandlerProvider,
-        $filter: $FilterProvider,
-        $interpolate: $InterpolateProvider,
-        $http: $HttpProvider,
-        $httpBackend: $HttpBackendProvider,
-        $location: $LocationProvider,
-        $log: $LogProvider,
-        $parse: $ParseProvider,
-        $route: $RouteProvider,
-        $routeParams: $RouteParamsProvider,
-        $rootScope: $RootScopeProvider,
-        $q: $QProvider,
-        $sniffer: $SnifferProvider,
-        $templateCache: $TemplateCacheProvider,
-        $timeout: $TimeoutProvider,
-        $window: $WindowProvider
-      });
-    }
-  ]);
-}
-
-//////////////////////////////////
-//JQLite
-//////////////////////////////////
-
-/**
- * @ngdoc function
- * @name angular.element
- * @function
- *
- * @description
- * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
- * `angular.element` can be either an alias for [jQuery](http://api.jquery.com/jQuery/) function, if
- * jQuery is available, or a function that wraps the element or string in Angular's jQuery lite
- * implementation (commonly referred to as jqLite).
- *
- * Real jQuery always takes precedence over jqLite, provided it was loaded before `DOMContentLoaded`
- * event fired.
- *
- * jqLite is a tiny, API-compatible subset of jQuery that allows
- * Angular to manipulate the DOM. jqLite implements only the most commonly needed functionality
- * within a very small footprint, so only a subset of the jQuery API - methods, arguments and
- * invocation styles - are supported.
- *
- * Note: All element references in Angular are always wrapped with jQuery or jqLite; they are never
- * raw DOM references.
- *
- * ## Angular's jQuery lite provides the following methods:
- *
- * - [addClass()](http://api.jquery.com/addClass/)
- * - [after()](http://api.jquery.com/after/)
- * - [append()](http://api.jquery.com/append/)
- * - [attr()](http://api.jquery.com/attr/)
- * - [bind()](http://api.jquery.com/bind/) - Does not support namespaces
- * - [children()](http://api.jquery.com/children/) - Does not support selectors
- * - [clone()](http://api.jquery.com/clone/)
- * - [contents()](http://api.jquery.com/contents/)
- * - [css()](http://api.jquery.com/css/)
- * - [data()](http://api.jquery.com/data/)
- * - [eq()](http://api.jquery.com/eq/)
- * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name
- * - [hasClass()](http://api.jquery.com/hasClass/)
- * - [html()](http://api.jquery.com/html/)
- * - [next()](http://api.jquery.com/next/) - Does not support selectors
- * - [parent()](http://api.jquery.com/parent/) - Does not support selectors
- * - [prepend()](http://api.jquery.com/prepend/)
- * - [prop()](http://api.jquery.com/prop/)
- * - [ready()](http://api.jquery.com/ready/)
- * - [remove()](http://api.jquery.com/remove/)
- * - [removeAttr()](http://api.jquery.com/removeAttr/)
- * - [removeClass()](http://api.jquery.com/removeClass/)
- * - [removeData()](http://api.jquery.com/removeData/)
- * - [replaceWith()](http://api.jquery.com/replaceWith/)
- * - [text()](http://api.jquery.com/text/)
- * - [toggleClass()](http://api.jquery.com/toggleClass/)
- * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers.
- * - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces
- * - [val()](http://api.jquery.com/val/)
- * - [wrap()](http://api.jquery.com/wrap/)
- *
- * ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite:
- *
- * - `controller(name)` - retrieves the controller of the current element or its parent. By default
- *   retrieves controller associated with the `ngController` directive. If `name` is provided as
- *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
- *   `'ngModel'`).
- * - `injector()` - retrieves the injector of the current element or its parent.
- * - `scope()` - retrieves the {@link api/ng.$rootScope.Scope scope} of the current
- *   element or its parent.
- * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
- *   parent element is reached.
- *
- * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
- * @returns {Object} jQuery object.
- */
-
-var jqCache = JQLite.cache = {},
-    jqName = JQLite.expando = 'ng-' + new Date().getTime(),
-    jqId = 1,
-    addEventListenerFn = (window.document.addEventListener
-      ? function(element, type, fn) {element.addEventListener(type, fn, false);}
-      : function(element, type, fn) {element.attachEvent('on' + type, fn);}),
-    removeEventListenerFn = (window.document.removeEventListener
-      ? function(element, type, fn) {element.removeEventListener(type, fn, false); }
-      : function(element, type, fn) {element.detachEvent('on' + type, fn); });
-
-function jqNextId() { return ++jqId; }
-
-
-var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
-var MOZ_HACK_REGEXP = /^moz([A-Z])/;
-
-/**
- * Converts snake_case to camelCase.
- * Also there is special case for Moz prefix starting with upper case letter.
- * @param name Name to normalize
- */
-function camelCase(name) {
-  return name.
-    replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
-      return offset ? letter.toUpperCase() : letter;
-    }).
-    replace(MOZ_HACK_REGEXP, 'Moz$1');
-}
-
-/////////////////////////////////////////////
-// jQuery mutation patch
-//
-//  In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
-// $destroy event on all DOM nodes being removed.
-//
-/////////////////////////////////////////////
-
-function JQLitePatchJQueryRemove(name, dispatchThis) {
-  var originalJqFn = jQuery.fn[name];
-  originalJqFn = originalJqFn.$original || originalJqFn;
-  removePatch.$original = originalJqFn;
-  jQuery.fn[name] = removePatch;
-
-  function removePatch() {
-    var list = [this],
-        fireEvent = dispatchThis,
-        set, setIndex, setLength,
-        element, childIndex, childLength, children,
-        fns, events;
-
-    while(list.length) {
-      set = list.shift();
-      for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
-        element = jqLite(set[setIndex]);
-        if (fireEvent) {
-          element.triggerHandler('$destroy');
-        } else {
-          fireEvent = !fireEvent;
-        }
-        for(childIndex = 0, childLength = (children = element.children()).length;
-            childIndex < childLength;
-            childIndex++) {
-          list.push(jQuery(children[childIndex]));
-        }
-      }
-    }
-    return originalJqFn.apply(this, arguments);
-  }
-}
-
-/////////////////////////////////////////////
-function JQLite(element) {
-  if (element instanceof JQLite) {
-    return element;
-  }
-  if (!(this instanceof JQLite)) {
-    if (isString(element) && element.charAt(0) != '<') {
-      throw Error('selectors not implemented');
-    }
-    return new JQLite(element);
-  }
-
-  if (isString(element)) {
-    var div = document.createElement('div');
-    // Read about the NoScope elements here:
-    // http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx
-    div.innerHTML = '<div>&#160;</div>' + element; // IE insanity to make NoScope elements work!
-    div.removeChild(div.firstChild); // remove the superfluous div
-    JQLiteAddNodes(this, div.childNodes);
-    this.remove(); // detach the elements from the temporary DOM div.
-  } else {
-    JQLiteAddNodes(this, element);
-  }
-}
-
-function JQLiteClone(element) {
-  return element.cloneNode(true);
-}
-
-function JQLiteDealoc(element){
-  JQLiteRemoveData(element);
-  for ( var i = 0, children = element.childNodes || []; i < children.length; i++) {
-    JQLiteDealoc(children[i]);
-  }
-}
-
-function JQLiteUnbind(element, type, fn) {
-  var events = JQLiteExpandoStore(element, 'events'),
-      handle = JQLiteExpandoStore(element, 'handle');
-
-  if (!handle) return; //no listeners registered
-
-  if (isUndefined(type)) {
-    forEach(events, function(eventHandler, type) {
-      removeEventListenerFn(element, type, eventHandler);
-      delete events[type];
-    });
-  } else {
-    if (isUndefined(fn)) {
-      removeEventListenerFn(element, type, events[type]);
-      delete events[type];
-    } else {
-      arrayRemove(events[type], fn);
-    }
-  }
-}
-
-function JQLiteRemoveData(element) {
-  var expandoId = element[jqName],
-      expandoStore = jqCache[expandoId];
-
-  if (expandoStore) {
-    if (expandoStore.handle) {
-      expandoStore.events.$destroy && expandoStore.handle({}, '$destroy');
-      JQLiteUnbind(element);
-    }
-    delete jqCache[expandoId];
-    element[jqName] = undefined; // ie does not allow deletion of attributes on elements.
-  }
-}
-
-function JQLiteExpandoStore(element, key, value) {
-  var expandoId = element[jqName],
-      expandoStore = jqCache[expandoId || -1];
-
-  if (isDefined(value)) {
-    if (!expandoStore) {
-      element[jqName] = expandoId = jqNextId();
-      expandoStore = jqCache[expandoId] = {};
-    }
-    expandoStore[key] = value;
-  } else {
-    return expandoStore && expandoStore[key];
-  }
-}
-
-function JQLiteData(element, key, value) {
-  var data = JQLiteExpandoStore(element, 'data'),
-      isSetter = isDefined(value),
-      keyDefined = !isSetter && isDefined(key),
-      isSimpleGetter = keyDefined && !isObject(key);
-
-  if (!data && !isSimpleGetter) {
-    JQLiteExpandoStore(element, 'data', data = {});
-  }
-
-  if (isSetter) {
-    data[key] = value;
-  } else {
-    if (keyDefined) {
-      if (isSimpleGetter) {
-        // don't create data in this case.
-        return data && data[key];
-      } else {
-        extend(data, key);
-      }
-    } else {
-      return data;
-    }
-  }
-}
-
-function JQLiteHasClass(element, selector) {
-  return ((" " + element.className + " ").replace(/[\n\t]/g, " ").
-      indexOf( " " + selector + " " ) > -1);
-}
-
-function JQLiteRemoveClass(element, cssClasses) {
-  if (cssClasses) {
-    forEach(cssClasses.split(' '), function(cssClass) {
-      element.className = trim(
-          (" " + element.className + " ")
-          .replace(/[\n\t]/g, " ")
-          .replace(" " + trim(cssClass) + " ", " ")
-      );
-    });
-  }
-}
-
-function JQLiteAddClass(element, cssClasses) {
-  if (cssClasses) {
-    forEach(cssClasses.split(' '), function(cssClass) {
-      if (!JQLiteHasClass(element, cssClass)) {
-        element.className = trim(element.className + ' ' + trim(cssClass));
-      }
-    });
-  }
-}
-
-function JQLiteAddNodes(root, elements) {
-  if (elements) {
-    elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements))
-      ? elements
-      : [ elements ];
-    for(var i=0; i < elements.length; i++) {
-      root.push(elements[i]);
-    }
-  }
-}
-
-function JQLiteController(element, name) {
-  return JQLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller');
-}
-
-function JQLiteInheritedData(element, name, value) {
-  element = jqLite(element);
-
-  // if element is the document object work with the html element instead
-  // this makes $(document).scope() possible
-  if(element[0].nodeType == 9) {
-    element = element.find('html');
-  }
-
-  while (element.length) {
-    if (value = element.data(name)) return value;
-    element = element.parent();
-  }
-}
-
-//////////////////////////////////////////
-// Functions which are declared directly.
-//////////////////////////////////////////
-var JQLitePrototype = JQLite.prototype = {
-  ready: function(fn) {
-    var fired = false;
-
-    function trigger() {
-      if (fired) return;
-      fired = true;
-      fn();
-    }
-
-    this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9
-    // we can not use jqLite since we are not done loading and jQuery could be loaded later.
-    JQLite(window).bind('load', trigger); // fallback to window.onload for others
-  },
-  toString: function() {
-    var value = [];
-    forEach(this, function(e){ value.push('' + e);});
-    return '[' + value.join(', ') + ']';
-  },
-
-  eq: function(index) {
-      return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
-  },
-
-  length: 0,
-  push: push,
-  sort: [].sort,
-  splice: [].splice
-};
-
-//////////////////////////////////////////
-// Functions iterating getter/setters.
-// these functions return self on setter and
-// value on get.
-//////////////////////////////////////////
-var BOOLEAN_ATTR = {};
-forEach('multiple,selected,checked,disabled,readOnly,required'.split(','), function(value) {
-  BOOLEAN_ATTR[lowercase(value)] = value;
-});
-var BOOLEAN_ELEMENTS = {};
-forEach('input,select,option,textarea,button,form'.split(','), function(value) {
-  BOOLEAN_ELEMENTS[uppercase(value)] = true;
-});
-
-function getBooleanAttrName(element, name) {
-  // check dom last since we will most likely fail on name
-  var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
-
-  // booleanAttr is here twice to minimize DOM access
-  return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr;
-}
-
-forEach({
-  data: JQLiteData,
-  inheritedData: JQLiteInheritedData,
-
-  scope: function(element) {
-    return JQLiteInheritedData(element, '$scope');
-  },
-
-  controller: JQLiteController ,
-
-  injector: function(element) {
-    return JQLiteInheritedData(element, '$injector');
-  },
-
-  removeAttr: function(element,name) {
-    element.removeAttribute(name);
-  },
-
-  hasClass: JQLiteHasClass,
-
-  css: function(element, name, value) {
-    name = camelCase(name);
-
-    if (isDefined(value)) {
-      element.style[name] = value;
-    } else {
-      var val;
-
-      if (msie <= 8) {
-        // this is some IE specific weirdness that jQuery 1.6.4 does not sure why
-        val = element.currentStyle && element.currentStyle[name];
-        if (val === '') val = 'auto';
-      }
-
-      val = val || element.style[name];
-
-      if (msie <= 8) {
-        // jquery weirdness :-/
-        val = (val === '') ? undefined : val;
-      }
-
-      return  val;
-    }
-  },
-
-  attr: function(element, name, value){
-    var lowercasedName = lowercase(name);
-    if (BOOLEAN_ATTR[lowercasedName]) {
-      if (isDefined(value)) {
-        if (!!value) {
-          element[name] = true;
-          element.setAttribute(name, lowercasedName);
-        } else {
-          element[name] = false;
-          element.removeAttribute(lowercasedName);
-        }
-      } else {
-        return (element[name] ||
-                 (element.attributes.getNamedItem(name)|| noop).specified)
-               ? lowercasedName
-               : undefined;
-      }
-    } else if (isDefined(value)) {
-      element.setAttribute(name, value);
-    } else if (element.getAttribute) {
-      // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
-      // some elements (e.g. Document) don't have get attribute, so return undefined
-      var ret = element.getAttribute(name, 2);
-      // normalize non-existing attributes to undefined (as jQuery)
-      return ret === null ? undefined : ret;
-    }
-  },
-
-  prop: function(element, name, value) {
-    if (isDefined(value)) {
-      element[name] = value;
-    } else {
-      return element[name];
-    }
-  },
-
-  text: extend((msie < 9)
-      ? function(element, value) {
-        if (element.nodeType == 1 /** Element */) {
-          if (isUndefined(value))
-            return element.innerText;
-          element.innerText = value;
-        } else {
-          if (isUndefined(value))
-            return element.nodeValue;
-          element.nodeValue = value;
-        }
-      }
-      : function(element, value) {
-        if (isUndefined(value)) {
-          return element.textContent;
-        }
-        element.textContent = value;
-      }, {$dv:''}),
-
-  val: function(element, value) {
-    if (isUndefined(value)) {
-      return element.value;
-    }
-    element.value = value;
-  },
-
-  html: function(element, value) {
-    if (isUndefined(value)) {
-      return element.innerHTML;
-    }
-    for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) {
-      JQLiteDealoc(childNodes[i]);
-    }
-    element.innerHTML = value;
-  }
-}, function(fn, name){
-  /**
-   * Properties: writes return selection, reads return first value
-   */
-  JQLite.prototype[name] = function(arg1, arg2) {
-    var i, key;
-
-    // JQLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
-    // in a way that survives minification.
-    if (((fn.length == 2 && (fn !== JQLiteHasClass && fn !== JQLiteController)) ? arg1 : arg2) === undefined) {
-      if (isObject(arg1)) {
-
-        // we are a write, but the object properties are the key/values
-        for(i=0; i < this.length; i++) {
-          if (fn === JQLiteData) {
-            // data() takes the whole object in jQuery
-            fn(this[i], arg1);
-          } else {
-            for (key in arg1) {
-              fn(this[i], key, arg1[key]);
-            }
-          }
-        }
-        // return self for chaining
-        return this;
-      } else {
-        // we are a read, so read the first child.
-        if (this.length)
-          return fn(this[0], arg1, arg2);
-      }
-    } else {
-      // we are a write, so apply to all children
-      for(i=0; i < this.length; i++) {
-        fn(this[i], arg1, arg2);
-      }
-      // return self for chaining
-      return this;
-    }
-    return fn.$dv;
-  };
-});
-
-function createEventHandler(element, events) {
-  var eventHandler = function (event, type) {
-    if (!event.preventDefault) {
-      event.preventDefault = function() {
-        event.returnValue = false; //ie
-      };
-    }
-
-    if (!event.stopPropagation) {
-      event.stopPropagation = function() {
-        event.cancelBubble = true; //ie
-      };
-    }
-
-    if (!event.target) {
-      event.target = event.srcElement || document;
-    }
-
-    if (isUndefined(event.defaultPrevented)) {
-      var prevent = event.preventDefault;
-      event.preventDefault = function() {
-        event.defaultPrevented = true;
-        prevent.call(event);
-      };
-      event.defaultPrevented = false;
-    }
-
-    event.isDefaultPrevented = function() {
-      return event.defaultPrevented;
-    };
-
-    forEach(events[type || event.type], function(fn) {
-      fn.call(element, event);
-    });
-
-    // Remove monkey-patched methods (IE),
-    // as they would cause memory leaks in IE8.
-    if (msie <= 8) {
-      // IE7/8 does not allow to delete property on native object
-      event.preventDefault = null;
-      event.stopPropagation = null;
-      event.isDefaultPrevented = null;
-    } else {
-      // It shouldn't affect normal browsers (native methods are defined on prototype).
-      delete event.preventDefault;
-      delete event.stopPropagation;
-      delete event.isDefaultPrevented;
-    }
-  };
-  eventHandler.elem = element;
-  return eventHandler;
-}
-
-//////////////////////////////////////////
-// Functions iterating traversal.
-// These functions chain results into a single
-// selector.
-//////////////////////////////////////////
-forEach({
-  removeData: JQLiteRemoveData,
-
-  dealoc: JQLiteDealoc,
-
-  bind: function bindFn(element, type, fn){
-    var events = JQLiteExpandoStore(element, 'events'),
-        handle = JQLiteExpandoStore(element, 'handle');
-
-    if (!events) JQLiteExpandoStore(element, 'events', events = {});
-    if (!handle) JQLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events));
-
-    forEach(type.split(' '), function(type){
-      var eventFns = events[type];
-
-      if (!eventFns) {
-        if (type == 'mouseenter' || type == 'mouseleave') {
-          var contains = document.body.contains || document.body.compareDocumentPosition ?
-          function( a, b ) {
-            var adown = a.nodeType === 9 ? a.documentElement : a,
-            bup = b && b.parentNode;
-            return a === bup || !!( bup && bup.nodeType === 1 && (
-              adown.contains ?
-              adown.contains( bup ) :
-              a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
-              ));
-            } :
-            function( a, b ) {
-              if ( b ) {
-                while ( (b = b.parentNode) ) {
-                  if ( b === a ) {
-                    return true;
-                  }
-                }
-              }
-              return false;
-            };	
-
-          events[type] = [];
-		
-		  // Refer to jQuery's implementation of mouseenter & mouseleave
-          // Read about mouseenter and mouseleave:
-          // http://www.quirksmode.org/js/events_mouse.html#link8
-          var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}          
-          bindFn(element, eventmap[type], function(event) {
-            var ret, target = this, related = event.relatedTarget;
-            // For mousenter/leave call the handler if related is outside the target.
-            // NB: No relatedTarget if the mouse left/entered the browser window
-            if ( !related || (related !== target && !contains(target, related)) ){
-              handle(event, type);
-            }	
-
-          });
-
-        } else {
-          addEventListenerFn(element, type, handle);
-          events[type] = [];
-        }
-        eventFns = events[type]
-      }
-      eventFns.push(fn);
-    });
-  },
-
-  unbind: JQLiteUnbind,
-
-  replaceWith: function(element, replaceNode) {
-    var index, parent = element.parentNode;
-    JQLiteDealoc(element);
-    forEach(new JQLite(replaceNode), function(node){
-      if (index) {
-        parent.insertBefore(node, index.nextSibling);
-      } else {
-        parent.replaceChild(node, element);
-      }
-      index = node;
-    });
-  },
-
-  children: function(element) {
-    var children = [];
-    forEach(element.childNodes, function(element){
-      if (element.nodeType === 1)
-        children.push(element);
-    });
-    return children;
-  },
-
-  contents: function(element) {
-    return element.childNodes || [];
-  },
-
-  append: function(element, node) {
-    forEach(new JQLite(node), function(child){
-      if (element.nodeType === 1)
-        element.appendChild(child);
-    });
-  },
-
-  prepend: function(element, node) {
-    if (element.nodeType === 1) {
-      var index = element.firstChild;
-      forEach(new JQLite(node), function(child){
-        if (index) {
-          element.insertBefore(child, index);
-        } else {
-          element.appendChild(child);
-          index = child;
-        }
-      });
-    }
-  },
-
-  wrap: function(element, wrapNode) {
-    wrapNode = jqLite(wrapNode)[0];
-    var parent = element.parentNode;
-    if (parent) {
-      parent.replaceChild(wrapNode, element);
-    }
-    wrapNode.appendChild(element);
-  },
-
-  remove: function(element) {
-    JQLiteDealoc(element);
-    var parent = element.parentNode;
-    if (parent) parent.removeChild(element);
-  },
-
-  after: function(element, newElement) {
-    var index = element, parent = element.parentNode;
-    forEach(new JQLite(newElement), function(node){
-      parent.insertBefore(node, index.nextSibling);
-      index = node;
-    });
-  },
-
-  addClass: JQLiteAddClass,
-  removeClass: JQLiteRemoveClass,
-
-  toggleClass: function(element, selector, condition) {
-    if (isUndefined(condition)) {
-      condition = !JQLiteHasClass(element, selector);
-    }
-    (condition ? JQLiteAddClass : JQLiteRemoveClass)(element, selector);
-  },
-
-  parent: function(element) {
-    var parent = element.parentNode;
-    return parent && parent.nodeType !== 11 ? parent : null;
-  },
-
-  next: function(element) {
-    if (element.nextElementSibling) {
-      return element.nextElementSibling;
-    }
-
-    // IE8 doesn't have nextElementSibling
-    var elm = element.nextSibling;
-    while (elm != null && elm.nodeType !== 1) {
-      elm = elm.nextSibling;
-    }
-    return elm;
-  },
-
-  find: function(element, selector) {
-    return element.getElementsByTagName(selector);
-  },
-
-  clone: JQLiteClone,
-
-  triggerHandler: function(element, eventName) {
-    var eventFns = (JQLiteExpandoStore(element, 'events') || {})[eventName];
-
-    forEach(eventFns, function(fn) {
-      fn.call(element, null);
-    });
-  }
-}, function(fn, name){
-  /**
-   * chaining functions
-   */
-  JQLite.prototype[name] = function(arg1, arg2) {
-    var value;
-    for(var i=0; i < this.length; i++) {
-      if (value == undefined) {
-        value = fn(this[i], arg1, arg2);
-        if (value !== undefined) {
-          // any function which returns a value needs to be wrapped
-          value = jqLite(value);
-        }
-      } else {
-        JQLiteAddNodes(value, fn(this[i], arg1, arg2));
-      }
-    }
-    return value == undefined ? this : value;
-  };
-});
-
-/**
- * Computes a hash of an 'obj'.
- * Hash of a:
- *  string is string
- *  number is number as string
- *  object is either result of calling $$hashKey function on the object or uniquely generated id,
- *         that is also assigned to the $$hashKey property of the object.
- *
- * @param obj
- * @returns {string} hash string such that the same input will have the same hash string.
- *         The resulting string key is in 'type:hashKey' format.
- */
-function hashKey(obj) {
-  var objType = typeof obj,
-      key;
-
-  if (objType == 'object' && obj !== null) {
-    if (typeof (key = obj.$$hashKey) == 'function') {
-      // must invoke on object to keep the right this
-      key = obj.$$hashKey();
-    } else if (key === undefined) {
-      key = obj.$$hashKey = nextUid();
-    }
-  } else {
-    key = obj;
-  }
-
-  return objType + ':' + key;
-}
-
-/**
- * HashMap which can use objects as keys
- */
-function HashMap(array){
-  forEach(array, this.put, this);
-}
-HashMap.prototype = {
-  /**
-   * Store key value pair
-   * @param key key to store can be any type
-   * @param value value to store can be any type
-   */
-  put: function(key, value) {
-    this[hashKey(key)] = value;
-  },
-
-  /**
-   * @param key
-   * @returns the value for the key
-   */
-  get: function(key) {
-    return this[hashKey(key)];
-  },
-
-  /**
-   * Remove the key/value pair
-   * @param key
-   */
-  remove: function(key) {
-    var value = this[key = hashKey(key)];
-    delete this[key];
-    return value;
-  }
-};
-
-/**
- * A map where multiple values can be added to the same key such that they form a queue.
- * @returns {HashQueueMap}
- */
-function HashQueueMap() {}
-HashQueueMap.prototype = {
-  /**
-   * Same as array push, but using an array as the value for the hash
-   */
-  push: function(key, value) {
-    var array = this[key = hashKey(key)];
-    if (!array) {
-      this[key] = [value];
-    } else {
-      array.push(value);
-    }
-  },
-
-  /**
-   * Same as array shift, but using an array as the value for the hash
-   */
-  shift: function(key) {
-    var array = this[key = hashKey(key)];
-    if (array) {
-      if (array.length == 1) {
-        delete this[key];
-        return array[0];
-      } else {
-        return array.shift();
-      }
-    }
-  },
-
-  /**
-   * return the first item without deleting it
-   */
-  peek: function(key) {
-    var array = this[hashKey(key)];
-    if (array) {
-    return array[0];
-    }
-  }
-};
-
-/**
- * @ngdoc function
- * @name angular.injector
- * @function
- *
- * @description
- * Creates an injector function that can be used for retrieving services as well as for
- * dependency injection (see {@link guide/di dependency injection}).
- *
-
- * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
- *        {@link angular.module}. The `ng` module must be explicitly added.
- * @returns {function()} Injector function. See {@link AUTO.$injector $injector}.
- *
- * @example
- * Typical usage
- * <pre>
- *   // create an injector
- *   var $injector = angular.injector(['ng']);
- *
- *   // use the injector to kick off your application
- *   // use the type inference to auto inject arguments, or use implicit injection
- *   $injector.invoke(function($rootScope, $compile, $document){
- *     $compile($document)($rootScope);
- *     $rootScope.$digest();
- *   });
- * </pre>
- */
-
-
-/**
- * @ngdoc overview
- * @name AUTO
- * @description
- *
- * Implicit module which gets automatically added to each {@link AUTO.$injector $injector}.
- */
-
-var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
-var FN_ARG_SPLIT = /,/;
-var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
-var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
-function annotate(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);
-      forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
-        arg.replace(FN_ARG, function(all, underscore, name){
-          $inject.push(name);
-        });
-      });
-      fn.$inject = $inject;
-    }
-  } else if (isArray(fn)) {
-    last = fn.length - 1;
-    assertArgFn(fn[last], 'fn');
-    $inject = fn.slice(0, last);
-  } else {
-    assertArgFn(fn, 'fn', true);
-  }
-  return $inject;
-}
-
-///////////////////////////////////////
-
-/**
- * @ngdoc object
- * @name AUTO.$injector
- * @function
- *
- * @description
- *
- * `$injector` is used to retrieve object instances as defined by
- * {@link AUTO.$provide provider}, instantiate types, invoke methods,
- * and load modules.
- *
- * The following always holds true:
- *
- * <pre>
- *   var $injector = angular.injector();
- *   expect($injector.get('$injector')).toBe($injector);
- *   expect($injector.invoke(function($injector){
- *     return $injector;
- *   }).toBe($injector);
- * </pre>
- *
- * # Injection Function Annotation
- *
- * JavaScript does not have annotations, and annotations are needed for dependency injection. The
- * following are all valid ways of annotating function with injection arguments and are equivalent.
- *
- * <pre>
- *   // inferred (only works if code not minified/obfuscated)
- *   $injector.invoke(function(serviceA){});
- *
- *   // annotated
- *   function explicit(serviceA) {};
- *   explicit.$inject = ['serviceA'];
- *   $injector.invoke(explicit);
- *
- *   // inline
- *   $injector.invoke(['serviceA', function(serviceA){}]);
- * </pre>
- *
- * ## Inference
- *
- * In JavaScript calling `toString()` on a function returns the function definition. The definition can then be
- * parsed and the function arguments can be extracted. *NOTE:* This does not work with minification, and obfuscation
- * tools since these tools change the argument names.
- *
- * ## `$inject` Annotation
- * By adding a `$inject` property onto a function the injection parameters can be specified.
- *
- * ## Inline
- * As an array of injection names, where the last item in the array is the function to call.
- */
-
-/**
- * @ngdoc method
- * @name AUTO.$injector#get
- * @methodOf AUTO.$injector
- *
- * @description
- * Return an instance of the service.
- *
- * @param {string} name The name of the instance to retrieve.
- * @return {*} The instance.
- */
-
-/**
- * @ngdoc method
- * @name AUTO.$injector#invoke
- * @methodOf AUTO.$injector
- *
- * @description
- * Invoke the method and supply the method arguments from the `$injector`.
- *
- * @param {!function} fn The function to invoke. The function arguments come form the function annotation.
- * @param {Object=} self The `this` for the invoked method.
- * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before
- *   the `$injector` is consulted.
- * @returns {*} the value returned by the invoked `fn` function.
- */
-
-/**
- * @ngdoc method
- * @name AUTO.$injector#instantiate
- * @methodOf AUTO.$injector
- * @description
- * Create a new instance of JS type. The method takes a constructor function invokes the new operator and supplies
- * all of the arguments to the constructor function as specified by the constructor annotation.
- *
- * @param {function} Type Annotated constructor function.
- * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before
- *   the `$injector` is consulted.
- * @returns {Object} new instance of `Type`.
- */
-
-/**
- * @ngdoc method
- * @name AUTO.$injector#annotate
- * @methodOf AUTO.$injector
- *
- * @description
- * Returns an array of service names which the function is requesting for injection. This API is used by the injector
- * to determine which services need to be injected into the function when the function is invoked. There are three
- * ways in which the function can be annotated with the needed dependencies.
- *
- * # Argument names
- *
- * The simplest form is to extract the dependencies from the arguments of the function. This is done by converting
- * the function into a string using `toString()` method and extracting the argument names.
- * <pre>
- *   // Given
- *   function MyController($scope, $route) {
- *     // ...
- *   }
- *
- *   // Then
- *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
- * </pre>
- *
- * This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
- * are supported.
- *
- * # The `$inject` property
- *
- * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
- * services to be injected into the function.
- * <pre>
- *   // Given
- *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
- *     // ...
- *   }
- *   // Define function dependencies
- *   MyController.$inject = ['$scope', '$route'];
- *
- *   // Then
- *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
- * </pre>
- *
- * # The array notation
- *
- * It is often desirable to inline Injected functions and that's when setting the `$inject` property is very
- * inconvenient. In these situations using the array notation to specify the dependencies in a way that survives
- * minification is a better choice:
- *
- * <pre>
- *   // We wish to write this (not minification / obfuscation safe)
- *   injector.invoke(function($compile, $rootScope) {
- *     // ...
- *   });
- *
- *   // We are forced to write break inlining
- *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
- *     // ...
- *   };
- *   tmpFn.$inject = ['$compile', '$rootScope'];
- *   injector.invoke(tmpFn);
- *
- *   // To better support inline function the inline annotation is supported
- *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
- *     // ...
- *   }]);
- *
- *   // Therefore
- *   expect(injector.annotate(
- *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
- *    ).toEqual(['$compile', '$rootScope']);
- * </pre>
- *
- * @param {function|Array.<string|Function>} fn Function for which dependent service names need to be retrieved as described
- *   above.
- *
- * @returns {Array.<string>} The names of the services which the function requires.
- */
-
-
-
-
-/**
- * @ngdoc object
- * @name AUTO.$provide
- *
- * @description
- *
- * Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance.
- * The providers share the same name as the instance they create with `Provider` suffixed to them.
- *
- * A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of
- * a service. The Provider can have additional methods which would allow for configuration of the provider.
- *
- * <pre>
- *   function GreetProvider() {
- *     var salutation = 'Hello';
- *
- *     this.salutation = function(text) {
- *       salutation = text;
- *     };
- *
- *     this.$get = function() {
- *       return function (name) {
- *         return salutation + ' ' + name + '!';
- *       };
- *     };
- *   }
- *
- *   describe('Greeter', function(){
- *
- *     beforeEach(module(function($provide) {
- *       $provide.provider('greet', GreetProvider);
- *     }));
- *
- *     it('should greet', inject(function(greet) {
- *       expect(greet('angular')).toEqual('Hello angular!');
- *     }));
- *
- *     it('should allow configuration of salutation', function() {
- *       module(function(greetProvider) {
- *         greetProvider.salutation('Ahoj');
- *       });
- *       inject(function(greet) {
- *         expect(greet('angular')).toEqual('Ahoj angular!');
- *       });
- *     });
- * </pre>
- */
-
-/**
- * @ngdoc method
- * @name AUTO.$provide#provider
- * @methodOf AUTO.$provide
- * @description
- *
- * Register a provider for a service. The providers can be retrieved and can have additional configuration methods.
- *
- * @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provider'` key.
- * @param {(Object|function())} provider If the provider is:
- *
- *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
- *               {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be created.
- *   - `Constructor`: a new instance of the provider will be created using
- *               {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`.
- *
- * @returns {Object} registered provider instance
- */
-
-/**
- * @ngdoc method
- * @name AUTO.$provide#factory
- * @methodOf AUTO.$provide
- * @description
- *
- * A short hand for configuring services if only `$get` method is required.
- *
- * @param {string} name The name of the instance.
- * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand for
- * `$provide.provider(name, {$get: $getFn})`.
- * @returns {Object} registered provider instance
- */
-
-
-/**
- * @ngdoc method
- * @name AUTO.$provide#service
- * @methodOf AUTO.$provide
- * @description
- *
- * A short hand for registering service of given class.
- *
- * @param {string} name The name of the instance.
- * @param {Function} constructor A class (constructor function) that will be instantiated.
- * @returns {Object} registered provider instance
- */
-
-
-/**
- * @ngdoc method
- * @name AUTO.$provide#value
- * @methodOf AUTO.$provide
- * @description
- *
- * A short hand for configuring services if the `$get` method is a constant.
- *
- * @param {string} name The name of the instance.
- * @param {*} value The value.
- * @returns {Object} registered provider instance
- */
-
-
-/**
- * @ngdoc method
- * @name AUTO.$provide#constant
- * @methodOf AUTO.$provide
- * @description
- *
- * A constant value, but unlike {@link AUTO.$provide#value value} it can be injected
- * into configuration function (other modules) and it is not interceptable by
- * {@link AUTO.$provide#decorator decorator}.
- *
- * @param {string} name The name of the constant.
- * @param {*} value The constant value.
- * @returns {Object} registered instance
- */
-
-
-/**
- * @ngdoc method
- * @name AUTO.$provide#decorator
- * @methodOf AUTO.$provide
- * @description
- *
- * Decoration of service, allows the decorator to intercept the service instance creation. The
- * returned instance may be the original instance, or a new instance which delegates to the
- * original instance.
- *
- * @param {string} name The name of the service to decorate.
- * @param {function()} decorator This function will be invoked when the service needs to be
- *    instantiated. The function is called using the {@link AUTO.$injector#invoke
- *    injector.invoke} method and is therefore fully injectable. Local injection arguments:
- *
- *    * `$delegate` - The original service instance, which can be monkey patched, configured,
- *      decorated or delegated to.
- */
-
-
-function createInjector(modulesToLoad) {
-  var INSTANTIATING = {},
-      providerSuffix = 'Provider',
-      path = [],
-      loadedModules = new HashMap(),
-      providerCache = {
-        $provide: {
-            provider: supportObject(provider),
-            factory: supportObject(factory),
-            service: supportObject(service),
-            value: supportObject(value),
-            constant: supportObject(constant),
-            decorator: decorator
-          }
-      },
-      providerInjector = createInternalInjector(providerCache, function() {
-        throw Error("Unknown provider: " + path.join(' <- '));
-      }),
-      instanceCache = {},
-      instanceInjector = (instanceCache.$injector =
-          createInternalInjector(instanceCache, function(servicename) {
-            var provider = providerInjector.get(servicename + providerSuffix);
-            return instanceInjector.invoke(provider.$get, provider);
-          }));
-
-
-  forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
-
-  return instanceInjector;
-
-  ////////////////////////////////////
-  // $provider
-  ////////////////////////////////////
-
-  function supportObject(delegate) {
-    return function(key, value) {
-      if (isObject(key)) {
-        forEach(key, reverseParams(delegate));
-      } else {
-        return delegate(key, value);
-      }
-    }
-  }
-
-  function provider(name, provider_) {
-    if (isFunction(provider_) || isArray(provider_)) {
-      provider_ = providerInjector.instantiate(provider_);
-    }
-    if (!provider_.$get) {
-      throw Error('Provider ' + name + ' must define $get factory method.');
-    }
-    return providerCache[name + providerSuffix] = provider_;
-  }
-
-  function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); }
-
-  function service(name, constructor) {
-    return factory(name, ['$injector', function($injector) {
-      return $injector.instantiate(constructor);
-    }]);
-  }
-
-  function value(name, value) { return factory(name, valueFn(value)); }
-
-  function constant(name, value) {
-    providerCache[name] = value;
-    instanceCache[name] = value;
-  }
-
-  function decorator(serviceName, decorFn) {
-    var origProvider = providerInjector.get(serviceName + providerSuffix),
-        orig$get = origProvider.$get;
-
-    origProvider.$get = function() {
-      var origInstance = instanceInjector.invoke(orig$get, origProvider);
-      return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
-    };
-  }
-
-  ////////////////////////////////////
-  // Module Loading
-  ////////////////////////////////////
-  function loadModules(modulesToLoad){
-    var runBlocks = [];
-    forEach(modulesToLoad, function(module) {
-      if (loadedModules.get(module)) return;
-      loadedModules.put(module, true);
-      if (isString(module)) {
-        var moduleFn = angularModule(module);
-        runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
-
-        try {
-          for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
-            var invokeArgs = invokeQueue[i],
-                provider = invokeArgs[0] == '$injector'
-                    ? providerInjector
-                    : providerInjector.get(invokeArgs[0]);
-
-            provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
-          }
-        } catch (e) {
-          if (e.message) e.message += ' from ' + module;
-          throw e;
-        }
-      } else if (isFunction(module)) {
-        try {
-          runBlocks.push(providerInjector.invoke(module));
-        } catch (e) {
-          if (e.message) e.message += ' from ' + module;
-          throw e;
-        }
-      } else if (isArray(module)) {
-        try {
-          runBlocks.push(providerInjector.invoke(module));
-        } catch (e) {
-          if (e.message) e.message += ' from ' + String(module[module.length - 1]);
-          throw e;
-        }
-      } else {
-        assertArgFn(module, 'module');
-      }
-    });
-    return runBlocks;
-  }
-
-  ////////////////////////////////////
-  // internal Injector
-  ////////////////////////////////////
-
-  function createInternalInjector(cache, factory) {
-
-    function getService(serviceName) {
-      if (typeof serviceName !== 'string') {
-        throw Error('Service name expected');
-      }
-      if (cache.hasOwnProperty(serviceName)) {
-        if (cache[serviceName] === INSTANTIATING) {
-          throw Error('Circular dependency: ' + path.join(' <- '));
-        }
-        return cache[serviceName];
-      } else {
-        try {
-          path.unshift(serviceName);
-          cache[serviceName] = INSTANTIATING;
-          return cache[serviceName] = factory(serviceName);
-        } finally {
-          path.shift();
-        }
-      }
-    }
-
-    function invoke(fn, self, locals){
-      var args = [],
-          $inject = annotate(fn),
-          length, i,
-          key;
-
-      for(i = 0, length = $inject.length; i < length; i++) {
-        key = $inject[i];
-        args.push(
-          locals && locals.hasOwnProperty(key)
-          ? locals[key]
-          : getService(key)
-        );
-      }
-      if (!fn.$inject) {
-        // this means that we must be an array.
-        fn = fn[length];
-      }
-
-
-      // Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke
-      switch (self ? -1 : args.length) {
-        case  0: return fn();
-        case  1: return fn(args[0]);
-        case  2: return fn(args[0], args[1]);
-        case  3: return fn(args[0], args[1], args[2]);
-        case  4: return fn(args[0], args[1], args[2], args[3]);
-        case  5: return fn(args[0], args[1], args[2], args[3], args[4]);
-        case  6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]);
-        case  7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
-        case  8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
-        case  9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
-        case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
-        default: return fn.apply(self, args);
-      }
-    }
-
-    function instantiate(Type, locals) {
-      var Constructor = function() {},
-          instance, returnedValue;
-
-      // Check if Type is annotated and use just the given function at n-1 as parameter
-      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
-      Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
-      instance = new Constructor();
-      returnedValue = invoke(Type, instance, locals);
-
-      return isObject(returnedValue) ? returnedValue : instance;
-    }
-
-    return {
-      invoke: invoke,
-      instantiate: instantiate,
-      get: getService,
-      annotate: annotate
-    };
-  }
-}
-
-/**
- * @ngdoc function
- * @name ng.$anchorScroll
- * @requires $window
- * @requires $location
- * @requires $rootScope
- *
- * @description
- * When called, it checks current value of `$location.hash()` and scroll to related element,
- * according to rules specified in
- * {@link http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document Html5 spec}.
- *
- * It also watches the `$location.hash()` and scroll whenever it changes to match any anchor.
- * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`.
- */
-function $AnchorScrollProvider() {
-
-  var autoScrollingEnabled = true;
-
-  this.disableAutoScrolling = function() {
-    autoScrollingEnabled = false;
-  };
-
-  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
-    var document = $window.document;
-
-    // helper function to get first anchor from a NodeList
-    // can't use filter.filter, as it accepts only instances of Array
-    // and IE can't convert NodeList to an array using [].slice
-    // TODO(vojta): use filter if we change it to accept lists as well
-    function getFirstAnchor(list) {
-      var result = null;
-      forEach(list, function(element) {
-        if (!result && lowercase(element.nodeName) === 'a') result = element;
-      });
-      return result;
-    }
-
-    function scroll() {
-      var hash = $location.hash(), elm;
-
-      // empty hash, scroll to the top of the page
-      if (!hash) $window.scrollTo(0, 0);
-
-      // element with given id
-      else if ((elm = document.getElementById(hash))) elm.scrollIntoView();
-
-      // first anchor with given name :-D
-      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView();
-
-      // no element and hash == 'top', scroll to the top of the page
-      else if (hash === 'top') $window.scrollTo(0, 0);
-    }
-
-    // does not scroll when user clicks on anchor link that is currently on
-    // (no url change, no $location.hash() change), browser native does scroll
-    if (autoScrollingEnabled) {
-      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
-        function autoScrollWatchAction() {
-          $rootScope.$evalAsync(scroll);
-        });
-    }
-
-    return scroll;
-  }];
-}
-
-/**
- * ! This is a private undocumented service !
- *
- * @name ng.$browser
- * @requires $log
- * @description
- * This object has two goals:
- *
- * - hide all the global state in the browser caused by the window object
- * - abstract away all the browser specific features and inconsistencies
- *
- * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
- * service, which can be used for convenient testing of the application without the interaction with
- * the real browser apis.
- */
-/**
- * @param {object} window The global window object.
- * @param {object} document jQuery wrapped document.
- * @param {function()} XHR XMLHttpRequest constructor.
- * @param {object} $log console.log or an object with the same interface.
- * @param {object} $sniffer $sniffer service
- */
-function Browser(window, document, $log, $sniffer) {
-  var self = this,
-      rawDocument = document[0],
-      location = window.location,
-      history = window.history,
-      setTimeout = window.setTimeout,
-      clearTimeout = window.clearTimeout,
-      pendingDeferIds = {};
-
-  self.isMock = false;
-
-  var outstandingRequestCount = 0;
-  var outstandingRequestCallbacks = [];
-
-  // TODO(vojta): remove this temporary api
-  self.$$completeOutstandingRequest = completeOutstandingRequest;
-  self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
-
-  /**
-   * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
-   * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
-   */
-  function completeOutstandingRequest(fn) {
-    try {
-      fn.apply(null, sliceArgs(arguments, 1));
-    } finally {
-      outstandingRequestCount--;
-      if (outstandingRequestCount === 0) {
-        while(outstandingRequestCallbacks.length) {
-          try {
-            outstandingRequestCallbacks.pop()();
-          } catch (e) {
-            $log.error(e);
-          }
-        }
-      }
-    }
-  }
-
-  /**
-   * @private
-   * Note: this method is used only by scenario runner
-   * TODO(vojta): prefix this method with $$ ?
-   * @param {function()} callback Function that will be called when no outstanding request
-   */
-  self.notifyWhenNoOutstandingRequests = function(callback) {
-    // force browser to execute all pollFns - this is needed so that cookies and other pollers fire
-    // at some deterministic time in respect to the test runner's actions. Leaving things up to the
-    // regular poller would result in flaky tests.
-    forEach(pollFns, function(pollFn){ pollFn(); });
-
-    if (outstandingRequestCount === 0) {
-      callback();
-    } else {
-      outstandingRequestCallbacks.push(callback);
-    }
-  };
-
-  //////////////////////////////////////////////////////////////
-  // Poll Watcher API
-  //////////////////////////////////////////////////////////////
-  var pollFns = [],
-      pollTimeout;
-
-  /**
-   * @name ng.$browser#addPollFn
-   * @methodOf ng.$browser
-   *
-   * @param {function()} fn Poll function to add
-   *
-   * @description
-   * Adds a function to the list of functions that poller periodically executes,
-   * and starts polling if not started yet.
-   *
-   * @returns {function()} the added function
-   */
-  self.addPollFn = function(fn) {
-    if (isUndefined(pollTimeout)) startPoller(100, setTimeout);
-    pollFns.push(fn);
-    return fn;
-  };
-
-  /**
-   * @param {number} interval How often should browser call poll functions (ms)
-   * @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
-   *
-   * @description
-   * Configures the poller to run in the specified intervals, using the specified
-   * setTimeout fn and kicks it off.
-   */
-  function startPoller(interval, setTimeout) {
-    (function check() {
-      forEach(pollFns, function(pollFn){ pollFn(); });
-      pollTimeout = setTimeout(check, interval);
-    })();
-  }
-
-  //////////////////////////////////////////////////////////////
-  // URL API
-  //////////////////////////////////////////////////////////////
-
-  var lastBrowserUrl = location.href,
-      baseElement = document.find('base');
-
-  /**
-   * @name ng.$browser#url
-   * @methodOf ng.$browser
-   *
-   * @description
-   * GETTER:
-   * Without any argument, this method just returns current value of location.href.
-   *
-   * SETTER:
-   * With at least one argument, this method sets url to new value.
-   * If html5 history api supported, pushState/replaceState is used, otherwise
-   * location.href/location.replace is used.
-   * Returns its own instance to allow chaining
-   *
-   * NOTE: this api is intended for use only by the $location service. Please use the
-   * {@link ng.$location $location service} to change url.
-   *
-   * @param {string} url New url (when used as setter)
-   * @param {boolean=} replace Should new url replace current history record ?
-   */
-  self.url = function(url, replace) {
-    // setter
-    if (url) {
-      if (lastBrowserUrl == url) return;
-      lastBrowserUrl = url;
-      if ($sniffer.history) {
-        if (replace) history.replaceState(null, '', url);
-        else {
-          history.pushState(null, '', url);
-          // Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462
-          baseElement.attr('href', baseElement.attr('href'));
-        }
-      } else {
-        if (replace) location.replace(url);
-        else location.href = url;
-      }
-      return self;
-    // getter
-    } else {
-      // the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
-      return location.href.replace(/%27/g,"'");
-    }
-  };
-
-  var urlChangeListeners = [],
-      urlChangeInit = false;
-
-  function fireUrlChange() {
-    if (lastBrowserUrl == self.url()) return;
-
-    lastBrowserUrl = self.url();
-    forEach(urlChangeListeners, function(listener) {
-      listener(self.url());
-    });
-  }
-
-  /**
-   * @name ng.$browser#onUrlChange
-   * @methodOf ng.$browser
-   * @TODO(vojta): refactor to use node's syntax for events
-   *
-   * @description
-   * Register callback function that will be called, when url changes.
-   *
-   * It's only called when the url is changed by outside of angular:
-   * - user types different url into address bar
-   * - user clicks on history (forward/back) button
-   * - user clicks on a link
-   *
-   * It's not called when url is changed by $browser.url() method
-   *
-   * The listener gets called with new url as parameter.
-   *
-   * NOTE: this api is intended for use only by the $location service. Please use the
-   * {@link ng.$location $location service} to monitor url changes in angular apps.
-   *
-   * @param {function(string)} listener Listener function to be called when url changes.
-   * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
-   */
-  self.onUrlChange = function(callback) {
-    if (!urlChangeInit) {
-      // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
-      // don't fire popstate when user change the address bar and don't fire hashchange when url
-      // changed by push/replaceState
-
-      // html5 history api - popstate event
-      if ($sniffer.history) jqLite(window).bind('popstate', fireUrlChange);
-      // hashchange event
-      if ($sniffer.hashchange) jqLite(window).bind('hashchange', fireUrlChange);
-      // polling
-      else self.addPollFn(fireUrlChange);
-
-      urlChangeInit = true;
-    }
-
-    urlChangeListeners.push(callback);
-    return callback;
-  };
-
-  //////////////////////////////////////////////////////////////
-  // Misc API
-  //////////////////////////////////////////////////////////////
-
-  /**
-   * Returns current <base href>
-   * (always relative - without domain)
-   *
-   * @returns {string=}
-   */
-  self.baseHref = function() {
-    var href = baseElement.attr('href');
-    return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
-  };
-
-  //////////////////////////////////////////////////////////////
-  // Cookies API
-  //////////////////////////////////////////////////////////////
-  var lastCookies = {};
-  var lastCookieString = '';
-  var cookiePath = self.baseHref();
-
-  /**
-   * @name ng.$browser#cookies
-   * @methodOf ng.$browser
-   *
-   * @param {string=} name Cookie name
-   * @param {string=} value Cokkie value
-   *
-   * @description
-   * The cookies method provides a 'private' low level access to browser cookies.
-   * It is not meant to be used directly, use the $cookie service instead.
-   *
-   * The return values vary depending on the arguments that the method was called with as follows:
-   * <ul>
-   *   <li>cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it</li>
-   *   <li>cookies(name, value) -> set name to value, if value is undefined delete the cookie</li>
-   *   <li>cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)</li>
-   * </ul>
-   *
-   * @returns {Object} Hash of all cookies (if called without any parameter)
-   */
-  self.cookies = function(name, value) {
-    var cookieLength, cookieArray, cookie, i, index;
-
-    if (name) {
-      if (value === undefined) {
-        rawDocument.cookie = escape(name) + "=;path=" + cookiePath + ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
-      } else {
-        if (isString(value)) {
-          cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + ';path=' + cookiePath).length + 1;
-
-          // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
-          // - 300 cookies
-          // - 20 cookies per unique domain
-          // - 4096 bytes per cookie
-          if (cookieLength > 4096) {
-            $log.warn("Cookie '"+ name +"' possibly not set or overflowed because it was too large ("+
-              cookieLength + " > 4096 bytes)!");
-          }
-        }
-      }
-    } else {
-      if (rawDocument.cookie !== lastCookieString) {
-        lastCookieString = rawDocument.cookie;
-        cookieArray = lastCookieString.split("; ");
-        lastCookies = {};
-
-        for (i = 0; i < cookieArray.length; i++) {
-          cookie = cookieArray[i];
-          index = cookie.indexOf('=');
-          if (index > 0) { //ignore nameless cookies
-            var name = unescape(cookie.substring(0, index));
-            // the first value that is seen for a cookie is the most
-            // specific one.  values for the same cookie name that
-            // follow are for less specific paths.
-            if (lastCookies[name] === undefined) {
-              lastCookies[name] = unescape(cookie.substring(index + 1));
-            }
-          }
-        }
-      }
-      return lastCookies;
-    }
-  };
-
-
-  /**
-   * @name ng.$browser#defer
-   * @methodOf ng.$browser
-   * @param {function()} fn A function, who's execution should be defered.
-   * @param {number=} [delay=0] of milliseconds to defer the function execution.
-   * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
-   *
-   * @description
-   * Executes a fn asynchroniously via `setTimeout(fn, delay)`.
-   *
-   * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
-   * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
-   * via `$browser.defer.flush()`.
-   *
-   */
-  self.defer = function(fn, delay) {
-    var timeoutId;
-    outstandingRequestCount++;
-    timeoutId = setTimeout(function() {
-      delete pendingDeferIds[timeoutId];
-      completeOutstandingRequest(fn);
-    }, delay || 0);
-    pendingDeferIds[timeoutId] = true;
-    return timeoutId;
-  };
-
-
-  /**
-   * @name ng.$browser#defer.cancel
-   * @methodOf ng.$browser.defer
-   *
-   * @description
-   * Cancels a defered task identified with `deferId`.
-   *
-   * @param {*} deferId Token returned by the `$browser.defer` function.
-   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfuly canceled.
-   */
-  self.defer.cancel = function(deferId) {
-    if (pendingDeferIds[deferId]) {
-      delete pendingDeferIds[deferId];
-      clearTimeout(deferId);
-      completeOutstandingRequest(noop);
-      return true;
-    }
-    return false;
-  };
-
-}
-
-function $BrowserProvider(){
-  this.$get = ['$window', '$log', '$sniffer', '$document',
-      function( $window,   $log,   $sniffer,   $document){
-        return new Browser($window, $document, $log, $sniffer);
-      }];
-}
-
-/**
- * @ngdoc object
- * @name ng.$cacheFactory
- *
- * @description
- * Factory that constructs cache objects.
- *
- *
- * @param {string} cacheId Name or id of the newly created cache.
- * @param {object=} options Options object that specifies the cache behavior. Properties:
- *
- *   - `{number=}` `capacity` — turns the cache into LRU cache.
- *
- * @returns {object} Newly created cache object with the following set of methods:
- *
- * - `{object}` `info()` — Returns id, size, and options of cache.
- * - `{void}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache.
- * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
- * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
- * - `{void}` `removeAll()` — Removes all cached values.
- * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
- *
- */
-function $CacheFactoryProvider() {
-
-  this.$get = function() {
-    var caches = {};
-
-    function cacheFactory(cacheId, options) {
-      if (cacheId in caches) {
-        throw Error('cacheId ' + cacheId + ' taken');
-      }
-
-      var size = 0,
-          stats = extend({}, options, {id: cacheId}),
-          data = {},
-          capacity = (options && options.capacity) || Number.MAX_VALUE,
-          lruHash = {},
-          freshEnd = null,
-          staleEnd = null;
-
-      return caches[cacheId] = {
-
-        put: function(key, value) {
-          var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
-
-          refresh(lruEntry);
-
-          if (isUndefined(value)) return;
-          if (!(key in data)) size++;
-          data[key] = value;
-
-          if (size > capacity) {
-            this.remove(staleEnd.key);
-          }
-        },
-
-
-        get: function(key) {
-          var lruEntry = lruHash[key];
-
-          if (!lruEntry) return;
-
-          refresh(lruEntry);
-
-          return data[key];
-        },
-
-
-        remove: function(key) {
-          var lruEntry = lruHash[key];
-
-          if (!lruEntry) return;
-
-          if (lruEntry == freshEnd) freshEnd = lruEntry.p;
-          if (lruEntry == staleEnd) staleEnd = lruEntry.n;
-          link(lruEntry.n,lruEntry.p);
-
-          delete lruHash[key];
-          delete data[key];
-          size--;
-        },
-
-
-        removeAll: function() {
-          data = {};
-          size = 0;
-          lruHash = {};
-          freshEnd = staleEnd = null;
-        },
-
-
-        destroy: function() {
-          data = null;
-          stats = null;
-          lruHash = null;
-          delete caches[cacheId];
-        },
-
-
-        info: function() {
-          return extend({}, stats, {size: size});
-        }
-      };
-
-
-      /**
-       * makes the `entry` the freshEnd of the LRU linked list
-       */
-      function refresh(entry) {
-        if (entry != freshEnd) {
-          if (!staleEnd) {
-            staleEnd = entry;
-          } else if (staleEnd == entry) {
-            staleEnd = entry.n;
-          }
-
-          link(entry.n, entry.p);
-          link(entry, freshEnd);
-          freshEnd = entry;
-          freshEnd.n = null;
-        }
-      }
-
-
-      /**
-       * bidirectionally links two entries of the LRU linked list
-       */
-      function link(nextEntry, prevEntry) {
-        if (nextEntry != prevEntry) {
-          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
-          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
-        }
-      }
-    }
-
-
-    cacheFactory.info = function() {
-      var info = {};
-      forEach(caches, function(cache, cacheId) {
-        info[cacheId] = cache.info();
-      });
-      return info;
-    };
-
-
-    cacheFactory.get = function(cacheId) {
-      return caches[cacheId];
-    };
-
-
-    return cacheFactory;
-  };
-}
-
-/**
- * @ngdoc object
- * @name ng.$templateCache
- *
- * @description
- * Cache used for storing html templates.
- *
- * See {@link ng.$cacheFactory $cacheFactory}.
- *
- */
-function $TemplateCacheProvider() {
-  this.$get = ['$cacheFactory', function($cacheFactory) {
-    return $cacheFactory('templates');
-  }];
-}
-
-/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
- *
- * DOM-related variables:
- *
- * - "node" - DOM Node
- * - "element" - DOM Element or Node
- * - "$node" or "$element" - jqLite-wrapped node or element
- *
- *
- * Compiler related stuff:
- *
- * - "linkFn" - linking fn of a single directive
- * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
- * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
- * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
- */
-
-
-var NON_ASSIGNABLE_MODEL_EXPRESSION = 'Non-assignable model expression: ';
-
-
-/**
- * @ngdoc function
- * @name ng.$compile
- * @function
- *
- * @description
- * Compiles a piece of HTML string or DOM into a template and produces a template function, which
- * can then be used to link {@link ng.$rootScope.Scope scope} and the template together.
- *
- * The compilation is a process of walking the DOM tree and trying to match DOM elements to
- * {@link ng.$compileProvider#directive directives}. For each match it
- * executes corresponding template function and collects the
- * instance functions into a single template function which is then returned.
- *
- * The template function can then be used once to produce the view or as it is the case with
- * {@link ng.directive:ngRepeat repeater} many-times, in which
- * case each call results in a view that is a DOM clone of the original template.
- *
- <doc:example module="compile">
-   <doc:source>
-    <script>
-      // declare a new module, and inject the $compileProvider
-      angular.module('compile', [], function($compileProvider) {
-        // configure new 'compile' directive by passing a directive
-        // factory function. The factory function injects the '$compile'
-        $compileProvider.directive('compile', function($compile) {
-          // directive factory creates a link function
-          return function(scope, element, attrs) {
-            scope.$watch(
-              function(scope) {
-                 // watch the 'compile' expression for changes
-                return scope.$eval(attrs.compile);
-              },
-              function(value) {
-                // when the 'compile' expression changes
-                // assign it into the current DOM
-                element.html(value);
-
-                // compile the new DOM and link it to the current
-                // scope.
-                // NOTE: we only compile .childNodes so that
-                // we don't get into infinite loop compiling ourselves
-                $compile(element.contents())(scope);
-              }
-            );
-          };
-        })
-      });
-
-      function Ctrl($scope) {
-        $scope.name = 'Angular';
-        $scope.html = 'Hello {{name}}';
-      }
-    </script>
-    <div ng-controller="Ctrl">
-      <input ng-model="name"> <br>
-      <textarea ng-model="html"></textarea> <br>
-      <div compile="html"></div>
-    </div>
-   </doc:source>
-   <doc:scenario>
-     it('should auto compile', function() {
-       expect(element('div[compile]').text()).toBe('Hello Angular');
-       input('html').enter('{{name}}!');
-       expect(element('div[compile]').text()).toBe('Angular!');
-     });
-   </doc:scenario>
- </doc:example>
-
- *
- *
- * @param {string|DOMElement} element Element or HTML string to compile into a template function.
- * @param {function(angular.Scope[, cloneAttachFn]} transclude function available to directives.
- * @param {number} maxPriority only apply directives lower then given priority (Only effects the
- *                 root element(s), not their children)
- * @returns {function(scope[, cloneAttachFn])} a link function which is used to bind template
- * (a DOM element/tree) to a scope. Where:
- *
- *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
- *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
- *               `template` and call the `cloneAttachFn` function allowing the caller to attach the
- *               cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
- *               called as: <br> `cloneAttachFn(clonedElement, scope)` where:
- *
- *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
- *      * `scope` - is the current scope with which the linking function is working with.
- *
- * Calling the linking function returns the element of the template. It is either the original element
- * passed in, or the clone of the element if the `cloneAttachFn` is provided.
- *
- * After linking the view is not updated until after a call to $digest which typically is done by
- * Angular automatically.
- *
- * If you need access to the bound view, there are two ways to do it:
- *
- * - If you are not asking the linking function to clone the template, create the DOM element(s)
- *   before you send them to the compiler and keep this reference around.
- *   <pre>
- *     var element = $compile('<p>{{total}}</p>')(scope);
- *   </pre>
- *
- * - if on the other hand, you need the element to be cloned, the view reference from the original
- *   example would not point to the clone, but rather to the original template that was cloned. In
- *   this case, you can access the clone via the cloneAttachFn:
- *   <pre>
- *     var templateHTML = angular.element('<p>{{total}}</p>'),
- *         scope = ....;
- *
- *     var clonedElement = $compile(templateHTML)(scope, function(clonedElement, scope) {
- *       //attach the clone to DOM document at the right place
- *     });
- *
- *     //now we have reference to the cloned DOM via `clone`
- *   </pre>
- *
- *
- * For information on how the compiler works, see the
- * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
- */
-
-
-/**
- * @ngdoc service
- * @name ng.$compileProvider
- * @function
- *
- * @description
- */
-$CompileProvider.$inject = ['$provide'];
-function $CompileProvider($provide) {
-  var hasDirectives = {},
-      Suffix = 'Directive',
-      COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
-      CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
-      MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ',
-      urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/;
-
-
-  /**
-   * @ngdoc function
-   * @name ng.$compileProvider#directive
-   * @methodOf ng.$compileProvider
-   * @function
-   *
-   * @description
-   * Register a new directives with the compiler.
-   *
-   * @param {string} name Name of the directive in camel-case. (ie <code>ngBind</code> which will match as
-   *                <code>ng-bind</code>).
-   * @param {function} directiveFactory An injectable directive factroy function. See {@link guide/directive} for more
-   *                info.
-   * @returns {ng.$compileProvider} Self for chaining.
-   */
-   this.directive = function registerDirective(name, directiveFactory) {
-    if (isString(name)) {
-      assertArg(directiveFactory, 'directive');
-      if (!hasDirectives.hasOwnProperty(name)) {
-        hasDirectives[name] = [];
-        $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
-          function($injector, $exceptionHandler) {
-            var directives = [];
-            forEach(hasDirectives[name], function(directiveFactory) {
-              try {
-                var directive = $injector.invoke(directiveFactory);
-                if (isFunction(directive)) {
-                  directive = { compile: valueFn(directive) };
-                } else if (!directive.compile && directive.link) {
-                  directive.compile = valueFn(directive.link);
-                }
-                directive.priority = directive.priority || 0;
-                directive.name = directive.name || name;
-                directive.require = directive.require || (directive.controller && directive.name);
-                directive.restrict = directive.restrict || 'A';
-                directives.push(directive);
-              } catch (e) {
-                $exceptionHandler(e);
-              }
-            });
-            return directives;
-          }]);
-      }
-      hasDirectives[name].push(directiveFactory);
-    } else {
-      forEach(name, reverseParams(registerDirective));
-    }
-    return this;
-  };
-
-
-  /**
-   * @ngdoc function
-   * @name ng.$compileProvider#urlSanitizationWhitelist
-   * @methodOf ng.$compileProvider
-   * @function
-   *
-   * @description
-   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
-   * urls during a[href] sanitization.
-   *
-   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
-   *
-   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into an
-   * absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular
-   * expression. If a match is found the original url is written into the dom. Otherwise the
-   * absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM.
-   *
-   * @param {RegExp=} regexp New regexp to whitelist urls with.
-   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
-   *    chaining otherwise.
-   */
-  this.urlSanitizationWhitelist = function(regexp) {
-    if (isDefined(regexp)) {
-      urlSanitizationWhitelist = regexp;
-      return this;
-    }
-    return urlSanitizationWhitelist;
-  };
-
-
-  this.$get = [
-            '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
-            '$controller', '$rootScope', '$document',
-    function($injector,   $interpolate,   $exceptionHandler,   $http,   $templateCache,   $parse,
-             $controller,   $rootScope,   $document) {
-
-    var Attributes = function(element, attr) {
-      this.$$element = element;
-      this.$attr = attr || {};
-    };
-
-    Attributes.prototype = {
-      $normalize: directiveNormalize,
-
-
-      /**
-       * Set a normalized attribute on the element in a way such that all directives
-       * can share the attribute. This function properly handles boolean attributes.
-       * @param {string} key Normalized key. (ie ngAttribute)
-       * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
-       * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
-       *     Defaults to true.
-       * @param {string=} attrName Optional none normalized name. Defaults to key.
-       */
-      $set: function(key, value, writeAttr, attrName) {
-        var booleanKey = getBooleanAttrName(this.$$element[0], key),
-            $$observers = this.$$observers,
-            normalizedVal;
-
-        if (booleanKey) {
-          this.$$element.prop(key, value);
-          attrName = booleanKey;
-        }
-
-        this[key] = value;
-
-        // translate normalized key to actual key
-        if (attrName) {
-          this.$attr[key] = attrName;
-        } else {
-          attrName = this.$attr[key];
-          if (!attrName) {
-            this.$attr[key] = attrName = snake_case(key, '-');
-          }
-        }
-
-
-        // sanitize a[href] values
-        if (nodeName_(this.$$element[0]) === 'A' && key === 'href') {
-          urlSanitizationNode.setAttribute('href', value);
-
-          // href property always returns normalized absolute url, so we can match against that
-          normalizedVal = urlSanitizationNode.href;
-          if (!normalizedVal.match(urlSanitizationWhitelist)) {
-            this[key] = value = 'unsafe:' + normalizedVal;
-          }
-        }
-
-
-        if (writeAttr !== false) {
-          if (value === null || value === undefined) {
-            this.$$element.removeAttr(attrName);
-          } else {
-            this.$$element.attr(attrName, value);
-          }
-        }
-
-        // fire observers
-        $$observers && forEach($$observers[key], function(fn) {
-          try {
-            fn(value);
-          } catch (e) {
-            $exceptionHandler(e);
-          }
-        });
-      },
-
-
-      /**
-       * Observe an interpolated attribute.
-       * The observer will never be called, if given attribute is not interpolated.
-       *
-       * @param {string} key Normalized key. (ie ngAttribute) .
-       * @param {function(*)} fn Function that will be called whenever the attribute value changes.
-       * @returns {function(*)} the `fn` Function passed in.
-       */
-      $observe: function(key, fn) {
-        var attrs = this,
-            $$observers = (attrs.$$observers || (attrs.$$observers = {})),
-            listeners = ($$observers[key] || ($$observers[key] = []));
-
-        listeners.push(fn);
-        $rootScope.$evalAsync(function() {
-          if (!listeners.$$inter) {
-            // no one registered attribute interpolation function, so lets call it manually
-            fn(attrs[key]);
-          }
-        });
-        return fn;
-      }
-    };
-
-    var urlSanitizationNode = $document[0].createElement('a'),
-        startSymbol = $interpolate.startSymbol(),
-        endSymbol = $interpolate.endSymbol(),
-        denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
-            ? identity
-            : function denormalizeTemplate(template) {
-              return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
-            };
-
-
-    return compile;
-
-    //================================
-
-    function compile($compileNodes, transcludeFn, maxPriority) {
-      if (!($compileNodes instanceof jqLite)) {
-        // jquery always rewraps, whereas we need to preserve the original selector so that we can modify it.
-        $compileNodes = jqLite($compileNodes);
-      }
-      // We can not compile top level text elements since text nodes can be merged and we will
-      // not be able to attach scope data to them, so we will wrap them in <span>
-      forEach($compileNodes, function(node, index){
-        if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) {
-          $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
-        }
-      });
-      var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority);
-      return function publicLinkFn(scope, cloneConnectFn){
-        assertArg(scope, 'scope');
-        // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
-        // and sometimes changes the structure of the DOM.
-        var $linkNode = cloneConnectFn
-          ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
-          : $compileNodes;
-
-        // Attach scope only to non-text nodes.
-        for(var i = 0, ii = $linkNode.length; i<ii; i++) {
-          var node = $linkNode[i];
-          if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
-            $linkNode.eq(i).data('$scope', scope);
-          }
-        }
-        safeAddClass($linkNode, 'ng-scope');
-        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
-        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
-        return $linkNode;
-      };
-    }
-
-    function wrongMode(localName, mode) {
-      throw Error("Unsupported '" + mode + "' for '" + localName + "'.");
-    }
-
-    function safeAddClass($element, className) {
-      try {
-        $element.addClass(className);
-      } catch(e) {
-        // ignore, since it means that we are trying to set class on
-        // SVG element, where class name is read-only.
-      }
-    }
-
-    /**
-     * Compile function matches each node in nodeList against the directives. Once all directives
-     * for a particular node are collected their compile functions are executed. The compile
-     * functions return values - the linking functions - are combined into a composite linking
-     * function, which is the a linking function for the node.
-     *
-     * @param {NodeList} nodeList an array of nodes or NodeList to compile
-     * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
-     *        scope argument is auto-generated to the new child of the transcluded parent scope.
-     * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the
-     *        rootElement must be set the jqLite collection of the compile root. This is
-     *        needed so that the jqLite collection items can be replaced with widgets.
-     * @param {number=} max directive priority
-     * @returns {?function} A composite linking function of all of the matched directives or null.
-     */
-    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority) {
-      var linkFns = [],
-          nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
-
-      for(var i = 0; i < nodeList.length; i++) {
-        attrs = new Attributes();
-
-        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
-        directives = collectDirectives(nodeList[i], [], attrs, maxPriority);
-
-        nodeLinkFn = (directives.length)
-            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
-            : null;
-
-        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes || !nodeList[i].childNodes.length)
-            ? null
-            : compileNodes(nodeList[i].childNodes,
-                 nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
-
-        linkFns.push(nodeLinkFn);
-        linkFns.push(childLinkFn);
-        linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
-      }
-
-      // return a linking function if we have found anything, null otherwise
-      return linkFnFound ? compositeLinkFn : null;
-
-      function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
-        var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn, i, ii, n;
-
-        // copy nodeList so that linking doesn't break due to live list updates.
-        var stableNodeList = [];
-        for (i = 0, ii = nodeList.length; i < ii; i++) {
-          stableNodeList.push(nodeList[i]);
-        }
-
-        for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
-          node = stableNodeList[n];
-          nodeLinkFn = linkFns[i++];
-          childLinkFn = linkFns[i++];
-
-          if (nodeLinkFn) {
-            if (nodeLinkFn.scope) {
-              childScope = scope.$new(isObject(nodeLinkFn.scope));
-              jqLite(node).data('$scope', childScope);
-            } else {
-              childScope = scope;
-            }
-            childTranscludeFn = nodeLinkFn.transclude;
-            if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
-              nodeLinkFn(childLinkFn, childScope, node, $rootElement,
-                  (function(transcludeFn) {
-                    return function(cloneFn) {
-                      var transcludeScope = scope.$new();
-                      transcludeScope.$$transcluded = true;
-
-                      return transcludeFn(transcludeScope, cloneFn).
-                          bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
-                    };
-                  })(childTranscludeFn || transcludeFn)
-              );
-            } else {
-              nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
-            }
-          } else if (childLinkFn) {
-            childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
-          }
-        }
-      }
-    }
-
-
-    /**
-     * Looks for directives on the given node and adds them to the directive collection which is
-     * sorted.
-     *
-     * @param node Node to search.
-     * @param directives An array to which the directives are added to. This array is sorted before
-     *        the function returns.
-     * @param attrs The shared attrs object which is used to populate the normalized attributes.
-     * @param {number=} maxPriority Max directive priority.
-     */
-    function collectDirectives(node, directives, attrs, maxPriority) {
-      var nodeType = node.nodeType,
-          attrsMap = attrs.$attr,
-          match,
-          className;
-
-      switch(nodeType) {
-        case 1: /* Element */
-          // use the node name: <directive>
-          addDirective(directives,
-              directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority);
-
-          // iterate over the attributes
-          for (var attr, name, nName, value, nAttrs = node.attributes,
-                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
-            attr = nAttrs[j];
-            if (attr.specified) {
-              name = attr.name;
-              nName = directiveNormalize(name.toLowerCase());
-              attrsMap[nName] = name;
-              attrs[nName] = value = trim((msie && name == 'href')
-                ? decodeURIComponent(node.getAttribute(name, 2))
-                : attr.value);
-              if (getBooleanAttrName(node, nName)) {
-                attrs[nName] = true; // presence means true
-              }
-              addAttrInterpolateDirective(node, directives, value, nName);
-              addDirective(directives, nName, 'A', maxPriority);
-            }
-          }
-
-          // use class as directive
-          className = node.className;
-          if (isString(className) && className !== '') {
-            while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
-              nName = directiveNormalize(match[2]);
-              if (addDirective(directives, nName, 'C', maxPriority)) {
-                attrs[nName] = trim(match[3]);
-              }
-              className = className.substr(match.index + match[0].length);
-            }
-          }
-          break;
-        case 3: /* Text Node */
-          addTextInterpolateDirective(directives, node.nodeValue);
-          break;
-        case 8: /* Comment */
-          try {
-            match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
-            if (match) {
-              nName = directiveNormalize(match[1]);
-              if (addDirective(directives, nName, 'M', maxPriority)) {
-                attrs[nName] = trim(match[2]);
-              }
-            }
-          } catch (e) {
-            // turns out that under some circumstances IE9 throws errors when one attempts to read comment's node value.
-            // Just ignore it and continue. (Can't seem to reproduce in test case.)
-          }
-          break;
-      }
-
-      directives.sort(byPriority);
-      return directives;
-    }
-
-
-    /**
-     * Once the directives have been collected, their compile functions are executed. This method
-     * is responsible for inlining directive templates as well as terminating the application
-     * of the directives if the terminal directive has been reached.
-     *
-     * @param {Array} directives Array of collected directives to execute their compile function.
-     *        this needs to be pre-sorted by priority order.
-     * @param {Node} compileNode The raw DOM node to apply the compile functions to
-     * @param {Object} templateAttrs The shared attribute function
-     * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
-     *        scope argument is auto-generated to the new child of the transcluded parent scope.
-     * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
-     *        argument has the root jqLite array so that we can replace nodes on it.
-     * @returns linkFn
-     */
-    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) {
-      var terminalPriority = -Number.MAX_VALUE,
-          preLinkFns = [],
-          postLinkFns = [],
-          newScopeDirective = null,
-          newIsolateScopeDirective = null,
-          templateDirective = null,
-          $compileNode = templateAttrs.$$element = jqLite(compileNode),
-          directive,
-          directiveName,
-          $template,
-          transcludeDirective,
-          childTranscludeFn = transcludeFn,
-          controllerDirectives,
-          linkFn,
-          directiveValue;
-
-      // executes all directives on the current element
-      for(var i = 0, ii = directives.length; i < ii; i++) {
-        directive = directives[i];
-        $template = undefined;
-
-        if (terminalPriority > directive.priority) {
-          break; // prevent further processing of directives
-        }
-
-        if (directiveValue = directive.scope) {
-          assertNoDuplicate('isolated scope', newIsolateScopeDirective, directive, $compileNode);
-          if (isObject(directiveValue)) {
-            safeAddClass($compileNode, 'ng-isolate-scope');
-            newIsolateScopeDirective = directive;
-          }
-          safeAddClass($compileNode, 'ng-scope');
-          newScopeDirective = newScopeDirective || directive;
-        }
-
-        directiveName = directive.name;
-
-        if (directiveValue = directive.controller) {
-          controllerDirectives = controllerDirectives || {};
-          assertNoDuplicate("'" + directiveName + "' controller",
-              controllerDirectives[directiveName], directive, $compileNode);
-          controllerDirectives[directiveName] = directive;
-        }
-
-        if (directiveValue = directive.transclude) {
-          assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode);
-          transcludeDirective = directive;
-          terminalPriority = directive.priority;
-          if (directiveValue == 'element') {
-            $template = jqLite(compileNode);
-            $compileNode = templateAttrs.$$element =
-                jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
-            compileNode = $compileNode[0];
-            replaceWith(jqCollection, jqLite($template[0]), compileNode);
-            childTranscludeFn = compile($template, transcludeFn, terminalPriority);
-          } else {
-            $template = jqLite(JQLiteClone(compileNode)).contents();
-            $compileNode.html(''); // clear contents
-            childTranscludeFn = compile($template, transcludeFn);
-          }
-        }
-
-        if ((directiveValue = directive.template)) {
-          assertNoDuplicate('template', templateDirective, directive, $compileNode);
-          templateDirective = directive;
-          directiveValue = denormalizeTemplate(directiveValue);
-
-          if (directive.replace) {
-            $template = jqLite('<div>' +
-                                 trim(directiveValue) +
-                               '</div>').contents();
-            compileNode = $template[0];
-
-            if ($template.length != 1 || compileNode.nodeType !== 1) {
-              throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue);
-            }
-
-            replaceWith(jqCollection, $compileNode, compileNode);
-
-            var newTemplateAttrs = {$attr: {}};
-
-            // combine directives from the original node and from the template:
-            // - take the array of directives for this element
-            // - split it into two parts, those that were already applied and those that weren't
-            // - collect directives from the template, add them to the second group and sort them
-            // - append the second group with new directives to the first group
-            directives = directives.concat(
-                collectDirectives(
-                    compileNode,
-                    directives.splice(i + 1, directives.length - (i + 1)),
-                    newTemplateAttrs
-                )
-            );
-            mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
-
-            ii = directives.length;
-          } else {
-            $compileNode.html(directiveValue);
-          }
-        }
-
-        if (directive.templateUrl) {
-          assertNoDuplicate('template', templateDirective, directive, $compileNode);
-          templateDirective = directive;
-          nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i),
-              nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace,
-              childTranscludeFn);
-          ii = directives.length;
-        } else if (directive.compile) {
-          try {
-            linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
-            if (isFunction(linkFn)) {
-              addLinkFns(null, linkFn);
-            } else if (linkFn) {
-              addLinkFns(linkFn.pre, linkFn.post);
-            }
-          } catch (e) {
-            $exceptionHandler(e, startingTag($compileNode));
-          }
-        }
-
-        if (directive.terminal) {
-          nodeLinkFn.terminal = true;
-          terminalPriority = Math.max(terminalPriority, directive.priority);
-        }
-
-      }
-
-      nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope;
-      nodeLinkFn.transclude = transcludeDirective && childTranscludeFn;
-
-      // might be normal or delayed nodeLinkFn depending on if templateUrl is present
-      return nodeLinkFn;
-
-      ////////////////////
-
-      function addLinkFns(pre, post) {
-        if (pre) {
-          pre.require = directive.require;
-          preLinkFns.push(pre);
-        }
-        if (post) {
-          post.require = directive.require;
-          postLinkFns.push(post);
-        }
-      }
-
-
-      function getControllers(require, $element) {
-        var value, retrievalMethod = 'data', optional = false;
-        if (isString(require)) {
-          while((value = require.charAt(0)) == '^' || value == '?') {
-            require = require.substr(1);
-            if (value == '^') {
-              retrievalMethod = 'inheritedData';
-            }
-            optional = optional || value == '?';
-          }
-          value = $element[retrievalMethod]('$' + require + 'Controller');
-          if (!value && !optional) {
-            throw Error("No controller: " + require);
-          }
-          return value;
-        } else if (isArray(require)) {
-          value = [];
-          forEach(require, function(require) {
-            value.push(getControllers(require, $element));
-          });
-        }
-        return value;
-      }
-
-
-      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
-        var attrs, $element, i, ii, linkFn, controller;
-
-        if (compileNode === linkNode) {
-          attrs = templateAttrs;
-        } else {
-          attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr));
-        }
-        $element = attrs.$$element;
-
-        if (newIsolateScopeDirective) {
-          var LOCAL_REGEXP = /^\s*([@=&])\s*(\w*)\s*$/;
-
-          var parentScope = scope.$parent || scope;
-
-          forEach(newIsolateScopeDirective.scope, function(definiton, scopeName) {
-            var match = definiton.match(LOCAL_REGEXP) || [],
-                attrName = match[2]|| scopeName,
-                mode = match[1], // @, =, or &
-                lastValue,
-                parentGet, parentSet;
-
-            scope.$$isolateBindings[scopeName] = mode + attrName;
-
-            switch (mode) {
-
-              case '@': {
-                attrs.$observe(attrName, function(value) {
-                  scope[scopeName] = value;
-                });
-                attrs.$$observers[attrName].$$scope = parentScope;
-                break;
-              }
-
-              case '=': {
-                parentGet = $parse(attrs[attrName]);
-                parentSet = parentGet.assign || function() {
-                  // reset the change, or we will throw this exception on every $digest
-                  lastValue = scope[scopeName] = parentGet(parentScope);
-                  throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + attrs[attrName] +
-                      ' (directive: ' + newIsolateScopeDirective.name + ')');
-                };
-                lastValue = scope[scopeName] = parentGet(parentScope);
-                scope.$watch(function parentValueWatch() {
-                  var parentValue = parentGet(parentScope);
-
-                  if (parentValue !== scope[scopeName]) {
-                    // we are out of sync and need to copy
-                    if (parentValue !== lastValue) {
-                      // parent changed and it has precedence
-                      lastValue = scope[scopeName] = parentValue;
-                    } else {
-                      // if the parent can be assigned then do so
-                      parentSet(parentScope, parentValue = lastValue = scope[scopeName]);
-                    }
-                  }
-                  return parentValue;
-                });
-                break;
-              }
-
-              case '&': {
-                parentGet = $parse(attrs[attrName]);
-                scope[scopeName] = function(locals) {
-                  return parentGet(parentScope, locals);
-                };
-                break;
-              }
-
-              default: {
-                throw Error('Invalid isolate scope definition for directive ' +
-                    newIsolateScopeDirective.name + ': ' + definiton);
-              }
-            }
-          });
-        }
-
-        if (controllerDirectives) {
-          forEach(controllerDirectives, function(directive) {
-            var locals = {
-              $scope: scope,
-              $element: $element,
-              $attrs: attrs,
-              $transclude: boundTranscludeFn
-            };
-
-            controller = directive.controller;
-            if (controller == '@') {
-              controller = attrs[directive.name];
-            }
-
-            $element.data(
-                '$' + directive.name + 'Controller',
-                $controller(controller, locals));
-          });
-        }
-
-        // PRELINKING
-        for(i = 0, ii = preLinkFns.length; i < ii; i++) {
-          try {
-            linkFn = preLinkFns[i];
-            linkFn(scope, $element, attrs,
-                linkFn.require && getControllers(linkFn.require, $element));
-          } catch (e) {
-            $exceptionHandler(e, startingTag($element));
-          }
-        }
-
-        // RECURSION
-        childLinkFn && childLinkFn(scope, linkNode.childNodes, undefined, boundTranscludeFn);
-
-        // POSTLINKING
-        for(i = 0, ii = postLinkFns.length; i < ii; i++) {
-          try {
-            linkFn = postLinkFns[i];
-            linkFn(scope, $element, attrs,
-                linkFn.require && getControllers(linkFn.require, $element));
-          } catch (e) {
-            $exceptionHandler(e, startingTag($element));
-          }
-        }
-      }
-    }
-
-
-    /**
-     * looks up the directive and decorates it with exception handling and proper parameters. We
-     * call this the boundDirective.
-     *
-     * @param {string} name name of the directive to look up.
-     * @param {string} location The directive must be found in specific format.
-     *   String containing any of theses characters:
-     *
-     *   * `E`: element name
-     *   * `A': attribute
-     *   * `C`: class
-     *   * `M`: comment
-     * @returns true if directive was added.
-     */
-    function addDirective(tDirectives, name, location, maxPriority) {
-      var match = false;
-      if (hasDirectives.hasOwnProperty(name)) {
-        for(var directive, directives = $injector.get(name + Suffix),
-            i = 0, ii = directives.length; i<ii; i++) {
-          try {
-            directive = directives[i];
-            if ( (maxPriority === undefined || maxPriority > directive.priority) &&
-                 directive.restrict.indexOf(location) != -1) {
-              tDirectives.push(directive);
-              match = true;
-            }
-          } catch(e) { $exceptionHandler(e); }
-        }
-      }
-      return match;
-    }
-
-
-    /**
-     * When the element is replaced with HTML template then the new attributes
-     * on the template need to be merged with the existing attributes in the DOM.
-     * The desired effect is to have both of the attributes present.
-     *
-     * @param {object} dst destination attributes (original DOM)
-     * @param {object} src source attributes (from the directive template)
-     */
-    function mergeTemplateAttributes(dst, src) {
-      var srcAttr = src.$attr,
-          dstAttr = dst.$attr,
-          $element = dst.$$element;
-
-      // reapply the old attributes to the new element
-      forEach(dst, function(value, key) {
-        if (key.charAt(0) != '$') {
-          if (src[key]) {
-            value += (key === 'style' ? ';' : ' ') + src[key];
-          }
-          dst.$set(key, value, true, srcAttr[key]);
-        }
-      });
-
-      // copy the new attributes on the old attrs object
-      forEach(src, function(value, key) {
-        if (key == 'class') {
-          safeAddClass($element, value);
-          dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
-        } else if (key == 'style') {
-          $element.attr('style', $element.attr('style') + ';' + value);
-        } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
-          dst[key] = value;
-          dstAttr[key] = srcAttr[key];
-        }
-      });
-    }
-
-
-    function compileTemplateUrl(directives, beforeTemplateNodeLinkFn, $compileNode, tAttrs,
-        $rootElement, replace, childTranscludeFn) {
-      var linkQueue = [],
-          afterTemplateNodeLinkFn,
-          afterTemplateChildLinkFn,
-          beforeTemplateCompileNode = $compileNode[0],
-          origAsyncDirective = directives.shift(),
-          // The fact that we have to copy and patch the directive seems wrong!
-          derivedSyncDirective = extend({}, origAsyncDirective, {
-            controller: null, templateUrl: null, transclude: null, scope: null
-          });
-
-      $compileNode.html('');
-
-      $http.get(origAsyncDirective.templateUrl, {cache: $templateCache}).
-        success(function(content) {
-          var compileNode, tempTemplateAttrs, $template;
-
-          content = denormalizeTemplate(content);
-
-          if (replace) {
-            $template = jqLite('<div>' + trim(content) + '</div>').contents();
-            compileNode = $template[0];
-
-            if ($template.length != 1 || compileNode.nodeType !== 1) {
-              throw new Error(MULTI_ROOT_TEMPLATE_ERROR + content);
-            }
-
-            tempTemplateAttrs = {$attr: {}};
-            replaceWith($rootElement, $compileNode, compileNode);
-            collectDirectives(compileNode, directives, tempTemplateAttrs);
-            mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
-          } else {
-            compileNode = beforeTemplateCompileNode;
-            $compileNode.html(content);
-          }
-
-          directives.unshift(derivedSyncDirective);
-          afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
-          afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
-
-
-          while(linkQueue.length) {
-            var controller = linkQueue.pop(),
-                linkRootElement = linkQueue.pop(),
-                beforeTemplateLinkNode = linkQueue.pop(),
-                scope = linkQueue.pop(),
-                linkNode = compileNode;
-
-            if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
-              // it was cloned therefore we have to clone as well.
-              linkNode = JQLiteClone(compileNode);
-              replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
-            }
-
-            afterTemplateNodeLinkFn(function() {
-              beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, controller);
-            }, scope, linkNode, $rootElement, controller);
-          }
-          linkQueue = null;
-        }).
-        error(function(response, code, headers, config) {
-          throw Error('Failed to load template: ' + config.url);
-        });
-
-      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, controller) {
-        if (linkQueue) {
-          linkQueue.push(scope);
-          linkQueue.push(node);
-          linkQueue.push(rootElement);
-          linkQueue.push(controller);
-        } else {
-          afterTemplateNodeLinkFn(function() {
-            beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, controller);
-          }, scope, node, rootElement, controller);
-        }
-      };
-    }
-
-
-    /**
-     * Sorting function for bound directives.
-     */
-    function byPriority(a, b) {
-      return b.priority - a.priority;
-    }
-
-
-    function assertNoDuplicate(what, previousDirective, directive, element) {
-      if (previousDirective) {
-        throw Error('Multiple directives [' + previousDirective.name + ', ' +
-          directive.name + '] asking for ' + what + ' on: ' +  startingTag(element));
-      }
-    }
-
-
-    function addTextInterpolateDirective(directives, text) {
-      var interpolateFn = $interpolate(text, true);
-      if (interpolateFn) {
-        directives.push({
-          priority: 0,
-          compile: valueFn(function textInterpolateLinkFn(scope, node) {
-            var parent = node.parent(),
-                bindings = parent.data('$binding') || [];
-            bindings.push(interpolateFn);
-            safeAddClass(parent.data('$binding', bindings), 'ng-binding');
-            scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
-              node[0].nodeValue = value;
-            });
-          })
-        });
-      }
-    }
-
-
-    function addAttrInterpolateDirective(node, directives, value, name) {
-      var interpolateFn = $interpolate(value, true);
-
-      // no interpolation found -> ignore
-      if (!interpolateFn) return;
-
-
-      directives.push({
-        priority: 100,
-        compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) {
-          var $$observers = (attr.$$observers || (attr.$$observers = {}));
-
-          if (name === 'class') {
-            // we need to interpolate classes again, in the case the element was replaced
-            // and therefore the two class attrs got merged - we want to interpolate the result
-            interpolateFn = $interpolate(attr[name], true);
-          }
-
-          attr[name] = undefined;
-          ($$observers[name] || ($$observers[name] = [])).$$inter = true;
-          (attr.$$observers && attr.$$observers[name].$$scope || scope).
-            $watch(interpolateFn, function interpolateFnWatchAction(value) {
-              attr.$set(name, value);
-            });
-        })
-      });
-    }
-
-
-    /**
-     * This is a special jqLite.replaceWith, which can replace items which
-     * have no parents, provided that the containing jqLite collection is provided.
-     *
-     * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
-     *    in the root of the tree.
-     * @param {JqLite} $element The jqLite element which we are going to replace. We keep the shell,
-     *    but replace its DOM node reference.
-     * @param {Node} newNode The new DOM node.
-     */
-    function replaceWith($rootElement, $element, newNode) {
-      var oldNode = $element[0],
-          parent = oldNode.parentNode,
-          i, ii;
-
-      if ($rootElement) {
-        for(i = 0, ii = $rootElement.length; i < ii; i++) {
-          if ($rootElement[i] == oldNode) {
-            $rootElement[i] = newNode;
-            break;
-          }
-        }
-      }
-
-      if (parent) {
-        parent.replaceChild(newNode, oldNode);
-      }
-
-      newNode[jqLite.expando] = oldNode[jqLite.expando];
-      $element[0] = newNode;
-    }
-  }];
-}
-
-var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i;
-/**
- * Converts all accepted directives format into proper directive name.
- * All of these will become 'myDirective':
- *   my:DiRective
- *   my-directive
- *   x-my-directive
- *   data-my:directive
- *
- * Also there is special case for Moz prefix starting with upper case letter.
- * @param name Name to normalize
- */
-function directiveNormalize(name) {
-  return camelCase(name.replace(PREFIX_REGEXP, ''));
-}
-
-/**
- * @ngdoc object
- * @name ng.$compile.directive.Attributes
- * @description
- *
- * A shared object between directive compile / linking functions which contains normalized DOM element
- * attributes. The the values reflect current binding state `{{ }}`. The normalization is needed
- * since all of these are treated as equivalent in Angular:
- *
- *          <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
- */
-
-/**
- * @ngdoc property
- * @name ng.$compile.directive.Attributes#$attr
- * @propertyOf ng.$compile.directive.Attributes
- * @returns {object} A map of DOM element attribute names to the normalized name. This is
- *          needed to do reverse lookup from normalized name back to actual name.
- */
-
-
-/**
- * @ngdoc function
- * @name ng.$compile.directive.Attributes#$set
- * @methodOf ng.$compile.directive.Attributes
- * @function
- *
- * @description
- * Set DOM element attribute value.
- *
- *
- * @param {string} name Normalized element attribute name of the property to modify. The name is
- *          revers translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
- *          property to the original name.
- * @param {string} value Value to set the attribute to.
- */
-
-
-
-/**
- * Closure compiler type information
- */
-
-function nodesetLinkingFn(
-  /* angular.Scope */ scope,
-  /* NodeList */ nodeList,
-  /* Element */ rootElement,
-  /* function(Function) */ boundTranscludeFn
-){}
-
-function directiveLinkingFn(
-  /* nodesetLinkingFn */ nodesetLinkingFn,
-  /* angular.Scope */ scope,
-  /* Node */ node,
-  /* Element */ rootElement,
-  /* function(Function) */ boundTranscludeFn
-){}
-
-/**
- * @ngdoc object
- * @name ng.$controllerProvider
- * @description
- * The {@link ng.$controller $controller service} is used by Angular to create new
- * controllers.
- *
- * This provider allows controller registration via the
- * {@link ng.$controllerProvider#register register} method.
- */
-function $ControllerProvider() {
-  var controllers = {};
-
-
-  /**
-   * @ngdoc function
-   * @name ng.$controllerProvider#register
-   * @methodOf ng.$controllerProvider
-   * @param {string} name Controller name
-   * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
-   *    annotations in the array notation).
-   */
-  this.register = function(name, constructor) {
-    if (isObject(name)) {
-      extend(controllers, name)
-    } else {
-      controllers[name] = constructor;
-    }
-  };
-
-
-  this.$get = ['$injector', '$window', function($injector, $window) {
-
-    /**
-     * @ngdoc function
-     * @name ng.$controller
-     * @requires $injector
-     *
-     * @param {Function|string} constructor If called with a function then it's considered to be the
-     *    controller constructor function. Otherwise it's considered to be a string which is used
-     *    to retrieve the controller constructor using the following steps:
-     *
-     *    * check if a controller with given name is registered via `$controllerProvider`
-     *    * check if evaluating the string on the current scope returns a constructor
-     *    * check `window[constructor]` on the global `window` object
-     *
-     * @param {Object} locals Injection locals for Controller.
-     * @return {Object} Instance of given controller.
-     *
-     * @description
-     * `$controller` service is responsible for instantiating controllers.
-     *
-     * It's just a simple call to {@link AUTO.$injector $injector}, but extracted into
-     * a service, so that one can override this service with {@link https://gist.github.com/1649788
-     * BC version}.
-     */
-    return function(constructor, locals) {
-      if(isString(constructor)) {
-        var name = constructor;
-        constructor = controllers.hasOwnProperty(name)
-            ? controllers[name]
-            : getter(locals.$scope, name, true) || getter($window, name, true);
-
-        assertArgFn(constructor, name, true);
-      }
-
-      return $injector.instantiate(constructor, locals);
-    };
-  }];
-}
-
-/**
- * @ngdoc object
- * @name ng.$document
- * @requires $window
- *
- * @description
- * A {@link angular.element jQuery (lite)}-wrapped reference to the browser's `window.document`
- * element.
- */
-function $DocumentProvider(){
-  this.$get = ['$window', function(window){
-    return jqLite(window.document);
-  }];
-}
-
-/**
- * @ngdoc function
- * @name ng.$exceptionHandler
- * @requires $log
- *
- * @description
- * Any uncaught exception in angular expressions is delegated to this service.
- * The default implementation simply delegates to `$log.error` which logs it into
- * the browser console.
- *
- * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
- * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
- *
- * @param {Error} exception Exception associated with the error.
- * @param {string=} cause optional information about the context in which
- *       the error was thrown.
- *
- */
-function $ExceptionHandlerProvider() {
-  this.$get = ['$log', function($log) {
-    return function(exception, cause) {
-      $log.error.apply($log, arguments);
-    };
-  }];
-}
-
-/**
- * @ngdoc object
- * @name ng.$interpolateProvider
- * @function
- *
- * @description
- *
- * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
- */
-function $InterpolateProvider() {
-  var startSymbol = '{{';
-  var endSymbol = '}}';
-
-  /**
-   * @ngdoc method
-   * @name ng.$interpolateProvider#startSymbol
-   * @methodOf ng.$interpolateProvider
-   * @description
-   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
-   *
-   * @param {string=} value new value to set the starting symbol to.
-   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
-   */
-  this.startSymbol = function(value){
-    if (value) {
-      startSymbol = value;
-      return this;
-    } else {
-      return startSymbol;
-    }
-  };
-
-  /**
-   * @ngdoc method
-   * @name ng.$interpolateProvider#endSymbol
-   * @methodOf ng.$interpolateProvider
-   * @description
-   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
-   *
-   * @param {string=} value new value to set the ending symbol to.
-   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
-   */
-  this.endSymbol = function(value){
-    if (value) {
-      endSymbol = value;
-      return this;
-    } else {
-      return endSymbol;
-    }
-  };
-
-
-  this.$get = ['$parse', function($parse) {
-    var startSymbolLength = startSymbol.length,
-        endSymbolLength = endSymbol.length;
-
-    /**
-     * @ngdoc function
-     * @name ng.$interpolate
-     * @function
-     *
-     * @requires $parse
-     *
-     * @description
-     *
-     * Compiles a string with markup into an interpolation function. This service is used by the
-     * HTML {@link ng.$compile $compile} service for data binding. See
-     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
-     * interpolation markup.
-     *
-     *
-       <pre>
-         var $interpolate = ...; // injected
-         var exp = $interpolate('Hello {{name}}!');
-         expect(exp({name:'Angular'}).toEqual('Hello Angular!');
-       </pre>
-     *
-     *
-     * @param {string} text The text with markup to interpolate.
-     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
-     *    embedded expression in order to return an interpolation function. Strings with no
-     *    embedded expression will return null for the interpolation function.
-     * @returns {function(context)} an interpolation function which is used to compute the interpolated
-     *    string. The function has these parameters:
-     *
-     *    * `context`: an object against which any expressions embedded in the strings are evaluated
-     *      against.
-     *
-     */
-    function $interpolate(text, mustHaveExpression) {
-      var startIndex,
-          endIndex,
-          index = 0,
-          parts = [],
-          length = text.length,
-          hasInterpolation = false,
-          fn,
-          exp,
-          concat = [];
-
-      while(index < length) {
-        if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) &&
-             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) {
-          (index != startIndex) && parts.push(text.substring(index, startIndex));
-          parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex)));
-          fn.exp = exp;
-          index = endIndex + endSymbolLength;
-          hasInterpolation = true;
-        } else {
-          // we did not find anything, so we have to add the remainder to the parts array
-          (index != length) && parts.push(text.substring(index));
-          index = length;
-        }
-      }
-
-      if (!(length = parts.length)) {
-        // we added, nothing, must have been an empty string.
-        parts.push('');
-        length = 1;
-      }
-
-      if (!mustHaveExpression  || hasInterpolation) {
-        concat.length = length;
-        fn = function(context) {
-          for(var i = 0, ii = length, part; i<ii; i++) {
-            if (typeof (part = parts[i]) == 'function') {
-              part = part(context);
-              if (part == null || part == undefined) {
-                part = '';
-              } else if (typeof part != 'string') {
-                part = toJson(part);
-              }
-            }
-            concat[i] = part;
-          }
-          return concat.join('');
-        };
-        fn.exp = text;
-        fn.parts = parts;
-        return fn;
-      }
-    }
-
-
-    /**
-     * @ngdoc method
-     * @name ng.$interpolate#startSymbol
-     * @methodOf ng.$interpolate
-     * @description
-     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
-     *
-     * Use {@link ng.$interpolateProvider#startSymbol $interpolateProvider#startSymbol} to change
-     * the symbol.
-     *
-     * @returns {string} start symbol.
-     */
-    $interpolate.startSymbol = function() {
-      return startSymbol;
-    }
-
-
-    /**
-     * @ngdoc method
-     * @name ng.$interpolate#endSymbol
-     * @methodOf ng.$interpolate
-     * @description
-     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
-     *
-     * Use {@link ng.$interpolateProvider#endSymbol $interpolateProvider#endSymbol} to change
-     * the symbol.
-     *
-     * @returns {string} start symbol.
-     */
-    $interpolate.endSymbol = function() {
-      return endSymbol;
-    }
-
-    return $interpolate;
-  }];
-}
-
-var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?(\{?[\w\.-]*\}?)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
-    PATH_MATCH = /^([^\?#]*)?(\?([^#]*))?(#(.*))?$/,
-    HASH_MATCH = PATH_MATCH,
-    DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
-
-
-/**
- * Encode path using encodeUriSegment, ignoring forward slashes
- *
- * @param {string} path Path to encode
- * @returns {string}
- */
-function encodePath(path) {
-  var segments = path.split('/'),
-      i = segments.length;
-
-  while (i--) {
-    segments[i] = encodeUriSegment(segments[i]);
-  }
-
-  return segments.join('/');
-}
-
-function stripHash(url) {
-  return url.split('#')[0];
-}
-
-
-function matchUrl(url, obj) {
-  var match = URL_MATCH.exec(url);
-
-  match = {
-      protocol: match[1],
-      host: match[3],
-      port: int(match[5]) || DEFAULT_PORTS[match[1]] || null,
-      path: match[6] || '/',
-      search: match[8],
-      hash: match[10]
-    };
-
-  if (obj) {
-    obj.$$protocol = match.protocol;
-    obj.$$host = match.host;
-    obj.$$port = match.port;
-  }
-
-  return match;
-}
-
-
-function composeProtocolHostPort(protocol, host, port) {
-  return protocol + '://' + host + (port == DEFAULT_PORTS[protocol] ? '' : ':' + port);
-}
-
-
-function pathPrefixFromBase(basePath) {
-  return basePath.substr(0, basePath.lastIndexOf('/'));
-}
-
-
-function convertToHtml5Url(url, basePath, hashPrefix) {
-  var match = matchUrl(url);
-
-  // already html5 url
-  if (decodeURIComponent(match.path) != basePath || isUndefined(match.hash) ||
-      match.hash.indexOf(hashPrefix) !== 0) {
-    return url;
-  // convert hashbang url -> html5 url
-  } else {
-    return composeProtocolHostPort(match.protocol, match.host, match.port) +
-           pathPrefixFromBase(basePath) + match.hash.substr(hashPrefix.length);
-  }
-}
-
-
-function convertToHashbangUrl(url, basePath, hashPrefix) {
-  var match = matchUrl(url);
-
-  // already hashbang url
-  if (decodeURIComponent(match.path) == basePath && !isUndefined(match.hash) &&
-      match.hash.indexOf(hashPrefix) === 0) {
-    return url;
-  // convert html5 url -> hashbang url
-  } else {
-    var search = match.search && '?' + match.search || '',
-        hash = match.hash && '#' + match.hash || '',
-        pathPrefix = pathPrefixFromBase(basePath),
-        path = match.path.substr(pathPrefix.length);
-
-    if (match.path.indexOf(pathPrefix) !== 0) {
-      throw Error('Invalid url "' + url + '", missing path prefix "' + pathPrefix + '" !');
-    }
-
-    return composeProtocolHostPort(match.protocol, match.host, match.port) + basePath +
-           '#' + hashPrefix + path + search + hash;
-  }
-}
-
-
-/**
- * LocationUrl represents an url
- * This object is exposed as $location service when HTML5 mode is enabled and supported
- *
- * @constructor
- * @param {string} url HTML5 url
- * @param {string} pathPrefix
- */
-function LocationUrl(url, pathPrefix, appBaseUrl) {
-  pathPrefix = pathPrefix || '';
-
-  /**
-   * Parse given html5 (regular) url string into properties
-   * @param {string} newAbsoluteUrl HTML5 url
-   * @private
-   */
-  this.$$parse = function(newAbsoluteUrl) {
-    var match = matchUrl(newAbsoluteUrl, this);
-
-    if (match.path.indexOf(pathPrefix) !== 0) {
-      throw Error('Invalid url "' + newAbsoluteUrl + '", missing path prefix "' + pathPrefix + '" !');
-    }
-
-    this.$$path = decodeURIComponent(match.path.substr(pathPrefix.length));
-    this.$$search = parseKeyValue(match.search);
-    this.$$hash = match.hash && decodeURIComponent(match.hash) || '';
-
-    this.$$compose();
-  };
-
-  /**
-   * Compose url and update `absUrl` property
-   * @private
-   */
-  this.$$compose = function() {
-    var search = toKeyValue(this.$$search),
-        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
-
-    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
-    this.$$absUrl = composeProtocolHostPort(this.$$protocol, this.$$host, this.$$port) +
-                    pathPrefix + this.$$url;
-  };
-
-
-  this.$$rewriteAppUrl = function(absoluteLinkUrl) {
-    if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
-      return absoluteLinkUrl;
-    }
-  }
-
-
-  this.$$parse(url);
-}
-
-
-/**
- * LocationHashbangUrl represents url
- * This object is exposed as $location service when html5 history api is disabled or not supported
- *
- * @constructor
- * @param {string} url Legacy url
- * @param {string} hashPrefix Prefix for hash part (containing path and search)
- */
-function LocationHashbangUrl(url, hashPrefix, appBaseUrl) {
-  var basePath;
-
-  /**
-   * Parse given hashbang url into properties
-   * @param {string} url Hashbang url
-   * @private
-   */
-  this.$$parse = function(url) {
-    var match = matchUrl(url, this);
-
-
-    if (match.hash && match.hash.indexOf(hashPrefix) !== 0) {
-      throw Error('Invalid url "' + url + '", missing hash prefix "' + hashPrefix + '" !');
-    }
-
-    basePath = match.path + (match.search ? '?' + match.search : '');
-    match = HASH_MATCH.exec((match.hash || '').substr(hashPrefix.length));
-    if (match[1]) {
-      this.$$path = (match[1].charAt(0) == '/' ? '' : '/') + decodeURIComponent(match[1]);
-    } else {
-      this.$$path = '';
-    }
-
-    this.$$search = parseKeyValue(match[3]);
-    this.$$hash = match[5] && decodeURIComponent(match[5]) || '';
-
-    this.$$compose();
-  };
-
-  /**
-   * Compose hashbang url and update `absUrl` property
-   * @private
-   */
-  this.$$compose = function() {
-    var search = toKeyValue(this.$$search),
-        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
-
-    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
-    this.$$absUrl = composeProtocolHostPort(this.$$protocol, this.$$host, this.$$port) +
-                    basePath + (this.$$url ? '#' + hashPrefix + this.$$url : '');
-  };
-
-  this.$$rewriteAppUrl = function(absoluteLinkUrl) {
-    if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
-      return absoluteLinkUrl;
-    }
-  }
-
-
-  this.$$parse(url);
-}
-
-
-LocationUrl.prototype = {
-
-  /**
-   * Has any change been replacing ?
-   * @private
-   */
-  $$replace: false,
-
-  /**
-   * @ngdoc method
-   * @name ng.$location#absUrl
-   * @methodOf ng.$location
-   *
-   * @description
-   * This method is getter only.
-   *
-   * Return full url representation with all segments encoded according to rules specified in
-   * {@link http://www.ietf.org/rfc/rfc3986.txt RFC 3986}.
-   *
-   * @return {string} full url
-   */
-  absUrl: locationGetter('$$absUrl'),
-
-  /**
-   * @ngdoc method
-   * @name ng.$location#url
-   * @methodOf ng.$location
-   *
-   * @description
-   * This method is getter / setter.
-   *
-   * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
-   *
-   * Change path, search and hash, when called with parameter and return `$location`.
-   *
-   * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
-   * @return {string} url
-   */
-  url: function(url, replace) {
-    if (isUndefined(url))
-      return this.$$url;
-
-    var match = PATH_MATCH.exec(url);
-    if (match[1]) this.path(decodeURIComponent(match[1]));
-    if (match[2] || match[1]) this.search(match[3] || '');
-    this.hash(match[5] || '', replace);
-
-    return this;
-  },
-
-  /**
-   * @ngdoc method
-   * @name ng.$location#protocol
-   * @methodOf ng.$location
-   *
-   * @description
-   * This method is getter only.
-   *
-   * Return protocol of current url.
-   *
-   * @return {string} protocol of current url
-   */
-  protocol: locationGetter('$$protocol'),
-
-  /**
-   * @ngdoc method
-   * @name ng.$location#host
-   * @methodOf ng.$location
-   *
-   * @description
-   * This method is getter only.
-   *
-   * Return host of current url.
-   *
-   * @return {string} host of current url.
-   */
-  host: locationGetter('$$host'),
-
-  /**
-   * @ngdoc method
-   * @name ng.$location#port
-   * @methodOf ng.$location
-   *
-   * @description
-   * This method is getter only.
-   *
-   * Return port of current url.
-   *
-   * @return {Number} port
-   */
-  port: locationGetter('$$port'),
-
-  /**
-   * @ngdoc method
-   * @name ng.$location#path
-   * @methodOf ng.$location
-   *
-   * @description
-   * This method is getter / setter.
-   *
-   * Return path of current url when called without any parameter.
-   *
-   * Change path when called with parameter and return `$location`.
-   *
-   * Note: Path should always begin with forward slash (/), this method will add the forward slash
-   * if it is missing.
-   *
-   * @param {string=} path New path
-   * @return {string} path
-   */
-  path: locationGetterSetter('$$path', function(path) {
-    return path.charAt(0) == '/' ? path : '/' + path;
-  }),
-
-  /**
-   * @ngdoc method
-   * @name ng.$location#search
-   * @methodOf ng.$location
-   *
-   * @description
-   * This method is getter / setter.
-   *
-   * Return search part (as object) of current url when called without any parameter.
-   *
-   * Change search part when called with parameter and return `$location`.
-   *
-   * @param {string|object<string,string>=} search New search params - string or hash object
-   * @param {string=} paramValue If `search` is a string, then `paramValue` will override only a
-   *    single search parameter. If the value is `null`, the parameter will be deleted.
-   *
-   * @return {string} search
-   */
-  search: function(search, paramValue) {
-    if (isUndefined(search))
-      return this.$$search;
-
-    if (isDefined(paramValue)) {
-      if (paramValue === null) {
-        delete this.$$search[search];
-      } else {
-        this.$$search[search] = paramValue;
-      }
-    } else {
-      this.$$search = isString(search) ? parseKeyValue(search) : search;
-    }
-
-    this.$$compose();
-    return this;
-  },
-
-  /**
-   * @ngdoc method
-   * @name ng.$location#hash
-   * @methodOf ng.$location
-   *
-   * @description
-   * This method is getter / setter.
-   *
-   * Return hash fragment when called without any parameter.
-   *
-   * Change hash fragment when called with parameter and return `$location`.
-   *
-   * @param {string=} hash New hash fragment
-   * @return {string} hash
-   */
-  hash: locationGetterSetter('$$hash', identity),
-
-  /**
-   * @ngdoc method
-   * @name ng.$location#replace
-   * @methodOf ng.$location
-   *
-   * @description
-   * If called, all changes to $location during current `$digest` will be replacing current history
-   * record, instead of adding new one.
-   */
-  replace: function() {
-    this.$$replace = true;
-    return this;
-  }
-};
-
-LocationHashbangUrl.prototype = inherit(LocationUrl.prototype);
-
-function LocationHashbangInHtml5Url(url, hashPrefix, appBaseUrl, baseExtra) {
-  LocationHashbangUrl.apply(this, arguments);
-
-
-  this.$$rewriteAppUrl = function(absoluteLinkUrl) {
-    if (absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
-      return appBaseUrl + baseExtra + '#' + hashPrefix  + absoluteLinkUrl.substr(appBaseUrl.length);
-    }
-  }
-}
-
-LocationHashbangInHtml5Url.prototype = inherit(LocationHashbangUrl.prototype);
-
-function locationGetter(property) {
-  return function() {
-    return this[property];
-  };
-}
-
-
-function locationGetterSetter(property, preprocess) {
-  return function(value) {
-    if (isUndefined(value))
-      return this[property];
-
-    this[property] = preprocess(value);
-    this.$$compose();
-
-    return this;
-  };
-}
-
-
-/**
- * @ngdoc object
- * @name ng.$location
- *
- * @requires $browser
- * @requires $sniffer
- * @requires $rootElement
- *
- * @description
- * The $location service parses the URL in the browser address bar (based on the
- * {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL
- * available to your application. Changes to the URL in the address bar are reflected into
- * $location service and changes to $location are reflected into the browser address bar.
- *
- * **The $location service:**
- *
- * - Exposes the current URL in the browser address bar, so you can
- *   - Watch and observe the URL.
- *   - Change the URL.
- * - Synchronizes the URL with the browser when the user
- *   - Changes the address bar.
- *   - Clicks the back or forward button (or clicks a History link).
- *   - Clicks on a link.
- * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
- *
- * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular
- * Services: Using $location}
- */
-
-/**
- * @ngdoc object
- * @name ng.$locationProvider
- * @description
- * Use the `$locationProvider` to configure how the application deep linking paths are stored.
- */
-function $LocationProvider(){
-  var hashPrefix = '',
-      html5Mode = false;
-
-  /**
-   * @ngdoc property
-   * @name ng.$locationProvider#hashPrefix
-   * @methodOf ng.$locationProvider
-   * @description
-   * @param {string=} prefix Prefix for hash part (containing path and search)
-   * @returns {*} current value if used as getter or itself (chaining) if used as setter
-   */
-  this.hashPrefix = function(prefix) {
-    if (isDefined(prefix)) {
-      hashPrefix = prefix;
-      return this;
-    } else {
-      return hashPrefix;
-    }
-  };
-
-  /**
-   * @ngdoc property
-   * @name ng.$locationProvider#html5Mode
-   * @methodOf ng.$locationProvider
-   * @description
-   * @param {string=} mode Use HTML5 strategy if available.
-   * @returns {*} current value if used as getter or itself (chaining) if used as setter
-   */
-  this.html5Mode = function(mode) {
-    if (isDefined(mode)) {
-      html5Mode = mode;
-      return this;
-    } else {
-      return html5Mode;
-    }
-  };
-
-  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
-      function( $rootScope,   $browser,   $sniffer,   $rootElement) {
-    var $location,
-        basePath,
-        pathPrefix,
-        initUrl = $browser.url(),
-        initUrlParts = matchUrl(initUrl),
-        appBaseUrl;
-
-    if (html5Mode) {
-      basePath = $browser.baseHref() || '/';
-      pathPrefix = pathPrefixFromBase(basePath);
-      appBaseUrl =
-          composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) +
-          pathPrefix + '/';
-
-      if ($sniffer.history) {
-        $location = new LocationUrl(
-          convertToHtml5Url(initUrl, basePath, hashPrefix),
-          pathPrefix, appBaseUrl);
-      } else {
-        $location = new LocationHashbangInHtml5Url(
-          convertToHashbangUrl(initUrl, basePath, hashPrefix),
-          hashPrefix, appBaseUrl, basePath.substr(pathPrefix.length + 1));
-      }
-    } else {
-      appBaseUrl =
-          composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) +
-          (initUrlParts.path || '') +
-          (initUrlParts.search ? ('?' + initUrlParts.search) : '') +
-          '#' + hashPrefix + '/';
-
-      $location = new LocationHashbangUrl(initUrl, hashPrefix, appBaseUrl);
-    }
-
-    $rootElement.bind('click', function(event) {
-      // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
-      // currently we open nice url link and redirect then
-
-      if (event.ctrlKey || event.metaKey || event.which == 2) return;
-
-      var elm = jqLite(event.target);
-
-      // traverse the DOM up to find first A tag
-      while (lowercase(elm[0].nodeName) !== 'a') {
-        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
-        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
-      }
-
-      var absHref = elm.prop('href'),
-          rewrittenUrl = $location.$$rewriteAppUrl(absHref);
-
-      if (absHref && !elm.attr('target') && rewrittenUrl) {
-        // update location manually
-        $location.$$parse(rewrittenUrl);
-        $rootScope.$apply();
-        event.preventDefault();
-        // hack to work around FF6 bug 684208 when scenario runner clicks on links
-        window.angular['ff-684208-preventDefault'] = true;
-      }
-    });
-
-
-    // rewrite hashbang url <> html5 url
-    if ($location.absUrl() != initUrl) {
-      $browser.url($location.absUrl(), true);
-    }
-
-    // update $location when $browser url changes
-    $browser.onUrlChange(function(newUrl) {
-      if ($location.absUrl() != newUrl) {
-        if ($rootScope.$broadcast('$locationChangeStart', newUrl, $location.absUrl()).defaultPrevented) {
-          $browser.url($location.absUrl());
-          return;
-        }
-        $rootScope.$evalAsync(function() {
-          var oldUrl = $location.absUrl();
-
-          $location.$$parse(newUrl);
-          afterLocationChange(oldUrl);
-        });
-        if (!$rootScope.$$phase) $rootScope.$digest();
-      }
-    });
-
-    // update browser
-    var changeCounter = 0;
-    $rootScope.$watch(function $locationWatch() {
-      var oldUrl = $browser.url();
-      var currentReplace = $location.$$replace;
-
-      if (!changeCounter || oldUrl != $location.absUrl()) {
-        changeCounter++;
-        $rootScope.$evalAsync(function() {
-          if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl).
-              defaultPrevented) {
-            $location.$$parse(oldUrl);
-          } else {
-            $browser.url($location.absUrl(), currentReplace);
-            afterLocationChange(oldUrl);
-          }
-        });
-      }
-      $location.$$replace = false;
-
-      return changeCounter;
-    });
-
-    return $location;
-
-    function afterLocationChange(oldUrl) {
-      $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl);
-    }
-}];
-}
-
-/**
- * @ngdoc object
- * @name ng.$log
- * @requires $window
- *
- * @description
- * Simple service for logging. Default implementation writes the message
- * into the browser's console (if present).
- *
- * The main purpose of this service is to simplify debugging and troubleshooting.
- *
- * @example
-   <example>
-     <file name="script.js">
-       function LogCtrl($scope, $log) {
-         $scope.$log = $log;
-         $scope.message = 'Hello World!';
-       }
-     </file>
-     <file name="index.html">
-       <div ng-controller="LogCtrl">
-         <p>Reload this page with open console, enter text and hit the log button...</p>
-         Message:
-         <input type="text" ng-model="message"/>
-         <button ng-click="$log.log(message)">log</button>
-         <button ng-click="$log.warn(message)">warn</button>
-         <button ng-click="$log.info(message)">info</button>
-         <button ng-click="$log.error(message)">error</button>
-       </div>
-     </file>
-   </example>
- */
-
-function $LogProvider(){
-  this.$get = ['$window', function($window){
-    return {
-      /**
-       * @ngdoc method
-       * @name ng.$log#log
-       * @methodOf ng.$log
-       *
-       * @description
-       * Write a log message
-       */
-      log: consoleLog('log'),
-
-      /**
-       * @ngdoc method
-       * @name ng.$log#warn
-       * @methodOf ng.$log
-       *
-       * @description
-       * Write a warning message
-       */
-      warn: consoleLog('warn'),
-
-      /**
-       * @ngdoc method
-       * @name ng.$log#info
-       * @methodOf ng.$log
-       *
-       * @description
-       * Write an information message
-       */
-      info: consoleLog('info'),
-
-      /**
-       * @ngdoc method
-       * @name ng.$log#error
-       * @methodOf ng.$log
-       *
-       * @description
-       * Write an error message
-       */
-      error: consoleLog('error')
-    };
-
-    function formatError(arg) {
-      if (arg instanceof Error) {
-        if (arg.stack) {
-          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
-              ? 'Error: ' + arg.message + '\n' + arg.stack
-              : arg.stack;
-        } else if (arg.sourceURL) {
-          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
-        }
-      }
-      return arg;
-    }
-
-    function consoleLog(type) {
-      var console = $window.console || {},
-          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);
-        };
-      }
-
-      // we are IE which either doesn't have window.console => this is noop and we do nothing,
-      // or we are IE where console.log doesn't have apply so we log at least first 2 args
-      return function(arg1, arg2) {
-        logFn(arg1, arg2);
-      }
-    }
-  }];
-}
-
-var OPERATORS = {
-    'null':function(){return null;},
-    'true':function(){return true;},
-    'false':function(){return false;},
-    undefined:noop,
-    '+':function(self, locals, a,b){
-      a=a(self, locals); b=b(self, locals);
-      if (isDefined(a)) {
-        if (isDefined(b)) {
-          return a + b;
-        }
-        return a;
-      }
-      return isDefined(b)?b:undefined;},
-    '-':function(self, locals, a,b){a=a(self, locals); b=b(self, locals); return (isDefined(a)?a:0)-(isDefined(b)?b:0);},
-    '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);},
-    '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);},
-    '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);},
-    '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);},
-    '=':noop,
-    '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);},
-    '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);},
-    '<':function(self, locals, a,b){return a(self, locals)<b(self, locals);},
-    '>':function(self, locals, a,b){return a(self, locals)>b(self, locals);},
-    '<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);},
-    '>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);},
-    '&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);},
-    '||':function(self, locals, a,b){return a(self, locals)||b(self, locals);},
-    '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);},
-//    '|':function(self, locals, a,b){return a|b;},
-    '|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));},
-    '!':function(self, locals, a){return !a(self, locals);}
-};
-var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
-
-function lex(text, csp){
-  var tokens = [],
-      token,
-      index = 0,
-      json = [],
-      ch,
-      lastCh = ':'; // can start regexp
-
-  while (index < text.length) {
-    ch = text.charAt(index);
-    if (is('"\'')) {
-      readString(ch);
-    } else if (isNumber(ch) || is('.') && isNumber(peek())) {
-      readNumber();
-    } else if (isIdent(ch)) {
-      readIdent();
-      // identifiers can only be if the preceding char was a { or ,
-      if (was('{,') && json[0]=='{' &&
-         (token=tokens[tokens.length-1])) {
-        token.json = token.text.indexOf('.') == -1;
-      }
-    } else if (is('(){}[].,;:')) {
-      tokens.push({
-        index:index,
-        text:ch,
-        json:(was(':[,') && is('{[')) || is('}]:,')
-      });
-      if (is('{[')) json.unshift(ch);
-      if (is('}]')) json.shift();
-      index++;
-    } else if (isWhitespace(ch)) {
-      index++;
-      continue;
-    } else {
-      var ch2 = ch + peek(),
-          fn = OPERATORS[ch],
-          fn2 = OPERATORS[ch2];
-      if (fn2) {
-        tokens.push({index:index, text:ch2, fn:fn2});
-        index += 2;
-      } else if (fn) {
-        tokens.push({index:index, text:ch, fn:fn, json: was('[,:') && is('+-')});
-        index += 1;
-      } else {
-        throwError("Unexpected next character ", index, index+1);
-      }
-    }
-    lastCh = ch;
-  }
-  return tokens;
-
-  function is(chars) {
-    return chars.indexOf(ch) != -1;
-  }
-
-  function was(chars) {
-    return chars.indexOf(lastCh) != -1;
-  }
-
-  function peek() {
-    return index + 1 < text.length ? text.charAt(index + 1) : false;
-  }
-  function isNumber(ch) {
-    return '0' <= ch && ch <= '9';
-  }
-  function isWhitespace(ch) {
-    return ch == ' ' || ch == '\r' || ch == '\t' ||
-           ch == '\n' || ch == '\v' || ch == '\u00A0'; // IE treats non-breaking space as \u00A0
-  }
-  function isIdent(ch) {
-    return 'a' <= ch && ch <= 'z' ||
-           'A' <= ch && ch <= 'Z' ||
-           '_' == ch || ch == '$';
-  }
-  function isExpOperator(ch) {
-    return ch == '-' || ch == '+' || isNumber(ch);
-  }
-
-  function throwError(error, start, end) {
-    end = end || index;
-    throw Error("Lexer Error: " + error + " at column" +
-        (isDefined(start)
-            ? "s " + start +  "-" + index + " [" + text.substring(start, end) + "]"
-            : " " + end) +
-        " in expression [" + text + "].");
-  }
-
-  function readNumber() {
-    var number = "";
-    var start = index;
-    while (index < text.length) {
-      var ch = lowercase(text.charAt(index));
-      if (ch == '.' || isNumber(ch)) {
-        number += ch;
-      } else {
-        var peekCh = peek();
-        if (ch == 'e' && isExpOperator(peekCh)) {
-          number += ch;
-        } else if (isExpOperator(ch) &&
-            peekCh && isNumber(peekCh) &&
-            number.charAt(number.length - 1) == 'e') {
-          number += ch;
-        } else if (isExpOperator(ch) &&
-            (!peekCh || !isNumber(peekCh)) &&
-            number.charAt(number.length - 1) == 'e') {
-          throwError('Invalid exponent');
-        } else {
-          break;
-        }
-      }
-      index++;
-    }
-    number = 1 * number;
-    tokens.push({index:start, text:number, json:true,
-      fn:function() {return number;}});
-  }
-  function readIdent() {
-    var ident = "",
-        start = index,
-        lastDot, peekIndex, methodName, ch;
-
-    while (index < text.length) {
-      ch = text.charAt(index);
-      if (ch == '.' || isIdent(ch) || isNumber(ch)) {
-        if (ch == '.') lastDot = index;
-        ident += ch;
-      } else {
-        break;
-      }
-      index++;
-    }
-
-    //check if this is not a method invocation and if it is back out to last dot
-    if (lastDot) {
-      peekIndex = index;
-      while(peekIndex < text.length) {
-        ch = text.charAt(peekIndex);
-        if (ch == '(') {
-          methodName = ident.substr(lastDot - start + 1);
-          ident = ident.substr(0, lastDot - start);
-          index = peekIndex;
-          break;
-        }
-        if(isWhitespace(ch)) {
-          peekIndex++;
-        } else {
-          break;
-        }
-      }
-    }
-
-
-    var token = {
-      index:start,
-      text:ident
-    };
-
-    if (OPERATORS.hasOwnProperty(ident)) {
-      token.fn = token.json = OPERATORS[ident];
-    } else {
-      var getter = getterFn(ident, csp);
-      token.fn = extend(function(self, locals) {
-        return (getter(self, locals));
-      }, {
-        assign: function(self, value) {
-          return setter(self, ident, value);
-        }
-      });
-    }
-
-    tokens.push(token);
-
-    if (methodName) {
-      tokens.push({
-        index:lastDot,
-        text: '.',
-        json: false
-      });
-      tokens.push({
-        index: lastDot + 1,
-        text: methodName,
-        json: false
-      });
-    }
-  }
-
-  function readString(quote) {
-    var start = index;
-    index++;
-    var string = "";
-    var rawString = quote;
-    var escape = false;
-    while (index < text.length) {
-      var ch = text.charAt(index);
-      rawString += ch;
-      if (escape) {
-        if (ch == 'u') {
-          var hex = text.substring(index + 1, index + 5);
-          if (!hex.match(/[\da-f]{4}/i))
-            throwError( "Invalid unicode escape [\\u" + hex + "]");
-          index += 4;
-          string += String.fromCharCode(parseInt(hex, 16));
-        } else {
-          var rep = ESCAPE[ch];
-          if (rep) {
-            string += rep;
-          } else {
-            string += ch;
-          }
-        }
-        escape = false;
-      } else if (ch == '\\') {
-        escape = true;
-      } else if (ch == quote) {
-        index++;
-        tokens.push({
-          index:start,
-          text:rawString,
-          string:string,
-          json:true,
-          fn:function() { return string; }
-        });
-        return;
-      } else {
-        string += ch;
-      }
-      index++;
-    }
-    throwError("Unterminated quote", start);
-  }
-}
-
-/////////////////////////////////////////
-
-function parser(text, json, $filter, csp){
-  var ZERO = valueFn(0),
-      value,
-      tokens = lex(text, csp),
-      assignment = _assignment,
-      functionCall = _functionCall,
-      fieldAccess = _fieldAccess,
-      objectIndex = _objectIndex,
-      filterChain = _filterChain;
-
-  if(json){
-    // The extra level of aliasing is here, just in case the lexer misses something, so that
-    // we prevent any accidental execution in JSON.
-    assignment = logicalOR;
-    functionCall =
-      fieldAccess =
-      objectIndex =
-      filterChain =
-        function() { throwError("is not valid json", {text:text, index:0}); };
-    value = primary();
-  } else {
-    value = statements();
-  }
-  if (tokens.length !== 0) {
-    throwError("is an unexpected token", tokens[0]);
-  }
-  return value;
-
-  ///////////////////////////////////
-  function throwError(msg, token) {
-    throw Error("Syntax Error: Token '" + token.text +
-      "' " + msg + " at column " +
-      (token.index + 1) + " of the expression [" +
-      text + "] starting at [" + text.substring(token.index) + "].");
-  }
-
-  function peekToken() {
-    if (tokens.length === 0)
-      throw Error("Unexpected end of expression: " + text);
-    return tokens[0];
-  }
-
-  function peek(e1, e2, e3, e4) {
-    if (tokens.length > 0) {
-      var token = tokens[0];
-      var t = token.text;
-      if (t==e1 || t==e2 || t==e3 || t==e4 ||
-          (!e1 && !e2 && !e3 && !e4)) {
-        return token;
-      }
-    }
-    return false;
-  }
-
-  function expect(e1, e2, e3, e4){
-    var token = peek(e1, e2, e3, e4);
-    if (token) {
-      if (json && !token.json) {
-        throwError("is not valid json", token);
-      }
-      tokens.shift();
-      return token;
-    }
-    return false;
-  }
-
-  function consume(e1){
-    if (!expect(e1)) {
-      throwError("is unexpected, expecting [" + e1 + "]", peek());
-    }
-  }
-
-  function unaryFn(fn, right) {
-    return function(self, locals) {
-      return fn(self, locals, right);
-    };
-  }
-
-  function binaryFn(left, fn, right) {
-    return function(self, locals) {
-      return fn(self, locals, left, right);
-    };
-  }
-
-  function statements() {
-    var statements = [];
-    while(true) {
-      if (tokens.length > 0 && !peek('}', ')', ';', ']'))
-        statements.push(filterChain());
-      if (!expect(';')) {
-        // optimize for the common case where there is only one statement.
-        // TODO(size): maybe we should not support multiple statements?
-        return statements.length == 1
-          ? statements[0]
-          : function(self, locals){
-            var value;
-            for ( var i = 0; i < statements.length; i++) {
-              var statement = statements[i];
-              if (statement)
-                value = statement(self, locals);
-            }
-            return value;
-          };
-      }
-    }
-  }
-
-  function _filterChain() {
-    var left = expression();
-    var token;
-    while(true) {
-      if ((token = expect('|'))) {
-        left = binaryFn(left, token.fn, filter());
-      } else {
-        return left;
-      }
-    }
-  }
-
-  function filter() {
-    var token = expect();
-    var fn = $filter(token.text);
-    var argsFn = [];
-    while(true) {
-      if ((token = expect(':'))) {
-        argsFn.push(expression());
-      } else {
-        var fnInvoke = function(self, locals, input){
-          var args = [input];
-          for ( var i = 0; i < argsFn.length; i++) {
-            args.push(argsFn[i](self, locals));
-          }
-          return fn.apply(self, args);
-        };
-        return function() {
-          return fnInvoke;
-        };
-      }
-    }
-  }
-
-  function expression() {
-    return assignment();
-  }
-
-  function _assignment() {
-    var left = logicalOR();
-    var right;
-    var token;
-    if ((token = expect('='))) {
-      if (!left.assign) {
-        throwError("implies assignment but [" +
-          text.substring(0, token.index) + "] can not be assigned to", token);
-      }
-      right = logicalOR();
-      return function(scope, locals){
-        return left.assign(scope, right(scope, locals), locals);
-      };
-    } else {
-      return left;
-    }
-  }
-
-  function logicalOR() {
-    var left = logicalAND();
-    var token;
-    while(true) {
-      if ((token = expect('||'))) {
-        left = binaryFn(left, token.fn, logicalAND());
-      } else {
-        return left;
-      }
-    }
-  }
-
-  function logicalAND() {
-    var left = equality();
-    var token;
-    if ((token = expect('&&'))) {
-      left = binaryFn(left, token.fn, logicalAND());
-    }
-    return left;
-  }
-
-  function equality() {
-    var left = relational();
-    var token;
-    if ((token = expect('==','!='))) {
-      left = binaryFn(left, token.fn, equality());
-    }
-    return left;
-  }
-
-  function relational() {
-    var left = additive();
-    var token;
-    if ((token = expect('<', '>', '<=', '>='))) {
-      left = binaryFn(left, token.fn, relational());
-    }
-    return left;
-  }
-
-  function additive() {
-    var left = multiplicative();
-    var token;
-    while ((token = expect('+','-'))) {
-      left = binaryFn(left, token.fn, multiplicative());
-    }
-    return left;
-  }
-
-  function multiplicative() {
-    var left = unary();
-    var token;
-    while ((token = expect('*','/','%'))) {
-      left = binaryFn(left, token.fn, unary());
-    }
-    return left;
-  }
-
-  function unary() {
-    var token;
-    if (expect('+')) {
-      return primary();
-    } else if ((token = expect('-'))) {
-      return binaryFn(ZERO, token.fn, unary());
-    } else if ((token = expect('!'))) {
-      return unaryFn(token.fn, unary());
-    } else {
-      return primary();
-    }
-  }
-
-
-  function primary() {
-    var primary;
-    if (expect('(')) {
-      primary = filterChain();
-      consume(')');
-    } else if (expect('[')) {
-      primary = arrayDeclaration();
-    } else if (expect('{')) {
-      primary = object();
-    } else {
-      var token = expect();
-      primary = token.fn;
-      if (!primary) {
-        throwError("not a primary expression", token);
-      }
-    }
-
-    var next, context;
-    while ((next = expect('(', '[', '.'))) {
-      if (next.text === '(') {
-        primary = functionCall(primary, context);
-        context = null;
-      } else if (next.text === '[') {
-        context = primary;
-        primary = objectIndex(primary);
-      } else if (next.text === '.') {
-        context = primary;
-        primary = fieldAccess(primary);
-      } else {
-        throwError("IMPOSSIBLE");
-      }
-    }
-    return primary;
-  }
-
-  function _fieldAccess(object) {
-    var field = expect().text;
-    var getter = getterFn(field, csp);
-    return extend(
-        function(scope, locals, self) {
-          return getter(self || object(scope, locals), locals);
-        },
-        {
-          assign:function(scope, value, locals) {
-            return setter(object(scope, locals), field, value);
-          }
-        }
-    );
-  }
-
-  function _objectIndex(obj) {
-    var indexFn = expression();
-    consume(']');
-    return extend(
-      function(self, locals){
-        var o = obj(self, locals),
-            i = indexFn(self, locals),
-            v, p;
-
-        if (!o) return undefined;
-        v = o[i];
-        if (v && v.then) {
-          p = v;
-          if (!('$$v' in v)) {
-            p.$$v = undefined;
-            p.then(function(val) { p.$$v = val; });
-          }
-          v = v.$$v;
-        }
-        return v;
-      }, {
-        assign:function(self, value, locals){
-          return obj(self, locals)[indexFn(self, locals)] = value;
-        }
-      });
-  }
-
-  function _functionCall(fn, contextGetter) {
-    var argsFn = [];
-    if (peekToken().text != ')') {
-      do {
-        argsFn.push(expression());
-      } while (expect(','));
-    }
-    consume(')');
-    return function(scope, locals){
-      var args = [],
-          context = contextGetter ? contextGetter(scope, locals) : scope;
-
-      for ( var i = 0; i < argsFn.length; i++) {
-        args.push(argsFn[i](scope, locals));
-      }
-      var fnPtr = fn(scope, locals, context) || noop;
-      // IE stupidity!
-      return fnPtr.apply
-          ? fnPtr.apply(context, args)
-          : fnPtr(args[0], args[1], args[2], args[3], args[4]);
-    };
-  }
-
-  // This is used with json array declaration
-  function arrayDeclaration () {
-    var elementFns = [];
-    if (peekToken().text != ']') {
-      do {
-        elementFns.push(expression());
-      } while (expect(','));
-    }
-    consume(']');
-    return function(self, locals){
-      var array = [];
-      for ( var i = 0; i < elementFns.length; i++) {
-        array.push(elementFns[i](self, locals));
-      }
-      return array;
-    };
-  }
-
-  function object () {
-    var keyValues = [];
-    if (peekToken().text != '}') {
-      do {
-        var token = expect(),
-        key = token.string || token.text;
-        consume(":");
-        var value = expression();
-        keyValues.push({key:key, value:value});
-      } while (expect(','));
-    }
-    consume('}');
-    return function(self, locals){
-      var object = {};
-      for ( var i = 0; i < keyValues.length; i++) {
-        var keyValue = keyValues[i];
-        object[keyValue.key] = keyValue.value(self, locals);
-      }
-      return object;
-    };
-  }
-}
-
-//////////////////////////////////////////////////
-// Parser helper functions
-//////////////////////////////////////////////////
-
-function setter(obj, path, setValue) {
-  var element = path.split('.');
-  for (var i = 0; element.length > 1; i++) {
-    var key = element.shift();
-    var propertyObj = obj[key];
-    if (!propertyObj) {
-      propertyObj = {};
-      obj[key] = propertyObj;
-    }
-    obj = propertyObj;
-  }
-  obj[element.shift()] = setValue;
-  return setValue;
-}
-
-/**
- * Return the value accesible from the object by path. Any undefined traversals are ignored
- * @param {Object} obj starting object
- * @param {string} path path to traverse
- * @param {boolean=true} bindFnToScope
- * @returns value as accesbile by path
- */
-//TODO(misko): this function needs to be removed
-function getter(obj, path, bindFnToScope) {
-  if (!path) return obj;
-  var keys = path.split('.');
-  var key;
-  var lastInstance = obj;
-  var len = keys.length;
-
-  for (var i = 0; i < len; i++) {
-    key = keys[i];
-    if (obj) {
-      obj = (lastInstance = obj)[key];
-    }
-  }
-  if (!bindFnToScope && isFunction(obj)) {
-    return bind(lastInstance, obj);
-  }
-  return obj;
-}
-
-var getterFnCache = {};
-
-/**
- * Implementation of the "Black Hole" variant from:
- * - http://jsperf.com/angularjs-parse-getter/4
- * - http://jsperf.com/path-evaluation-simplified/7
- */
-function cspSafeGetterFn(key0, key1, key2, key3, key4) {
-  return function(scope, locals) {
-    var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope,
-        promise;
-
-    if (pathVal === null || pathVal === undefined) return pathVal;
-
-    pathVal = pathVal[key0];
-    if (pathVal && pathVal.then) {
-      if (!("$$v" in pathVal)) {
-        promise = pathVal;
-        promise.$$v = undefined;
-        promise.then(function(val) { promise.$$v = val; });
-      }
-      pathVal = pathVal.$$v;
-    }
-    if (!key1 || pathVal === null || pathVal === undefined) return pathVal;
-
-    pathVal = pathVal[key1];
-    if (pathVal && pathVal.then) {
-      if (!("$$v" in pathVal)) {
-        promise = pathVal;
-        promise.$$v = undefined;
-        promise.then(function(val) { promise.$$v = val; });
-      }
-      pathVal = pathVal.$$v;
-    }
-    if (!key2 || pathVal === null || pathVal === undefined) return pathVal;
-
-    pathVal = pathVal[key2];
-    if (pathVal && pathVal.then) {
-      if (!("$$v" in pathVal)) {
-        promise = pathVal;
-        promise.$$v = undefined;
-        promise.then(function(val) { promise.$$v = val; });
-      }
-      pathVal = pathVal.$$v;
-    }
-    if (!key3 || pathVal === null || pathVal === undefined) return pathVal;
-
-    pathVal = pathVal[key3];
-    if (pathVal && pathVal.then) {
-      if (!("$$v" in pathVal)) {
-        promise = pathVal;
-        promise.$$v = undefined;
-        promise.then(function(val) { promise.$$v = val; });
-      }
-      pathVal = pathVal.$$v;
-    }
-    if (!key4 || pathVal === null || pathVal === undefined) return pathVal;
-
-    pathVal = pathVal[key4];
-    if (pathVal && pathVal.then) {
-      if (!("$$v" in pathVal)) {
-        promise = pathVal;
-        promise.$$v = undefined;
-        promise.then(function(val) { promise.$$v = val; });
-      }
-      pathVal = pathVal.$$v;
-    }
-    return pathVal;
-  };
-}
-
-function getterFn(path, csp) {
-  if (getterFnCache.hasOwnProperty(path)) {
-    return getterFnCache[path];
-  }
-
-  var pathKeys = path.split('.'),
-      pathKeysLength = pathKeys.length,
-      fn;
-
-  if (csp) {
-    fn = (pathKeysLength < 6)
-        ? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4])
-        : function(scope, locals) {
-          var i = 0, val;
-          do {
-            val = cspSafeGetterFn(
-                    pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++]
-                  )(scope, locals);
-
-            locals = undefined; // clear after first iteration
-            scope = val;
-          } while (i < pathKeysLength);
-          return val;
-        }
-  } else {
-    var code = 'var l, fn, p;\n';
-    forEach(pathKeys, function(key, index) {
-      code += 'if(s === null || s === undefined) return s;\n' +
-              'l=s;\n' +
-              's='+ (index
-                      // we simply dereference 's' on any .dot notation
-                      ? 's'
-                      // but if we are first then we check locals first, and if so read it first
-                      : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' +
-              'if (s && s.then) {\n' +
-                ' if (!("$$v" in s)) {\n' +
-                  ' p=s;\n' +
-                  ' p.$$v = undefined;\n' +
-                  ' p.then(function(v) {p.$$v=v;});\n' +
-                  '}\n' +
-                ' s=s.$$v\n' +
-              '}\n';
-    });
-    code += 'return s;';
-    fn = Function('s', 'k', code); // s=scope, k=locals
-    fn.toString = function() { return code; };
-  }
-
-  return getterFnCache[path] = fn;
-}
-
-///////////////////////////////////
-
-/**
- * @ngdoc function
- * @name ng.$parse
- * @function
- *
- * @description
- *
- * Converts Angular {@link guide/expression expression} into a function.
- *
- * <pre>
- *   var getter = $parse('user.name');
- *   var setter = getter.assign;
- *   var context = {user:{name:'angular'}};
- *   var locals = {user:{name:'local'}};
- *
- *   expect(getter(context)).toEqual('angular');
- *   setter(context, 'newValue');
- *   expect(context.user.name).toEqual('newValue');
- *   expect(getter(context, locals)).toEqual('local');
- * </pre>
- *
- *
- * @param {string} expression String expression to compile.
- * @returns {function(context, locals)} a function which represents the compiled expression:
- *
- *    * `context` – `{object}` – an object against which any expressions embedded in the strings
- *      are evaluated against (tipically a scope object).
- *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
- *      `context`.
- *
- *    The return function also has an `assign` property, if the expression is assignable, which
- *    allows one to set values to expressions.
- *
- */
-function $ParseProvider() {
-  var cache = {};
-  this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
-    return function(exp) {
-      switch(typeof exp) {
-        case 'string':
-          return cache.hasOwnProperty(exp)
-            ? cache[exp]
-            : cache[exp] =  parser(exp, false, $filter, $sniffer.csp);
-        case 'function':
-          return exp;
-        default:
-          return noop;
-      }
-    };
-  }];
-}
-
-/**
- * @ngdoc service
- * @name ng.$q
- * @requires $rootScope
- *
- * @description
- * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q).
- *
- * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
- * interface for interacting with an object that represents the result of an action that is
- * performed asynchronously, and may or may not be finished at any given point in time.
- *
- * From the perspective of dealing with error handling, deferred and promise APIs are to
- * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
- *
- * <pre>
- *   // for the purpose of this example let's assume that variables `$q` and `scope` are
- *   // available in the current lexical scope (they could have been injected or passed in).
- *
- *   function asyncGreet(name) {
- *     var deferred = $q.defer();
- *
- *     setTimeout(function() {
- *       // since this fn executes async in a future turn of the event loop, we need to wrap
- *       // our code into an $apply call so that the model changes are properly observed.
- *       scope.$apply(function() {
- *         if (okToGreet(name)) {
- *           deferred.resolve('Hello, ' + name + '!');
- *         } else {
- *           deferred.reject('Greeting ' + name + ' is not allowed.');
- *         }
- *       });
- *     }, 1000);
- *
- *     return deferred.promise;
- *   }
- *
- *   var promise = asyncGreet('Robin Hood');
- *   promise.then(function(greeting) {
- *     alert('Success: ' + greeting);
- *   }, function(reason) {
- *     alert('Failed: ' + reason);
- *   });
- * </pre>
- *
- * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
- * comes in the way of
- * [guarantees that promise and deferred APIs make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
- *
- * Additionally the promise api allows for composition that is very hard to do with the
- * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
- * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
- * section on serial or parallel joining of promises.
- *
- *
- * # The Deferred API
- *
- * A new instance of deferred is constructed by calling `$q.defer()`.
- *
- * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
- * that can be used for signaling the successful or unsuccessful completion of the task.
- *
- * **Methods**
- *
- * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
- *   constructed via `$q.reject`, the promise will be rejected instead.
- * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
- *   resolving it with a rejection constructed via `$q.reject`.
- *
- * **Properties**
- *
- * - promise – `{Promise}` – promise object associated with this deferred.
- *
- *
- * # The Promise API
- *
- * A new promise instance is created when a deferred instance is created and can be retrieved by
- * calling `deferred.promise`.
- *
- * The purpose of the promise object is to allow for interested parties to get access to the result
- * of the deferred task when it completes.
- *
- * **Methods**
- *
- * - `then(successCallback, errorCallback)` – regardless of when the promise was or will be resolved
- *   or rejected calls one of the success or error callbacks asynchronously as soon as the result
- *   is available. The callbacks are called with a single argument the result or rejection reason.
- *
- *   This method *returns a new promise* which is resolved or rejected via the return value of the
- *   `successCallback` or `errorCallback`.
- *
- *
- * # Chaining promises
- *
- * Because calling `then` api of a promise returns a new derived promise, it is easily possible
- * to create a chain of promises:
- *
- * <pre>
- *   promiseB = promiseA.then(function(result) {
- *     return result + 1;
- *   });
- *
- *   // promiseB will be resolved immediately after promiseA is resolved and its value will be
- *   // the result of promiseA incremented by 1
- * </pre>
- *
- * It is possible to create chains of any length and since a promise can be resolved with another
- * promise (which will defer its resolution further), it is possible to pause/defer resolution of
- * the promises at any point in the chain. This makes it possible to implement powerful apis like
- * $http's response interceptors.
- *
- *
- * # Differences between Kris Kowal's Q and $q
- *
- *  There are three main differences:
- *
- * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
- *   mechanism in angular, which means faster propagation of resolution or rejection into your
- *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
- * - $q promises are recognized by the templating engine in angular, which means that in templates
- *   you can treat promises attached to a scope as if they were the resulting values.
- * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
- *   all the important functionality needed for common async tasks.
- * 
- *  # Testing
- * 
- *  <pre>
- *    it('should simulate promise', inject(function($q, $rootScope) {
- *      var deferred = $q.defer();
- *      var promise = deferred.promise;
- *      var resolvedValue;
- * 
- *      promise.then(function(value) { resolvedValue = value; });
- *      expect(resolvedValue).toBeUndefined();
- * 
- *      // Simulate resolving of promise
- *      deferred.resolve(123);
- *      // Note that the 'then' function does not get called synchronously.
- *      // This is because we want the promise API to always be async, whether or not
- *      // it got called synchronously or asynchronously.
- *      expect(resolvedValue).toBeUndefined();
- * 
- *      // Propagate promise resolution to 'then' functions using $apply().
- *      $rootScope.$apply();
- *      expect(resolvedValue).toEqual(123);
- *    });
- *  </pre>
- */
-function $QProvider() {
-
-  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
-    return qFactory(function(callback) {
-      $rootScope.$evalAsync(callback);
-    }, $exceptionHandler);
-  }];
-}
-
-
-/**
- * Constructs a promise manager.
- *
- * @param {function(function)} nextTick Function for executing functions in the next turn.
- * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
- *     debugging purposes.
- * @returns {object} Promise manager.
- */
-function qFactory(nextTick, exceptionHandler) {
-
-  /**
-   * @ngdoc
-   * @name ng.$q#defer
-   * @methodOf ng.$q
-   * @description
-   * Creates a `Deferred` object which represents a task which will finish in the future.
-   *
-   * @returns {Deferred} Returns a new instance of deferred.
-   */
-  var defer = function() {
-    var pending = [],
-        value, deferred;
-
-    deferred = {
-
-      resolve: function(val) {
-        if (pending) {
-          var callbacks = pending;
-          pending = undefined;
-          value = ref(val);
-
-          if (callbacks.length) {
-            nextTick(function() {
-              var callback;
-              for (var i = 0, ii = callbacks.length; i < ii; i++) {
-                callback = callbacks[i];
-                value.then(callback[0], callback[1]);
-              }
-            });
-          }
-        }
-      },
-
-
-      reject: function(reason) {
-        deferred.resolve(reject(reason));
-      },
-
-
-      promise: {
-        then: function(callback, errback) {
-          var result = defer();
-
-          var wrappedCallback = function(value) {
-            try {
-              result.resolve((callback || defaultCallback)(value));
-            } catch(e) {
-              exceptionHandler(e);
-              result.reject(e);
-            }
-          };
-
-          var wrappedErrback = function(reason) {
-            try {
-              result.resolve((errback || defaultErrback)(reason));
-            } catch(e) {
-              exceptionHandler(e);
-              result.reject(e);
-            }
-          };
-
-          if (pending) {
-            pending.push([wrappedCallback, wrappedErrback]);
-          } else {
-            value.then(wrappedCallback, wrappedErrback);
-          }
-
-          return result.promise;
-        }
-      }
-    };
-
-    return deferred;
-  };
-
-
-  var ref = function(value) {
-    if (value && value.then) return value;
-    return {
-      then: function(callback) {
-        var result = defer();
-        nextTick(function() {
-          result.resolve(callback(value));
-        });
-        return result.promise;
-      }
-    };
-  };
-
-
-  /**
-   * @ngdoc
-   * @name ng.$q#reject
-   * @methodOf ng.$q
-   * @description
-   * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
-   * used to forward rejection in a chain of promises. If you are dealing with the last promise in
-   * a promise chain, you don't need to worry about it.
-   *
-   * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
-   * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
-   * a promise error callback and you want to forward the error to the promise derived from the
-   * current promise, you have to "rethrow" the error by returning a rejection constructed via
-   * `reject`.
-   *
-   * <pre>
-   *   promiseB = promiseA.then(function(result) {
-   *     // success: do something and resolve promiseB
-   *     //          with the old or a new result
-   *     return result;
-   *   }, function(reason) {
-   *     // error: handle the error if possible and
-   *     //        resolve promiseB with newPromiseOrValue,
-   *     //        otherwise forward the rejection to promiseB
-   *     if (canHandle(reason)) {
-   *      // handle the error and recover
-   *      return newPromiseOrValue;
-   *     }
-   *     return $q.reject(reason);
-   *   });
-   * </pre>
-   *
-   * @param {*} reason Constant, message, exception or an object representing the rejection reason.
-   * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
-   */
-  var reject = function(reason) {
-    return {
-      then: function(callback, errback) {
-        var result = defer();
-        nextTick(function() {
-          result.resolve((errback || defaultErrback)(reason));
-        });
-        return result.promise;
-      }
-    };
-  };
-
-
-  /**
-   * @ngdoc
-   * @name ng.$q#when
-   * @methodOf ng.$q
-   * @description
-   * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
-   * This is useful when you are dealing with an object that might or might not be a promise, or if
-   * the promise comes from a source that can't be trusted.
-   *
-   * @param {*} value Value or a promise
-   * @returns {Promise} Returns a promise of the passed value or promise
-   */
-  var when = function(value, callback, errback) {
-    var result = defer(),
-        done;
-
-    var wrappedCallback = function(value) {
-      try {
-        return (callback || defaultCallback)(value);
-      } catch (e) {
-        exceptionHandler(e);
-        return reject(e);
-      }
-    };
-
-    var wrappedErrback = function(reason) {
-      try {
-        return (errback || defaultErrback)(reason);
-      } catch (e) {
-        exceptionHandler(e);
-        return reject(e);
-      }
-    };
-
-    nextTick(function() {
-      ref(value).then(function(value) {
-        if (done) return;
-        done = true;
-        result.resolve(ref(value).then(wrappedCallback, wrappedErrback));
-      }, function(reason) {
-        if (done) return;
-        done = true;
-        result.resolve(wrappedErrback(reason));
-      });
-    });
-
-    return result.promise;
-  };
-
-
-  function defaultCallback(value) {
-    return value;
-  }
-
-
-  function defaultErrback(reason) {
-    return reject(reason);
-  }
-
-
-  /**
-   * @ngdoc
-   * @name ng.$q#all
-   * @methodOf ng.$q
-   * @description
-   * Combines multiple promises into a single promise that is resolved when all of the input
-   * promises are resolved.
-   *
-   * @param {Array.<Promise>} promises An array of promises.
-   * @returns {Promise} Returns a single promise that will be resolved with an array of values,
-   *   each value corresponding to the promise at the same index in the `promises` array. If any of
-   *   the promises is resolved with a rejection, this resulting promise will be resolved with the
-   *   same rejection.
-   */
-  function all(promises) {
-    var deferred = defer(),
-        counter = promises.length,
-        results = [];
-
-    if (counter) {
-      forEach(promises, function(promise, index) {
-        ref(promise).then(function(value) {
-          if (index in results) return;
-          results[index] = value;
-          if (!(--counter)) deferred.resolve(results);
-        }, function(reason) {
-          if (index in results) return;
-          deferred.reject(reason);
-        });
-      });
-    } else {
-      deferred.resolve(results);
-    }
-
-    return deferred.promise;
-  }
-
-  return {
-    defer: defer,
-    reject: reject,
-    when: when,
-    all: all
-  };
-}
-
-/**
- * @ngdoc object
- * @name ng.$routeProvider
- * @function
- *
- * @description
- *
- * Used for configuring routes. See {@link ng.$route $route} for an example.
- */
-function $RouteProvider(){
-  var routes = {};
-
-  /**
-   * @ngdoc method
-   * @name ng.$routeProvider#when
-   * @methodOf ng.$routeProvider
-   *
-   * @param {string} path Route path (matched against `$location.path`). If `$location.path`
-   *    contains redundant trailing slash or is missing one, the route will still match and the
-   *    `$location.path` will be updated to add or drop the trailing slash to exactly match the
-   *    route definition.
-   *
-   *    `path` can contain named groups starting with a colon (`:name`). All characters up to the
-   *    next slash are matched and stored in `$routeParams` under the given `name` when the route
-   *    matches.
-   *
-   * @param {Object} route Mapping information to be assigned to `$route.current` on route
-   *    match.
-   *
-   *    Object properties:
-   *
-   *    - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly
-   *      created scope or the name of a {@link angular.Module#controller registered controller}
-   *      if passed as a string.
-   *    - `template` – `{string=}` –  html template as a string that should be used by
-   *      {@link ng.directive:ngView ngView} or
-   *      {@link ng.directive:ngInclude ngInclude} directives.
-   *      this property takes precedence over `templateUrl`.
-   *    - `templateUrl` – `{string=}` – path to an html template that should be used by
-   *      {@link ng.directive:ngView ngView}.
-   *    - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
-   *      be injected into the controller. If any of these dependencies are promises, they will be
-   *      resolved and converted to a value before the controller is instantiated and the
-   *      `$routeChangeSuccess` event is fired. The map object is:
-   *
-   *      - `key` – `{string}`: a name of a dependency to be injected into the controller.
-   *      - `factory` - `{string|function}`: If `string` then it is an alias for a service.
-   *        Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected}
-   *        and the return value is treated as the dependency. If the result is a promise, it is resolved
-   *        before its value is injected into the controller.
-   *
-   *    - `redirectTo` – {(string|function())=} – value to update
-   *      {@link ng.$location $location} path with and trigger route redirection.
-   *
-   *      If `redirectTo` is a function, it will be called with the following parameters:
-   *
-   *      - `{Object.<string>}` - route parameters extracted from the current
-   *        `$location.path()` by applying the current route templateUrl.
-   *      - `{string}` - current `$location.path()`
-   *      - `{Object}` - current `$location.search()`
-   *
-   *      The custom `redirectTo` function is expected to return a string which will be used
-   *      to update `$location.path()` and `$location.search()`.
-   *
-   *    - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search()
-   *    changes.
-   *
-   *      If the option is set to `false` and url in the browser changes, then
-   *      `$routeUpdate` event is broadcasted on the root scope.
-   *
-   * @returns {Object} self
-   *
-   * @description
-   * Adds a new route definition to the `$route` service.
-   */
-  this.when = function(path, route) {
-    routes[path] = extend({reloadOnSearch: true}, route);
-
-    // create redirection for trailing slashes
-    if (path) {
-      var redirectPath = (path[path.length-1] == '/')
-          ? path.substr(0, path.length-1)
-          : path +'/';
-
-      routes[redirectPath] = {redirectTo: path};
-    }
-
-    return this;
-  };
-
-  /**
-   * @ngdoc method
-   * @name ng.$routeProvider#otherwise
-   * @methodOf ng.$routeProvider
-   *
-   * @description
-   * Sets route definition that will be used on route change when no other route definition
-   * is matched.
-   *
-   * @param {Object} params Mapping information to be assigned to `$route.current`.
-   * @returns {Object} self
-   */
-  this.otherwise = function(params) {
-    this.when(null, params);
-    return this;
-  };
-
-
-  this.$get = ['$rootScope', '$location', '$routeParams', '$q', '$injector', '$http', '$templateCache',
-      function( $rootScope,   $location,   $routeParams,   $q,   $injector,   $http,   $templateCache) {
-
-    /**
-     * @ngdoc object
-     * @name ng.$route
-     * @requires $location
-     * @requires $routeParams
-     *
-     * @property {Object} current Reference to the current route definition.
-     * The route definition contains:
-     *
-     *   - `controller`: The controller constructor as define in route definition.
-     *   - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
-     *     controller instantiation. The `locals` contain
-     *     the resolved values of the `resolve` map. Additionally the `locals` also contain:
-     *
-     *     - `$scope` - The current route scope.
-     *     - `$template` - The current route template HTML.
-     *
-     * @property {Array.<Object>} routes Array of all configured routes.
-     *
-     * @description
-     * Is used for deep-linking URLs to controllers and views (HTML partials).
-     * It watches `$location.url()` and tries to map the path to an existing route definition.
-     *
-     * You can define routes through {@link ng.$routeProvider $routeProvider}'s API.
-     *
-     * The `$route` service is typically used in conjunction with {@link ng.directive:ngView ngView}
-     * directive and the {@link ng.$routeParams $routeParams} service.
-     *
-     * @example
-       This example shows how changing the URL hash causes the `$route` to match a route against the
-       URL, and the `ngView` pulls in the partial.
-
-       Note that this example is using {@link ng.directive:script inlined templates}
-       to get it working on jsfiddle as well.
-
-     <example module="ngView">
-       <file name="index.html">
-         <div ng-controller="MainCntl">
-           Choose:
-           <a href="Book/Moby">Moby</a> |
-           <a href="Book/Moby/ch/1">Moby: Ch1</a> |
-           <a href="Book/Gatsby">Gatsby</a> |
-           <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
-           <a href="Book/Scarlet">Scarlet Letter</a><br/>
-
-           <div ng-view></div>
-           <hr />
-
-           <pre>$location.path() = {{$location.path()}}</pre>
-           <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
-           <pre>$route.current.params = {{$route.current.params}}</pre>
-           <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
-           <pre>$routeParams = {{$routeParams}}</pre>
-         </div>
-       </file>
-
-       <file name="book.html">
-         controller: {{name}}<br />
-         Book Id: {{params.bookId}}<br />
-       </file>
-
-       <file name="chapter.html">
-         controller: {{name}}<br />
-         Book Id: {{params.bookId}}<br />
-         Chapter Id: {{params.chapterId}}
-       </file>
-
-       <file name="script.js">
-         angular.module('ngView', [], function($routeProvider, $locationProvider) {
-           $routeProvider.when('/Book/:bookId', {
-             templateUrl: 'book.html',
-             controller: BookCntl,
-             resolve: {
-               // I will cause a 1 second delay
-               delay: function($q, $timeout) {
-                 var delay = $q.defer();
-                 $timeout(delay.resolve, 1000);
-                 return delay.promise;
-               }
-             }
-           });
-           $routeProvider.when('/Book/:bookId/ch/:chapterId', {
-             templateUrl: 'chapter.html',
-             controller: ChapterCntl
-           });
-
-           // configure html5 to get links working on jsfiddle
-           $locationProvider.html5Mode(true);
-         });
-
-         function MainCntl($scope, $route, $routeParams, $location) {
-           $scope.$route = $route;
-           $scope.$location = $location;
-           $scope.$routeParams = $routeParams;
-         }
-
-         function BookCntl($scope, $routeParams) {
-           $scope.name = "BookCntl";
-           $scope.params = $routeParams;
-         }
-
-         function ChapterCntl($scope, $routeParams) {
-           $scope.name = "ChapterCntl";
-           $scope.params = $routeParams;
-         }
-       </file>
-
-       <file name="scenario.js">
-         it('should load and compile correct template', function() {
-           element('a:contains("Moby: Ch1")').click();
-           var content = element('.doc-example-live [ng-view]').text();
-           expect(content).toMatch(/controller\: ChapterCntl/);
-           expect(content).toMatch(/Book Id\: Moby/);
-           expect(content).toMatch(/Chapter Id\: 1/);
-
-           element('a:contains("Scarlet")').click();
-           sleep(2); // promises are not part of scenario waiting
-           content = element('.doc-example-live [ng-view]').text();
-           expect(content).toMatch(/controller\: BookCntl/);
-           expect(content).toMatch(/Book Id\: Scarlet/);
-         });
-       </file>
-     </example>
-     */
-
-    /**
-     * @ngdoc event
-     * @name ng.$route#$routeChangeStart
-     * @eventOf ng.$route
-     * @eventType broadcast on root scope
-     * @description
-     * Broadcasted before a route change. At this  point the route services starts
-     * resolving all of the dependencies needed for the route change to occurs.
-     * Typically this involves fetching the view template as well as any dependencies
-     * defined in `resolve` route property. Once  all of the dependencies are resolved
-     * `$routeChangeSuccess` is fired.
-     *
-     * @param {Route} next Future route information.
-     * @param {Route} current Current route information.
-     */
-
-    /**
-     * @ngdoc event
-     * @name ng.$route#$routeChangeSuccess
-     * @eventOf ng.$route
-     * @eventType broadcast on root scope
-     * @description
-     * Broadcasted after a route dependencies are resolved.
-     * {@link ng.directive:ngView ngView} listens for the directive
-     * to instantiate the controller and render the view.
-     *
-     * @param {Object} angularEvent Synthetic event object.
-     * @param {Route} current Current route information.
-     * @param {Route|Undefined} previous Previous route information, or undefined if current is first route entered.
-     */
-
-    /**
-     * @ngdoc event
-     * @name ng.$route#$routeChangeError
-     * @eventOf ng.$route
-     * @eventType broadcast on root scope
-     * @description
-     * Broadcasted if any of the resolve promises are rejected.
-     *
-     * @param {Route} current Current route information.
-     * @param {Route} previous Previous route information.
-     * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
-     */
-
-    /**
-     * @ngdoc event
-     * @name ng.$route#$routeUpdate
-     * @eventOf ng.$route
-     * @eventType broadcast on root scope
-     * @description
-     *
-     * The `reloadOnSearch` property has been set to false, and we are reusing the same
-     * instance of the Controller.
-     */
-
-    var forceReload = false,
-        $route = {
-          routes: routes,
-
-          /**
-           * @ngdoc method
-           * @name ng.$route#reload
-           * @methodOf ng.$route
-           *
-           * @description
-           * Causes `$route` service to reload the current route even if
-           * {@link ng.$location $location} hasn't changed.
-           *
-           * As a result of that, {@link ng.directive:ngView ngView}
-           * creates new scope, reinstantiates the controller.
-           */
-          reload: function() {
-            forceReload = true;
-            $rootScope.$evalAsync(updateRoute);
-          }
-        };
-
-    $rootScope.$on('$locationChangeSuccess', updateRoute);
-
-    return $route;
-
-    /////////////////////////////////////////////////////
-
-    /**
-     * @param on {string} current url
-     * @param when {string} route when template to match the url against
-     * @return {?Object}
-     */
-    function switchRouteMatcher(on, when) {
-      // TODO(i): this code is convoluted and inefficient, we should construct the route matching
-      //   regex only once and then reuse it
-
-      // Escape regexp special characters.
-      when = '^' + when.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + '$';
-      var regex = '',
-          params = [],
-          dst = {};
-
-      var re = /:(\w+)/g,
-          paramMatch,
-          lastMatchedIndex = 0;
-
-      while ((paramMatch = re.exec(when)) !== null) {
-        // Find each :param in `when` and replace it with a capturing group.
-        // Append all other sections of when unchanged.
-        regex += when.slice(lastMatchedIndex, paramMatch.index);
-        regex += '([^\\/]*)';
-        params.push(paramMatch[1]);
-        lastMatchedIndex = re.lastIndex;
-      }
-      // Append trailing path part.
-      regex += when.substr(lastMatchedIndex);
-
-      var match = on.match(new RegExp(regex));
-      if (match) {
-        forEach(params, function(name, index) {
-          dst[name] = match[index + 1];
-        });
-      }
-      return match ? dst : null;
-    }
-
-    function updateRoute() {
-      var next = parseRoute(),
-          last = $route.current;
-
-      if (next && last && next.$$route === last.$$route
-          && equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) {
-        last.params = next.params;
-        copy(last.params, $routeParams);
-        $rootScope.$broadcast('$routeUpdate', last);
-      } else if (next || last) {
-        forceReload = false;
-        $rootScope.$broadcast('$routeChangeStart', next, last);
-        $route.current = next;
-        if (next) {
-          if (next.redirectTo) {
-            if (isString(next.redirectTo)) {
-              $location.path(interpolate(next.redirectTo, next.params)).search(next.params)
-                       .replace();
-            } else {
-              $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
-                       .replace();
-            }
-          }
-        }
-
-        $q.when(next).
-          then(function() {
-            if (next) {
-              var keys = [],
-                  values = [],
-                  template;
-
-              forEach(next.resolve || {}, function(value, key) {
-                keys.push(key);
-                values.push(isString(value) ? $injector.get(value) : $injector.invoke(value));
-              });
-              if (isDefined(template = next.template)) {
-              } else if (isDefined(template = next.templateUrl)) {
-                template = $http.get(template, {cache: $templateCache}).
-                    then(function(response) { return response.data; });
-              }
-              if (isDefined(template)) {
-                keys.push('$template');
-                values.push(template);
-              }
-              return $q.all(values).then(function(values) {
-                var locals = {};
-                forEach(values, function(value, index) {
-                  locals[keys[index]] = value;
-                });
-                return locals;
-              });
-            }
-          }).
-          // after route change
-          then(function(locals) {
-            if (next == $route.current) {
-              if (next) {
-                next.locals = locals;
-                copy(next.params, $routeParams);
-              }
-              $rootScope.$broadcast('$routeChangeSuccess', next, last);
-            }
-          }, function(error) {
-            if (next == $route.current) {
-              $rootScope.$broadcast('$routeChangeError', next, last, error);
-            }
-          });
-      }
-    }
-
-
-    /**
-     * @returns the current active route, by matching it against the URL
-     */
-    function parseRoute() {
-      // Match a route
-      var params, match;
-      forEach(routes, function(route, path) {
-        if (!match && (params = switchRouteMatcher($location.path(), path))) {
-          match = inherit(route, {
-            params: extend({}, $location.search(), params),
-            pathParams: params});
-          match.$$route = route;
-        }
-      });
-      // No route matched; fallback to "otherwise" route
-      return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
-    }
-
-    /**
-     * @returns interpolation of the redirect path with the parametrs
-     */
-    function interpolate(string, params) {
-      var result = [];
-      forEach((string||'').split(':'), function(segment, i) {
-        if (i == 0) {
-          result.push(segment);
-        } else {
-          var segmentMatch = segment.match(/(\w+)(.*)/);
-          var key = segmentMatch[1];
-          result.push(params[key]);
-          result.push(segmentMatch[2] || '');
-          delete params[key];
-        }
-      });
-      return result.join('');
-    }
-  }];
-}
-
-/**
- * @ngdoc object
- * @name ng.$routeParams
- * @requires $route
- *
- * @description
- * Current set of route parameters. The route parameters are a combination of the
- * {@link ng.$location $location} `search()`, and `path()`. The `path` parameters
- * are extracted when the {@link ng.$route $route} path is matched.
- *
- * In case of parameter name collision, `path` params take precedence over `search` params.
- *
- * The service guarantees that the identity of the `$routeParams` object will remain unchanged
- * (but its properties will likely change) even when a route change occurs.
- *
- * @example
- * <pre>
- *  // Given:
- *  // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
- *  // Route: /Chapter/:chapterId/Section/:sectionId
- *  //
- *  // Then
- *  $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
- * </pre>
- */
-function $RouteParamsProvider() {
-  this.$get = valueFn({});
-}
-
-/**
- * DESIGN NOTES
- *
- * The design decisions behind the scope are heavily favored for speed and memory consumption.
- *
- * The typical use of scope is to watch the expressions, which most of the time return the same
- * value as last time so we optimize the operation.
- *
- * Closures construction is expensive in terms of speed as well as memory:
- *   - No closures, instead use prototypical inheritance for API
- *   - Internal state needs to be stored on scope directly, which means that private state is
- *     exposed as $$____ properties
- *
- * Loop operations are optimized by using while(count--) { ... }
- *   - this means that in order to keep the same order of execution as addition we have to add
- *     items to the array at the beginning (shift) instead of at the end (push)
- *
- * Child scopes are created and removed often
- *   - Using an array would be slow since inserts in middle are expensive so we use linked list
- *
- * There are few watches then a lot of observers. This is why you don't want the observer to be
- * implemented in the same way as watch. Watch requires return of initialization function which
- * are expensive to construct.
- */
-
-
-/**
- * @ngdoc object
- * @name ng.$rootScopeProvider
- * @description
- *
- * Provider for the $rootScope service.
- */
-
-/**
- * @ngdoc function
- * @name ng.$rootScopeProvider#digestTtl
- * @methodOf ng.$rootScopeProvider
- * @description
- *
- * Sets the number of digest iterations the scope should attempt to execute before giving up and
- * assuming that the model is unstable.
- *
- * The current default is 10 iterations.
- *
- * @param {number} limit The number of digest iterations.
- */
-
-
-/**
- * @ngdoc object
- * @name ng.$rootScope
- * @description
- *
- * Every application has a single root {@link ng.$rootScope.Scope scope}.
- * All other scopes are child scopes of the root scope. Scopes provide mechanism for watching the model and provide
- * event processing life-cycle. See {@link guide/scope developer guide on scopes}.
- */
-function $RootScopeProvider(){
-  var TTL = 10;
-
-  this.digestTtl = function(value) {
-    if (arguments.length) {
-      TTL = value;
-    }
-    return TTL;
-  };
-
-  this.$get = ['$injector', '$exceptionHandler', '$parse',
-      function( $injector,   $exceptionHandler,   $parse) {
-
-    /**
-     * @ngdoc function
-     * @name ng.$rootScope.Scope
-     *
-     * @description
-     * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
-     * {@link AUTO.$injector $injector}. Child scopes are created using the
-     * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
-     * compiled HTML template is executed.)
-     *
-     * Here is a simple scope snippet to show how you can interact with the scope.
-     * <pre>
-        angular.injector(['ng']).invoke(function($rootScope) {
-           var scope = $rootScope.$new();
-           scope.salutation = 'Hello';
-           scope.name = 'World';
-
-           expect(scope.greeting).toEqual(undefined);
-
-           scope.$watch('name', function() {
-             scope.greeting = scope.salutation + ' ' + scope.name + '!';
-           }); // initialize the watch
-
-           expect(scope.greeting).toEqual(undefined);
-           scope.name = 'Misko';
-           // still old value, since watches have not been called yet
-           expect(scope.greeting).toEqual(undefined);
-
-           scope.$digest(); // fire all  the watches
-           expect(scope.greeting).toEqual('Hello Misko!');
-        });
-     * </pre>
-     *
-     * # Inheritance
-     * A scope can inherit from a parent scope, as in this example:
-     * <pre>
-         var parent = $rootScope;
-         var child = parent.$new();
-
-         parent.salutation = "Hello";
-         child.name = "World";
-         expect(child.salutation).toEqual('Hello');
-
-         child.salutation = "Welcome";
-         expect(child.salutation).toEqual('Welcome');
-         expect(parent.salutation).toEqual('Hello');
-     * </pre>
-     *
-     *
-     * @param {Object.<string, function()>=} providers Map of service factory which need to be provided
-     *     for the current scope. Defaults to {@link ng}.
-     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
-     *     append/override services provided by `providers`. This is handy when unit-testing and having
-     *     the need to override a default service.
-     * @returns {Object} Newly created scope.
-     *
-     */
-    function Scope() {
-      this.$id = nextUid();
-      this.$$phase = this.$parent = this.$$watchers =
-                     this.$$nextSibling = this.$$prevSibling =
-                     this.$$childHead = this.$$childTail = null;
-      this['this'] = this.$root =  this;
-      this.$$destroyed = false;
-      this.$$asyncQueue = [];
-      this.$$listeners = {};
-      this.$$isolateBindings = {};
-    }
-
-    /**
-     * @ngdoc property
-     * @name ng.$rootScope.Scope#$id
-     * @propertyOf ng.$rootScope.Scope
-     * @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for
-     *   debugging.
-     */
-
-
-    Scope.prototype = {
-      /**
-       * @ngdoc function
-       * @name ng.$rootScope.Scope#$new
-       * @methodOf ng.$rootScope.Scope
-       * @function
-       *
-       * @description
-       * Creates a new child {@link ng.$rootScope.Scope scope}.
-       *
-       * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and
-       * {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the scope
-       * hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
-       *
-       * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is desired for
-       * the scope and its child scopes to be permanently detached from the parent and thus stop
-       * participating in model change detection and listener notification by invoking.
-       *
-       * @param {boolean} isolate if true then the scope does not prototypically inherit from the
-       *         parent scope. The scope is isolated, as it can not see parent scope properties.
-       *         When creating widgets it is useful for the widget to not accidentally read parent
-       *         state.
-       *
-       * @returns {Object} The newly created child scope.
-       *
-       */
-      $new: function(isolate) {
-        var Child,
-            child;
-
-        if (isFunction(isolate)) {
-          // TODO: remove at some point
-          throw Error('API-CHANGE: Use $controller to instantiate controllers.');
-        }
-        if (isolate) {
-          child = new Scope();
-          child.$root = this.$root;
-        } else {
-          Child = function() {}; // should be anonymous; This is so that when the minifier munges
-            // the name it does not become random set of chars. These will then show up as class
-            // name in the debugger.
-          Child.prototype = this;
-          child = new Child();
-          child.$id = nextUid();
-        }
-        child['this'] = child;
-        child.$$listeners = {};
-        child.$parent = this;
-        child.$$asyncQueue = [];
-        child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null;
-        child.$$prevSibling = this.$$childTail;
-        if (this.$$childHead) {
-          this.$$childTail.$$nextSibling = child;
-          this.$$childTail = child;
-        } else {
-          this.$$childHead = this.$$childTail = child;
-        }
-        return child;
-      },
-
-      /**
-       * @ngdoc function
-       * @name ng.$rootScope.Scope#$watch
-       * @methodOf ng.$rootScope.Scope
-       * @function
-       *
-       * @description
-       * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
-       *
-       * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest $digest()} and
-       *   should return the value which will be watched. (Since {@link ng.$rootScope.Scope#$digest $digest()}
-       *   reruns when it detects changes the `watchExpression` can execute multiple times per
-       *   {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
-       * - The `listener` is called only when the value from the current `watchExpression` and the
-       *   previous call to `watchExpression` are not equal (with the exception of the initial run,
-       *   see below). The inequality is determined according to
-       *   {@link angular.equals} function. To save the value of the object for later comparison, the
-       *   {@link angular.copy} function is used. It also means that watching complex options will
-       *   have adverse memory and performance implications.
-       * - The watch `listener` may change the model, which may trigger other `listener`s to fire. This
-       *   is achieved by rerunning the watchers until no changes are detected. The rerun iteration
-       *   limit is 10 to prevent an infinite loop deadlock.
-       *
-       *
-       * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
-       * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
-       * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a change is
-       * detected, be prepared for multiple calls to your listener.)
-       *
-       * After a watcher is registered with the scope, the `listener` fn is called asynchronously
-       * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
-       * watcher. In rare cases, this is undesirable because the listener is called when the result
-       * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
-       * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
-       * listener was called due to initialization.
-       *
-       *
-       * # Example
-       * <pre>
-           // let's assume that scope was dependency injected as the $rootScope
-           var scope = $rootScope;
-           scope.name = 'misko';
-           scope.counter = 0;
-
-           expect(scope.counter).toEqual(0);
-           scope.$watch('name', function(newValue, oldValue) { scope.counter = scope.counter + 1; });
-           expect(scope.counter).toEqual(0);
-
-           scope.$digest();
-           // no variable change
-           expect(scope.counter).toEqual(0);
-
-           scope.name = 'adam';
-           scope.$digest();
-           expect(scope.counter).toEqual(1);
-       * </pre>
-       *
-       *
-       *
-       * @param {(function()|string)} watchExpression Expression that is evaluated on each
-       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers a
-       *    call to the `listener`.
-       *
-       *    - `string`: Evaluated as {@link guide/expression expression}
-       *    - `function(scope)`: called with current `scope` as a parameter.
-       * @param {(function()|string)=} listener Callback called whenever the return value of
-       *   the `watchExpression` changes.
-       *
-       *    - `string`: Evaluated as {@link guide/expression expression}
-       *    - `function(newValue, oldValue, scope)`: called with current and previous values as parameters.
-       *
-       * @param {boolean=} objectEquality Compare object for equality rather than for reference.
-       * @returns {function()} Returns a deregistration function for this listener.
-       */
-      $watch: function(watchExp, listener, objectEquality) {
-        var scope = this,
-            get = compileToFn(watchExp, 'watch'),
-            array = scope.$$watchers,
-            watcher = {
-              fn: listener,
-              last: initWatchVal,
-              get: get,
-              exp: watchExp,
-              eq: !!objectEquality
-            };
-
-        // in the case user pass string, we need to compile it, do we really need this ?
-        if (!isFunction(listener)) {
-          var listenFn = compileToFn(listener || noop, 'listener');
-          watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};
-        }
-
-        if (!array) {
-          array = scope.$$watchers = [];
-        }
-        // we use unshift since we use a while loop in $digest for speed.
-        // the while loop reads in reverse order.
-        array.unshift(watcher);
-
-        return function() {
-          arrayRemove(array, watcher);
-        };
-      },
-
-      /**
-       * @ngdoc function
-       * @name ng.$rootScope.Scope#$digest
-       * @methodOf ng.$rootScope.Scope
-       * @function
-       *
-       * @description
-       * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children.
-       * Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the
-       * `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are
-       * firing. This means that it is possible to get into an infinite loop. This function will throw
-       * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 10.
-       *
-       * Usually you don't call `$digest()` directly in
-       * {@link ng.directive:ngController controllers} or in
-       * {@link ng.$compileProvider#directive directives}.
-       * Instead a call to {@link ng.$rootScope.Scope#$apply $apply()} (typically from within a
-       * {@link ng.$compileProvider#directive directives}) will force a `$digest()`.
-       *
-       * If you want to be notified whenever `$digest()` is called,
-       * you can register a `watchExpression` function  with {@link ng.$rootScope.Scope#$watch $watch()}
-       * with no `listener`.
-       *
-       * You may have a need to call `$digest()` from within unit-tests, to simulate the scope
-       * life-cycle.
-       *
-       * # Example
-       * <pre>
-           var scope = ...;
-           scope.name = 'misko';
-           scope.counter = 0;
-
-           expect(scope.counter).toEqual(0);
-           scope.$watch('name', function(newValue, oldValue) {
-             scope.counter = scope.counter + 1;
-           });
-           expect(scope.counter).toEqual(0);
-
-           scope.$digest();
-           // no variable change
-           expect(scope.counter).toEqual(0);
-
-           scope.name = 'adam';
-           scope.$digest();
-           expect(scope.counter).toEqual(1);
-       * </pre>
-       *
-       */
-      $digest: function() {
-        var watch, value, last,
-            watchers,
-            asyncQueue,
-            length,
-            dirty, ttl = TTL,
-            next, current, target = this,
-            watchLog = [],
-            logIdx, logMsg;
-
-        beginPhase('$digest');
-
-        do {
-          dirty = false;
-          current = target;
-          do {
-            asyncQueue = current.$$asyncQueue;
-            while(asyncQueue.length) {
-              try {
-                current.$eval(asyncQueue.shift());
-              } catch (e) {
-                $exceptionHandler(e);
-              }
-            }
-            if ((watchers = current.$$watchers)) {
-              // process our watches
-              length = watchers.length;
-              while (length--) {
-                try {
-                  watch = watchers[length];
-                  // Most common watches are on primitives, in which case we can short
-                  // circuit it with === operator, only when === fails do we use .equals
-                  if ((value = watch.get(current)) !== (last = watch.last) &&
-                      !(watch.eq
-                          ? equals(value, last)
-                          : (typeof value == 'number' && typeof last == 'number'
-                             && isNaN(value) && isNaN(last)))) {
-                    dirty = true;
-                    watch.last = watch.eq ? copy(value) : value;
-                    watch.fn(value, ((last === initWatchVal) ? value : last), current);
-                    if (ttl < 5) {
-                      logIdx = 4 - ttl;
-                      if (!watchLog[logIdx]) watchLog[logIdx] = [];
-                      logMsg = (isFunction(watch.exp))
-                          ? 'fn: ' + (watch.exp.name || watch.exp.toString())
-                          : watch.exp;
-                      logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
-                      watchLog[logIdx].push(logMsg);
-                    }
-                  }
-                } catch (e) {
-                  $exceptionHandler(e);
-                }
-              }
-            }
-
-            // Insanity Warning: scope depth-first traversal
-            // yes, this code is a bit crazy, but it works and we have tests to prove it!
-            // this piece should be kept in sync with the traversal in $broadcast
-            if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
-              while(current !== target && !(next = current.$$nextSibling)) {
-                current = current.$parent;
-              }
-            }
-          } while ((current = next));
-
-          if(dirty && !(ttl--)) {
-            clearPhase();
-            throw Error(TTL + ' $digest() iterations reached. Aborting!\n' +
-                'Watchers fired in the last 5 iterations: ' + toJson(watchLog));
-          }
-        } while (dirty || asyncQueue.length);
-
-        clearPhase();
-      },
-
-
-      /**
-       * @ngdoc event
-       * @name ng.$rootScope.Scope#$destroy
-       * @eventOf ng.$rootScope.Scope
-       * @eventType broadcast on scope being destroyed
-       *
-       * @description
-       * Broadcasted when a scope and its children are being destroyed.
-       */
-
-      /**
-       * @ngdoc function
-       * @name ng.$rootScope.Scope#$destroy
-       * @methodOf ng.$rootScope.Scope
-       * @function
-       *
-       * @description
-       * Removes the current scope (and all of its children) from the parent scope. Removal implies
-       * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
-       * propagate to the current scope and its children. Removal also implies that the current
-       * scope is eligible for garbage collection.
-       *
-       * The `$destroy()` is usually used by directives such as
-       * {@link ng.directive:ngRepeat ngRepeat} for managing the
-       * unrolling of the loop.
-       *
-       * Just before a scope is destroyed a `$destroy` event is broadcasted on this scope.
-       * Application code can register a `$destroy` event handler that will give it chance to
-       * perform any necessary cleanup.
-       */
-      $destroy: function() {
-        // we can't destroy the root scope or a scope that has been already destroyed
-        if ($rootScope == this || this.$$destroyed) return;
-        var parent = this.$parent;
-
-        this.$broadcast('$destroy');
-        this.$$destroyed = true;
-
-        if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
-        if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
-        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
-        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
-
-        // This is bogus code that works around Chrome's GC leak
-        // see: https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
-        this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
-            this.$$childTail = null;
-      },
-
-      /**
-       * @ngdoc function
-       * @name ng.$rootScope.Scope#$eval
-       * @methodOf ng.$rootScope.Scope
-       * @function
-       *
-       * @description
-       * Executes the `expression` on the current scope returning the result. Any exceptions in the
-       * expression are propagated (uncaught). This is useful when evaluating Angular expressions.
-       *
-       * # Example
-       * <pre>
-           var scope = ng.$rootScope.Scope();
-           scope.a = 1;
-           scope.b = 2;
-
-           expect(scope.$eval('a+b')).toEqual(3);
-           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
-       * </pre>
-       *
-       * @param {(string|function())=} expression An angular expression to be executed.
-       *
-       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
-       *    - `function(scope)`: execute the function with the current `scope` parameter.
-       *
-       * @returns {*} The result of evaluating the expression.
-       */
-      $eval: function(expr, locals) {
-        return $parse(expr)(this, locals);
-      },
-
-      /**
-       * @ngdoc function
-       * @name ng.$rootScope.Scope#$evalAsync
-       * @methodOf ng.$rootScope.Scope
-       * @function
-       *
-       * @description
-       * Executes the expression on the current scope at a later point in time.
-       *
-       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only that:
-       *
-       *   - it will execute in the current script execution context (before any DOM rendering).
-       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
-       *     `expression` execution.
-       *
-       * Any exceptions from the execution of the expression are forwarded to the
-       * {@link ng.$exceptionHandler $exceptionHandler} service.
-       *
-       * @param {(string|function())=} expression An angular expression to be executed.
-       *
-       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
-       *    - `function(scope)`: execute the function with the current `scope` parameter.
-       *
-       */
-      $evalAsync: function(expr) {
-        this.$$asyncQueue.push(expr);
-      },
-
-      /**
-       * @ngdoc function
-       * @name ng.$rootScope.Scope#$apply
-       * @methodOf ng.$rootScope.Scope
-       * @function
-       *
-       * @description
-       * `$apply()` is used to execute an expression in angular from outside of the angular framework.
-       * (For example from browser DOM events, setTimeout, XHR or third party libraries).
-       * Because we are calling into the angular framework we need to perform proper scope life-cycle
-       * of {@link ng.$exceptionHandler exception handling},
-       * {@link ng.$rootScope.Scope#$digest executing watches}.
-       *
-       * ## Life cycle
-       *
-       * # Pseudo-Code of `$apply()`
-       * <pre>
-           function $apply(expr) {
-             try {
-               return $eval(expr);
-             } catch (e) {
-               $exceptionHandler(e);
-             } finally {
-               $root.$digest();
-             }
-           }
-       * </pre>
-       *
-       *
-       * Scope's `$apply()` method transitions through the following stages:
-       *
-       * 1. The {@link guide/expression expression} is executed using the
-       *    {@link ng.$rootScope.Scope#$eval $eval()} method.
-       * 2. Any exceptions from the execution of the expression are forwarded to the
-       *    {@link ng.$exceptionHandler $exceptionHandler} service.
-       * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the expression
-       *    was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
-       *
-       *
-       * @param {(string|function())=} exp An angular expression to be executed.
-       *
-       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
-       *    - `function(scope)`: execute the function with current `scope` parameter.
-       *
-       * @returns {*} The result of evaluating the expression.
-       */
-      $apply: function(expr) {
-        try {
-          beginPhase('$apply');
-          return this.$eval(expr);
-        } catch (e) {
-          $exceptionHandler(e);
-        } finally {
-          clearPhase();
-          try {
-            $rootScope.$digest();
-          } catch (e) {
-            $exceptionHandler(e);
-            throw e;
-          }
-        }
-      },
-
-      /**
-       * @ngdoc function
-       * @name ng.$rootScope.Scope#$on
-       * @methodOf ng.$rootScope.Scope
-       * @function
-       *
-       * @description
-       * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of
-       * event life cycle.
-       *
-       * The event listener function format is: `function(event, args...)`. The `event` object
-       * passed into the listener has the following attributes:
-       *
-       *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or `$broadcast`-ed.
-       *   - `currentScope` - `{Scope}`: the current scope which is handling the event.
-       *   - `name` - `{string}`: Name of the event.
-       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel further event
-       *     propagation (available only for events that were `$emit`-ed).
-       *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true.
-       *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
-       *
-       * @param {string} name Event name to listen on.
-       * @param {function(event, args...)} listener Function to call when the event is emitted.
-       * @returns {function()} Returns a deregistration function for this listener.
-       */
-      $on: function(name, listener) {
-        var namedListeners = this.$$listeners[name];
-        if (!namedListeners) {
-          this.$$listeners[name] = namedListeners = [];
-        }
-        namedListeners.push(listener);
-
-        return function() {
-          namedListeners[indexOf(namedListeners, listener)] = null;
-        };
-      },
-
-
-      /**
-       * @ngdoc function
-       * @name ng.$rootScope.Scope#$emit
-       * @methodOf ng.$rootScope.Scope
-       * @function
-       *
-       * @description
-       * Dispatches an event `name` upwards through the scope hierarchy notifying the
-       * registered {@link ng.$rootScope.Scope#$on} listeners.
-       *
-       * The event life cycle starts at the scope on which `$emit` was called. All
-       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get notified.
-       * Afterwards, the event traverses upwards toward the root scope and calls all registered
-       * listeners along the way. The event will stop propagating if one of the listeners cancels it.
-       *
-       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
-       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
-       *
-       * @param {string} name Event name to emit.
-       * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
-       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
-       */
-      $emit: function(name, args) {
-        var empty = [],
-            namedListeners,
-            scope = this,
-            stopPropagation = false,
-            event = {
-              name: name,
-              targetScope: scope,
-              stopPropagation: function() {stopPropagation = true;},
-              preventDefault: function() {
-                event.defaultPrevented = true;
-              },
-              defaultPrevented: false
-            },
-            listenerArgs = concat([event], arguments, 1),
-            i, length;
-
-        do {
-          namedListeners = scope.$$listeners[name] || empty;
-          event.currentScope = scope;
-          for (i=0, length=namedListeners.length; i<length; i++) {
-
-            // if listeners were deregistered, defragment the array
-            if (!namedListeners[i]) {
-              namedListeners.splice(i, 1);
-              i--;
-              length--;
-              continue;
-            }
-            try {
-              namedListeners[i].apply(null, listenerArgs);
-              if (stopPropagation) return event;
-            } catch (e) {
-              $exceptionHandler(e);
-            }
-          }
-          //traverse upwards
-          scope = scope.$parent;
-        } while (scope);
-
-        return event;
-      },
-
-
-      /**
-       * @ngdoc function
-       * @name ng.$rootScope.Scope#$broadcast
-       * @methodOf ng.$rootScope.Scope
-       * @function
-       *
-       * @description
-       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
-       * registered {@link ng.$rootScope.Scope#$on} listeners.
-       *
-       * The event life cycle starts at the scope on which `$broadcast` was called. All
-       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get notified.
-       * Afterwards, the event propagates to all direct and indirect scopes of the current scope and
-       * calls all registered listeners along the way. The event cannot be canceled.
-       *
-       * Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed
-       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
-       *
-       * @param {string} name Event name to broadcast.
-       * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
-       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
-       */
-      $broadcast: function(name, args) {
-        var target = this,
-            current = target,
-            next = target,
-            event = {
-              name: name,
-              targetScope: target,
-              preventDefault: function() {
-                event.defaultPrevented = true;
-              },
-              defaultPrevented: false
-            },
-            listenerArgs = concat([event], arguments, 1),
-            listeners, i, length;
-
-        //down while you can, then up and next sibling or up and next sibling until back at root
-        do {
-          current = next;
-          event.currentScope = current;
-          listeners = current.$$listeners[name] || [];
-          for (i=0, length = listeners.length; i<length; i++) {
-            // if listeners were deregistered, defragment the array
-            if (!listeners[i]) {
-              listeners.splice(i, 1);
-              i--;
-              length--;
-              continue;
-            }
-
-            try {
-              listeners[i].apply(null, listenerArgs);
-            } catch(e) {
-              $exceptionHandler(e);
-            }
-          }
-
-          // Insanity Warning: scope depth-first traversal
-          // yes, this code is a bit crazy, but it works and we have tests to prove it!
-          // this piece should be kept in sync with the traversal in $digest
-          if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
-            while(current !== target && !(next = current.$$nextSibling)) {
-              current = current.$parent;
-            }
-          }
-        } while ((current = next));
-
-        return event;
-      }
-    };
-
-    var $rootScope = new Scope();
-
-    return $rootScope;
-
-
-    function beginPhase(phase) {
-      if ($rootScope.$$phase) {
-        throw Error($rootScope.$$phase + ' already in progress');
-      }
-
-      $rootScope.$$phase = phase;
-    }
-
-    function clearPhase() {
-      $rootScope.$$phase = null;
-    }
-
-    function compileToFn(exp, name) {
-      var fn = $parse(exp);
-      assertArgFn(fn, name);
-      return fn;
-    }
-
-    /**
-     * function used as an initial value for watchers.
-     * because it's unique we can easily tell it apart from other values
-     */
-    function initWatchVal() {}
-  }];
-}
-
-/**
- * !!! This is an undocumented "private" service !!!
- *
- * @name ng.$sniffer
- * @requires $window
- *
- * @property {boolean} history Does the browser support html5 history api ?
- * @property {boolean} hashchange Does the browser support hashchange event ?
- *
- * @description
- * This is very simple implementation of testing browser's features.
- */
-function $SnifferProvider() {
-  this.$get = ['$window', function($window) {
-    var eventSupport = {},
-        android = int((/android (\d+)/.exec(lowercase($window.navigator.userAgent)) || [])[1]);
-
-    return {
-      // Android has history.pushState, but it does not update location correctly
-      // so let's not use the history API at all.
-      // http://code.google.com/p/android/issues/detail?id=17471
-      // https://github.com/angular/angular.js/issues/904
-      history: !!($window.history && $window.history.pushState && !(android < 4)),
-      hashchange: 'onhashchange' in $window &&
-                  // IE8 compatible mode lies
-                  (!$window.document.documentMode || $window.document.documentMode > 7),
-      hasEvent: function(event) {
-        // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
-        // it. In particular the event is not fired when backspace or delete key are pressed or
-        // when cut operation is performed.
-        if (event == 'input' && msie == 9) return false;
-
-        if (isUndefined(eventSupport[event])) {
-          var divElm = $window.document.createElement('div');
-          eventSupport[event] = 'on' + event in divElm;
-        }
-
-        return eventSupport[event];
-      },
-      // TODO(i): currently there is no way to feature detect CSP without triggering alerts
-      csp: false
-    };
-  }];
-}
-
-/**
- * @ngdoc object
- * @name ng.$window
- *
- * @description
- * A reference to the browser's `window` object. While `window`
- * is globally available in JavaScript, it causes testability problems, because
- * it is a global variable. In angular we always refer to it through the
- * `$window` service, so it may be overriden, removed or mocked for testing.
- *
- * All expressions are evaluated with respect to current scope so they don't
- * suffer from window globality.
- *
- * @example
-   <doc:example>
-     <doc:source>
-       <script>
-         function Ctrl($scope, $window) {
-           $scope.$window = $window;
-           $scope.greeting = 'Hello, World!';
-         }
-       </script>
-       <div ng-controller="Ctrl">
-         <input type="text" ng-model="greeting" />
-         <button ng-click="$window.alert(greeting)">ALERT</button>
-       </div>
-     </doc:source>
-     <doc:scenario>
-      it('should display the greeting in the input box', function() {
-       input('greeting').enter('Hello, E2E Tests');
-       // If we click the button it will block the test runner
-       // element(':button').click();
-      });
-     </doc:scenario>
-   </doc:example>
- */
-function $WindowProvider(){
-  this.$get = valueFn(window);
-}
-
-/**
- * Parse headers into key value object
- *
- * @param {string} headers Raw headers as a string
- * @returns {Object} Parsed headers as key value object
- */
-function parseHeaders(headers) {
-  var parsed = {}, key, val, i;
-
-  if (!headers) return parsed;
-
-  forEach(headers.split('\n'), function(line) {
-    i = line.indexOf(':');
-    key = lowercase(trim(line.substr(0, i)));
-    val = trim(line.substr(i + 1));
-
-    if (key) {
-      if (parsed[key]) {
-        parsed[key] += ', ' + val;
-      } else {
-        parsed[key] = val;
-      }
-    }
-  });
-
-  return parsed;
-}
-
-
-/**
- * Returns a function that provides access to parsed headers.
- *
- * Headers are lazy parsed when first requested.
- * @see parseHeaders
- *
- * @param {(string|Object)} headers Headers to provide access to.
- * @returns {function(string=)} Returns a getter function which if called with:
- *
- *   - if called with single an argument returns a single header value or null
- *   - if called with no arguments returns an object containing all headers.
- */
-function headersGetter(headers) {
-  var headersObj = isObject(headers) ? headers : undefined;
-
-  return function(name) {
-    if (!headersObj) headersObj =  parseHeaders(headers);
-
-    if (name) {
-      return headersObj[lowercase(name)] || null;
-    }
-
-    return headersObj;
-  };
-}
-
-
-/**
- * Chain all given functions
- *
- * This function is used for both request and response transforming
- *
- * @param {*} data Data to transform.
- * @param {function(string=)} headers Http headers getter fn.
- * @param {(function|Array.<function>)} fns Function or an array of functions.
- * @returns {*} Transformed data.
- */
-function transformData(data, headers, fns) {
-  if (isFunction(fns))
-    return fns(data, headers);
-
-  forEach(fns, function(fn) {
-    data = fn(data, headers);
-  });
-
-  return data;
-}
-
-
-function isSuccess(status) {
-  return 200 <= status && status < 300;
-}
-
-
-function $HttpProvider() {
-  var JSON_START = /^\s*(\[|\{[^\{])/,
-      JSON_END = /[\}\]]\s*$/,
-      PROTECTION_PREFIX = /^\)\]\}',?\n/;
-
-  var $config = this.defaults = {
-    // transform incoming response data
-    transformResponse: [function(data) {
-      if (isString(data)) {
-        // strip json vulnerability protection prefix
-        data = data.replace(PROTECTION_PREFIX, '');
-        if (JSON_START.test(data) && JSON_END.test(data))
-          data = fromJson(data, true);
-      }
-      return data;
-    }],
-
-    // transform outgoing request data
-    transformRequest: [function(d) {
-      return isObject(d) && !isFile(d) ? toJson(d) : d;
-    }],
-
-    // default headers
-    headers: {
-      common: {
-        'Accept': 'application/json, text/plain, */*',
-        'X-Requested-With': 'XMLHttpRequest'
-      },
-      post: {'Content-Type': 'application/json;charset=utf-8'},
-      put:  {'Content-Type': 'application/json;charset=utf-8'}
-    }
-  };
-
-  var providerResponseInterceptors = this.responseInterceptors = [];
-
-  this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
-      function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
-
-    var defaultCache = $cacheFactory('$http'),
-        responseInterceptors = [];
-
-    forEach(providerResponseInterceptors, function(interceptor) {
-      responseInterceptors.push(
-          isString(interceptor)
-              ? $injector.get(interceptor)
-              : $injector.invoke(interceptor)
-      );
-    });
-
-
-    /**
-     * @ngdoc function
-     * @name ng.$http
-     * @requires $httpBackend
-     * @requires $browser
-     * @requires $cacheFactory
-     * @requires $rootScope
-     * @requires $q
-     * @requires $injector
-     *
-     * @description
-     * The `$http` service is a core Angular service that facilitates communication with the remote
-     * HTTP servers via the browser's {@link https://developer.mozilla.org/en/xmlhttprequest
-     * XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}.
-     *
-     * For unit testing applications that use `$http` service, see
-     * {@link ngMock.$httpBackend $httpBackend mock}.
-     *
-     * For a higher level of abstraction, please check out the {@link ngResource.$resource
-     * $resource} service.
-     *
-     * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
-     * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
-     * it is important to familiarize yourself with these APIs and the guarantees they provide.
-     *
-     *
-     * # General usage
-     * The `$http` service is a function which takes a single argument — a configuration object —
-     * that is used to generate an HTTP request and returns  a {@link ng.$q promise}
-     * with two $http specific methods: `success` and `error`.
-     *
-     * <pre>
-     *   $http({method: 'GET', url: '/someUrl'}).
-     *     success(function(data, status, headers, config) {
-     *       // this callback will be called asynchronously
-     *       // when the response is available
-     *     }).
-     *     error(function(data, status, headers, config) {
-     *       // called asynchronously if an error occurs
-     *       // or server returns response with an error status.
-     *     });
-     * </pre>
-     *
-     * Since the returned value of calling the $http function is a `promise`, you can also use
-     * the `then` method to register callbacks, and these callbacks will receive a single argument –
-     * an object representing the response. See the API signature and type info below for more
-     * details.
-     *
-     * A response status code between 200 and 299 is considered a success status and
-     * will result in the success callback being called. Note that if the response is a redirect,
-     * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
-     * called for such responses.
-     *
-     * # Shortcut methods
-     *
-     * Since all invocations of the $http service require passing in an HTTP method and URL, and
-     * POST/PUT requests require request data to be provided as well, shortcut methods
-     * were created:
-     *
-     * <pre>
-     *   $http.get('/someUrl').success(successCallback);
-     *   $http.post('/someUrl', data).success(successCallback);
-     * </pre>
-     *
-     * Complete list of shortcut methods:
-     *
-     * - {@link ng.$http#get $http.get}
-     * - {@link ng.$http#head $http.head}
-     * - {@link ng.$http#post $http.post}
-     * - {@link ng.$http#put $http.put}
-     * - {@link ng.$http#delete $http.delete}
-     * - {@link ng.$http#jsonp $http.jsonp}
-     *
-     *
-     * # Setting HTTP Headers
-     *
-     * The $http service will automatically add certain HTTP headers to all requests. These defaults
-     * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
-     * object, which currently contains this default configuration:
-     *
-     * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
-     *   - `Accept: application/json, text/plain, * / *`
-     *   - `X-Requested-With: XMLHttpRequest`
-     * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
-     *   - `Content-Type: application/json`
-     * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
-     *   - `Content-Type: application/json`
-     *
-     * To add or overwrite these defaults, simply add or remove a property from these configuration
-     * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
-     * with the lowercased HTTP method name as the key, e.g.
-     * `$httpProvider.defaults.headers.get['My-Header']='value'`.
-     *
-     * Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same
-     * fashion.
-     *
-     *
-     * # Transforming Requests and Responses
-     *
-     * Both requests and responses can be transformed using transform functions. By default, Angular
-     * applies these transformations:
-     *
-     * Request transformations:
-     *
-     * - If the `data` property of the request configuration object contains an object, serialize it into
-     *   JSON format.
-     *
-     * Response transformations:
-     *
-     *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
-     *  - If JSON response is detected, deserialize it using a JSON parser.
-     *
-     * To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and
-     * `$httpProvider.defaults.transformResponse` properties. These properties are by default an
-     * array of transform functions, which allows you to `push` or `unshift` a new transformation function into the
-     * transformation chain. You can also decide to completely override any default transformations by assigning your
-     * transformation functions to these properties directly without the array wrapper.
-     *
-     * Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or
-     * `transformResponse` properties of the configuration object passed into `$http`.
-     *
-     *
-     * # Caching
-     *
-     * To enable caching, set the configuration property `cache` to `true`. When the cache is
-     * enabled, `$http` stores the response from the server in local cache. Next time the
-     * response is served from the cache without sending a request to the server.
-     *
-     * Note that even if the response is served from cache, delivery of the data is asynchronous in
-     * the same way that real requests are.
-     *
-     * If there are multiple GET requests for the same URL that should be cached using the same
-     * cache, but the cache is not populated yet, only one request to the server will be made and
-     * the remaining requests will be fulfilled using the response from the first request.
-     *
-     *
-     * # Response interceptors
-     *
-     * Before you start creating interceptors, be sure to understand the
-     * {@link ng.$q $q and deferred/promise APIs}.
-     *
-     * For purposes of global error handling, authentication or any kind of synchronous or
-     * asynchronous preprocessing of received responses, it is desirable to be able to intercept
-     * responses for http requests before they are handed over to the application code that
-     * initiated these requests. The response interceptors leverage the {@link ng.$q
-     * promise apis} to fulfil this need for both synchronous and asynchronous preprocessing.
-     *
-     * The interceptors are service factories that are registered with the $httpProvider by
-     * adding them to the `$httpProvider.responseInterceptors` array. The factory is called and
-     * injected with dependencies (if specified) and returns the interceptor  — a function that
-     * takes a {@link ng.$q promise} and returns the original or a new promise.
-     *
-     * <pre>
-     *   // register the interceptor as a service
-     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
-     *     return function(promise) {
-     *       return promise.then(function(response) {
-     *         // do something on success
-     *       }, function(response) {
-     *         // do something on error
-     *         if (canRecover(response)) {
-     *           return responseOrNewPromise
-     *         }
-     *         return $q.reject(response);
-     *       });
-     *     }
-     *   });
-     *
-     *   $httpProvider.responseInterceptors.push('myHttpInterceptor');
-     *
-     *
-     *   // register the interceptor via an anonymous factory
-     *   $httpProvider.responseInterceptors.push(function($q, dependency1, dependency2) {
-     *     return function(promise) {
-     *       // same as above
-     *     }
-     *   });
-     * </pre>
-     *
-     *
-     * # Security Considerations
-     *
-     * When designing web applications, consider security threats from:
-     *
-     * - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
-     *   JSON vulnerability}
-     * - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF}
-     *
-     * Both server and the client must cooperate in order to eliminate these threats. Angular comes
-     * pre-configured with strategies that address these issues, but for this to work backend server
-     * cooperation is required.
-     *
-     * ## JSON Vulnerability Protection
-     *
-     * A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
-     * JSON vulnerability} allows third party website to turn your JSON resource URL into
-     * {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To
-     * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
-     * Angular will automatically strip the prefix before processing it as JSON.
-     *
-     * For example if your server needs to return:
-     * <pre>
-     * ['one','two']
-     * </pre>
-     *
-     * which is vulnerable to attack, your server can return:
-     * <pre>
-     * )]}',
-     * ['one','two']
-     * </pre>
-     *
-     * Angular will strip the prefix, before processing the JSON.
-     *
-     *
-     * ## Cross Site Request Forgery (XSRF) Protection
-     *
-     * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which
-     * an unauthorized site can gain your user's private data. Angular provides a mechanism
-     * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
-     * called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that
-     * runs on your domain could read the cookie, your server can be assured that the XHR came from
-     * JavaScript running on your domain.
-     *
-     * To take advantage of this, your server needs to set a token in a JavaScript readable session
-     * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
-     * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
-     * that only JavaScript running on your domain could have sent the request. The token must be
-     * unique for each user and must be verifiable by the server (to prevent the JavaScript from making
-     * up its own tokens). We recommend that the token is a digest of your site's authentication
-     * cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} for added security.
-     *
-     *
-     * @param {object} config Object describing the request to be made and how it should be
-     *    processed. The object has following properties:
-     *
-     *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
-     *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
-     *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned to
-     *      `?key1=value1&key2=value2` after the url. If the value is not a string, it will be JSONified.
-     *    - **data** – `{string|Object}` – Data to be sent as the request message data.
-     *    - **headers** – `{Object}` – Map of strings representing HTTP headers to send to the server.
-     *    - **transformRequest** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
-     *      transform function or an array of such functions. The transform function takes the http
-     *      request body and headers and returns its transformed (typically serialized) version.
-     *    - **transformResponse** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
-     *      transform function or an array of such functions. The transform function takes the http
-     *      response body and headers and returns its transformed (typically deserialized) version.
-     *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
-     *      GET request, otherwise if a cache instance built with
-     *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
-     *      caching.
-     *    - **timeout** – `{number}` – timeout in milliseconds.
-     *    - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the
-     *      XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
-     *      requests with credentials} for more information.
-     *
-     * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
-     *   standard `then` method and two http specific methods: `success` and `error`. The `then`
-     *   method takes two arguments a success and an error callback which will be called with a
-     *   response object. The `success` and `error` methods take a single argument - a function that
-     *   will be called when the request succeeds or fails respectively. The arguments passed into
-     *   these functions are destructured representation of the response object passed into the
-     *   `then` method. The response object has these properties:
-     *
-     *   - **data** – `{string|Object}` – The response body transformed with the transform functions.
-     *   - **status** – `{number}` – HTTP status code of the response.
-     *   - **headers** – `{function([headerName])}` – Header getter function.
-     *   - **config** – `{Object}` – The configuration object that was used to generate the request.
-     *
-     * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
-     *   requests. This is primarily meant to be used for debugging purposes.
-     *
-     *
-     * @example
-      <example>
-        <file name="index.html">
-          <div ng-controller="FetchCtrl">
-            <select ng-model="method">
-              <option>GET</option>
-              <option>JSONP</option>
-            </select>
-            <input type="text" ng-model="url" size="80"/>
-            <button ng-click="fetch()">fetch</button><br>
-            <button ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
-            <button ng-click="updateModel('JSONP', 'http://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">Sample JSONP</button>
-            <button ng-click="updateModel('JSONP', 'http://angularjs.org/doesntexist&callback=JSON_CALLBACK')">Invalid JSONP</button>
-            <pre>http status code: {{status}}</pre>
-            <pre>http response data: {{data}}</pre>
-          </div>
-        </file>
-        <file name="script.js">
-          function FetchCtrl($scope, $http, $templateCache) {
-            $scope.method = 'GET';
-            $scope.url = 'http-hello.html';
-
-            $scope.fetch = function() {
-              $scope.code = null;
-              $scope.response = null;
-
-              $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
-                success(function(data, status) {
-                  $scope.status = status;
-                  $scope.data = data;
-                }).
-                error(function(data, status) {
-                  $scope.data = data || "Request failed";
-                  $scope.status = status;
-              });
-            };
-
-            $scope.updateModel = function(method, url) {
-              $scope.method = method;
-              $scope.url = url;
-            };
-          }
-        </file>
-        <file name="http-hello.html">
-          Hello, $http!
-        </file>
-        <file name="scenario.js">
-          it('should make an xhr GET request', function() {
-            element(':button:contains("Sample GET")').click();
-            element(':button:contains("fetch")').click();
-            expect(binding('status')).toBe('200');
-            expect(binding('data')).toMatch(/Hello, \$http!/);
-          });
-
-          it('should make a JSONP request to angularjs.org', function() {
-            element(':button:contains("Sample JSONP")').click();
-            element(':button:contains("fetch")').click();
-            expect(binding('status')).toBe('200');
-            expect(binding('data')).toMatch(/Super Hero!/);
-          });
-
-          it('should make JSONP request to invalid URL and invoke the error handler',
-              function() {
-            element(':button:contains("Invalid JSONP")').click();
-            element(':button:contains("fetch")').click();
-            expect(binding('status')).toBe('0');
-            expect(binding('data')).toBe('Request failed');
-          });
-        </file>
-      </example>
-     */
-    function $http(config) {
-      config.method = uppercase(config.method);
-
-      var reqTransformFn = config.transformRequest || $config.transformRequest,
-          respTransformFn = config.transformResponse || $config.transformResponse,
-          defHeaders = $config.headers,
-          reqHeaders = extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']},
-              defHeaders.common, defHeaders[lowercase(config.method)], config.headers),
-          reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn),
-          promise;
-
-      // strip content-type if data is undefined
-      if (isUndefined(config.data)) {
-        delete reqHeaders['Content-Type'];
-      }
-
-      // send request
-      promise = sendReq(config, reqData, reqHeaders);
-
-
-      // transform future response
-      promise = promise.then(transformResponse, transformResponse);
-
-      // apply interceptors
-      forEach(responseInterceptors, function(interceptor) {
-        promise = interceptor(promise);
-      });
-
-      promise.success = function(fn) {
-        promise.then(function(response) {
-          fn(response.data, response.status, response.headers, config);
-        });
-        return promise;
-      };
-
-      promise.error = function(fn) {
-        promise.then(null, function(response) {
-          fn(response.data, response.status, response.headers, config);
-        });
-        return promise;
-      };
-
-      return promise;
-
-      function transformResponse(response) {
-        // make a copy since the response must be cacheable
-        var resp = extend({}, response, {
-          data: transformData(response.data, response.headers, respTransformFn)
-        });
-        return (isSuccess(response.status))
-          ? resp
-          : $q.reject(resp);
-      }
-    }
-
-    $http.pendingRequests = [];
-
-    /**
-     * @ngdoc method
-     * @name ng.$http#get
-     * @methodOf ng.$http
-     *
-     * @description
-     * Shortcut method to perform `GET` request.
-     *
-     * @param {string} url Relative or absolute URL specifying the destination of the request
-     * @param {Object=} config Optional configuration object
-     * @returns {HttpPromise} Future object
-     */
-
-    /**
-     * @ngdoc method
-     * @name ng.$http#delete
-     * @methodOf ng.$http
-     *
-     * @description
-     * Shortcut method to perform `DELETE` request.
-     *
-     * @param {string} url Relative or absolute URL specifying the destination of the request
-     * @param {Object=} config Optional configuration object
-     * @returns {HttpPromise} Future object
-     */
-
-    /**
-     * @ngdoc method
-     * @name ng.$http#head
-     * @methodOf ng.$http
-     *
-     * @description
-     * Shortcut method to perform `HEAD` request.
-     *
-     * @param {string} url Relative or absolute URL specifying the destination of the request
-     * @param {Object=} config Optional configuration object
-     * @returns {HttpPromise} Future object
-     */
-
-    /**
-     * @ngdoc method
-     * @name ng.$http#jsonp
-     * @methodOf ng.$http
-     *
-     * @description
-     * Shortcut method to perform `JSONP` request.
-     *
-     * @param {string} url Relative or absolute URL specifying the destination of the request.
-     *                     Should contain `JSON_CALLBACK` string.
-     * @param {Object=} config Optional configuration object
-     * @returns {HttpPromise} Future object
-     */
-    createShortMethods('get', 'delete', 'head', 'jsonp');
-
-    /**
-     * @ngdoc method
-     * @name ng.$http#post
-     * @methodOf ng.$http
-     *
-     * @description
-     * Shortcut method to perform `POST` request.
-     *
-     * @param {string} url Relative or absolute URL specifying the destination of the request
-     * @param {*} data Request content
-     * @param {Object=} config Optional configuration object
-     * @returns {HttpPromise} Future object
-     */
-
-    /**
-     * @ngdoc method
-     * @name ng.$http#put
-     * @methodOf ng.$http
-     *
-     * @description
-     * Shortcut method to perform `PUT` request.
-     *
-     * @param {string} url Relative or absolute URL specifying the destination of the request
-     * @param {*} data Request content
-     * @param {Object=} config Optional configuration object
-     * @returns {HttpPromise} Future object
-     */
-    createShortMethodsWithData('post', 'put');
-
-        /**
-         * @ngdoc property
-         * @name ng.$http#defaults
-         * @propertyOf ng.$http
-         *
-         * @description
-         * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
-         * default headers as well as request and response transformations.
-         *
-         * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
-         */
-    $http.defaults = $config;
-
-
-    return $http;
-
-
-    function createShortMethods(names) {
-      forEach(arguments, function(name) {
-        $http[name] = function(url, config) {
-          return $http(extend(config || {}, {
-            method: name,
-            url: url
-          }));
-        };
-      });
-    }
-
-
-    function createShortMethodsWithData(name) {
-      forEach(arguments, function(name) {
-        $http[name] = function(url, data, config) {
-          return $http(extend(config || {}, {
-            method: name,
-            url: url,
-            data: data
-          }));
-        };
-      });
-    }
-
-
-    /**
-     * Makes the request.
-     *
-     * !!! ACCESSES CLOSURE VARS:
-     * $httpBackend, $config, $log, $rootScope, defaultCache, $http.pendingRequests
-     */
-    function sendReq(config, reqData, reqHeaders) {
-      var deferred = $q.defer(),
-          promise = deferred.promise,
-          cache,
-          cachedResp,
-          url = buildUrl(config.url, config.params);
-
-      $http.pendingRequests.push(config);
-      promise.then(removePendingReq, removePendingReq);
-
-
-      if (config.cache && config.method == 'GET') {
-        cache = isObject(config.cache) ? config.cache : defaultCache;
-      }
-
-      if (cache) {
-        cachedResp = cache.get(url);
-        if (cachedResp) {
-          if (cachedResp.then) {
-            // cached request has already been sent, but there is no response yet
-            cachedResp.then(removePendingReq, removePendingReq);
-            return cachedResp;
-          } else {
-            // serving from cache
-            if (isArray(cachedResp)) {
-              resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2]));
-            } else {
-              resolvePromise(cachedResp, 200, {});
-            }
-          }
-        } else {
-          // put the promise for the non-transformed response into cache as a placeholder
-          cache.put(url, promise);
-        }
-      }
-
-      // if we won't have the response in cache, send the request to the backend
-      if (!cachedResp) {
-        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
-            config.withCredentials);
-      }
-
-      return promise;
-
-
-      /**
-       * Callback registered to $httpBackend():
-       *  - caches the response if desired
-       *  - resolves the raw $http promise
-       *  - calls $apply
-       */
-      function done(status, response, headersString) {
-        if (cache) {
-          if (isSuccess(status)) {
-            cache.put(url, [status, response, parseHeaders(headersString)]);
-          } else {
-            // remove promise from the cache
-            cache.remove(url);
-          }
-        }
-
-        resolvePromise(response, status, headersString);
-        $rootScope.$apply();
-      }
-
-
-      /**
-       * Resolves the raw $http promise.
-       */
-      function resolvePromise(response, status, headers) {
-        // normalize internal statuses to 0
-        status = Math.max(status, 0);
-
-        (isSuccess(status) ? deferred.resolve : deferred.reject)({
-          data: response,
-          status: status,
-          headers: headersGetter(headers),
-          config: config
-        });
-      }
-
-
-      function removePendingReq() {
-        var idx = indexOf($http.pendingRequests, config);
-        if (idx !== -1) $http.pendingRequests.splice(idx, 1);
-      }
-    }
-
-
-    function buildUrl(url, params) {
-          if (!params) return url;
-          var parts = [];
-          forEachSorted(params, function(value, key) {
-            if (value == null || value == undefined) return;
-            if (isObject(value)) {
-              value = toJson(value);
-            }
-            parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
-          });
-          return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
-        }
-
-
-  }];
-}
-
-var XHR = window.XMLHttpRequest || function() {
-  try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
-  try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
-  try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {}
-  throw new Error("This browser does not support XMLHttpRequest.");
-};
-
-
-/**
- * @ngdoc object
- * @name ng.$httpBackend
- * @requires $browser
- * @requires $window
- * @requires $document
- *
- * @description
- * HTTP backend used by the {@link ng.$http service} that delegates to
- * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
- *
- * You should never need to use this service directly, instead use the higher-level abstractions:
- * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
- *
- * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
- * $httpBackend} which can be trained with responses.
- */
-function $HttpBackendProvider() {
-  this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
-    return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks,
-        $document[0], $window.location.protocol.replace(':', ''));
-  }];
-}
-
-function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) {
-  // TODO(vojta): fix the signature
-  return function(method, url, post, callback, headers, timeout, withCredentials) {
-    $browser.$$incOutstandingRequestCount();
-    url = url || $browser.url();
-
-    if (lowercase(method) == 'jsonp') {
-      var callbackId = '_' + (callbacks.counter++).toString(36);
-      callbacks[callbackId] = function(data) {
-        callbacks[callbackId].data = data;
-      };
-
-      jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
-          function() {
-        if (callbacks[callbackId].data) {
-          completeRequest(callback, 200, callbacks[callbackId].data);
-        } else {
-          completeRequest(callback, -2);
-        }
-        delete callbacks[callbackId];
-      });
-    } else {
-      var xhr = new XHR();
-      xhr.open(method, url, true);
-      forEach(headers, function(value, key) {
-        if (value) xhr.setRequestHeader(key, value);
-      });
-
-      var status;
-
-      // In IE6 and 7, this might be called synchronously when xhr.send below is called and the
-      // response is in the cache. the promise api will ensure that to the app code the api is
-      // always async
-      xhr.onreadystatechange = function() {
-        if (xhr.readyState == 4) {
-          var responseHeaders = xhr.getAllResponseHeaders();
-
-          // TODO(vojta): remove once Firefox 21 gets released.
-          // begin: workaround to overcome Firefox CORS http response headers bug
-          // https://bugzilla.mozilla.org/show_bug.cgi?id=608735
-          // Firefox already patched in nightly. Should land in Firefox 21.
-
-          // CORS "simple response headers" http://www.w3.org/TR/cors/
-          var value,
-              simpleHeaders = ["Cache-Control", "Content-Language", "Content-Type",
-                                  "Expires", "Last-Modified", "Pragma"];
-          if (!responseHeaders) {
-            responseHeaders = "";
-            forEach(simpleHeaders, function (header) {
-              var value = xhr.getResponseHeader(header);
-              if (value) {
-                  responseHeaders += header + ": " + value + "\n";
-              }
-            });
-          }
-          // end of the workaround.
-
-          completeRequest(callback, status || xhr.status, xhr.responseText,
-                          responseHeaders);
-        }
-      };
-
-      if (withCredentials) {
-        xhr.withCredentials = true;
-      }
-
-      xhr.send(post || '');
-
-      if (timeout > 0) {
-        $browserDefer(function() {
-          status = -1;
-          xhr.abort();
-        }, timeout);
-      }
-    }
-
-
-    function completeRequest(callback, status, response, headersString) {
-      // URL_MATCH is defined in src/service/location.js
-      var protocol = (url.match(URL_MATCH) || ['', locationProtocol])[1];
-
-      // fix status code for file protocol (it's always 0)
-      status = (protocol == 'file') ? (response ? 200 : 404) : status;
-
-      // normalize IE bug (http://bugs.jquery.com/ticket/1450)
-      status = status == 1223 ? 204 : status;
-
-      callback(status, response, headersString);
-      $browser.$$completeOutstandingRequest(noop);
-    }
-  };
-
-  function jsonpReq(url, done) {
-    // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
-    // - fetches local scripts via XHR and evals them
-    // - adds and immediately removes script elements from the document
-    var script = rawDocument.createElement('script'),
-        doneWrapper = function() {
-          rawDocument.body.removeChild(script);
-          if (done) done();
-        };
-
-    script.type = 'text/javascript';
-    script.src = url;
-
-    if (msie) {
-      script.onreadystatechange = function() {
-        if (/loaded|complete/.test(script.readyState)) doneWrapper();
-      };
-    } else {
-      script.onload = script.onerror = doneWrapper;
-    }
-
-    rawDocument.body.appendChild(script);
-  }
-}
-
-/**
- * @ngdoc object
- * @name ng.$locale
- *
- * @description
- * $locale service provides localization rules for various Angular components. As of right now the
- * only public api is:
- *
- * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
- */
-function $LocaleProvider(){
-  this.$get = function() {
-    return {
-      id: 'en-us',
-
-      NUMBER_FORMATS: {
-        DECIMAL_SEP: '.',
-        GROUP_SEP: ',',
-        PATTERNS: [
-          { // Decimal Pattern
-            minInt: 1,
-            minFrac: 0,
-            maxFrac: 3,
-            posPre: '',
-            posSuf: '',
-            negPre: '-',
-            negSuf: '',
-            gSize: 3,
-            lgSize: 3
-          },{ //Currency Pattern
-            minInt: 1,
-            minFrac: 2,
-            maxFrac: 2,
-            posPre: '\u00A4',
-            posSuf: '',
-            negPre: '(\u00A4',
-            negSuf: ')',
-            gSize: 3,
-            lgSize: 3
-          }
-        ],
-        CURRENCY_SYM: '$'
-      },
-
-      DATETIME_FORMATS: {
-        MONTH: 'January,February,March,April,May,June,July,August,September,October,November,December'
-                .split(','),
-        SHORTMONTH:  'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
-        DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
-        SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
-        AMPMS: ['AM','PM'],
-        medium: 'MMM d, y h:mm:ss a',
-        short: 'M/d/yy h:mm a',
-        fullDate: 'EEEE, MMMM d, y',
-        longDate: 'MMMM d, y',
-        mediumDate: 'MMM d, y',
-        shortDate: 'M/d/yy',
-        mediumTime: 'h:mm:ss a',
-        shortTime: 'h:mm a'
-      },
-
-      pluralCat: function(num) {
-        if (num === 1) {
-          return 'one';
-        }
-        return 'other';
-      }
-    };
-  };
-}
-
-function $TimeoutProvider() {
-  this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler',
-       function($rootScope,   $browser,   $q,   $exceptionHandler) {
-    var deferreds = {};
-
-
-     /**
-      * @ngdoc function
-      * @name ng.$timeout
-      * @requires $browser
-      *
-      * @description
-      * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
-      * block and delegates any exceptions to
-      * {@link ng.$exceptionHandler $exceptionHandler} service.
-      *
-      * The return value of registering a timeout function is a promise, which will be resolved when
-      * the timeout is reached and the timeout function is executed.
-      *
-      * To cancel a timeout request, call `$timeout.cancel(promise)`.
-      *
-      * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
-      * synchronously flush the queue of deferred functions.
-      *
-      * @param {function()} fn A function, whose execution should be delayed.
-      * @param {number=} [delay=0] Delay in milliseconds.
-      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
-      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
-      * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
-      *   promise will be resolved with is the return value of the `fn` function.
-      */
-    function timeout(fn, delay, invokeApply) {
-      var deferred = $q.defer(),
-          promise = deferred.promise,
-          skipApply = (isDefined(invokeApply) && !invokeApply),
-          timeoutId, cleanup;
-
-      timeoutId = $browser.defer(function() {
-        try {
-          deferred.resolve(fn());
-        } catch(e) {
-          deferred.reject(e);
-          $exceptionHandler(e);
-        }
-
-        if (!skipApply) $rootScope.$apply();
-      }, delay);
-
-      cleanup = function() {
-        delete deferreds[promise.$$timeoutId];
-      };
-
-      promise.$$timeoutId = timeoutId;
-      deferreds[timeoutId] = deferred;
-      promise.then(cleanup, cleanup);
-
-      return promise;
-    }
-
-
-     /**
-      * @ngdoc function
-      * @name ng.$timeout#cancel
-      * @methodOf ng.$timeout
-      *
-      * @description
-      * Cancels a task associated with the `promise`. As a result of this, the promise will be
-      * resolved with a rejection.
-      *
-      * @param {Promise=} promise Promise returned by the `$timeout` function.
-      * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
-      *   canceled.
-      */
-    timeout.cancel = function(promise) {
-      if (promise && promise.$$timeoutId in deferreds) {
-        deferreds[promise.$$timeoutId].reject('canceled');
-        return $browser.defer.cancel(promise.$$timeoutId);
-      }
-      return false;
-    };
-
-    return timeout;
-  }];
-}
-
-/**
- * @ngdoc object
- * @name ng.$filterProvider
- * @description
- *
- * Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To
- * achieve this a filter definition consists of a factory function which is annotated with dependencies and is
- * responsible for creating a filter function.
- *
- * <pre>
- *   // Filter registration
- *   function MyModule($provide, $filterProvider) {
- *     // create a service to demonstrate injection (not always needed)
- *     $provide.value('greet', function(name){
- *       return 'Hello ' + name + '!';
- *     });
- *
- *     // register a filter factory which uses the
- *     // greet service to demonstrate DI.
- *     $filterProvider.register('greet', function(greet){
- *       // return the filter function which uses the greet service
- *       // to generate salutation
- *       return function(text) {
- *         // filters need to be forgiving so check input validity
- *         return text && greet(text) || text;
- *       };
- *     });
- *   }
- * </pre>
- *
- * The filter function is registered with the `$injector` under the filter name suffixe with `Filter`.
- * <pre>
- *   it('should be the same instance', inject(
- *     function($filterProvider) {
- *       $filterProvider.register('reverse', function(){
- *         return ...;
- *       });
- *     },
- *     function($filter, reverseFilter) {
- *       expect($filter('reverse')).toBe(reverseFilter);
- *     });
- * </pre>
- *
- *
- * For more information about how angular filters work, and how to create your own filters, see
- * {@link guide/dev_guide.templates.filters Understanding Angular Filters} in the angular Developer
- * Guide.
- */
-/**
- * @ngdoc method
- * @name ng.$filterProvider#register
- * @methodOf ng.$filterProvider
- * @description
- * Register filter factory function.
- *
- * @param {String} name Name of the filter.
- * @param {function} fn The filter factory function which is injectable.
- */
-
-
-/**
- * @ngdoc function
- * @name ng.$filter
- * @function
- * @description
- * Filters are used for formatting data displayed to the user.
- *
- * The general syntax in templates is as follows:
- *
- *         {{ expression [| filter_name[:parameter_value] ... ] }}
- *
- * @param {String} name Name of the filter function to retrieve
- * @return {Function} the filter function
- */
-$FilterProvider.$inject = ['$provide'];
-function $FilterProvider($provide) {
-  var suffix = 'Filter';
-
-  function register(name, factory) {
-    return $provide.factory(name + suffix, factory);
-  }
-  this.register = register;
-
-  this.$get = ['$injector', function($injector) {
-    return function(name) {
-      return $injector.get(name + suffix);
-    }
-  }];
-
-  ////////////////////////////////////////
-
-  register('currency', currencyFilter);
-  register('date', dateFilter);
-  register('filter', filterFilter);
-  register('json', jsonFilter);
-  register('limitTo', limitToFilter);
-  register('lowercase', lowercaseFilter);
-  register('number', numberFilter);
-  register('orderBy', orderByFilter);
-  register('uppercase', uppercaseFilter);
-}
-
-/**
- * @ngdoc filter
- * @name ng.filter:filter
- * @function
- *
- * @description
- * Selects a subset of items from `array` and returns it as a new array.
- *
- * Note: This function is used to augment the `Array` type in Angular expressions. See
- * {@link ng.$filter} for more information about Angular arrays.
- *
- * @param {Array} array The source array.
- * @param {string|Object|function()} expression The predicate to be used for selecting items from
- *   `array`.
- *
- *   Can be one of:
- *
- *   - `string`: Predicate that results in a substring match using the value of `expression`
- *     string. All strings or objects with string properties in `array` that contain this string
- *     will be returned. The predicate can be negated by prefixing the string with `!`.
- *
- *   - `Object`: A pattern object can be used to filter specific properties on objects contained
- *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
- *     which have property `name` containing "M" and property `phone` containing "1". A special
- *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
- *     property of the object. That's equivalent to the simple substring match with a `string`
- *     as described above.
- *
- *   - `function`: A predicate function can be used to write arbitrary filters. The function is
- *     called for each element of `array`. The final result is an array of those elements that
- *     the predicate returned true for.
- *
- * @example
-   <doc:example>
-     <doc:source>
-       <div ng-init="friends = [{name:'John', phone:'555-1276'},
-                                {name:'Mary', phone:'800-BIG-MARY'},
-                                {name:'Mike', phone:'555-4321'},
-                                {name:'Adam', phone:'555-5678'},
-                                {name:'Julie', phone:'555-8765'}]"></div>
-
-       Search: <input ng-model="searchText">
-       <table id="searchTextResults">
-         <tr><th>Name</th><th>Phone</th></tr>
-         <tr ng-repeat="friend in friends | filter:searchText">
-           <td>{{friend.name}}</td>
-           <td>{{friend.phone}}</td>
-         </tr>
-       </table>
-       <hr>
-       Any: <input ng-model="search.$"> <br>
-       Name only <input ng-model="search.name"><br>
-       Phone only <input ng-model="search.phone"><br>
-       <table id="searchObjResults">
-         <tr><th>Name</th><th>Phone</th></tr>
-         <tr ng-repeat="friend in friends | filter:search">
-           <td>{{friend.name}}</td>
-           <td>{{friend.phone}}</td>
-         </tr>
-       </table>
-     </doc:source>
-     <doc:scenario>
-       it('should search across all fields when filtering with a string', function() {
-         input('searchText').enter('m');
-         expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')).
-           toEqual(['Mary', 'Mike', 'Adam']);
-
-         input('searchText').enter('76');
-         expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')).
-           toEqual(['John', 'Julie']);
-       });
-
-       it('should search in specific fields when filtering with a predicate object', function() {
-         input('search.$').enter('i');
-         expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')).
-           toEqual(['Mary', 'Mike', 'Julie']);
-       });
-     </doc:scenario>
-   </doc:example>
- */
-function filterFilter() {
-  return function(array, expression) {
-    if (!isArray(array)) return array;
-    var predicates = [];
-    predicates.check = function(value) {
-      for (var j = 0; j < predicates.length; j++) {
-        if(!predicates[j](value)) {
-          return false;
-        }
-      }
-      return true;
-    };
-    var search = function(obj, text){
-      if (text.charAt(0) === '!') {
-        return !search(obj, text.substr(1));
-      }
-      switch (typeof obj) {
-        case "boolean":
-        case "number":
-        case "string":
-          return ('' + obj).toLowerCase().indexOf(text) > -1;
-        case "object":
-          for ( var objKey in obj) {
-            if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
-              return true;
-            }
-          }
-          return false;
-        case "array":
-          for ( var i = 0; i < obj.length; i++) {
-            if (search(obj[i], text)) {
-              return true;
-            }
-          }
-          return false;
-        default:
-          return false;
-      }
-    };
-    switch (typeof expression) {
-      case "boolean":
-      case "number":
-      case "string":
-        expression = {$:expression};
-      case "object":
-        for (var key in expression) {
-          if (key == '$') {
-            (function() {
-              var text = (''+expression[key]).toLowerCase();
-              if (!text) return;
-              predicates.push(function(value) {
-                return search(value, text);
-              });
-            })();
-          } else {
-            (function() {
-              var path = key;
-              var text = (''+expression[key]).toLowerCase();
-              if (!text) return;
-              predicates.push(function(value) {
-                return search(getter(value, path), text);
-              });
-            })();
-          }
-        }
-        break;
-      case 'function':
-        predicates.push(expression);
-        break;
-      default:
-        return array;
-    }
-    var filtered = [];
-    for ( var j = 0; j < array.length; j++) {
-      var value = array[j];
-      if (predicates.check(value)) {
-        filtered.push(value);
-      }
-    }
-    return filtered;
-  }
-}
-
-/**
- * @ngdoc filter
- * @name ng.filter:currency
- * @function
- *
- * @description
- * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
- * symbol for current locale is used.
- *
- * @param {number} amount Input to filter.
- * @param {string=} symbol Currency symbol or identifier to be displayed.
- * @returns {string} Formatted number.
- *
- *
- * @example
-   <doc:example>
-     <doc:source>
-       <script>
-         function Ctrl($scope) {
-           $scope.amount = 1234.56;
-         }
-       </script>
-       <div ng-controller="Ctrl">
-         <input type="number" ng-model="amount"> <br>
-         default currency symbol ($): {{amount | currency}}<br>
-         custom currency identifier (USD$): {{amount | currency:"USD$"}}
-       </div>
-     </doc:source>
-     <doc:scenario>
-       it('should init with 1234.56', function() {
-         expect(binding('amount | currency')).toBe('$1,234.56');
-         expect(binding('amount | currency:"USD$"')).toBe('USD$1,234.56');
-       });
-       it('should update', function() {
-         input('amount').enter('-1234');
-         expect(binding('amount | currency')).toBe('($1,234.00)');
-         expect(binding('amount | currency:"USD$"')).toBe('(USD$1,234.00)');
-       });
-     </doc:scenario>
-   </doc:example>
- */
-currencyFilter.$inject = ['$locale'];
-function currencyFilter($locale) {
-  var formats = $locale.NUMBER_FORMATS;
-  return function(amount, currencySymbol){
-    if (isUndefined(currencySymbol)) currencySymbol = formats.CURRENCY_SYM;
-    return formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2).
-                replace(/\u00A4/g, currencySymbol);
-  };
-}
-
-/**
- * @ngdoc filter
- * @name ng.filter:number
- * @function
- *
- * @description
- * Formats a number as text.
- *
- * If the input is not a number an empty string is returned.
- *
- * @param {number|string} number Number to format.
- * @param {(number|string)=} [fractionSize=2] Number of decimal places to round the number to.
- * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
- *
- * @example
-   <doc:example>
-     <doc:source>
-       <script>
-         function Ctrl($scope) {
-           $scope.val = 1234.56789;
-         }
-       </script>
-       <div ng-controller="Ctrl">
-         Enter number: <input ng-model='val'><br>
-         Default formatting: {{val | number}}<br>
-         No fractions: {{val | number:0}}<br>
-         Negative number: {{-val | number:4}}
-       </div>
-     </doc:source>
-     <doc:scenario>
-       it('should format numbers', function() {
-         expect(binding('val | number')).toBe('1,234.568');
-         expect(binding('val | number:0')).toBe('1,235');
-         expect(binding('-val | number:4')).toBe('-1,234.5679');
-       });
-
-       it('should update', function() {
-         input('val').enter('3374.333');
-         expect(binding('val | number')).toBe('3,374.333');
-         expect(binding('val | number:0')).toBe('3,374');
-         expect(binding('-val | number:4')).toBe('-3,374.3330');
-       });
-     </doc:scenario>
-   </doc:example>
- */
-
-
-numberFilter.$inject = ['$locale'];
-function numberFilter($locale) {
-  var formats = $locale.NUMBER_FORMATS;
-  return function(number, fractionSize) {
-    return formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
-      fractionSize);
-  };
-}
-
-var DECIMAL_SEP = '.';
-function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
-  if (isNaN(number) || !isFinite(number)) return '';
-
-  var isNegative = number < 0;
-  number = Math.abs(number);
-  var numStr = number + '',
-      formatedText = '',
-      parts = [];
-
-  var hasExponent = false;
-  if (numStr.indexOf('e') !== -1) {
-    var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
-    if (match && match[2] == '-' && match[3] > fractionSize + 1) {
-      numStr = '0';
-    } else {
-      formatedText = numStr;
-      hasExponent = true;
-    }
-  }
-
-  if (!hasExponent) {
-    var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
-
-    // determine fractionSize if it is not specified
-    if (isUndefined(fractionSize)) {
-      fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
-    }
-
-    var pow = Math.pow(10, fractionSize);
-    number = Math.round(number * pow) / pow;
-    var fraction = ('' + number).split(DECIMAL_SEP);
-    var whole = fraction[0];
-    fraction = fraction[1] || '';
-
-    var pos = 0,
-        lgroup = pattern.lgSize,
-        group = pattern.gSize;
-
-    if (whole.length >= (lgroup + group)) {
-      pos = whole.length - lgroup;
-      for (var i = 0; i < pos; i++) {
-        if ((pos - i)%group === 0 && i !== 0) {
-          formatedText += groupSep;
-        }
-        formatedText += whole.charAt(i);
-      }
-    }
-
-    for (i = pos; i < whole.length; i++) {
-      if ((whole.length - i)%lgroup === 0 && i !== 0) {
-        formatedText += groupSep;
-      }
-      formatedText += whole.charAt(i);
-    }
-
-    // format fraction part.
-    while(fraction.length < fractionSize) {
-      fraction += '0';
-    }
-
-    if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
-  }
-
-  parts.push(isNegative ? pattern.negPre : pattern.posPre);
-  parts.push(formatedText);
-  parts.push(isNegative ? pattern.negSuf : pattern.posSuf);
-  return parts.join('');
-}
-
-function padNumber(num, digits, trim) {
-  var neg = '';
-  if (num < 0) {
-    neg =  '-';
-    num = -num;
-  }
-  num = '' + num;
-  while(num.length < digits) num = '0' + num;
-  if (trim)
-    num = num.substr(num.length - digits);
-  return neg + num;
-}
-
-
-function dateGetter(name, size, offset, trim) {
-  offset = offset || 0;
-  return function(date) {
-    var value = date['get' + name]();
-    if (offset > 0 || value > -offset)
-      value += offset;
-    if (value === 0 && offset == -12 ) value = 12;
-    return padNumber(value, size, trim);
-  };
-}
-
-function dateStrGetter(name, shortForm) {
-  return function(date, formats) {
-    var value = date['get' + name]();
-    var get = uppercase(shortForm ? ('SHORT' + name) : name);
-
-    return formats[get][value];
-  };
-}
-
-function timeZoneGetter(date) {
-  var zone = -1 * date.getTimezoneOffset();
-  var paddedZone = (zone >= 0) ? "+" : "";
-
-  paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
-                padNumber(Math.abs(zone % 60), 2);
-
-  return paddedZone;
-}
-
-function ampmGetter(date, formats) {
-  return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
-}
-
-var DATE_FORMATS = {
-  yyyy: dateGetter('FullYear', 4),
-    yy: dateGetter('FullYear', 2, 0, true),
-     y: dateGetter('FullYear', 1),
-  MMMM: dateStrGetter('Month'),
-   MMM: dateStrGetter('Month', true),
-    MM: dateGetter('Month', 2, 1),
-     M: dateGetter('Month', 1, 1),
-    dd: dateGetter('Date', 2),
-     d: dateGetter('Date', 1),
-    HH: dateGetter('Hours', 2),
-     H: dateGetter('Hours', 1),
-    hh: dateGetter('Hours', 2, -12),
-     h: dateGetter('Hours', 1, -12),
-    mm: dateGetter('Minutes', 2),
-     m: dateGetter('Minutes', 1),
-    ss: dateGetter('Seconds', 2),
-     s: dateGetter('Seconds', 1),
-  EEEE: dateStrGetter('Day'),
-   EEE: dateStrGetter('Day', true),
-     a: ampmGetter,
-     Z: timeZoneGetter
-};
-
-var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,
-    NUMBER_STRING = /^\d+$/;
-
-/**
- * @ngdoc filter
- * @name ng.filter:date
- * @function
- *
- * @description
- *   Formats `date` to a string based on the requested `format`.
- *
- *   `format` string can be composed of the following elements:
- *
- *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
- *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
- *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
- *   * `'MMMM'`: Month in year (January-December)
- *   * `'MMM'`: Month in year (Jan-Dec)
- *   * `'MM'`: Month in year, padded (01-12)
- *   * `'M'`: Month in year (1-12)
- *   * `'dd'`: Day in month, padded (01-31)
- *   * `'d'`: Day in month (1-31)
- *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
- *   * `'EEE'`: Day in Week, (Sun-Sat)
- *   * `'HH'`: Hour in day, padded (00-23)
- *   * `'H'`: Hour in day (0-23)
- *   * `'hh'`: Hour in am/pm, padded (01-12)
- *   * `'h'`: Hour in am/pm, (1-12)
- *   * `'mm'`: Minute in hour, padded (00-59)
- *   * `'m'`: Minute in hour (0-59)
- *   * `'ss'`: Second in minute, padded (00-59)
- *   * `'s'`: Second in minute (0-59)
- *   * `'a'`: am/pm marker
- *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
- *
- *   `format` string can also be one of the following predefined
- *   {@link guide/i18n localizable formats}:
- *
- *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
- *     (e.g. Sep 3, 2010 12:05:08 pm)
- *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 pm)
- *   * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US  locale
- *     (e.g. Friday, September 3, 2010)
- *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010
- *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
- *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
- *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm)
- *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm)
- *
- *   `format` string can contain literal values. These need to be quoted with single quotes (e.g.
- *   `"h 'in the morning'"`). In order to output single quote, use two single quotes in a sequence
- *   (e.g. `"h o''clock"`).
- *
- * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
- *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
- *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
- *    specified in the string input, the time is considered to be in the local timezone.
- * @param {string=} format Formatting rules (see Description). If not specified,
- *    `mediumDate` is used.
- * @returns {string} Formatted string or the input if input is not recognized as date/millis.
- *
- * @example
-   <doc:example>
-     <doc:source>
-       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
-           {{1288323623006 | date:'medium'}}<br>
-       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
-          {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}<br>
-       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
-          {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}<br>
-     </doc:source>
-     <doc:scenario>
-       it('should format date', function() {
-         expect(binding("1288323623006 | date:'medium'")).
-            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
-         expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).
-            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
-         expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).
-            toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
-       });
-     </doc:scenario>
-   </doc:example>
- */
-dateFilter.$inject = ['$locale'];
-function dateFilter($locale) {
-
-
-  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
-  function jsonStringToDate(string){
-    var match;
-    if (match = string.match(R_ISO8601_STR)) {
-      var date = new Date(0),
-          tzHour = 0,
-          tzMin  = 0;
-      if (match[9]) {
-        tzHour = int(match[9] + match[10]);
-        tzMin = int(match[9] + match[11]);
-      }
-      date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
-      date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
-      return date;
-    }
-    return string;
-  }
-
-
-  return function(date, format) {
-    var text = '',
-        parts = [],
-        fn, match;
-
-    format = format || 'mediumDate';
-    format = $locale.DATETIME_FORMATS[format] || format;
-    if (isString(date)) {
-      if (NUMBER_STRING.test(date)) {
-        date = int(date);
-      } else {
-        date = jsonStringToDate(date);
-      }
-    }
-
-    if (isNumber(date)) {
-      date = new Date(date);
-    }
-
-    if (!isDate(date)) {
-      return date;
-    }
-
-    while(format) {
-      match = DATE_FORMATS_SPLIT.exec(format);
-      if (match) {
-        parts = concat(parts, match, 1);
-        format = parts.pop();
-      } else {
-        parts.push(format);
-        format = null;
-      }
-    }
-
-    forEach(parts, function(value){
-      fn = DATE_FORMATS[value];
-      text += fn ? fn(date, $locale.DATETIME_FORMATS)
-                 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
-    });
-
-    return text;
-  };
-}
-
-
-/**
- * @ngdoc filter
- * @name ng.filter:json
- * @function
- *
- * @description
- *   Allows you to convert a JavaScript object into JSON string.
- *
- *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
- *   the binding is automatically converted to JSON.
- *
- * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
- * @returns {string} JSON string.
- *
- *
- * @example:
-   <doc:example>
-     <doc:source>
-       <pre>{{ {'name':'value'} | json }}</pre>
-     </doc:source>
-     <doc:scenario>
-       it('should jsonify filtered objects', function() {
-         expect(binding("{'name':'value'}")).toMatch(/\{\n  "name": ?"value"\n}/);
-       });
-     </doc:scenario>
-   </doc:example>
- *
- */
-function jsonFilter() {
-  return function(object) {
-    return toJson(object, true);
-  };
-}
-
-
-/**
- * @ngdoc filter
- * @name ng.filter:lowercase
- * @function
- * @description
- * Converts string to lowercase.
- * @see angular.lowercase
- */
-var lowercaseFilter = valueFn(lowercase);
-
-
-/**
- * @ngdoc filter
- * @name ng.filter:uppercase
- * @function
- * @description
- * Converts string to uppercase.
- * @see angular.uppercase
- */
-var uppercaseFilter = valueFn(uppercase);
-
-/**
- * @ngdoc function
- * @name ng.filter:limitTo
- * @function
- *
- * @description
- * Creates a new array containing only a specified number of elements in an array. The elements
- * are taken from either the beginning or the end of the source array, as specified by the
- * value and sign (positive or negative) of `limit`.
- *
- * Note: This function is used to augment the `Array` type in Angular expressions. See
- * {@link ng.$filter} for more information about Angular arrays.
- *
- * @param {Array} array Source array to be limited.
- * @param {string|Number} limit The length of the returned array. If the `limit` number is
- *     positive, `limit` number of items from the beginning of the source array are copied.
- *     If the number is negative, `limit` number  of items from the end of the source array are
- *     copied. The `limit` will be trimmed if it exceeds `array.length`
- * @returns {Array} A new sub-array of length `limit` or less if input array had less than `limit`
- *     elements.
- *
- * @example
-   <doc:example>
-     <doc:source>
-       <script>
-         function Ctrl($scope) {
-           $scope.numbers = [1,2,3,4,5,6,7,8,9];
-           $scope.limit = 3;
-         }
-       </script>
-       <div ng-controller="Ctrl">
-         Limit {{numbers}} to: <input type="integer" ng-model="limit">
-         <p>Output: {{ numbers | limitTo:limit }}</p>
-       </div>
-     </doc:source>
-     <doc:scenario>
-       it('should limit the numer array to first three items', function() {
-         expect(element('.doc-example-live input[ng-model=limit]').val()).toBe('3');
-         expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3]');
-       });
-
-       it('should update the output when -3 is entered', function() {
-         input('limit').enter(-3);
-         expect(binding('numbers | limitTo:limit')).toEqual('[7,8,9]');
-       });
-
-       it('should not exceed the maximum size of input array', function() {
-         input('limit').enter(100);
-         expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3,4,5,6,7,8,9]');
-       });
-     </doc:scenario>
-   </doc:example>
- */
-function limitToFilter(){
-  return function(array, limit) {
-    if (!(array instanceof Array)) return array;
-    limit = int(limit);
-    var out = [],
-      i, n;
-
-    // check that array is iterable
-    if (!array || !(array instanceof Array))
-      return out;
-
-    // if abs(limit) exceeds maximum length, trim it
-    if (limit > array.length)
-      limit = array.length;
-    else if (limit < -array.length)
-      limit = -array.length;
-
-    if (limit > 0) {
-      i = 0;
-      n = limit;
-    } else {
-      i = array.length + limit;
-      n = array.length;
-    }
-
-    for (; i<n; i++) {
-      out.push(array[i]);
-    }
-
-    return out;
-  }
-}
-
-/**
- * @ngdoc function
- * @name ng.filter:orderBy
- * @function
- *
- * @description
- * Orders a specified `array` by the `expression` predicate.
- *
- * Note: this function is used to augment the `Array` type in Angular expressions. See
- * {@link ng.$filter} for more informaton about Angular arrays.
- *
- * @param {Array} array The array to sort.
- * @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be
- *    used by the comparator to determine the order of elements.
- *
- *    Can be one of:
- *
- *    - `function`: Getter function. The result of this function will be sorted using the
- *      `<`, `=`, `>` operator.
- *    - `string`: An Angular expression which evaluates to an object to order by, such as 'name'
- *      to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control
- *      ascending or descending sort order (for example, +name or -name).
- *    - `Array`: An array of function or string predicates. The first predicate in the array
- *      is used for sorting, but when two items are equivalent, the next predicate is used.
- *
- * @param {boolean=} reverse Reverse the order the array.
- * @returns {Array} Sorted copy of the source array.
- *
- * @example
-   <doc:example>
-     <doc:source>
-       <script>
-         function Ctrl($scope) {
-           $scope.friends =
-               [{name:'John', phone:'555-1212', age:10},
-                {name:'Mary', phone:'555-9876', age:19},
-                {name:'Mike', phone:'555-4321', age:21},
-                {name:'Adam', phone:'555-5678', age:35},
-                {name:'Julie', phone:'555-8765', age:29}]
-           $scope.predicate = '-age';
-         }
-       </script>
-       <div ng-controller="Ctrl">
-         <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
-         <hr/>
-         [ <a href="" ng-click="predicate=''">unsorted</a> ]
-         <table class="friend">
-           <tr>
-             <th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a>
-                 (<a href ng-click="predicate = '-name'; reverse=false">^</a>)</th>
-             <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
-             <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
-           </tr>
-           <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
-             <td>{{friend.name}}</td>
-             <td>{{friend.phone}}</td>
-             <td>{{friend.age}}</td>
-           </tr>
-         </table>
-       </div>
-     </doc:source>
-     <doc:scenario>
-       it('should be reverse ordered by aged', function() {
-         expect(binding('predicate')).toBe('-age');
-         expect(repeater('table.friend', 'friend in friends').column('friend.age')).
-           toEqual(['35', '29', '21', '19', '10']);
-         expect(repeater('table.friend', 'friend in friends').column('friend.name')).
-           toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']);
-       });
-
-       it('should reorder the table when user selects different predicate', function() {
-         element('.doc-example-live a:contains("Name")').click();
-         expect(repeater('table.friend', 'friend in friends').column('friend.name')).
-           toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']);
-         expect(repeater('table.friend', 'friend in friends').column('friend.age')).
-           toEqual(['35', '10', '29', '19', '21']);
-
-         element('.doc-example-live a:contains("Phone")').click();
-         expect(repeater('table.friend', 'friend in friends').column('friend.phone')).
-           toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']);
-         expect(repeater('table.friend', 'friend in friends').column('friend.name')).
-           toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']);
-       });
-     </doc:scenario>
-   </doc:example>
- */
-orderByFilter.$inject = ['$parse'];
-function orderByFilter($parse){
-  return function(array, sortPredicate, reverseOrder) {
-    if (!isArray(array)) return array;
-    if (!sortPredicate) return array;
-    sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
-    sortPredicate = map(sortPredicate, function(predicate){
-      var descending = false, get = predicate || identity;
-      if (isString(predicate)) {
-        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
-          descending = predicate.charAt(0) == '-';
-          predicate = predicate.substring(1);
-        }
-        get = $parse(predicate);
-      }
-      return reverseComparator(function(a,b){
-        return compare(get(a),get(b));
-      }, descending);
-    });
-    var arrayCopy = [];
-    for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
-    return arrayCopy.sort(reverseComparator(comparator, reverseOrder));
-
-    function comparator(o1, o2){
-      for ( var i = 0; i < sortPredicate.length; i++) {
-        var comp = sortPredicate[i](o1, o2);
-        if (comp !== 0) return comp;
-      }
-      return 0;
-    }
-    function reverseComparator(comp, descending) {
-      return toBoolean(descending)
-          ? function(a,b){return comp(b,a);}
-          : comp;
-    }
-    function compare(v1, v2){
-      var t1 = typeof v1;
-      var t2 = typeof v2;
-      if (t1 == t2) {
-        if (t1 == "string") v1 = v1.toLowerCase();
-        if (t1 == "string") v2 = v2.toLowerCase();
-        if (v1 === v2) return 0;
-        return v1 < v2 ? -1 : 1;
-      } else {
-        return t1 < t2 ? -1 : 1;
-      }
-    }
-  }
-}
-
-function ngDirective(directive) {
-  if (isFunction(directive)) {
-    directive = {
-      link: directive
-    }
-  }
-  directive.restrict = directive.restrict || 'AC';
-  return valueFn(directive);
-}
-
-/**
- * @ngdoc directive
- * @name ng.directive:a
- * @restrict E
- *
- * @description
- * Modifies the default behavior of html A tag, so that the default action is prevented when href
- * attribute is empty.
- *
- * The reasoning for this change is to allow easy creation of action links with `ngClick` directive
- * without changing the location or causing page reloads, e.g.:
- * `<a href="" ng-click="model.$save()">Save</a>`
- */
-var htmlAnchorDirective = valueFn({
-  restrict: 'E',
-  compile: function(element, attr) {
-
-    if (msie <= 8) {
-
-      // turn <a href ng-click="..">link</a> into a stylable link in IE
-      // but only if it doesn't have name attribute, in which case it's an anchor
-      if (!attr.href && !attr.name) {
-        attr.$set('href', '');
-      }
-
-      // add a comment node to anchors to workaround IE bug that causes element content to be reset
-      // to new attribute content if attribute is updated with value containing @ and element also
-      // contains value with @
-      // see issue #1949
-      element.append(document.createComment('IE fix'));
-    }
-
-    return function(scope, element) {
-      element.bind('click', function(event){
-        // if we have no href url, then don't navigate anywhere.
-        if (!element.attr('href')) {
-          event.preventDefault();
-        }
-      });
-    }
-  }
-});
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngHref
- * @restrict A
- *
- * @description
- * Using Angular markup like {{hash}} in an href attribute makes
- * the page open to a wrong URL, if the user clicks that link before
- * angular has a chance to replace the {{hash}} with actual URL, the
- * link will be broken and will most likely return a 404 error.
- * The `ngHref` directive solves this problem.
- *
- * The buggy way to write it:
- * <pre>
- * <a href="http://www.gravatar.com/avatar/{{hash}}"/>
- * </pre>
- *
- * The correct way to write it:
- * <pre>
- * <a ng-href="http://www.gravatar.com/avatar/{{hash}}"/>
- * </pre>
- *
- * @element A
- * @param {template} ngHref any string which can contain `{{}}` markup.
- *
- * @example
- * This example uses `link` variable inside `href` attribute:
-    <doc:example>
-      <doc:source>
-        <input ng-model="value" /><br />
-        <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
-        <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
-        <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
-        <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
-        <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
-        <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
-      </doc:source>
-      <doc:scenario>
-        it('should execute ng-click but not reload when href without value', function() {
-          element('#link-1').click();
-          expect(input('value').val()).toEqual('1');
-          expect(element('#link-1').attr('href')).toBe("");
-        });
-
-        it('should execute ng-click but not reload when href empty string', function() {
-          element('#link-2').click();
-          expect(input('value').val()).toEqual('2');
-          expect(element('#link-2').attr('href')).toBe("");
-        });
-
-        it('should execute ng-click and change url when ng-href specified', function() {
-          expect(element('#link-3').attr('href')).toBe("/123");
-
-          element('#link-3').click();
-          expect(browser().window().path()).toEqual('/123');
-        });
-
-        it('should execute ng-click but not reload when href empty string and name specified', function() {
-          element('#link-4').click();
-          expect(input('value').val()).toEqual('4');
-          expect(element('#link-4').attr('href')).toBe('');
-        });
-
-        it('should execute ng-click but not reload when no href but name specified', function() {
-          element('#link-5').click();
-          expect(input('value').val()).toEqual('5');
-          expect(element('#link-5').attr('href')).toBe(undefined);
-        });
-
-        it('should only change url when only ng-href', function() {
-          input('value').enter('6');
-          expect(element('#link-6').attr('href')).toBe('6');
-
-          element('#link-6').click();
-          expect(browser().location().url()).toEqual('/6');
-        });
-      </doc:scenario>
-    </doc:example>
- */
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngSrc
- * @restrict A
- *
- * @description
- * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
- * work right: The browser will fetch from the URL with the literal
- * text `{{hash}}` until Angular replaces the expression inside
- * `{{hash}}`. The `ngSrc` directive solves this problem.
- *
- * The buggy way to write it:
- * <pre>
- * <img src="http://www.gravatar.com/avatar/{{hash}}"/>
- * </pre>
- *
- * The correct way to write it:
- * <pre>
- * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>
- * </pre>
- *
- * @element IMG
- * @param {template} ngSrc any string which can contain `{{}}` markup.
- */
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngDisabled
- * @restrict A
- *
- * @description
- *
- * The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
- * <pre>
- * <div ng-init="scope = { isDisabled: false }">
- *  <button disabled="{{scope.isDisabled}}">Disabled</button>
- * </div>
- * </pre>
- *
- * The HTML specs do not require browsers to preserve the special attributes such as disabled.
- * (The presence of them means true and absence means false)
- * This prevents the angular compiler from correctly retrieving the binding expression.
- * To solve this problem, we introduce the `ngDisabled` directive.
- *
- * @example
-    <doc:example>
-      <doc:source>
-        Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
-        <button ng-model="button" ng-disabled="checked">Button</button>
-      </doc:source>
-      <doc:scenario>
-        it('should toggle button', function() {
-          expect(element('.doc-example-live :button').prop('disabled')).toBeFalsy();
-          input('checked').check();
-          expect(element('.doc-example-live :button').prop('disabled')).toBeTruthy();
-        });
-      </doc:scenario>
-    </doc:example>
- *
- * @element INPUT
- * @param {expression} ngDisabled Angular expression that will be evaluated.
- */
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngChecked
- * @restrict A
- *
- * @description
- * The HTML specs do not require browsers to preserve the special attributes such as checked.
- * (The presence of them means true and absence means false)
- * This prevents the angular compiler from correctly retrieving the binding expression.
- * To solve this problem, we introduce the `ngChecked` directive.
- * @example
-    <doc:example>
-      <doc:source>
-        Check me to check both: <input type="checkbox" ng-model="master"><br/>
-        <input id="checkSlave" type="checkbox" ng-checked="master">
-      </doc:source>
-      <doc:scenario>
-        it('should check both checkBoxes', function() {
-          expect(element('.doc-example-live #checkSlave').prop('checked')).toBeFalsy();
-          input('master').check();
-          expect(element('.doc-example-live #checkSlave').prop('checked')).toBeTruthy();
-        });
-      </doc:scenario>
-    </doc:example>
- *
- * @element INPUT
- * @param {expression} ngChecked Angular expression that will be evaluated.
- */
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngMultiple
- * @restrict A
- *
- * @description
- * The HTML specs do not require browsers to preserve the special attributes such as multiple.
- * (The presence of them means true and absence means false)
- * This prevents the angular compiler from correctly retrieving the binding expression.
- * To solve this problem, we introduce the `ngMultiple` directive.
- *
- * @example
-     <doc:example>
-       <doc:source>
-         Check me check multiple: <input type="checkbox" ng-model="checked"><br/>
-         <select id="select" ng-multiple="checked">
-           <option>Misko</option>
-           <option>Igor</option>
-           <option>Vojta</option>
-           <option>Di</option>
-         </select>
-       </doc:source>
-       <doc:scenario>
-         it('should toggle multiple', function() {
-           expect(element('.doc-example-live #select').prop('multiple')).toBeFalsy();
-           input('checked').check();
-           expect(element('.doc-example-live #select').prop('multiple')).toBeTruthy();
-         });
-       </doc:scenario>
-     </doc:example>
- *
- * @element SELECT
- * @param {expression} ngMultiple Angular expression that will be evaluated.
- */
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngReadonly
- * @restrict A
- *
- * @description
- * The HTML specs do not require browsers to preserve the special attributes such as readonly.
- * (The presence of them means true and absence means false)
- * This prevents the angular compiler from correctly retrieving the binding expression.
- * To solve this problem, we introduce the `ngReadonly` directive.
- * @example
-    <doc:example>
-      <doc:source>
-        Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
-        <input type="text" ng-readonly="checked" value="I'm Angular"/>
-      </doc:source>
-      <doc:scenario>
-        it('should toggle readonly attr', function() {
-          expect(element('.doc-example-live :text').prop('readonly')).toBeFalsy();
-          input('checked').check();
-          expect(element('.doc-example-live :text').prop('readonly')).toBeTruthy();
-        });
-      </doc:scenario>
-    </doc:example>
- *
- * @element INPUT
- * @param {string} expression Angular expression that will be evaluated.
- */
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngSelected
- * @restrict A
- *
- * @description
- * The HTML specs do not require browsers to preserve the special attributes such as selected.
- * (The presence of them means true and absence means false)
- * This prevents the angular compiler from correctly retrieving the binding expression.
- * To solve this problem, we introduced the `ngSelected` directive.
- * @example
-    <doc:example>
-      <doc:source>
-        Check me to select: <input type="checkbox" ng-model="selected"><br/>
-        <select>
-          <option>Hello!</option>
-          <option id="greet" ng-selected="selected">Greetings!</option>
-        </select>
-      </doc:source>
-      <doc:scenario>
-        it('should select Greetings!', function() {
-          expect(element('.doc-example-live #greet').prop('selected')).toBeFalsy();
-          input('selected').check();
-          expect(element('.doc-example-live #greet').prop('selected')).toBeTruthy();
-        });
-      </doc:scenario>
-    </doc:example>
- *
- * @element OPTION
- * @param {string} expression Angular expression that will be evaluated.
- */
-
-
-var ngAttributeAliasDirectives = {};
-
-
-// boolean attrs are evaluated
-forEach(BOOLEAN_ATTR, function(propName, attrName) {
-  var normalized = directiveNormalize('ng-' + attrName);
-  ngAttributeAliasDirectives[normalized] = function() {
-    return {
-      priority: 100,
-      compile: function() {
-        return function(scope, element, attr) {
-          scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
-            attr.$set(attrName, !!value);
-          });
-        };
-      }
-    };
-  };
-});
-
-
-// ng-src, ng-href are interpolated
-forEach(['src', 'href'], function(attrName) {
-  var normalized = directiveNormalize('ng-' + attrName);
-  ngAttributeAliasDirectives[normalized] = function() {
-    return {
-      priority: 99, // it needs to run after the attributes are interpolated
-      link: function(scope, element, attr) {
-        attr.$observe(normalized, function(value) {
-          if (!value)
-             return;
-
-          attr.$set(attrName, value);
-
-          // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
-          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
-          // to set the property as well to achieve the desired effect.
-          // we use attr[attrName] value since $set can sanitize the url.
-          if (msie) element.prop(attrName, attr[attrName]);
-        });
-      }
-    };
-  };
-});
-
-var nullFormCtrl = {
-  $addControl: noop,
-  $removeControl: noop,
-  $setValidity: noop,
-  $setDirty: noop
-};
-
-/**
- * @ngdoc object
- * @name ng.directive:form.FormController
- *
- * @property {boolean} $pristine True if user has not interacted with the form yet.
- * @property {boolean} $dirty True if user has already interacted with the form.
- * @property {boolean} $valid True if all of the containing forms and controls are valid.
- * @property {boolean} $invalid True if at least one containing control or form is invalid.
- *
- * @property {Object} $error Is an object hash, containing references to all invalid controls or
- *  forms, where:
- *
- *  - keys are validation tokens (error names) — such as `required`, `url` or `email`),
- *  - values are arrays of controls or forms that are invalid with given error.
- *
- * @description
- * `FormController` keeps track of all its controls and nested forms as well as state of them,
- * such as being valid/invalid or dirty/pristine.
- *
- * Each {@link ng.directive:form form} directive creates an instance
- * of `FormController`.
- *
- */
-//asks for $scope to fool the BC controller module
-FormController.$inject = ['$element', '$attrs', '$scope'];
-function FormController(element, attrs) {
-  var form = this,
-      parentForm = element.parent().controller('form') || nullFormCtrl,
-      invalidCount = 0, // used to easily determine if we are valid
-      errors = form.$error = {};
-
-  // init state
-  form.$name = attrs.name;
-  form.$dirty = false;
-  form.$pristine = true;
-  form.$valid = true;
-  form.$invalid = false;
-
-  parentForm.$addControl(form);
-
-  // Setup initial state of the control
-  element.addClass(PRISTINE_CLASS);
-  toggleValidCss(true);
-
-  // convenience method for easy toggling of classes
-  function toggleValidCss(isValid, validationErrorKey) {
-    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
-    element.
-      removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey).
-      addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
-  }
-
-  form.$addControl = function(control) {
-    if (control.$name && !form.hasOwnProperty(control.$name)) {
-      form[control.$name] = control;
-    }
-  };
-
-  form.$removeControl = function(control) {
-    if (control.$name && form[control.$name] === control) {
-      delete form[control.$name];
-    }
-    forEach(errors, function(queue, validationToken) {
-      form.$setValidity(validationToken, true, control);
-    });
-  };
-
-  form.$setValidity = function(validationToken, isValid, control) {
-    var queue = errors[validationToken];
-
-    if (isValid) {
-      if (queue) {
-        arrayRemove(queue, control);
-        if (!queue.length) {
-          invalidCount--;
-          if (!invalidCount) {
-            toggleValidCss(isValid);
-            form.$valid = true;
-            form.$invalid = false;
-          }
-          errors[validationToken] = false;
-          toggleValidCss(true, validationToken);
-          parentForm.$setValidity(validationToken, true, form);
-        }
-      }
-
-    } else {
-      if (!invalidCount) {
-        toggleValidCss(isValid);
-      }
-      if (queue) {
-        if (includes(queue, control)) return;
-      } else {
-        errors[validationToken] = queue = [];
-        invalidCount++;
-        toggleValidCss(false, validationToken);
-        parentForm.$setValidity(validationToken, false, form);
-      }
-      queue.push(control);
-
-      form.$valid = false;
-      form.$invalid = true;
-    }
-  };
-
-  form.$setDirty = function() {
-    element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS);
-    form.$dirty = true;
-    form.$pristine = false;
-    parentForm.$setDirty();
-  };
-
-}
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngForm
- * @restrict EAC
- *
- * @description
- * Nestable alias of {@link ng.directive:form `form`} directive. HTML
- * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
- * sub-group of controls needs to be determined.
- *
- * @param {string=} name|ngForm Name of the form. If specified, the form controller will be published into
- *                       related scope, under this name.
- *
- */
-
- /**
- * @ngdoc directive
- * @name ng.directive:form
- * @restrict E
- *
- * @description
- * Directive that instantiates
- * {@link ng.directive:form.FormController FormController}.
- *
- * If `name` attribute is specified, the form controller is published onto the current scope under
- * this name.
- *
- * # Alias: {@link ng.directive:ngForm `ngForm`}
- *
- * In angular forms can be nested. This means that the outer form is valid when all of the child
- * forms are valid as well. However browsers do not allow nesting of `<form>` elements, for this
- * reason angular provides {@link ng.directive:ngForm `ngForm`} alias
- * which behaves identical to `<form>` but allows form nesting.
- *
- *
- * # CSS classes
- *  - `ng-valid` Is set if the form is valid.
- *  - `ng-invalid` Is set if the form is invalid.
- *  - `ng-pristine` Is set if the form is pristine.
- *  - `ng-dirty` Is set if the form is dirty.
- *
- *
- * # Submitting a form and preventing default action
- *
- * Since the role of forms in client-side Angular applications is different than in classical
- * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
- * page reload that sends the data to the server. Instead some javascript logic should be triggered
- * to handle the form submission in application specific way.
- *
- * For this reason, Angular prevents the default action (form submission to the server) unless the
- * `<form>` element has an `action` attribute specified.
- *
- * You can use one of the following two ways to specify what javascript method should be called when
- * a form is submitted:
- *
- * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
- * - {@link ng.directive:ngClick ngClick} directive on the first
-  *  button or input field of type submit (input[type=submit])
- *
- * To prevent double execution of the handler, use only one of ngSubmit or ngClick directives. This
- * is because of the following form submission rules coming from the html spec:
- *
- * - If a form has only one input field then hitting enter in this field triggers form submit
- * (`ngSubmit`)
- * - if a form has has 2+ input fields and no buttons or input[type=submit] then hitting enter
- * doesn't trigger submit
- * - if a form has one or more input fields and one or more buttons or input[type=submit] then
- * hitting enter in any of the input fields will trigger the click handler on the *first* button or
- * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
- *
- * @param {string=} name Name of the form. If specified, the form controller will be published into
- *                       related scope, under this name.
- *
- * @example
-    <doc:example>
-      <doc:source>
-       <script>
-         function Ctrl($scope) {
-           $scope.userType = 'guest';
-         }
-       </script>
-       <form name="myForm" ng-controller="Ctrl">
-         userType: <input name="input" ng-model="userType" required>
-         <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
-         <tt>userType = {{userType}}</tt><br>
-         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br>
-         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br>
-         <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
-         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
-        </form>
-      </doc:source>
-      <doc:scenario>
-        it('should initialize to model', function() {
-         expect(binding('userType')).toEqual('guest');
-         expect(binding('myForm.input.$valid')).toEqual('true');
-        });
-
-        it('should be invalid if empty', function() {
-         input('userType').enter('');
-         expect(binding('userType')).toEqual('');
-         expect(binding('myForm.input.$valid')).toEqual('false');
-        });
-      </doc:scenario>
-    </doc:example>
- */
-var formDirectiveFactory = function(isNgForm) {
-  return ['$timeout', function($timeout) {
-    var formDirective = {
-      name: 'form',
-      restrict: 'E',
-      controller: FormController,
-      compile: function() {
-        return {
-          pre: function(scope, formElement, attr, controller) {
-            if (!attr.action) {
-              // we can't use jq events because if a form is destroyed during submission the default
-              // action is not prevented. see #1238
-              //
-              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
-              // page reload if the form was destroyed by submission of the form via a click handler
-              // on a button in the form. Looks like an IE9 specific bug.
-              var preventDefaultListener = function(event) {
-                event.preventDefault
-                  ? event.preventDefault()
-                  : event.returnValue = false; // IE
-              };
-
-              addEventListenerFn(formElement[0], 'submit', preventDefaultListener);
-
-              // unregister the preventDefault listener so that we don't not leak memory but in a
-              // way that will achieve the prevention of the default action.
-              formElement.bind('$destroy', function() {
-                $timeout(function() {
-                  removeEventListenerFn(formElement[0], 'submit', preventDefaultListener);
-                }, 0, false);
-              });
-            }
-
-            var parentFormCtrl = formElement.parent().controller('form'),
-                alias = attr.name || attr.ngForm;
-
-            if (alias) {
-              scope[alias] = controller;
-            }
-            if (parentFormCtrl) {
-              formElement.bind('$destroy', function() {
-                parentFormCtrl.$removeControl(controller);
-                if (alias) {
-                  scope[alias] = undefined;
-                }
-                extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
-              });
-            }
-          }
-        };
-      }
-    };
-
-    return isNgForm ? extend(copy(formDirective), {restrict: 'EAC'}) : formDirective;
-  }];
-};
-
-var formDirective = formDirectiveFactory();
-var ngFormDirective = formDirectiveFactory(true);
-
-var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
-var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/;
-var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
-
-var inputType = {
-
-  /**
-   * @ngdoc inputType
-   * @name ng.directive:input.text
-   *
-   * @description
-   * Standard HTML text input with angular data binding.
-   *
-   * @param {string} ngModel Assignable angular expression to data-bind to.
-   * @param {string=} name Property name of the form under which the control is published.
-   * @param {string=} required Adds `required` validation error key if the value is not entered.
-   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
-   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
-   *    `required` when you want to data-bind to the `required` attribute.
-   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
-   *    minlength.
-   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
-   *    maxlength.
-   * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
-   *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
-   *    patterns defined as scope expressions.
-   * @param {string=} ngChange Angular expression to be executed when input changes due to user
-   *    interaction with the input element.
-   *
-   * @example
-      <doc:example>
-        <doc:source>
-         <script>
-           function Ctrl($scope) {
-             $scope.text = 'guest';
-             $scope.word = /^\w*$/;
-           }
-         </script>
-         <form name="myForm" ng-controller="Ctrl">
-           Single word: <input type="text" name="input" ng-model="text"
-                               ng-pattern="word" required>
-           <span class="error" ng-show="myForm.input.$error.required">
-             Required!</span>
-           <span class="error" ng-show="myForm.input.$error.pattern">
-             Single word only!</span>
-
-           <tt>text = {{text}}</tt><br/>
-           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
-           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
-           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
-           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
-          </form>
-        </doc:source>
-        <doc:scenario>
-          it('should initialize to model', function() {
-            expect(binding('text')).toEqual('guest');
-            expect(binding('myForm.input.$valid')).toEqual('true');
-          });
-
-          it('should be invalid if empty', function() {
-            input('text').enter('');
-            expect(binding('text')).toEqual('');
-            expect(binding('myForm.input.$valid')).toEqual('false');
-          });
-
-          it('should be invalid if multi word', function() {
-            input('text').enter('hello world');
-            expect(binding('myForm.input.$valid')).toEqual('false');
-          });
-        </doc:scenario>
-      </doc:example>
-   */
-  'text': textInputType,
-
-
-  /**
-   * @ngdoc inputType
-   * @name ng.directive:input.number
-   *
-   * @description
-   * Text input with number validation and transformation. Sets the `number` validation
-   * error if not a valid number.
-   *
-   * @param {string} ngModel Assignable angular expression to data-bind to.
-   * @param {string=} name Property name of the form under which the control is published.
-   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
-   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
-   * @param {string=} required Sets `required` validation error key if the value is not entered.
-   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
-   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
-   *    `required` when you want to data-bind to the `required` attribute.
-   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
-   *    minlength.
-   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
-   *    maxlength.
-   * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
-   *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
-   *    patterns defined as scope expressions.
-   * @param {string=} ngChange Angular expression to be executed when input changes due to user
-   *    interaction with the input element.
-   *
-   * @example
-      <doc:example>
-        <doc:source>
-         <script>
-           function Ctrl($scope) {
-             $scope.value = 12;
-           }
-         </script>
-         <form name="myForm" ng-controller="Ctrl">
-           Number: <input type="number" name="input" ng-model="value"
-                          min="0" max="99" required>
-           <span class="error" ng-show="myForm.list.$error.required">
-             Required!</span>
-           <span class="error" ng-show="myForm.list.$error.number">
-             Not valid number!</span>
-           <tt>value = {{value}}</tt><br/>
-           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
-           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
-           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
-           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
-          </form>
-        </doc:source>
-        <doc:scenario>
-          it('should initialize to model', function() {
-           expect(binding('value')).toEqual('12');
-           expect(binding('myForm.input.$valid')).toEqual('true');
-          });
-
-          it('should be invalid if empty', function() {
-           input('value').enter('');
-           expect(binding('value')).toEqual('');
-           expect(binding('myForm.input.$valid')).toEqual('false');
-          });
-
-          it('should be invalid if over max', function() {
-           input('value').enter('123');
-           expect(binding('value')).toEqual('');
-           expect(binding('myForm.input.$valid')).toEqual('false');
-          });
-        </doc:scenario>
-      </doc:example>
-   */
-  'number': numberInputType,
-
-
-  /**
-   * @ngdoc inputType
-   * @name ng.directive:input.url
-   *
-   * @description
-   * Text input with URL validation. Sets the `url` validation error key if the content is not a
-   * valid URL.
-   *
-   * @param {string} ngModel Assignable angular expression to data-bind to.
-   * @param {string=} name Property name of the form under which the control is published.
-   * @param {string=} required Sets `required` validation error key if the value is not entered.
-   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
-   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
-   *    `required` when you want to data-bind to the `required` attribute.
-   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
-   *    minlength.
-   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
-   *    maxlength.
-   * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
-   *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
-   *    patterns defined as scope expressions.
-   * @param {string=} ngChange Angular expression to be executed when input changes due to user
-   *    interaction with the input element.
-   *
-   * @example
-      <doc:example>
-        <doc:source>
-         <script>
-           function Ctrl($scope) {
-             $scope.text = 'http://google.com';
-           }
-         </script>
-         <form name="myForm" ng-controller="Ctrl">
-           URL: <input type="url" name="input" ng-model="text" required>
-           <span class="error" ng-show="myForm.input.$error.required">
-             Required!</span>
-           <span class="error" ng-show="myForm.input.$error.url">
-             Not valid url!</span>
-           <tt>text = {{text}}</tt><br/>
-           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
-           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
-           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
-           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
-           <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
-          </form>
-        </doc:source>
-        <doc:scenario>
-          it('should initialize to model', function() {
-            expect(binding('text')).toEqual('http://google.com');
-            expect(binding('myForm.input.$valid')).toEqual('true');
-          });
-
-          it('should be invalid if empty', function() {
-            input('text').enter('');
-            expect(binding('text')).toEqual('');
-            expect(binding('myForm.input.$valid')).toEqual('false');
-          });
-
-          it('should be invalid if not url', function() {
-            input('text').enter('xxx');
-            expect(binding('myForm.input.$valid')).toEqual('false');
-          });
-        </doc:scenario>
-      </doc:example>
-   */
-  'url': urlInputType,
-
-
-  /**
-   * @ngdoc inputType
-   * @name ng.directive:input.email
-   *
-   * @description
-   * Text input with email validation. Sets the `email` validation error key if not a valid email
-   * address.
-   *
-   * @param {string} ngModel Assignable angular expression to data-bind to.
-   * @param {string=} name Property name of the form under which the control is published.
-   * @param {string=} required Sets `required` validation error key if the value is not entered.
-   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
-   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
-   *    `required` when you want to data-bind to the `required` attribute.
-   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
-   *    minlength.
-   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
-   *    maxlength.
-   * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
-   *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
-   *    patterns defined as scope expressions.
-   *
-   * @example
-      <doc:example>
-        <doc:source>
-         <script>
-           function Ctrl($scope) {
-             $scope.text = 'me@example.com';
-           }
-         </script>
-           <form name="myForm" ng-controller="Ctrl">
-             Email: <input type="email" name="input" ng-model="text" required>
-             <span class="error" ng-show="myForm.input.$error.required">
-               Required!</span>
-             <span class="error" ng-show="myForm.input.$error.email">
-               Not valid email!</span>
-             <tt>text = {{text}}</tt><br/>
-             <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
-             <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
-             <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
-             <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
-             <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
-           </form>
-        </doc:source>
-        <doc:scenario>
-          it('should initialize to model', function() {
-            expect(binding('text')).toEqual('me@example.com');
-            expect(binding('myForm.input.$valid')).toEqual('true');
-          });
-
-          it('should be invalid if empty', function() {
-            input('text').enter('');
-            expect(binding('text')).toEqual('');
-            expect(binding('myForm.input.$valid')).toEqual('false');
-          });
-
-          it('should be invalid if not email', function() {
-            input('text').enter('xxx');
-            expect(binding('myForm.input.$valid')).toEqual('false');
-          });
-        </doc:scenario>
-      </doc:example>
-   */
-  'email': emailInputType,
-
-
-  /**
-   * @ngdoc inputType
-   * @name ng.directive:input.radio
-   *
-   * @description
-   * HTML radio button.
-   *
-   * @param {string} ngModel Assignable angular expression to data-bind to.
-   * @param {string} value The value to which the expression should be set when selected.
-   * @param {string=} name Property name of the form under which the control is published.
-   * @param {string=} ngChange Angular expression to be executed when input changes due to user
-   *    interaction with the input element.
-   *
-   * @example
-      <doc:example>
-        <doc:source>
-         <script>
-           function Ctrl($scope) {
-             $scope.color = 'blue';
-           }
-         </script>
-         <form name="myForm" ng-controller="Ctrl">
-           <input type="radio" ng-model="color" value="red">  Red <br/>
-           <input type="radio" ng-model="color" value="green"> Green <br/>
-           <input type="radio" ng-model="color" value="blue"> Blue <br/>
-           <tt>color = {{color}}</tt><br/>
-          </form>
-        </doc:source>
-        <doc:scenario>
-          it('should change state', function() {
-            expect(binding('color')).toEqual('blue');
-
-            input('color').select('red');
-            expect(binding('color')).toEqual('red');
-          });
-        </doc:scenario>
-      </doc:example>
-   */
-  'radio': radioInputType,
-
-
-  /**
-   * @ngdoc inputType
-   * @name ng.directive:input.checkbox
-   *
-   * @description
-   * HTML checkbox.
-   *
-   * @param {string} ngModel Assignable angular expression to data-bind to.
-   * @param {string=} name Property name of the form under which the control is published.
-   * @param {string=} ngTrueValue The value to which the expression should be set when selected.
-   * @param {string=} ngFalseValue The value to which the expression should be set when not selected.
-   * @param {string=} ngChange Angular expression to be executed when input changes due to user
-   *    interaction with the input element.
-   *
-   * @example
-      <doc:example>
-        <doc:source>
-         <script>
-           function Ctrl($scope) {
-             $scope.value1 = true;
-             $scope.value2 = 'YES'
-           }
-         </script>
-         <form name="myForm" ng-controller="Ctrl">
-           Value1: <input type="checkbox" ng-model="value1"> <br/>
-           Value2: <input type="checkbox" ng-model="value2"
-                          ng-true-value="YES" ng-false-value="NO"> <br/>
-           <tt>value1 = {{value1}}</tt><br/>
-           <tt>value2 = {{value2}}</tt><br/>
-          </form>
-        </doc:source>
-        <doc:scenario>
-          it('should change state', function() {
-            expect(binding('value1')).toEqual('true');
-            expect(binding('value2')).toEqual('YES');
-
-            input('value1').check();
-            input('value2').check();
-            expect(binding('value1')).toEqual('false');
-            expect(binding('value2')).toEqual('NO');
-          });
-        </doc:scenario>
-      </doc:example>
-   */
-  'checkbox': checkboxInputType,
-
-  'hidden': noop,
-  'button': noop,
-  'submit': noop,
-  'reset': noop
-};
-
-
-function isEmpty(value) {
-  return isUndefined(value) || value === '' || value === null || value !== value;
-}
-
-
-function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
-
-  var listener = function() {
-    var value = trim(element.val());
-
-    if (ctrl.$viewValue !== value) {
-      scope.$apply(function() {
-        ctrl.$setViewValue(value);
-      });
-    }
-  };
-
-  // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
-  // input event on backspace, delete or cut
-  if ($sniffer.hasEvent('input')) {
-    element.bind('input', listener);
-  } else {
-    var timeout;
-
-    var deferListener = function() {
-      if (!timeout) {
-        timeout = $browser.defer(function() {
-          listener();
-          timeout = null;
-        });
-      }
-    };
-
-    element.bind('keydown', function(event) {
-      var key = event.keyCode;
-
-      // ignore
-      //    command            modifiers                   arrows
-      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
-
-      deferListener();
-    });
-
-    // if user paste into input using mouse, we need "change" event to catch it
-    element.bind('change', listener);
-
-    // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
-    if ($sniffer.hasEvent('paste')) {
-      element.bind('paste cut', deferListener);
-    }
-  }
-
-
-  ctrl.$render = function() {
-    element.val(isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
-  };
-
-  // pattern validator
-  var pattern = attr.ngPattern,
-      patternValidator;
-
-  var validate = function(regexp, value) {
-    if (isEmpty(value) || regexp.test(value)) {
-      ctrl.$setValidity('pattern', true);
-      return value;
-    } else {
-      ctrl.$setValidity('pattern', false);
-      return undefined;
-    }
-  };
-
-  if (pattern) {
-    if (pattern.match(/^\/(.*)\/$/)) {
-      pattern = new RegExp(pattern.substr(1, pattern.length - 2));
-      patternValidator = function(value) {
-        return validate(pattern, value)
-      };
-    } else {
-      patternValidator = function(value) {
-        var patternObj = scope.$eval(pattern);
-
-        if (!patternObj || !patternObj.test) {
-          throw new Error('Expected ' + pattern + ' to be a RegExp but was ' + patternObj);
-        }
-        return validate(patternObj, value);
-      };
-    }
-
-    ctrl.$formatters.push(patternValidator);
-    ctrl.$parsers.push(patternValidator);
-  }
-
-  // min length validator
-  if (attr.ngMinlength) {
-    var minlength = int(attr.ngMinlength);
-    var minLengthValidator = function(value) {
-      if (!isEmpty(value) && value.length < minlength) {
-        ctrl.$setValidity('minlength', false);
-        return undefined;
-      } else {
-        ctrl.$setValidity('minlength', true);
-        return value;
-      }
-    };
-
-    ctrl.$parsers.push(minLengthValidator);
-    ctrl.$formatters.push(minLengthValidator);
-  }
-
-  // max length validator
-  if (attr.ngMaxlength) {
-    var maxlength = int(attr.ngMaxlength);
-    var maxLengthValidator = function(value) {
-      if (!isEmpty(value) && value.length > maxlength) {
-        ctrl.$setValidity('maxlength', false);
-        return undefined;
-      } else {
-        ctrl.$setValidity('maxlength', true);
-        return value;
-      }
-    };
-
-    ctrl.$parsers.push(maxLengthValidator);
-    ctrl.$formatters.push(maxLengthValidator);
-  }
-}
-
-function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
-  textInputType(scope, element, attr, ctrl, $sniffer, $browser);
-
-  ctrl.$parsers.push(function(value) {
-    var empty = isEmpty(value);
-    if (empty || NUMBER_REGEXP.test(value)) {
-      ctrl.$setValidity('number', true);
-      return value === '' ? null : (empty ? value : parseFloat(value));
-    } else {
-      ctrl.$setValidity('number', false);
-      return undefined;
-    }
-  });
-
-  ctrl.$formatters.push(function(value) {
-    return isEmpty(value) ? '' : '' + value;
-  });
-
-  if (attr.min) {
-    var min = parseFloat(attr.min);
-    var minValidator = function(value) {
-      if (!isEmpty(value) && value < min) {
-        ctrl.$setValidity('min', false);
-        return undefined;
-      } else {
-        ctrl.$setValidity('min', true);
-        return value;
-      }
-    };
-
-    ctrl.$parsers.push(minValidator);
-    ctrl.$formatters.push(minValidator);
-  }
-
-  if (attr.max) {
-    var max = parseFloat(attr.max);
-    var maxValidator = function(value) {
-      if (!isEmpty(value) && value > max) {
-        ctrl.$setValidity('max', false);
-        return undefined;
-      } else {
-        ctrl.$setValidity('max', true);
-        return value;
-      }
-    };
-
-    ctrl.$parsers.push(maxValidator);
-    ctrl.$formatters.push(maxValidator);
-  }
-
-  ctrl.$formatters.push(function(value) {
-
-    if (isEmpty(value) || isNumber(value)) {
-      ctrl.$setValidity('number', true);
-      return value;
-    } else {
-      ctrl.$setValidity('number', false);
-      return undefined;
-    }
-  });
-}
-
-function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
-  textInputType(scope, element, attr, ctrl, $sniffer, $browser);
-
-  var urlValidator = function(value) {
-    if (isEmpty(value) || URL_REGEXP.test(value)) {
-      ctrl.$setValidity('url', true);
-      return value;
-    } else {
-      ctrl.$setValidity('url', false);
-      return undefined;
-    }
-  };
-
-  ctrl.$formatters.push(urlValidator);
-  ctrl.$parsers.push(urlValidator);
-}
-
-function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
-  textInputType(scope, element, attr, ctrl, $sniffer, $browser);
-
-  var emailValidator = function(value) {
-    if (isEmpty(value) || EMAIL_REGEXP.test(value)) {
-      ctrl.$setValidity('email', true);
-      return value;
-    } else {
-      ctrl.$setValidity('email', false);
-      return undefined;
-    }
-  };
-
-  ctrl.$formatters.push(emailValidator);
-  ctrl.$parsers.push(emailValidator);
-}
-
-function radioInputType(scope, element, attr, ctrl) {
-  // make the name unique, if not defined
-  if (isUndefined(attr.name)) {
-    element.attr('name', nextUid());
-  }
-
-  element.bind('click', function() {
-    if (element[0].checked) {
-      scope.$apply(function() {
-        ctrl.$setViewValue(attr.value);
-      });
-    }
-  });
-
-  ctrl.$render = function() {
-    var value = attr.value;
-    element[0].checked = (value == ctrl.$viewValue);
-  };
-
-  attr.$observe('value', ctrl.$render);
-}
-
-function checkboxInputType(scope, element, attr, ctrl) {
-  var trueValue = attr.ngTrueValue,
-      falseValue = attr.ngFalseValue;
-
-  if (!isString(trueValue)) trueValue = true;
-  if (!isString(falseValue)) falseValue = false;
-
-  element.bind('click', function() {
-    scope.$apply(function() {
-      ctrl.$setViewValue(element[0].checked);
-    });
-  });
-
-  ctrl.$render = function() {
-    element[0].checked = ctrl.$viewValue;
-  };
-
-  ctrl.$formatters.push(function(value) {
-    return value === trueValue;
-  });
-
-  ctrl.$parsers.push(function(value) {
-    return value ? trueValue : falseValue;
-  });
-}
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:textarea
- * @restrict E
- *
- * @description
- * HTML textarea element control with angular data-binding. The data-binding and validation
- * properties of this element are exactly the same as those of the
- * {@link ng.directive:input input element}.
- *
- * @param {string} ngModel Assignable angular expression to data-bind to.
- * @param {string=} name Property name of the form under which the control is published.
- * @param {string=} required Sets `required` validation error key if the value is not entered.
- * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
- *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
- *    `required` when you want to data-bind to the `required` attribute.
- * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
- *    minlength.
- * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
- *    maxlength.
- * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
- *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
- *    patterns defined as scope expressions.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
- *    interaction with the input element.
- */
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:input
- * @restrict E
- *
- * @description
- * HTML input element control with angular data-binding. Input control follows HTML5 input types
- * and polyfills the HTML5 validation behavior for older browsers.
- *
- * @param {string} ngModel Assignable angular expression to data-bind to.
- * @param {string=} name Property name of the form under which the control is published.
- * @param {string=} required Sets `required` validation error key if the value is not entered.
- * @param {boolean=} ngRequired Sets `required` attribute if set to true
- * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
- *    minlength.
- * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
- *    maxlength.
- * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
- *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
- *    patterns defined as scope expressions.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
- *    interaction with the input element.
- *
- * @example
-    <doc:example>
-      <doc:source>
-       <script>
-         function Ctrl($scope) {
-           $scope.user = {name: 'guest', last: 'visitor'};
-         }
-       </script>
-       <div ng-controller="Ctrl">
-         <form name="myForm">
-           User name: <input type="text" name="userName" ng-model="user.name" required>
-           <span class="error" ng-show="myForm.userName.$error.required">
-             Required!</span><br>
-           Last name: <input type="text" name="lastName" ng-model="user.last"
-             ng-minlength="3" ng-maxlength="10">
-           <span class="error" ng-show="myForm.lastName.$error.minlength">
-             Too short!</span>
-           <span class="error" ng-show="myForm.lastName.$error.maxlength">
-             Too long!</span><br>
-         </form>
-         <hr>
-         <tt>user = {{user}}</tt><br/>
-         <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
-         <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
-         <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
-         <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
-         <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
-         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
-         <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
-         <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>
-       </div>
-      </doc:source>
-      <doc:scenario>
-        it('should initialize to model', function() {
-          expect(binding('user')).toEqual('{"name":"guest","last":"visitor"}');
-          expect(binding('myForm.userName.$valid')).toEqual('true');
-          expect(binding('myForm.$valid')).toEqual('true');
-        });
-
-        it('should be invalid if empty when required', function() {
-          input('user.name').enter('');
-          expect(binding('user')).toEqual('{"last":"visitor"}');
-          expect(binding('myForm.userName.$valid')).toEqual('false');
-          expect(binding('myForm.$valid')).toEqual('false');
-        });
-
-        it('should be valid if empty when min length is set', function() {
-          input('user.last').enter('');
-          expect(binding('user')).toEqual('{"name":"guest","last":""}');
-          expect(binding('myForm.lastName.$valid')).toEqual('true');
-          expect(binding('myForm.$valid')).toEqual('true');
-        });
-
-        it('should be invalid if less than required min length', function() {
-          input('user.last').enter('xx');
-          expect(binding('user')).toEqual('{"name":"guest"}');
-          expect(binding('myForm.lastName.$valid')).toEqual('false');
-          expect(binding('myForm.lastName.$error')).toMatch(/minlength/);
-          expect(binding('myForm.$valid')).toEqual('false');
-        });
-
-        it('should be invalid if longer than max length', function() {
-          input('user.last').enter('some ridiculously long name');
-          expect(binding('user'))
-            .toEqual('{"name":"guest"}');
-          expect(binding('myForm.lastName.$valid')).toEqual('false');
-          expect(binding('myForm.lastName.$error')).toMatch(/maxlength/);
-          expect(binding('myForm.$valid')).toEqual('false');
-        });
-      </doc:scenario>
-    </doc:example>
- */
-var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) {
-  return {
-    restrict: 'E',
-    require: '?ngModel',
-    link: function(scope, element, attr, ctrl) {
-      if (ctrl) {
-        (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer,
-                                                            $browser);
-      }
-    }
-  };
-}];
-
-var VALID_CLASS = 'ng-valid',
-    INVALID_CLASS = 'ng-invalid',
-    PRISTINE_CLASS = 'ng-pristine',
-    DIRTY_CLASS = 'ng-dirty';
-
-/**
- * @ngdoc object
- * @name ng.directive:ngModel.NgModelController
- *
- * @property {string} $viewValue Actual string value in the view.
- * @property {*} $modelValue The value in the model, that the control is bound to.
- * @property {Array.<Function>} $parsers Whenever the control reads value from the DOM, it executes
- *     all of these functions to sanitize / convert the value as well as validate.
- *
- * @property {Array.<Function>} $formatters Whenever the model value changes, it executes all of
- *     these functions to convert the value as well as validate.
- *
- * @property {Object} $error An bject hash with all errors as keys.
- *
- * @property {boolean} $pristine True if user has not interacted with the control yet.
- * @property {boolean} $dirty True if user has already interacted with the control.
- * @property {boolean} $valid True if there is no error.
- * @property {boolean} $invalid True if at least one error on the control.
- *
- * @description
- *
- * `NgModelController` provides API for the `ng-model` directive. The controller contains
- * services for data-binding, validation, CSS update, value formatting and parsing. It
- * specifically does not contain any logic which deals with DOM rendering or listening to
- * DOM events. The `NgModelController` is meant to be extended by other directives where, the
- * directive provides DOM manipulation and the `NgModelController` provides the data-binding.
- *
- * This example shows how to use `NgModelController` with a custom control to achieve
- * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
- * collaborate together to achieve the desired result.
- *
- * <example module="customControl">
-    <file name="style.css">
-      [contenteditable] {
-        border: 1px solid black;
-        background-color: white;
-        min-height: 20px;
-      }
-
-      .ng-invalid {
-        border: 1px solid red;
-      }
-
-    </file>
-    <file name="script.js">
-      angular.module('customControl', []).
-        directive('contenteditable', function() {
-          return {
-            restrict: 'A', // only activate on element attribute
-            require: '?ngModel', // get a hold of NgModelController
-            link: function(scope, element, attrs, ngModel) {
-              if(!ngModel) return; // do nothing if no ng-model
-
-              // Specify how UI should be updated
-              ngModel.$render = function() {
-                element.html(ngModel.$viewValue || '');
-              };
-
-              // Listen for change events to enable binding
-              element.bind('blur keyup change', function() {
-                scope.$apply(read);
-              });
-              read(); // initialize
-
-              // Write data to the model
-              function read() {
-                ngModel.$setViewValue(element.html());
-              }
-            }
-          };
-        });
-    </file>
-    <file name="index.html">
-      <form name="myForm">
-       <div contenteditable
-            name="myWidget" ng-model="userContent"
-            required>Change me!</div>
-        <span ng-show="myForm.myWidget.$error.required">Required!</span>
-       <hr>
-       <textarea ng-model="userContent"></textarea>
-      </form>
-    </file>
-    <file name="scenario.js">
-      it('should data-bind and become invalid', function() {
-        var contentEditable = element('[contenteditable]');
-
-        expect(contentEditable.text()).toEqual('Change me!');
-        input('userContent').enter('');
-        expect(contentEditable.text()).toEqual('');
-        expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/);
-      });
-    </file>
- * </example>
- *
- */
-var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse',
-    function($scope, $exceptionHandler, $attr, $element, $parse) {
-  this.$viewValue = Number.NaN;
-  this.$modelValue = Number.NaN;
-  this.$parsers = [];
-  this.$formatters = [];
-  this.$viewChangeListeners = [];
-  this.$pristine = true;
-  this.$dirty = false;
-  this.$valid = true;
-  this.$invalid = false;
-  this.$name = $attr.name;
-
-  var ngModelGet = $parse($attr.ngModel),
-      ngModelSet = ngModelGet.assign;
-
-  if (!ngModelSet) {
-    throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + $attr.ngModel +
-        ' (' + startingTag($element) + ')');
-  }
-
-  /**
-   * @ngdoc function
-   * @name ng.directive:ngModel.NgModelController#$render
-   * @methodOf ng.directive:ngModel.NgModelController
-   *
-   * @description
-   * Called when the view needs to be updated. It is expected that the user of the ng-model
-   * directive will implement this method.
-   */
-  this.$render = noop;
-
-  var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
-      invalidCount = 0, // used to easily determine if we are valid
-      $error = this.$error = {}; // keep invalid keys here
-
-
-  // Setup initial state of the control
-  $element.addClass(PRISTINE_CLASS);
-  toggleValidCss(true);
-
-  // convenience method for easy toggling of classes
-  function toggleValidCss(isValid, validationErrorKey) {
-    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
-    $element.
-      removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey).
-      addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
-  }
-
-  /**
-   * @ngdoc function
-   * @name ng.directive:ngModel.NgModelController#$setValidity
-   * @methodOf ng.directive:ngModel.NgModelController
-   *
-   * @description
-   * Change the validity state, and notifies the form when the control changes validity. (i.e. it
-   * does not notify form if given validator is already marked as invalid).
-   *
-   * This method should be called by validators - i.e. the parser or formatter functions.
-   *
-   * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign
-   *        to `$error[validationErrorKey]=isValid` so that it is available for data-binding.
-   *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
-   *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
-   *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
-   * @param {boolean} isValid Whether the current state is valid (true) or invalid (false).
-   */
-  this.$setValidity = function(validationErrorKey, isValid) {
-    if ($error[validationErrorKey] === !isValid) return;
-
-    if (isValid) {
-      if ($error[validationErrorKey]) invalidCount--;
-      if (!invalidCount) {
-        toggleValidCss(true);
-        this.$valid = true;
-        this.$invalid = false;
-      }
-    } else {
-      toggleValidCss(false);
-      this.$invalid = true;
-      this.$valid = false;
-      invalidCount++;
-    }
-
-    $error[validationErrorKey] = !isValid;
-    toggleValidCss(isValid, validationErrorKey);
-
-    parentForm.$setValidity(validationErrorKey, isValid, this);
-  };
-
-
-  /**
-   * @ngdoc function
-   * @name ng.directive:ngModel.NgModelController#$setViewValue
-   * @methodOf ng.directive:ngModel.NgModelController
-   *
-   * @description
-   * Read a value from view.
-   *
-   * This method should be called from within a DOM event handler.
-   * For example {@link ng.directive:input input} or
-   * {@link ng.directive:select select} directives call it.
-   *
-   * It internally calls all `parsers` and if resulted value is valid, updates the model and
-   * calls all registered change listeners.
-   *
-   * @param {string} value Value from the view.
-   */
-  this.$setViewValue = function(value) {
-    this.$viewValue = value;
-
-    // change to dirty
-    if (this.$pristine) {
-      this.$dirty = true;
-      this.$pristine = false;
-      $element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS);
-      parentForm.$setDirty();
-    }
-
-    forEach(this.$parsers, function(fn) {
-      value = fn(value);
-    });
-
-    if (this.$modelValue !== value) {
-      this.$modelValue = value;
-      ngModelSet($scope, value);
-      forEach(this.$viewChangeListeners, function(listener) {
-        try {
-          listener();
-        } catch(e) {
-          $exceptionHandler(e);
-        }
-      })
-    }
-  };
-
-  // model -> value
-  var ctrl = this;
-
-  $scope.$watch(function ngModelWatch() {
-    var value = ngModelGet($scope);
-
-    // if scope model value and ngModel value are out of sync
-    if (ctrl.$modelValue !== value) {
-
-      var formatters = ctrl.$formatters,
-          idx = formatters.length;
-
-      ctrl.$modelValue = value;
-      while(idx--) {
-        value = formatters[idx](value);
-      }
-
-      if (ctrl.$viewValue !== value) {
-        ctrl.$viewValue = value;
-        ctrl.$render();
-      }
-    }
-  });
-}];
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngModel
- *
- * @element input
- *
- * @description
- * Is directive that tells Angular to do two-way data binding. It works together with `input`,
- * `select`, `textarea`. You can easily write your own directives to use `ngModel` as well.
- *
- * `ngModel` is responsible for:
- *
- * - binding the view into the model, which other directives such as `input`, `textarea` or `select`
- *   require,
- * - providing validation behavior (i.e. required, number, email, url),
- * - keeping state of the control (valid/invalid, dirty/pristine, validation errors),
- * - setting related css class onto the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`),
- * - register the control with parent {@link ng.directive:form form}.
- *
- * For basic examples, how to use `ngModel`, see:
- *
- *  - {@link ng.directive:input input}
- *    - {@link ng.directive:input.text text}
- *    - {@link ng.directive:input.checkbox checkbox}
- *    - {@link ng.directive:input.radio radio}
- *    - {@link ng.directive:input.number number}
- *    - {@link ng.directive:input.email email}
- *    - {@link ng.directive:input.url url}
- *  - {@link ng.directive:select select}
- *  - {@link ng.directive:textarea textarea}
- *
- */
-var ngModelDirective = function() {
-  return {
-    require: ['ngModel', '^?form'],
-    controller: NgModelController,
-    link: function(scope, element, attr, ctrls) {
-      // notify others, especially parent forms
-
-      var modelCtrl = ctrls[0],
-          formCtrl = ctrls[1] || nullFormCtrl;
-
-      formCtrl.$addControl(modelCtrl);
-
-      element.bind('$destroy', function() {
-        formCtrl.$removeControl(modelCtrl);
-      });
-    }
-  };
-};
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngChange
- * @restrict E
- *
- * @description
- * Evaluate given expression when user changes the input.
- * The expression is not evaluated when the value change is coming from the model.
- *
- * Note, this directive requires `ngModel` to be present.
- *
- * @element input
- *
- * @example
- * <doc:example>
- *   <doc:source>
- *     <script>
- *       function Controller($scope) {
- *         $scope.counter = 0;
- *         $scope.change = function() {
- *           $scope.counter++;
- *         };
- *       }
- *     </script>
- *     <div ng-controller="Controller">
- *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
- *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
- *       <label for="ng-change-example2">Confirmed</label><br />
- *       debug = {{confirmed}}<br />
- *       counter = {{counter}}
- *     </div>
- *   </doc:source>
- *   <doc:scenario>
- *     it('should evaluate the expression if changing from view', function() {
- *       expect(binding('counter')).toEqual('0');
- *       element('#ng-change-example1').click();
- *       expect(binding('counter')).toEqual('1');
- *       expect(binding('confirmed')).toEqual('true');
- *     });
- *
- *     it('should not evaluate the expression if changing from model', function() {
- *       element('#ng-change-example2').click();
- *       expect(binding('counter')).toEqual('0');
- *       expect(binding('confirmed')).toEqual('true');
- *     });
- *   </doc:scenario>
- * </doc:example>
- */
-var ngChangeDirective = valueFn({
-  require: 'ngModel',
-  link: function(scope, element, attr, ctrl) {
-    ctrl.$viewChangeListeners.push(function() {
-      scope.$eval(attr.ngChange);
-    });
-  }
-});
-
-
-var requiredDirective = function() {
-  return {
-    require: '?ngModel',
-    link: function(scope, elm, attr, ctrl) {
-      if (!ctrl) return;
-      attr.required = true; // force truthy in case we are on non input element
-
-      var validator = function(value) {
-        if (attr.required && (isEmpty(value) || value === false)) {
-          ctrl.$setValidity('required', false);
-          return;
-        } else {
-          ctrl.$setValidity('required', true);
-          return value;
-        }
-      };
-
-      ctrl.$formatters.push(validator);
-      ctrl.$parsers.unshift(validator);
-
-      attr.$observe('required', function() {
-        validator(ctrl.$viewValue);
-      });
-    }
-  };
-};
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngList
- *
- * @description
- * Text input that converts between comma-separated string into an array of strings.
- *
- * @element input
- * @param {string=} ngList optional delimiter that should be used to split the value. If
- *   specified in form `/something/` then the value will be converted into a regular expression.
- *
- * @example
-    <doc:example>
-      <doc:source>
-       <script>
-         function Ctrl($scope) {
-           $scope.names = ['igor', 'misko', 'vojta'];
-         }
-       </script>
-       <form name="myForm" ng-controller="Ctrl">
-         List: <input name="namesInput" ng-model="names" ng-list required>
-         <span class="error" ng-show="myForm.list.$error.required">
-           Required!</span>
-         <tt>names = {{names}}</tt><br/>
-         <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
-         <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
-         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
-         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
-        </form>
-      </doc:source>
-      <doc:scenario>
-        it('should initialize to model', function() {
-          expect(binding('names')).toEqual('["igor","misko","vojta"]');
-          expect(binding('myForm.namesInput.$valid')).toEqual('true');
-        });
-
-        it('should be invalid if empty', function() {
-          input('names').enter('');
-          expect(binding('names')).toEqual('[]');
-          expect(binding('myForm.namesInput.$valid')).toEqual('false');
-        });
-      </doc:scenario>
-    </doc:example>
- */
-var ngListDirective = function() {
-  return {
-    require: 'ngModel',
-    link: function(scope, element, attr, ctrl) {
-      var match = /\/(.*)\//.exec(attr.ngList),
-          separator = match && new RegExp(match[1]) || attr.ngList || ',';
-
-      var parse = function(viewValue) {
-        var list = [];
-
-        if (viewValue) {
-          forEach(viewValue.split(separator), function(value) {
-            if (value) list.push(trim(value));
-          });
-        }
-
-        return list;
-      };
-
-      ctrl.$parsers.push(parse);
-      ctrl.$formatters.push(function(value) {
-        if (isArray(value)) {
-          return value.join(', ');
-        }
-
-        return undefined;
-      });
-    }
-  };
-};
-
-
-var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
-
-var ngValueDirective = function() {
-  return {
-    priority: 100,
-    compile: function(tpl, tplAttr) {
-      if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
-        return function(scope, elm, attr) {
-          attr.$set('value', scope.$eval(attr.ngValue));
-        };
-      } else {
-        return function(scope, elm, attr) {
-          scope.$watch(attr.ngValue, function valueWatchAction(value) {
-            attr.$set('value', value, false);
-          });
-        };
-      }
-    }
-  };
-};
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngBind
- *
- * @description
- * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
- * with the value of a given expression, and to update the text content when the value of that
- * expression changes.
- *
- * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
- * `{{ expression }}` which is similar but less verbose.
- *
- * One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
- * it's desirable to put bindings into template that is momentarily displayed by the browser in its
- * raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the
- * bindings invisible to the user while the page is loading.
- *
- * An alternative solution to this problem would be using the
- * {@link ng.directive:ngCloak ngCloak} directive.
- *
- *
- * @element ANY
- * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
- *
- * @example
- * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
-   <doc:example>
-     <doc:source>
-       <script>
-         function Ctrl($scope) {
-           $scope.name = 'Whirled';
-         }
-       </script>
-       <div ng-controller="Ctrl">
-         Enter name: <input type="text" ng-model="name"><br>
-         Hello <span ng-bind="name"></span>!
-       </div>
-     </doc:source>
-     <doc:scenario>
-       it('should check ng-bind', function() {
-         expect(using('.doc-example-live').binding('name')).toBe('Whirled');
-         using('.doc-example-live').input('name').enter('world');
-         expect(using('.doc-example-live').binding('name')).toBe('world');
-       });
-     </doc:scenario>
-   </doc:example>
- */
-var ngBindDirective = ngDirective(function(scope, element, attr) {
-  element.addClass('ng-binding').data('$binding', attr.ngBind);
-  scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
-    element.text(value == undefined ? '' : value);
-  });
-});
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngBindTemplate
- *
- * @description
- * The `ngBindTemplate` directive specifies that the element
- * text should be replaced with the template in ngBindTemplate.
- * Unlike ngBind the ngBindTemplate can contain multiple `{{` `}}`
- * expressions. (This is required since some HTML elements
- * can not have SPAN elements such as TITLE, or OPTION to name a few.)
- *
- * @element ANY
- * @param {string} ngBindTemplate template of form
- *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
- *
- * @example
- * Try it here: enter text in text box and watch the greeting change.
-   <doc:example>
-     <doc:source>
-       <script>
-         function Ctrl($scope) {
-           $scope.salutation = 'Hello';
-           $scope.name = 'World';
-         }
-       </script>
-       <div ng-controller="Ctrl">
-        Salutation: <input type="text" ng-model="salutation"><br>
-        Name: <input type="text" ng-model="name"><br>
-        <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
-       </div>
-     </doc:source>
-     <doc:scenario>
-       it('should check ng-bind', function() {
-         expect(using('.doc-example-live').binding('salutation')).
-           toBe('Hello');
-         expect(using('.doc-example-live').binding('name')).
-           toBe('World');
-         using('.doc-example-live').input('salutation').enter('Greetings');
-         using('.doc-example-live').input('name').enter('user');
-         expect(using('.doc-example-live').binding('salutation')).
-           toBe('Greetings');
-         expect(using('.doc-example-live').binding('name')).
-           toBe('user');
-       });
-     </doc:scenario>
-   </doc:example>
- */
-var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
-  return function(scope, element, attr) {
-    // TODO: move this to scenario runner
-    var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
-    element.addClass('ng-binding').data('$binding', interpolateFn);
-    attr.$observe('ngBindTemplate', function(value) {
-      element.text(value);
-    });
-  }
-}];
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngBindHtmlUnsafe
- *
- * @description
- * Creates a binding that will innerHTML the result of evaluating the `expression` into the current
- * element. *The innerHTML-ed content will not be sanitized!* You should use this directive only if
- * {@link ngSanitize.directive:ngBindHtml ngBindHtml} directive is too
- * restrictive and when you absolutely trust the source of the content you are binding to.
- *
- * See {@link ngSanitize.$sanitize $sanitize} docs for examples.
- *
- * @element ANY
- * @param {expression} ngBindHtmlUnsafe {@link guide/expression Expression} to evaluate.
- */
-var ngBindHtmlUnsafeDirective = [function() {
-  return function(scope, element, attr) {
-    element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe);
-    scope.$watch(attr.ngBindHtmlUnsafe, function ngBindHtmlUnsafeWatchAction(value) {
-      element.html(value || '');
-    });
-  };
-}];
-
-function classDirective(name, selector) {
-  name = 'ngClass' + name;
-  return ngDirective(function(scope, element, attr) {
-    var oldVal = undefined;
-
-    scope.$watch(attr[name], ngClassWatchAction, true);
-
-    attr.$observe('class', function(value) {
-      var ngClass = scope.$eval(attr[name]);
-      ngClassWatchAction(ngClass, ngClass);
-    });
-
-
-    if (name !== 'ngClass') {
-      scope.$watch('$index', function($index, old$index) {
-        var mod = $index & 1;
-        if (mod !== old$index & 1) {
-          if (mod === selector) {
-            addClass(scope.$eval(attr[name]));
-          } else {
-            removeClass(scope.$eval(attr[name]));
-          }
-        }
-      });
-    }
-
-
-    function ngClassWatchAction(newVal) {
-      if (selector === true || scope.$index % 2 === selector) {
-        if (oldVal && !equals(newVal,oldVal)) {
-          removeClass(oldVal);
-        }
-        addClass(newVal);
-      }
-      oldVal = copy(newVal);
-    }
-
-
-    function removeClass(classVal) {
-      if (isObject(classVal) && !isArray(classVal)) {
-        classVal = map(classVal, function(v, k) { if (v) return k });
-      }
-      element.removeClass(isArray(classVal) ? classVal.join(' ') : classVal);
-    }
-
-
-    function addClass(classVal) {
-      if (isObject(classVal) && !isArray(classVal)) {
-        classVal = map(classVal, function(v, k) { if (v) return k });
-      }
-      if (classVal) {
-        element.addClass(isArray(classVal) ? classVal.join(' ') : classVal);
-      }
-    }
-  });
-}
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngClass
- *
- * @description
- * The `ngClass` allows you to set CSS class on HTML element dynamically by databinding an
- * expression that represents all classes to be added.
- *
- * The directive won't add duplicate classes if a particular class was already set.
- *
- * When the expression changes, the previously added classes are removed and only then the
- * new classes are added.
- *
- * @element ANY
- * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
- *   of the evaluation can be a string representing space delimited class
- *   names, an array, or a map of class names to boolean values.
- *
- * @example
-   <example>
-     <file name="index.html">
-      <input type="button" value="set" ng-click="myVar='my-class'">
-      <input type="button" value="clear" ng-click="myVar=''">
-      <br>
-      <span ng-class="myVar">Sample Text</span>
-     </file>
-     <file name="style.css">
-       .my-class {
-         color: red;
-       }
-     </file>
-     <file name="scenario.js">
-       it('should check ng-class', function() {
-         expect(element('.doc-example-live span').prop('className')).not().
-           toMatch(/my-class/);
-
-         using('.doc-example-live').element(':button:first').click();
-
-         expect(element('.doc-example-live span').prop('className')).
-           toMatch(/my-class/);
-
-         using('.doc-example-live').element(':button:last').click();
-
-         expect(element('.doc-example-live span').prop('className')).not().
-           toMatch(/my-class/);
-       });
-     </file>
-   </example>
- */
-var ngClassDirective = classDirective('', true);
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngClassOdd
- *
- * @description
- * The `ngClassOdd` and `ngClassEven` directives work exactly as
- * {@link ng.directive:ngClass ngClass}, except it works in
- * conjunction with `ngRepeat` and takes affect only on odd (even) rows.
- *
- * This directive can be applied only within a scope of an
- * {@link ng.directive:ngRepeat ngRepeat}.
- *
- * @element ANY
- * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
- *   of the evaluation can be a string representing space delimited class names or an array.
- *
- * @example
-   <example>
-     <file name="index.html">
-        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
-          <li ng-repeat="name in names">
-           <span ng-class-odd="'odd'" ng-class-even="'even'">
-             {{name}}
-           </span>
-          </li>
-        </ol>
-     </file>
-     <file name="style.css">
-       .odd {
-         color: red;
-       }
-       .even {
-         color: blue;
-       }
-     </file>
-     <file name="scenario.js">
-       it('should check ng-class-odd and ng-class-even', function() {
-         expect(element('.doc-example-live li:first span').prop('className')).
-           toMatch(/odd/);
-         expect(element('.doc-example-live li:last span').prop('className')).
-           toMatch(/even/);
-       });
-     </file>
-   </example>
- */
-var ngClassOddDirective = classDirective('Odd', 0);
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngClassEven
- *
- * @description
- * The `ngClassOdd` and `ngClassEven` directives work exactly as
- * {@link ng.directive:ngClass ngClass}, except it works in
- * conjunction with `ngRepeat` and takes affect only on odd (even) rows.
- *
- * This directive can be applied only within a scope of an
- * {@link ng.directive:ngRepeat ngRepeat}.
- *
- * @element ANY
- * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
- *   result of the evaluation can be a string representing space delimited class names or an array.
- *
- * @example
-   <example>
-     <file name="index.html">
-        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
-          <li ng-repeat="name in names">
-           <span ng-class-odd="'odd'" ng-class-even="'even'">
-             {{name}} &nbsp; &nbsp; &nbsp;
-           </span>
-          </li>
-        </ol>
-     </file>
-     <file name="style.css">
-       .odd {
-         color: red;
-       }
-       .even {
-         color: blue;
-       }
-     </file>
-     <file name="scenario.js">
-       it('should check ng-class-odd and ng-class-even', function() {
-         expect(element('.doc-example-live li:first span').prop('className')).
-           toMatch(/odd/);
-         expect(element('.doc-example-live li:last span').prop('className')).
-           toMatch(/even/);
-       });
-     </file>
-   </example>
- */
-var ngClassEvenDirective = classDirective('Even', 1);
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngCloak
- *
- * @description
- * The `ngCloak` directive is used to prevent the Angular html template from being briefly
- * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
- * directive to avoid the undesirable flicker effect caused by the html template display.
- *
- * The directive can be applied to the `<body>` element, but typically a fine-grained application is
- * prefered in order to benefit from progressive rendering of the browser view.
- *
- * `ngCloak` works in cooperation with a css rule that is embedded within `angular.js` and
- *  `angular.min.js` files. Following is the css rule:
- *
- * <pre>
- * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
- *   display: none;
- * }
- * </pre>
- *
- * When this css rule is loaded by the browser, all html elements (including their children) that
- * are tagged with the `ng-cloak` directive are hidden. When Angular comes across this directive
- * during the compilation of the template it deletes the `ngCloak` element attribute, which
- * makes the compiled element visible.
- *
- * For the best result, `angular.js` script must be loaded in the head section of the html file;
- * alternatively, the css rule (above) must be included in the external stylesheet of the
- * application.
- *
- * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
- * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
- * class `ngCloak` in addition to `ngCloak` directive as shown in the example below.
- *
- * @element ANY
- *
- * @example
-   <doc:example>
-     <doc:source>
-        <div id="template1" ng-cloak>{{ 'hello' }}</div>
-        <div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div>
-     </doc:source>
-     <doc:scenario>
-       it('should remove the template directive and css class', function() {
-         expect(element('.doc-example-live #template1').attr('ng-cloak')).
-           not().toBeDefined();
-         expect(element('.doc-example-live #template2').attr('ng-cloak')).
-           not().toBeDefined();
-       });
-     </doc:scenario>
-   </doc:example>
- *
- */
-var ngCloakDirective = ngDirective({
-  compile: function(element, attr) {
-    attr.$set('ngCloak', undefined);
-    element.removeClass('ng-cloak');
-  }
-});
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngController
- *
- * @description
- * The `ngController` directive assigns behavior to a scope. This is a key aspect of how angular
- * supports the principles behind the Model-View-Controller design pattern.
- *
- * MVC components in angular:
- *
- * * Model — The Model is data in scope properties; scopes are attached to the DOM.
- * * View — The template (HTML with data bindings) is rendered into the View.
- * * Controller — The `ngController` directive specifies a Controller class; the class has
- *   methods that typically express the business logic behind the application.
- *
- * Note that an alternative way to define controllers is via the {@link ng.$route $route} service.
- *
- * @element ANY
- * @scope
- * @param {expression} ngController Name of a globally accessible constructor function or an
- *     {@link guide/expression expression} that on the current scope evaluates to a
- *     constructor function.
- *
- * @example
- * Here is a simple form for editing user contact information. Adding, removing, clearing, and
- * greeting are methods declared on the controller (see source tab). These methods can
- * easily be called from the angular markup. Notice that the scope becomes the `this` for the
- * controller's instance. This allows for easy access to the view data from the controller. Also
- * notice that any changes to the data are automatically reflected in the View without the need
- * for a manual update.
-   <doc:example>
-     <doc:source>
-      <script>
-        function SettingsController($scope) {
-          $scope.name = "John Smith";
-          $scope.contacts = [
-            {type:'phone', value:'408 555 1212'},
-            {type:'email', value:'john.smith@example.org'} ];
-
-          $scope.greet = function() {
-           alert(this.name);
-          };
-
-          $scope.addContact = function() {
-           this.contacts.push({type:'email', value:'yourname@example.org'});
-          };
-
-          $scope.removeContact = function(contactToRemove) {
-           var index = this.contacts.indexOf(contactToRemove);
-           this.contacts.splice(index, 1);
-          };
-
-          $scope.clearContact = function(contact) {
-           contact.type = 'phone';
-           contact.value = '';
-          };
-        }
-      </script>
-      <div ng-controller="SettingsController">
-        Name: <input type="text" ng-model="name"/>
-        [ <a href="" ng-click="greet()">greet</a> ]<br/>
-        Contact:
-        <ul>
-          <li ng-repeat="contact in contacts">
-            <select ng-model="contact.type">
-               <option>phone</option>
-               <option>email</option>
-            </select>
-            <input type="text" ng-model="contact.value"/>
-            [ <a href="" ng-click="clearContact(contact)">clear</a>
-            | <a href="" ng-click="removeContact(contact)">X</a> ]
-          </li>
-          <li>[ <a href="" ng-click="addContact()">add</a> ]</li>
-       </ul>
-      </div>
-     </doc:source>
-     <doc:scenario>
-       it('should check controller', function() {
-         expect(element('.doc-example-live div>:input').val()).toBe('John Smith');
-         expect(element('.doc-example-live li:nth-child(1) input').val())
-           .toBe('408 555 1212');
-         expect(element('.doc-example-live li:nth-child(2) input').val())
-           .toBe('john.smith@example.org');
-
-         element('.doc-example-live li:first a:contains("clear")').click();
-         expect(element('.doc-example-live li:first input').val()).toBe('');
-
-         element('.doc-example-live li:last a:contains("add")').click();
-         expect(element('.doc-example-live li:nth-child(3) input').val())
-           .toBe('yourname@example.org');
-       });
-     </doc:scenario>
-   </doc:example>
- */
-var ngControllerDirective = [function() {
-  return {
-    scope: true,
-    controller: '@'
-  };
-}];
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngCsp
- * @priority 1000
- *
- * @element html
- * @description
- * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
- * 
- * This is necessary when developing things like Google Chrome Extensions.
- * 
- * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
- * For us to be compatible, we just need to implement the "getterFn" in $parse without violating
- * any of these restrictions.
- * 
- * AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp`
- * it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will
- * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
- * be raised.
- * 
- * In order to use this feature put `ngCsp` directive on the root element of the application.
- * 
- * @example
- * This example shows how to apply the `ngCsp` directive to the `html` tag.
-   <pre>
-     <!doctype html>
-     <html ng-app ng-csp>
-     ...
-     ...
-     </html>
-   </pre>
- */
-
-var ngCspDirective = ['$sniffer', function($sniffer) {
-  return {
-    priority: 1000,
-    compile: function() {
-      $sniffer.csp = true;
-    }
-  };
-}];
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngClick
- *
- * @description
- * The ngClick allows you to specify custom behavior when
- * element is clicked.
- *
- * @element ANY
- * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
- * click. (Event object is available as `$event`)
- *
- * @example
-   <doc:example>
-     <doc:source>
-      <button ng-click="count = count + 1" ng-init="count=0">
-        Increment
-      </button>
-      count: {{count}}
-     </doc:source>
-     <doc:scenario>
-       it('should check ng-click', function() {
-         expect(binding('count')).toBe('0');
-         element('.doc-example-live :button').click();
-         expect(binding('count')).toBe('1');
-       });
-     </doc:scenario>
-   </doc:example>
- */
-/*
- * A directive that allows creation of custom onclick handlers that are defined as angular
- * expressions and are compiled and executed within the current scope.
- *
- * Events that are handled via these handler are always configured not to propagate further.
- */
-var ngEventDirectives = {};
-forEach(
-  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave'.split(' '),
-  function(name) {
-    var directiveName = directiveNormalize('ng-' + name);
-    ngEventDirectives[directiveName] = ['$parse', function($parse) {
-      return function(scope, element, attr) {
-        var fn = $parse(attr[directiveName]);
-        element.bind(lowercase(name), function(event) {
-          scope.$apply(function() {
-            fn(scope, {$event:event});
-          });
-        });
-      };
-    }];
-  }
-);
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngDblclick
- *
- * @description
- * The `ngDblclick` directive allows you to specify custom behavior on dblclick event.
- *
- * @element ANY
- * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
- * dblclick. (Event object is available as `$event`)
- *
- * @example
- * See {@link ng.directive:ngClick ngClick}
- */
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngMousedown
- *
- * @description
- * The ngMousedown directive allows you to specify custom behavior on mousedown event.
- *
- * @element ANY
- * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
- * mousedown. (Event object is available as `$event`)
- *
- * @example
- * See {@link ng.directive:ngClick ngClick}
- */
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngMouseup
- *
- * @description
- * Specify custom behavior on mouseup event.
- *
- * @element ANY
- * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
- * mouseup. (Event object is available as `$event`)
- *
- * @example
- * See {@link ng.directive:ngClick ngClick}
- */
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngMouseover
- *
- * @description
- * Specify custom behavior on mouseover event.
- *
- * @element ANY
- * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
- * mouseover. (Event object is available as `$event`)
- *
- * @example
- * See {@link ng.directive:ngClick ngClick}
- */
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngMouseenter
- *
- * @description
- * Specify custom behavior on mouseenter event.
- *
- * @element ANY
- * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
- * mouseenter. (Event object is available as `$event`)
- *
- * @example
- * See {@link ng.directive:ngClick ngClick}
- */
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngMouseleave
- *
- * @description
- * Specify custom behavior on mouseleave event.
- *
- * @element ANY
- * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
- * mouseleave. (Event object is available as `$event`)
- *
- * @example
- * See {@link ng.directive:ngClick ngClick}
- */
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngMousemove
- *
- * @description
- * Specify custom behavior on mousemove event.
- *
- * @element ANY
- * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
- * mousemove. (Event object is available as `$event`)
- *
- * @example
- * See {@link ng.directive:ngClick ngClick}
- */
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngSubmit
- *
- * @description
- * Enables binding angular expressions to onsubmit events.
- *
- * Additionally it prevents the default action (which for form means sending the request to the
- * server and reloading the current page).
- *
- * @element form
- * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
- *
- * @example
-   <doc:example>
-     <doc:source>
-      <script>
-        function Ctrl($scope) {
-          $scope.list = [];
-          $scope.text = 'hello';
-          $scope.submit = function() {
-            if (this.text) {
-              this.list.push(this.text);
-              this.text = '';
-            }
-          };
-        }
-      </script>
-      <form ng-submit="submit()" ng-controller="Ctrl">
-        Enter text and hit enter:
-        <input type="text" ng-model="text" name="text" />
-        <input type="submit" id="submit" value="Submit" />
-        <pre>list={{list}}</pre>
-      </form>
-     </doc:source>
-     <doc:scenario>
-       it('should check ng-submit', function() {
-         expect(binding('list')).toBe('[]');
-         element('.doc-example-live #submit').click();
-         expect(binding('list')).toBe('["hello"]');
-         expect(input('text').val()).toBe('');
-       });
-       it('should ignore empty strings', function() {
-         expect(binding('list')).toBe('[]');
-         element('.doc-example-live #submit').click();
-         element('.doc-example-live #submit').click();
-         expect(binding('list')).toBe('["hello"]');
-       });
-     </doc:scenario>
-   </doc:example>
- */
-var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
-  element.bind('submit', function() {
-    scope.$apply(attrs.ngSubmit);
-  });
-});
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngInclude
- * @restrict ECA
- *
- * @description
- * Fetches, compiles and includes an external HTML fragment.
- *
- * Keep in mind that Same Origin Policy applies to included resources
- * (e.g. ngInclude won't work for cross-domain requests on all browsers and for
- *  file:// access on some browsers).
- *
- * @scope
- *
- * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
- *                 make sure you wrap it in quotes, e.g. `src="'myPartialTemplate.html'"`.
- * @param {string=} onload Expression to evaluate when a new partial is loaded.
- *
- * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
- *                  $anchorScroll} to scroll the viewport after the content is loaded.
- *
- *                  - If the attribute is not set, disable scrolling.
- *                  - If the attribute is set without value, enable scrolling.
- *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
- *
- * @example
-  <example>
-    <file name="index.html">
-     <div ng-controller="Ctrl">
-       <select ng-model="template" ng-options="t.name for t in templates">
-        <option value="">(blank)</option>
-       </select>
-       url of the template: <tt>{{template.url}}</tt>
-       <hr/>
-       <div ng-include src="template.url"></div>
-     </div>
-    </file>
-    <file name="script.js">
-      function Ctrl($scope) {
-        $scope.templates =
-          [ { name: 'template1.html', url: 'template1.html'}
-          , { name: 'template2.html', url: 'template2.html'} ];
-        $scope.template = $scope.templates[0];
-      }
-     </file>
-    <file name="template1.html">
-      Content of template1.html
-    </file>
-    <file name="template2.html">
-      Content of template2.html
-    </file>
-    <file name="scenario.js">
-      it('should load template1.html', function() {
-       expect(element('.doc-example-live [ng-include]').text()).
-         toMatch(/Content of template1.html/);
-      });
-      it('should load template2.html', function() {
-       select('template').option('1');
-       expect(element('.doc-example-live [ng-include]').text()).
-         toMatch(/Content of template2.html/);
-      });
-      it('should change to blank', function() {
-       select('template').option('');
-       expect(element('.doc-example-live [ng-include]').text()).toEqual('');
-      });
-    </file>
-  </example>
- */
-
-
-/**
- * @ngdoc event
- * @name ng.directive:ngInclude#$includeContentLoaded
- * @eventOf ng.directive:ngInclude
- * @eventType emit on the current ngInclude scope
- * @description
- * Emitted every time the ngInclude content is reloaded.
- */
-var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile',
-                  function($http,   $templateCache,   $anchorScroll,   $compile) {
-  return {
-    restrict: 'ECA',
-    terminal: true,
-    compile: function(element, attr) {
-      var srcExp = attr.ngInclude || attr.src,
-          onloadExp = attr.onload || '',
-          autoScrollExp = attr.autoscroll;
-
-      return function(scope, element) {
-        var changeCounter = 0,
-            childScope;
-
-        var clearContent = function() {
-          if (childScope) {
-            childScope.$destroy();
-            childScope = null;
-          }
-
-          element.html('');
-        };
-
-        scope.$watch(srcExp, function ngIncludeWatchAction(src) {
-          var thisChangeId = ++changeCounter;
-
-          if (src) {
-            $http.get(src, {cache: $templateCache}).success(function(response) {
-              if (thisChangeId !== changeCounter) return;
-
-              if (childScope) childScope.$destroy();
-              childScope = scope.$new();
-
-              element.html(response);
-              $compile(element.contents())(childScope);
-
-              if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
-                $anchorScroll();
-              }
-
-              childScope.$emit('$includeContentLoaded');
-              scope.$eval(onloadExp);
-            }).error(function() {
-              if (thisChangeId === changeCounter) clearContent();
-            });
-          } else clearContent();
-        });
-      };
-    }
-  };
-}];
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngInit
- *
- * @description
- * The `ngInit` directive specifies initialization tasks to be executed
- *  before the template enters execution mode during bootstrap.
- *
- * @element ANY
- * @param {expression} ngInit {@link guide/expression Expression} to eval.
- *
- * @example
-   <doc:example>
-     <doc:source>
-    <div ng-init="greeting='Hello'; person='World'">
-      {{greeting}} {{person}}!
-    </div>
-     </doc:source>
-     <doc:scenario>
-       it('should check greeting', function() {
-         expect(binding('greeting')).toBe('Hello');
-         expect(binding('person')).toBe('World');
-       });
-     </doc:scenario>
-   </doc:example>
- */
-var ngInitDirective = ngDirective({
-  compile: function() {
-    return {
-      pre: function(scope, element, attrs) {
-        scope.$eval(attrs.ngInit);
-      }
-    }
-  }
-});
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngNonBindable
- * @priority 1000
- *
- * @description
- * Sometimes it is necessary to write code which looks like bindings but which should be left alone
- * by angular. Use `ngNonBindable` to make angular ignore a chunk of HTML.
- *
- * @element ANY
- *
- * @example
- * In this example there are two location where a simple binding (`{{}}`) is present, but the one
- * wrapped in `ngNonBindable` is left alone.
- *
- * @example
-    <doc:example>
-      <doc:source>
-        <div>Normal: {{1 + 2}}</div>
-        <div ng-non-bindable>Ignored: {{1 + 2}}</div>
-      </doc:source>
-      <doc:scenario>
-       it('should check ng-non-bindable', function() {
-         expect(using('.doc-example-live').binding('1 + 2')).toBe('3');
-         expect(using('.doc-example-live').element('div:last').text()).
-           toMatch(/1 \+ 2/);
-       });
-      </doc:scenario>
-    </doc:example>
- */
-var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngPluralize
- * @restrict EA
- *
- * @description
- * # Overview
- * `ngPluralize` is a directive that displays messages according to en-US localization rules.
- * These rules are bundled with angular.js and the rules can be overridden
- * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
- * by specifying the mappings between
- * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
- * plural categories} and the strings to be displayed.
- *
- * # Plural categories and explicit number rules
- * There are two
- * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
- * plural categories} in Angular's default en-US locale: "one" and "other".
- *
- * While a pural category may match many numbers (for example, in en-US locale, "other" can match
- * any number that is not 1), an explicit number rule can only match one number. For example, the
- * explicit number rule for "3" matches the number 3. You will see the use of plural categories
- * and explicit number rules throughout later parts of this documentation.
- *
- * # Configuring ngPluralize
- * You configure ngPluralize by providing 2 attributes: `count` and `when`.
- * You can also provide an optional attribute, `offset`.
- *
- * The value of the `count` attribute can be either a string or an {@link guide/expression
- * Angular expression}; these are evaluated on the current scope for its bound value.
- *
- * The `when` attribute specifies the mappings between plural categories and the actual
- * string to be displayed. The value of the attribute should be a JSON object so that Angular
- * can interpret it correctly.
- *
- * The following example shows how to configure ngPluralize:
- *
- * <pre>
- * <ng-pluralize count="personCount"
-                 when="{'0': 'Nobody is viewing.',
- *                      'one': '1 person is viewing.',
- *                      'other': '{} people are viewing.'}">
- * </ng-pluralize>
- *</pre>
- *
- * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
- * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
- * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
- * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
- * show "a dozen people are viewing".
- *
- * You can use a set of closed braces(`{}`) as a placeholder for the number that you want substituted
- * into pluralized strings. In the previous example, Angular will replace `{}` with
- * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
- * for <span ng-non-bindable>{{numberExpression}}</span>.
- *
- * # Configuring ngPluralize with offset
- * The `offset` attribute allows further customization of pluralized text, which can result in
- * a better user experience. For example, instead of the message "4 people are viewing this document",
- * you might display "John, Kate and 2 others are viewing this document".
- * The offset attribute allows you to offset a number by any desired value.
- * Let's take a look at an example:
- *
- * <pre>
- * <ng-pluralize count="personCount" offset=2
- *               when="{'0': 'Nobody is viewing.',
- *                      '1': '{{person1}} is viewing.',
- *                      '2': '{{person1}} and {{person2}} are viewing.',
- *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
- *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
- * </ng-pluralize>
- * </pre>
- *
- * Notice that we are still using two plural categories(one, other), but we added
- * three explicit number rules 0, 1 and 2.
- * When one person, perhaps John, views the document, "John is viewing" will be shown.
- * When three people view the document, no explicit number rule is found, so
- * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
- * In this case, plural category 'one' is matched and "John, Marry and one other person are viewing"
- * is shown.
- *
- * Note that when you specify offsets, you must provide explicit number rules for
- * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
- * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
- * plural categories "one" and "other".
- *
- * @param {string|expression} count The variable to be bounded to.
- * @param {string} when The mapping between plural category to its correspoding strings.
- * @param {number=} offset Offset to deduct from the total number.
- *
- * @example
-    <doc:example>
-      <doc:source>
-        <script>
-          function Ctrl($scope) {
-            $scope.person1 = 'Igor';
-            $scope.person2 = 'Misko';
-            $scope.personCount = 1;
-          }
-        </script>
-        <div ng-controller="Ctrl">
-          Person 1:<input type="text" ng-model="person1" value="Igor" /><br/>
-          Person 2:<input type="text" ng-model="person2" value="Misko" /><br/>
-          Number of People:<input type="text" ng-model="personCount" value="1" /><br/>
-
-          <!--- Example with simple pluralization rules for en locale --->
-          Without Offset:
-          <ng-pluralize count="personCount"
-                        when="{'0': 'Nobody is viewing.',
-                               'one': '1 person is viewing.',
-                               'other': '{} people are viewing.'}">
-          </ng-pluralize><br>
-
-          <!--- Example with offset --->
-          With Offset(2):
-          <ng-pluralize count="personCount" offset=2
-                        when="{'0': 'Nobody is viewing.',
-                               '1': '{{person1}} is viewing.',
-                               '2': '{{person1}} and {{person2}} are viewing.',
-                               'one': '{{person1}}, {{person2}} and one other person are viewing.',
-                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
-          </ng-pluralize>
-        </div>
-      </doc:source>
-      <doc:scenario>
-        it('should show correct pluralized string', function() {
-          expect(element('.doc-example-live ng-pluralize:first').text()).
-                                             toBe('1 person is viewing.');
-          expect(element('.doc-example-live ng-pluralize:last').text()).
-                                                toBe('Igor is viewing.');
-
-          using('.doc-example-live').input('personCount').enter('0');
-          expect(element('.doc-example-live ng-pluralize:first').text()).
-                                               toBe('Nobody is viewing.');
-          expect(element('.doc-example-live ng-pluralize:last').text()).
-                                              toBe('Nobody is viewing.');
-
-          using('.doc-example-live').input('personCount').enter('2');
-          expect(element('.doc-example-live ng-pluralize:first').text()).
-                                            toBe('2 people are viewing.');
-          expect(element('.doc-example-live ng-pluralize:last').text()).
-                              toBe('Igor and Misko are viewing.');
-
-          using('.doc-example-live').input('personCount').enter('3');
-          expect(element('.doc-example-live ng-pluralize:first').text()).
-                                            toBe('3 people are viewing.');
-          expect(element('.doc-example-live ng-pluralize:last').text()).
-                              toBe('Igor, Misko and one other person are viewing.');
-
-          using('.doc-example-live').input('personCount').enter('4');
-          expect(element('.doc-example-live ng-pluralize:first').text()).
-                                            toBe('4 people are viewing.');
-          expect(element('.doc-example-live ng-pluralize:last').text()).
-                              toBe('Igor, Misko and 2 other people are viewing.');
-        });
-
-        it('should show data-binded names', function() {
-          using('.doc-example-live').input('personCount').enter('4');
-          expect(element('.doc-example-live ng-pluralize:last').text()).
-              toBe('Igor, Misko and 2 other people are viewing.');
-
-          using('.doc-example-live').input('person1').enter('Di');
-          using('.doc-example-live').input('person2').enter('Vojta');
-          expect(element('.doc-example-live ng-pluralize:last').text()).
-              toBe('Di, Vojta and 2 other people are viewing.');
-        });
-      </doc:scenario>
-    </doc:example>
- */
-var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
-  var BRACE = /{}/g;
-  return {
-    restrict: 'EA',
-    link: function(scope, element, attr) {
-      var numberExp = attr.count,
-          whenExp = element.attr(attr.$attr.when), // this is because we have {{}} in attrs
-          offset = attr.offset || 0,
-          whens = scope.$eval(whenExp),
-          whensExpFns = {},
-          startSymbol = $interpolate.startSymbol(),
-          endSymbol = $interpolate.endSymbol();
-
-      forEach(whens, function(expression, key) {
-        whensExpFns[key] =
-          $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' +
-            offset + endSymbol));
-      });
-
-      scope.$watch(function ngPluralizeWatch() {
-        var value = parseFloat(scope.$eval(numberExp));
-
-        if (!isNaN(value)) {
-          //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
-          //check it against pluralization rules in $locale service
-          if (!(value in whens)) value = $locale.pluralCat(value - offset);
-           return whensExpFns[value](scope, element, true);
-        } else {
-          return '';
-        }
-      }, function ngPluralizeWatchAction(newVal) {
-        element.text(newVal);
-      });
-    }
-  };
-}];
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngRepeat
- *
- * @description
- * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
- * instance gets its own scope, where the given loop variable is set to the current collection item,
- * and `$index` is set to the item index or key.
- *
- * Special properties are exposed on the local scope of each template instance, including:
- *
- *   * `$index` – `{number}` – iterator offset of the repeated element (0..length-1)
- *   * `$first` – `{boolean}` – true if the repeated element is first in the iterator.
- *   * `$middle` – `{boolean}` – true if the repeated element is between the first and last in the iterator.
- *   * `$last` – `{boolean}` – true if the repeated element is last in the iterator.
- *
- *
- * @element ANY
- * @scope
- * @priority 1000
- * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. Two
- *   formats are currently supported:
- *
- *   * `variable in expression` – where variable is the user defined loop variable and `expression`
- *     is a scope expression giving the collection to enumerate.
- *
- *     For example: `track in cd.tracks`.
- *
- *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
- *     and `expression` is the scope expression giving the collection to enumerate.
- *
- *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
- *
- * @example
- * This example initializes the scope to a list of names and
- * then uses `ngRepeat` to display every person:
-    <doc:example>
-      <doc:source>
-        <div ng-init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]">
-          I have {{friends.length}} friends. They are:
-          <ul>
-            <li ng-repeat="friend in friends">
-              [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
-            </li>
-          </ul>
-        </div>
-      </doc:source>
-      <doc:scenario>
-         it('should check ng-repeat', function() {
-           var r = using('.doc-example-live').repeater('ul li');
-           expect(r.count()).toBe(2);
-           expect(r.row(0)).toEqual(["1","John","25"]);
-           expect(r.row(1)).toEqual(["2","Mary","28"]);
-         });
-      </doc:scenario>
-    </doc:example>
- */
-var ngRepeatDirective = ngDirective({
-  transclude: 'element',
-  priority: 1000,
-  terminal: true,
-  compile: function(element, attr, linker) {
-    return function(scope, iterStartElement, attr){
-      var expression = attr.ngRepeat;
-      var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
-        lhs, rhs, valueIdent, keyIdent;
-      if (! match) {
-        throw Error("Expected ngRepeat in form of '_item_ in _collection_' but got '" +
-          expression + "'.");
-      }
-      lhs = match[1];
-      rhs = match[2];
-      match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);
-      if (!match) {
-        throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" +
-            lhs + "'.");
-      }
-      valueIdent = match[3] || match[1];
-      keyIdent = match[2];
-
-      // Store a list of elements from previous run. This is a hash where key is the item from the
-      // iterator, and the value is an array of objects with following properties.
-      //   - scope: bound scope
-      //   - element: previous element.
-      //   - index: position
-      // We need an array of these objects since the same object can be returned from the iterator.
-      // We expect this to be a rare case.
-      var lastOrder = new HashQueueMap();
-
-      scope.$watch(function ngRepeatWatch(scope){
-        var index, length,
-            collection = scope.$eval(rhs),
-            cursor = iterStartElement,     // current position of the node
-            // Same as lastOrder but it has the current state. It will become the
-            // lastOrder on the next iteration.
-            nextOrder = new HashQueueMap(),
-            arrayBound,
-            childScope,
-            key, value, // key/value of iteration
-            array,
-            last;       // last object information {scope, element, index}
-
-
-
-        if (!isArray(collection)) {
-          // if object, extract keys, sort them and use to determine order of iteration over obj props
-          array = [];
-          for(key in collection) {
-            if (collection.hasOwnProperty(key) && key.charAt(0) != '$') {
-              array.push(key);
-            }
-          }
-          array.sort();
-        } else {
-          array = collection || [];
-        }
-
-        arrayBound = array.length-1;
-
-        // we are not using forEach for perf reasons (trying to avoid #call)
-        for (index = 0, length = array.length; index < length; index++) {
-          key = (collection === array) ? index : array[index];
-          value = collection[key];
-
-          last = lastOrder.shift(value);
-
-          if (last) {
-            // if we have already seen this object, then we need to reuse the
-            // associated scope/element
-            childScope = last.scope;
-            nextOrder.push(value, last);
-
-            if (index === last.index) {
-              // do nothing
-              cursor = last.element;
-            } else {
-              // existing item which got moved
-              last.index = index;
-              // This may be a noop, if the element is next, but I don't know of a good way to
-              // figure this out,  since it would require extra DOM access, so let's just hope that
-              // the browsers realizes that it is noop, and treats it as such.
-              cursor.after(last.element);
-              cursor = last.element;
-            }
-          } else {
-            // new item which we don't know about
-            childScope = scope.$new();
-          }
-
-          childScope[valueIdent] = value;
-          if (keyIdent) childScope[keyIdent] = key;
-          childScope.$index = index;
-
-          childScope.$first = (index === 0);
-          childScope.$last = (index === arrayBound);
-          childScope.$middle = !(childScope.$first || childScope.$last);
-
-          if (!last) {
-            linker(childScope, function(clone){
-              cursor.after(clone);
-              last = {
-                  scope: childScope,
-                  element: (cursor = clone),
-                  index: index
-                };
-              nextOrder.push(value, last);
-            });
-          }
-        }
-
-        //shrink children
-        for (key in lastOrder) {
-          if (lastOrder.hasOwnProperty(key)) {
-            array = lastOrder[key];
-            while(array.length) {
-              value = array.pop();
-              value.element.remove();
-              value.scope.$destroy();
-            }
-          }
-        }
-
-        lastOrder = nextOrder;
-      });
-    };
-  }
-});
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngShow
- *
- * @description
- * The `ngShow` and `ngHide` directives show or hide a portion of the DOM tree (HTML)
- * conditionally.
- *
- * @element ANY
- * @param {expression} ngShow If the {@link guide/expression expression} is truthy
- *     then the element is shown or hidden respectively.
- *
- * @example
-   <doc:example>
-     <doc:source>
-        Click me: <input type="checkbox" ng-model="checked"><br/>
-        Show: <span ng-show="checked">I show up when your checkbox is checked.</span> <br/>
-        Hide: <span ng-hide="checked">I hide when your checkbox is checked.</span>
-     </doc:source>
-     <doc:scenario>
-       it('should check ng-show / ng-hide', function() {
-         expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
-         expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
-
-         input('checked').check();
-
-         expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
-         expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
-       });
-     </doc:scenario>
-   </doc:example>
- */
-//TODO(misko): refactor to remove element from the DOM
-var ngShowDirective = ngDirective(function(scope, element, attr){
-  scope.$watch(attr.ngShow, function ngShowWatchAction(value){
-    element.css('display', toBoolean(value) ? '' : 'none');
-  });
-});
-
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngHide
- *
- * @description
- * The `ngHide` and `ngShow` directives hide or show a portion of the DOM tree (HTML)
- * conditionally.
- *
- * @element ANY
- * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
- *     the element is shown or hidden respectively.
- *
- * @example
-   <doc:example>
-     <doc:source>
-        Click me: <input type="checkbox" ng-model="checked"><br/>
-        Show: <span ng-show="checked">I show up when you checkbox is checked?</span> <br/>
-        Hide: <span ng-hide="checked">I hide when you checkbox is checked?</span>
-     </doc:source>
-     <doc:scenario>
-       it('should check ng-show / ng-hide', function() {
-         expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
-         expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
-
-         input('checked').check();
-
-         expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
-         expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
-       });
-     </doc:scenario>
-   </doc:example>
- */
-//TODO(misko): refactor to remove element from the DOM
-var ngHideDirective = ngDirective(function(scope, element, attr){
-  scope.$watch(attr.ngHide, function ngHideWatchAction(value){
-    element.css('display', toBoolean(value) ? 'none' : '');
-  });
-});
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngStyle
- *
- * @description
- * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
- *
- * @element ANY
- * @param {expression} ngStyle {@link guide/expression Expression} which evals to an
- *      object whose keys are CSS style names and values are corresponding values for those CSS
- *      keys.
- *
- * @example
-   <example>
-     <file name="index.html">
-        <input type="button" value="set" ng-click="myStyle={color:'red'}">
-        <input type="button" value="clear" ng-click="myStyle={}">
-        <br/>
-        <span ng-style="myStyle">Sample Text</span>
-        <pre>myStyle={{myStyle}}</pre>
-     </file>
-     <file name="style.css">
-       span {
-         color: black;
-       }
-     </file>
-     <file name="scenario.js">
-       it('should check ng-style', function() {
-         expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
-         element('.doc-example-live :button[value=set]').click();
-         expect(element('.doc-example-live span').css('color')).toBe('rgb(255, 0, 0)');
-         element('.doc-example-live :button[value=clear]').click();
-         expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
-       });
-     </file>
-   </example>
- */
-var ngStyleDirective = ngDirective(function(scope, element, attr) {
-  scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
-    if (oldStyles && (newStyles !== oldStyles)) {
-      forEach(oldStyles, function(val, style) { element.css(style, '');});
-    }
-    if (newStyles) element.css(newStyles);
-  }, true);
-});
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngSwitch
- * @restrict EA
- *
- * @description
- * Conditionally change the DOM structure.
- *
- * @usage
- * <ANY ng-switch="expression">
- *   <ANY ng-switch-when="matchValue1">...</ANY>
- *   <ANY ng-switch-when="matchValue2">...</ANY>
- *   ...
- *   <ANY ng-switch-default>...</ANY>
- * </ANY>
- *
- * @scope
- * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
- * @paramDescription
- * On child elments add:
- *
- * * `ngSwitchWhen`: the case statement to match against. If match then this
- *   case will be displayed.
- * * `ngSwitchDefault`: the default case when no other casses match.
- *
- * @example
-    <doc:example>
-      <doc:source>
-        <script>
-          function Ctrl($scope) {
-            $scope.items = ['settings', 'home', 'other'];
-            $scope.selection = $scope.items[0];
-          }
-        </script>
-        <div ng-controller="Ctrl">
-          <select ng-model="selection" ng-options="item for item in items">
-          </select>
-          <tt>selection={{selection}}</tt>
-          <hr/>
-          <div ng-switch on="selection" >
-            <div ng-switch-when="settings">Settings Div</div>
-            <span ng-switch-when="home">Home Span</span>
-            <span ng-switch-default>default</span>
-          </div>
-        </div>
-      </doc:source>
-      <doc:scenario>
-        it('should start in settings', function() {
-         expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Settings Div/);
-        });
-        it('should change to home', function() {
-         select('selection').option('home');
-         expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Home Span/);
-        });
-        it('should select deafault', function() {
-         select('selection').option('other');
-         expect(element('.doc-example-live [ng-switch]').text()).toMatch(/default/);
-        });
-      </doc:scenario>
-    </doc:example>
- */
-var NG_SWITCH = 'ng-switch';
-var ngSwitchDirective = valueFn({
-  restrict: 'EA',
-  require: 'ngSwitch',
-  // asks for $scope to fool the BC controller module
-  controller: ['$scope', function ngSwitchController() {
-    this.cases = {};
-  }],
-  link: function(scope, element, attr, ctrl) {
-    var watchExpr = attr.ngSwitch || attr.on,
-        selectedTransclude,
-        selectedElement,
-        selectedScope;
-
-    scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
-      if (selectedElement) {
-        selectedScope.$destroy();
-        selectedElement.remove();
-        selectedElement = selectedScope = null;
-      }
-      if ((selectedTransclude = ctrl.cases['!' + value] || ctrl.cases['?'])) {
-        scope.$eval(attr.change);
-        selectedScope = scope.$new();
-        selectedTransclude(selectedScope, function(caseElement) {
-          selectedElement = caseElement;
-          element.append(caseElement);
-        });
-      }
-    });
-  }
-});
-
-var ngSwitchWhenDirective = ngDirective({
-  transclude: 'element',
-  priority: 500,
-  require: '^ngSwitch',
-  compile: function(element, attrs, transclude) {
-    return function(scope, element, attr, ctrl) {
-      ctrl.cases['!' + attrs.ngSwitchWhen] = transclude;
-    };
-  }
-});
-
-var ngSwitchDefaultDirective = ngDirective({
-  transclude: 'element',
-  priority: 500,
-  require: '^ngSwitch',
-  compile: function(element, attrs, transclude) {
-    return function(scope, element, attr, ctrl) {
-      ctrl.cases['?'] = transclude;
-    };
-  }
-});
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngTransclude
- *
- * @description
- * Insert the transcluded DOM here.
- *
- * @element ANY
- *
- * @example
-   <doc:example module="transclude">
-     <doc:source>
-       <script>
-         function Ctrl($scope) {
-           $scope.title = 'Lorem Ipsum';
-           $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
-         }
-
-         angular.module('transclude', [])
-          .directive('pane', function(){
-             return {
-               restrict: 'E',
-               transclude: true,
-               scope: 'isolate',
-               locals: { title:'bind' },
-               template: '<div style="border: 1px solid black;">' +
-                           '<div style="background-color: gray">{{title}}</div>' +
-                           '<div ng-transclude></div>' +
-                         '</div>'
-             };
-         });
-       </script>
-       <div ng-controller="Ctrl">
-         <input ng-model="title"><br>
-         <textarea ng-model="text"></textarea> <br/>
-         <pane title="{{title}}">{{text}}</pane>
-       </div>
-     </doc:source>
-     <doc:scenario>
-        it('should have transcluded', function() {
-          input('title').enter('TITLE');
-          input('text').enter('TEXT');
-          expect(binding('title')).toEqual('TITLE');
-          expect(binding('text')).toEqual('TEXT');
-        });
-     </doc:scenario>
-   </doc:example>
- *
- */
-var ngTranscludeDirective = ngDirective({
-  controller: ['$transclude', '$element', function($transclude, $element) {
-    $transclude(function(clone) {
-      $element.append(clone);
-    });
-  }]
-});
-
-/**
- * @ngdoc directive
- * @name ng.directive:ngView
- * @restrict ECA
- *
- * @description
- * # Overview
- * `ngView` is a directive that complements the {@link ng.$route $route} service by
- * including the rendered template of the current route into the main layout (`index.html`) file.
- * Every time the current route changes, the included view changes with it according to the
- * configuration of the `$route` service.
- *
- * @scope
- * @example
-    <example module="ngView">
-      <file name="index.html">
-        <div ng-controller="MainCntl">
-          Choose:
-          <a href="Book/Moby">Moby</a> |
-          <a href="Book/Moby/ch/1">Moby: Ch1</a> |
-          <a href="Book/Gatsby">Gatsby</a> |
-          <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
-          <a href="Book/Scarlet">Scarlet Letter</a><br/>
-
-          <div ng-view></div>
-          <hr />
-
-          <pre>$location.path() = {{$location.path()}}</pre>
-          <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
-          <pre>$route.current.params = {{$route.current.params}}</pre>
-          <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
-          <pre>$routeParams = {{$routeParams}}</pre>
-        </div>
-      </file>
-
-      <file name="book.html">
-        controller: {{name}}<br />
-        Book Id: {{params.bookId}}<br />
-      </file>
-
-      <file name="chapter.html">
-        controller: {{name}}<br />
-        Book Id: {{params.bookId}}<br />
-        Chapter Id: {{params.chapterId}}
-      </file>
-
-      <file name="script.js">
-        angular.module('ngView', [], function($routeProvider, $locationProvider) {
-          $routeProvider.when('/Book/:bookId', {
-            templateUrl: 'book.html',
-            controller: BookCntl
-          });
-          $routeProvider.when('/Book/:bookId/ch/:chapterId', {
-            templateUrl: 'chapter.html',
-            controller: ChapterCntl
-          });
-
-          // configure html5 to get links working on jsfiddle
-          $locationProvider.html5Mode(true);
-        });
-
-        function MainCntl($scope, $route, $routeParams, $location) {
-          $scope.$route = $route;
-          $scope.$location = $location;
-          $scope.$routeParams = $routeParams;
-        }
-
-        function BookCntl($scope, $routeParams) {
-          $scope.name = "BookCntl";
-          $scope.params = $routeParams;
-        }
-
-        function ChapterCntl($scope, $routeParams) {
-          $scope.name = "ChapterCntl";
-          $scope.params = $routeParams;
-        }
-      </file>
-
-      <file name="scenario.js">
-        it('should load and compile correct template', function() {
-          element('a:contains("Moby: Ch1")').click();
-          var content = element('.doc-example-live [ng-view]').text();
-          expect(content).toMatch(/controller\: ChapterCntl/);
-          expect(content).toMatch(/Book Id\: Moby/);
-          expect(content).toMatch(/Chapter Id\: 1/);
-
-          element('a:contains("Scarlet")').click();
-          content = element('.doc-example-live [ng-view]').text();
-          expect(content).toMatch(/controller\: BookCntl/);
-          expect(content).toMatch(/Book Id\: Scarlet/);
-        });
-      </file>
-    </example>
- */
-
-
-/**
- * @ngdoc event
- * @name ng.directive:ngView#$viewContentLoaded
- * @eventOf ng.directive:ngView
- * @eventType emit on the current ngView scope
- * @description
- * Emitted every time the ngView content is reloaded.
- */
-var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile',
-                       '$controller',
-               function($http,   $templateCache,   $route,   $anchorScroll,   $compile,
-                        $controller) {
-  return {
-    restrict: 'ECA',
-    terminal: true,
-    link: function(scope, element, attr) {
-      var lastScope,
-          onloadExp = attr.onload || '';
-
-      scope.$on('$routeChangeSuccess', update);
-      update();
-
-
-      function destroyLastScope() {
-        if (lastScope) {
-          lastScope.$destroy();
-          lastScope = null;
-        }
-      }
-
-      function clearContent() {
-        element.html('');
-        destroyLastScope();
-      }
-
-      function update() {
-        var locals = $route.current && $route.current.locals,
-            template = locals && locals.$template;
-
-        if (template) {
-          element.html(template);
-          destroyLastScope();
-
-          var link = $compile(element.contents()),
-              current = $route.current,
-              controller;
-
-          lastScope = current.scope = scope.$new();
-          if (current.controller) {
-            locals.$scope = lastScope;
-            controller = $controller(current.controller, locals);
-            element.children().data('$ngControllerController', controller);
-          }
-
-          link(lastScope);
-          lastScope.$emit('$viewContentLoaded');
-          lastScope.$eval(onloadExp);
-
-          // $anchorScroll might listen on event...
-          $anchorScroll();
-        } else {
-          clearContent();
-        }
-      }
-    }
-  };
-}];
-
-/**
- * @ngdoc directive
- * @name ng.directive:script
- *
- * @description
- * Load content of a script tag, with type `text/ng-template`, into `$templateCache`, so that the
- * template can be used by `ngInclude`, `ngView` or directive templates.
- *
- * @restrict E
- * @param {'text/ng-template'} type must be set to `'text/ng-template'`
- *
- * @example
-  <doc:example>
-    <doc:source>
-      <script type="text/ng-template" id="/tpl.html">
-        Content of the template.
-      </script>
-
-      <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
-      <div id="tpl-content" ng-include src="currentTpl"></div>
-    </doc:source>
-    <doc:scenario>
-      it('should load template defined inside script tag', function() {
-        element('#tpl-link').click();
-        expect(element('#tpl-content').text()).toMatch(/Content of the template/);
-      });
-    </doc:scenario>
-  </doc:example>
- */
-var scriptDirective = ['$templateCache', function($templateCache) {
-  return {
-    restrict: 'E',
-    terminal: true,
-    compile: function(element, attr) {
-      if (attr.type == 'text/ng-template') {
-        var templateUrl = attr.id,
-            // IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent
-            text = element[0].text;
-
-        $templateCache.put(templateUrl, text);
-      }
-    }
-  };
-}];
-
-/**
- * @ngdoc directive
- * @name ng.directive:select
- * @restrict E
- *
- * @description
- * HTML `SELECT` element with angular data-binding.
- *
- * # `ngOptions`
- *
- * Optionally `ngOptions` attribute can be used to dynamically generate a list of `<option>`
- * elements for a `<select>` element using an array or an object obtained by evaluating the
- * `ngOptions` expression.
- *˝˝
- * When an item in the select menu is select, the value of array element or object property
- * represented by the selected option will be bound to the model identified by the `ngModel`
- * directive of the parent select element.
- *
- * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
- * be nested into the `<select>` element. This element will then represent `null` or "not selected"
- * option. See example below for demonstration.
- *
- * Note: `ngOptions` provides iterator facility for `<option>` element which should be used instead
- * of {@link ng.directive:ngRepeat ngRepeat} when you want the
- * `select` model to be bound to a non-string value. This is because an option element can currently
- * be bound to string values only.
- *
- * @param {string} ngModel Assignable angular expression to data-bind to.
- * @param {string=} name Property name of the form under which the control is published.
- * @param {string=} required The control is considered valid only if value is entered.
- * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
- *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
- *    `required` when you want to data-bind to the `required` attribute.
- * @param {comprehension_expression=} ngOptions in one of the following forms:
- *
- *   * for array data sources:
- *     * `label` **`for`** `value` **`in`** `array`
- *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
- *     * `label`  **`group by`** `group` **`for`** `value` **`in`** `array`
- *     * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array`
- *   * for object data sources:
- *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
- *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
- *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
- *     * `select` **`as`** `label` **`group by`** `group`
- *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
- *
- * Where:
- *
- *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
- *   * `value`: local variable which will refer to each item in the `array` or each property value
- *      of `object` during iteration.
- *   * `key`: local variable which will refer to a property name in `object` during iteration.
- *   * `label`: The result of this expression will be the label for `<option>` element. The
- *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
- *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
- *      element. If not specified, `select` expression will default to `value`.
- *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
- *      DOM element.
- *
- * @example
-    <doc:example>
-      <doc:source>
-        <script>
-        function MyCntrl($scope) {
-          $scope.colors = [
-            {name:'black', shade:'dark'},
-            {name:'white', shade:'light'},
-            {name:'red', shade:'dark'},
-            {name:'blue', shade:'dark'},
-            {name:'yellow', shade:'light'}
-          ];
-          $scope.color = $scope.colors[2]; // red
-        }
-        </script>
-        <div ng-controller="MyCntrl">
-          <ul>
-            <li ng-repeat="color in colors">
-              Name: <input ng-model="color.name">
-              [<a href ng-click="colors.splice($index, 1)">X</a>]
-            </li>
-            <li>
-              [<a href ng-click="colors.push({})">add</a>]
-            </li>
-          </ul>
-          <hr/>
-          Color (null not allowed):
-          <select ng-model="color" ng-options="c.name for c in colors"></select><br>
-
-          Color (null allowed):
-          <span  class="nullable">
-            <select ng-model="color" ng-options="c.name for c in colors">
-              <option value="">-- chose color --</option>
-            </select>
-          </span><br/>
-
-          Color grouped by shade:
-          <select ng-model="color" ng-options="c.name group by c.shade for c in colors">
-          </select><br/>
-
-
-          Select <a href ng-click="color={name:'not in list'}">bogus</a>.<br>
-          <hr/>
-          Currently selected: {{ {selected_color:color}  }}
-          <div style="border:solid 1px black; height:20px"
-               ng-style="{'background-color':color.name}">
-          </div>
-        </div>
-      </doc:source>
-      <doc:scenario>
-         it('should check ng-options', function() {
-           expect(binding('{selected_color:color}')).toMatch('red');
-           select('color').option('0');
-           expect(binding('{selected_color:color}')).toMatch('black');
-           using('.nullable').select('color').option('');
-           expect(binding('{selected_color:color}')).toMatch('null');
-         });
-      </doc:scenario>
-    </doc:example>
- */
-
-var ngOptionsDirective = valueFn({ terminal: true });
-var selectDirective = ['$compile', '$parse', function($compile,   $parse) {
-                         //0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000077770
-  var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/,
-      nullModelCtrl = {$setViewValue: noop};
-
-  return {
-    restrict: 'E',
-    require: ['select', '?ngModel'],
-    controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
-      var self = this,
-          optionsMap = {},
-          ngModelCtrl = nullModelCtrl,
-          nullOption,
-          unknownOption;
-
-
-      self.databound = $attrs.ngModel;
-
-
-      self.init = function(ngModelCtrl_, nullOption_, unknownOption_) {
-        ngModelCtrl = ngModelCtrl_;
-        nullOption = nullOption_;
-        unknownOption = unknownOption_;
-      }
-
-
-      self.addOption = function(value) {
-        optionsMap[value] = true;
-
-        if (ngModelCtrl.$viewValue == value) {
-          $element.val(value);
-          if (unknownOption.parent()) unknownOption.remove();
-        }
-      };
-
-
-      self.removeOption = function(value) {
-        if (this.hasOption(value)) {
-          delete optionsMap[value];
-          if (ngModelCtrl.$viewValue == value) {
-            this.renderUnknownOption(value);
-          }
-        }
-      };
-
-
-      self.renderUnknownOption = function(val) {
-        var unknownVal = '? ' + hashKey(val) + ' ?';
-        unknownOption.val(unknownVal);
-        $element.prepend(unknownOption);
-        $element.val(unknownVal);
-        unknownOption.prop('selected', true); // needed for IE
-      }
-
-
-      self.hasOption = function(value) {
-        return optionsMap.hasOwnProperty(value);
-      }
-
-      $scope.$on('$destroy', function() {
-        // disable unknown option so that we don't do work when the whole select is being destroyed
-        self.renderUnknownOption = noop;
-      });
-    }],
-
-    link: function(scope, element, attr, ctrls) {
-      // if ngModel is not defined, we don't need to do anything
-      if (!ctrls[1]) return;
-
-      var selectCtrl = ctrls[0],
-          ngModelCtrl = ctrls[1],
-          multiple = attr.multiple,
-          optionsExp = attr.ngOptions,
-          nullOption = false, // if false, user will not be able to select it (used by ngOptions)
-          emptyOption,
-          // we can't just jqLite('<option>') since jqLite is not smart enough
-          // to create it in <select> and IE barfs otherwise.
-          optionTemplate = jqLite(document.createElement('option')),
-          optGroupTemplate =jqLite(document.createElement('optgroup')),
-          unknownOption = optionTemplate.clone();
-
-      // find "null" option
-      for(var i = 0, children = element.children(), ii = children.length; i < ii; i++) {
-        if (children[i].value == '') {
-          emptyOption = nullOption = children.eq(i);
-          break;
-        }
-      }
-
-      selectCtrl.init(ngModelCtrl, nullOption, unknownOption);
-
-      // required validator
-      if (multiple && (attr.required || attr.ngRequired)) {
-        var requiredValidator = function(value) {
-          ngModelCtrl.$setValidity('required', !attr.required || (value && value.length));
-          return value;
-        };
-
-        ngModelCtrl.$parsers.push(requiredValidator);
-        ngModelCtrl.$formatters.unshift(requiredValidator);
-
-        attr.$observe('required', function() {
-          requiredValidator(ngModelCtrl.$viewValue);
-        });
-      }
-
-      if (optionsExp) Options(scope, element, ngModelCtrl);
-      else if (multiple) Multiple(scope, element, ngModelCtrl);
-      else Single(scope, element, ngModelCtrl, selectCtrl);
-
-
-      ////////////////////////////
-
-
-
-      function Single(scope, selectElement, ngModelCtrl, selectCtrl) {
-        ngModelCtrl.$render = function() {
-          var viewValue = ngModelCtrl.$viewValue;
-
-          if (selectCtrl.hasOption(viewValue)) {
-            if (unknownOption.parent()) unknownOption.remove();
-            selectElement.val(viewValue);
-            if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy
-          } else {
-            if (isUndefined(viewValue) && emptyOption) {
-              selectElement.val('');
-            } else {
-              selectCtrl.renderUnknownOption(viewValue);
-            }
-          }
-        };
-
-        selectElement.bind('change', function() {
-          scope.$apply(function() {
-            if (unknownOption.parent()) unknownOption.remove();
-            ngModelCtrl.$setViewValue(selectElement.val());
-          });
-        });
-      }
-
-      function Multiple(scope, selectElement, ctrl) {
-        var lastView;
-        ctrl.$render = function() {
-          var items = new HashMap(ctrl.$viewValue);
-          forEach(selectElement.find('option'), function(option) {
-            option.selected = isDefined(items.get(option.value));
-          });
-        };
-
-        // we have to do it on each watch since ngModel watches reference, but
-        // we need to work of an array, so we need to see if anything was inserted/removed
-        scope.$watch(function selectMultipleWatch() {
-          if (!equals(lastView, ctrl.$viewValue)) {
-            lastView = copy(ctrl.$viewValue);
-            ctrl.$render();
-          }
-        });
-
-        selectElement.bind('change', function() {
-          scope.$apply(function() {
-            var array = [];
-            forEach(selectElement.find('option'), function(option) {
-              if (option.selected) {
-                array.push(option.value);
-              }
-            });
-            ctrl.$setViewValue(array);
-          });
-        });
-      }
-
-      function Options(scope, selectElement, ctrl) {
-        var match;
-
-        if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) {
-          throw Error(
-            "Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
-            " but got '" + optionsExp + "'.");
-        }
-
-        var displayFn = $parse(match[2] || match[1]),
-            valueName = match[4] || match[6],
-            keyName = match[5],
-            groupByFn = $parse(match[3] || ''),
-            valueFn = $parse(match[2] ? match[1] : valueName),
-            valuesFn = $parse(match[7]),
-            // This is an array of array of existing option groups in DOM. We try to reuse these if possible
-            // optionGroupsCache[0] is the options with no option group
-            // optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
-            optionGroupsCache = [[{element: selectElement, label:''}]];
-
-        if (nullOption) {
-          // compile the element since there might be bindings in it
-          $compile(nullOption)(scope);
-
-          // remove the class, which is added automatically because we recompile the element and it
-          // becomes the compilation root
-          nullOption.removeClass('ng-scope');
-
-          // we need to remove it before calling selectElement.html('') because otherwise IE will
-          // remove the label from the element. wtf?
-          nullOption.remove();
-        }
-
-        // clear contents, we'll add what's needed based on the model
-        selectElement.html('');
-
-        selectElement.bind('change', function() {
-          scope.$apply(function() {
-            var optionGroup,
-                collection = valuesFn(scope) || [],
-                locals = {},
-                key, value, optionElement, index, groupIndex, length, groupLength;
-
-            if (multiple) {
-              value = [];
-              for (groupIndex = 0, groupLength = optionGroupsCache.length;
-                   groupIndex < groupLength;
-                   groupIndex++) {
-                // list of options for that group. (first item has the parent)
-                optionGroup = optionGroupsCache[groupIndex];
-
-                for(index = 1, length = optionGroup.length; index < length; index++) {
-                  if ((optionElement = optionGroup[index].element)[0].selected) {
-                    key = optionElement.val();
-                    if (keyName) locals[keyName] = key;
-                    locals[valueName] = collection[key];
-                    value.push(valueFn(scope, locals));
-                  }
-                }
-              }
-            } else {
-              key = selectElement.val();
-              if (key == '?') {
-                value = undefined;
-              } else if (key == ''){
-                value = null;
-              } else {
-                locals[valueName] = collection[key];
-                if (keyName) locals[keyName] = key;
-                value = valueFn(scope, locals);
-              }
-            }
-            ctrl.$setViewValue(value);
-          });
-        });
-
-        ctrl.$render = render;
-
-        // TODO(vojta): can't we optimize this ?
-        scope.$watch(render);
-
-        function render() {
-          var optionGroups = {'':[]}, // Temporary location for the option groups before we render them
-              optionGroupNames = [''],
-              optionGroupName,
-              optionGroup,
-              option,
-              existingParent, existingOptions, existingOption,
-              modelValue = ctrl.$modelValue,
-              values = valuesFn(scope) || [],
-              keys = keyName ? sortedKeys(values) : values,
-              groupLength, length,
-              groupIndex, index,
-              locals = {},
-              selected,
-              selectedSet = false, // nothing is selected yet
-              lastElement,
-              element,
-              label;
-
-          if (multiple) {
-            selectedSet = new HashMap(modelValue);
-          }
-
-          // We now build up the list of options we need (we merge later)
-          for (index = 0; length = keys.length, index < length; index++) {
-               locals[valueName] = values[keyName ? locals[keyName]=keys[index]:index];
-               optionGroupName = groupByFn(scope, locals) || '';
-            if (!(optionGroup = optionGroups[optionGroupName])) {
-              optionGroup = optionGroups[optionGroupName] = [];
-              optionGroupNames.push(optionGroupName);
-            }
-            if (multiple) {
-              selected = selectedSet.remove(valueFn(scope, locals)) != undefined;
-            } else {
-              selected = modelValue === valueFn(scope, locals);
-              selectedSet = selectedSet || selected; // see if at least one item is selected
-            }
-            label = displayFn(scope, locals); // what will be seen by the user
-            label = label === undefined ? '' : label; // doing displayFn(scope, locals) || '' overwrites zero values
-            optionGroup.push({
-              id: keyName ? keys[index] : index,   // either the index into array or key from object
-              label: label,
-              selected: selected                   // determine if we should be selected
-            });
-          }
-          if (!multiple) {
-            if (nullOption || modelValue === null) {
-              // insert null option if we have a placeholder, or the model is null
-              optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
-            } else if (!selectedSet) {
-              // option could not be found, we have to insert the undefined item
-              optionGroups[''].unshift({id:'?', label:'', selected:true});
-            }
-          }
-
-          // Now we need to update the list of DOM nodes to match the optionGroups we computed above
-          for (groupIndex = 0, groupLength = optionGroupNames.length;
-               groupIndex < groupLength;
-               groupIndex++) {
-            // current option group name or '' if no group
-            optionGroupName = optionGroupNames[groupIndex];
-
-            // list of options for that group. (first item has the parent)
-            optionGroup = optionGroups[optionGroupName];
-
-            if (optionGroupsCache.length <= groupIndex) {
-              // we need to grow the optionGroups
-              existingParent = {
-                element: optGroupTemplate.clone().attr('label', optionGroupName),
-                label: optionGroup.label
-              };
-              existingOptions = [existingParent];
-              optionGroupsCache.push(existingOptions);
-              selectElement.append(existingParent.element);
-            } else {
-              existingOptions = optionGroupsCache[groupIndex];
-              existingParent = existingOptions[0];  // either SELECT (no group) or OPTGROUP element
-
-              // update the OPTGROUP label if not the same.
-              if (existingParent.label != optionGroupName) {
-                existingParent.element.attr('label', existingParent.label = optionGroupName);
-              }
-            }
-
-            lastElement = null;  // start at the beginning
-            for(index = 0, length = optionGroup.length; index < length; index++) {
-              option = optionGroup[index];
-              if ((existingOption = existingOptions[index+1])) {
-                // reuse elements
-                lastElement = existingOption.element;
-                if (existingOption.label !== option.label) {
-                  lastElement.text(existingOption.label = option.label);
-                }
-                if (existingOption.id !== option.id) {
-                  lastElement.val(existingOption.id = option.id);
-                }
-                // lastElement.prop('selected') provided by jQuery has side-effects
-                if (lastElement[0].selected !== option.selected) {
-                  lastElement.prop('selected', (existingOption.selected = option.selected));
-                }
-              } else {
-                // grow elements
-
-                // if it's a null option
-                if (option.id === '' && nullOption) {
-                  // put back the pre-compiled element
-                  element = nullOption;
-                } else {
-                  // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
-                  // in this version of jQuery on some browser the .text() returns a string
-                  // rather then the element.
-                  (element = optionTemplate.clone())
-                      .val(option.id)
-                      .attr('selected', option.selected)
-                      .text(option.label);
-                }
-
-                existingOptions.push(existingOption = {
-                    element: element,
-                    label: option.label,
-                    id: option.id,
-                    selected: option.selected
-                });
-                if (lastElement) {
-                  lastElement.after(element);
-                } else {
-                  existingParent.element.append(element);
-                }
-                lastElement = element;
-              }
-            }
-            // remove any excessive OPTIONs in a group
-            index++; // increment since the existingOptions[0] is parent element not OPTION
-            while(existingOptions.length > index) {
-              existingOptions.pop().element.remove();
-            }
-          }
-          // remove any excessive OPTGROUPs from select
-          while(optionGroupsCache.length > groupIndex) {
-            optionGroupsCache.pop()[0].element.remove();
-          }
-        }
-      }
-    }
-  }
-}];
-
-var optionDirective = ['$interpolate', function($interpolate) {
-  var nullSelectCtrl = {
-    addOption: noop,
-    removeOption: noop
-  };
-
-  return {
-    restrict: 'E',
-    priority: 100,
-    compile: function(element, attr) {
-      if (isUndefined(attr.value)) {
-        var interpolateFn = $interpolate(element.text(), true);
-        if (!interpolateFn) {
-          attr.$set('value', element.text());
-        }
-      }
-
-      return function (scope, element, attr) {
-        var selectCtrlName = '$selectController',
-            parent = element.parent(),
-            selectCtrl = parent.data(selectCtrlName) ||
-              parent.parent().data(selectCtrlName); // in case we are in optgroup
-
-        if (selectCtrl && selectCtrl.databound) {
-          // For some reason Opera defaults to true and if not overridden this messes up the repeater.
-          // We don't want the view to drive the initialization of the model anyway.
-          element.prop('selected', false);
-        } else {
-          selectCtrl = nullSelectCtrl;
-        }
-
-        if (interpolateFn) {
-          scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
-            attr.$set('value', newVal);
-            if (newVal !== oldVal) selectCtrl.removeOption(oldVal);
-            selectCtrl.addOption(newVal);
-          });
-        } else {
-          selectCtrl.addOption(attr.value);
-        }
-
-        element.bind('$destroy', function() {
-          selectCtrl.removeOption(attr.value);
-        });
-      };
-    }
-  }
-}];
-
-var styleDirective = valueFn({
-  restrict: 'E',
-  terminal: true
-});
-
-  //try to bind to jquery now so that one can write angular.element().read()
-  //but we will rebind on bootstrap again.
-  bindJQuery();
-
-  publishExternalAPI(angular);
-
-  jqLite(document).ready(function() {
-    angularInit(document, bootstrap);
-  });
-
-})(window, document);
-angular.element(document).find('head').append('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}</style>');
\ No newline at end of file
diff --git a/lib/angular-mock-1.0.7.js b/lib/angular-mock.js
similarity index 71%
rename from lib/angular-mock-1.0.7.js
rename to lib/angular-mock.js
index 4647ee2f9..90ba8d952 100755
--- a/lib/angular-mock-1.0.7.js
+++ b/lib/angular-mock.js
@@ -1,5 +1,7 @@
+'use strict';
+
 /**
- * @license AngularJS v"NG_VERSION_FULL"
+ * @license AngularJS v1.2.2
  * (c) 2010-2012 Google, Inc. http://angularjs.org
  * License: MIT
  *
@@ -29,7 +31,7 @@ angular.mock = {};
  * that there are several helper methods available which can be used in tests.
  */
 angular.mock.$BrowserProvider = function() {
-    this.$get = function(){
+    this.$get = function() {
         return new angular.mock.$Browser();
     };
 };
@@ -75,6 +77,13 @@ angular.mock.$Browser = function() {
     };
 
 
+    /**
+     * @name ngMock.$browser#defer.now
+     * @propertyOf ngMock.$browser
+     *
+     * @description
+     * Current milliseconds mock time.
+     */
     self.defer.now = 0;
 
 
@@ -110,7 +119,7 @@ angular.mock.$Browser = function() {
             if (self.deferredFns.length) {
                 self.defer.now = self.deferredFns[self.deferredFns.length-1].time;
             } else {
-                throw Error('No deferred tasks to be flushed');
+                throw new Error('No deferred tasks to be flushed');
             }
         }
 
@@ -118,13 +127,6 @@ angular.mock.$Browser = function() {
             self.deferredFns.shift().fn();
         }
     };
-    /**
-     * @name ngMock.$browser#defer.now
-     * @propertyOf ngMock.$browser
-     *
-     * @description
-     * Current milliseconds mock time.
-     */
 
     self.$$baseHref = '';
     self.baseHref = function() {
@@ -162,7 +164,7 @@ angular.mock.$Browser.prototype = {
 
     cookies:  function(name, value) {
         if (name) {
-            if (value == undefined) {
+            if (angular.isUndefined(value)) {
                 delete this.cookieHash[name];
             } else {
                 if (angular.isString(value) &&       //strings only
@@ -190,8 +192,8 @@ angular.mock.$Browser.prototype = {
  * @name ngMock.$exceptionHandlerProvider
  *
  * @description
- * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors passed
- * into the `$exceptionHandler`.
+ * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors
+ * passed into the `$exceptionHandler`.
  */
 
 /**
@@ -244,10 +246,10 @@ angular.mock.$ExceptionHandlerProvider = function() {
      *   - `rethrow`: If any errors are passed into the handler in tests, it typically
      *                means that there is a bug in the application or test, so this mock will
      *                make these tests fail.
-     *   - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log` mode stores an
-     *            array of errors in `$exceptionHandler.errors`, to allow later assertion of them.
-     *            See {@link ngMock.$log#assertEmpty assertEmpty()} and
-     *             {@link ngMock.$log#reset reset()}
+     *   - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
+     *            mode stores an array of errors in `$exceptionHandler.errors`, to allow later
+     *            assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
+     *            {@link ngMock.$log#reset reset()}
      */
     this.mode = function(mode) {
         switch(mode) {
@@ -270,7 +272,7 @@ angular.mock.$ExceptionHandlerProvider = function() {
                 handler.errors = errors;
                 break;
             default:
-                throw Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");
+                throw new Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");
         }
     };
 
@@ -293,18 +295,32 @@ angular.mock.$ExceptionHandlerProvider = function() {
  *
  */
 angular.mock.$LogProvider = function() {
+    var debug = true;
 
     function concat(array1, array2, index) {
         return array1.concat(Array.prototype.slice.call(array2, index));
     }
 
+    this.debugEnabled = function(flag) {
+        if (angular.isDefined(flag)) {
+            debug = flag;
+            return this;
+        } else {
+            return debug;
+        }
+    };
 
     this.$get = function () {
         var $log = {
             log: function() { $log.log.logs.push(concat([], arguments, 0)); },
             warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
             info: function() { $log.info.logs.push(concat([], arguments, 0)); },
-            error: function() { $log.error.logs.push(concat([], arguments, 0)); }
+            error: function() { $log.error.logs.push(concat([], arguments, 0)); },
+            debug: function() {
+                if (debug) {
+                    $log.debug.logs.push(concat([], arguments, 0));
+                }
+            }
         };
 
         /**
@@ -333,34 +349,34 @@ angular.mock.$LogProvider = function() {
             $log.log.logs = [];
             /**
              * @ngdoc property
-             * @name ngMock.$log#warn.logs
+             * @name ngMock.$log#info.logs
              * @propertyOf ngMock.$log
              *
              * @description
-             * Array of messages logged using {@link ngMock.$log#warn}.
+             * Array of messages logged using {@link ngMock.$log#info}.
              *
              * @example
              * <pre>
-             * $log.warn('Some Warning');
-             * var first = $log.warn.logs.unshift();
+             * $log.info('Some Info');
+             * var first = $log.info.logs.unshift();
              * </pre>
              */
-            $log.warn.logs = [];
+            $log.info.logs = [];
             /**
              * @ngdoc property
-             * @name ngMock.$log#info.logs
+             * @name ngMock.$log#warn.logs
              * @propertyOf ngMock.$log
              *
              * @description
-             * Array of messages logged using {@link ngMock.$log#info}.
+             * Array of messages logged using {@link ngMock.$log#warn}.
              *
              * @example
              * <pre>
-             * $log.info('Some Info');
-             * var first = $log.info.logs.unshift();
+             * $log.warn('Some Warning');
+             * var first = $log.warn.logs.unshift();
              * </pre>
              */
-            $log.info.logs = [];
+            $log.warn.logs = [];
             /**
              * @ngdoc property
              * @name ngMock.$log#error.logs
@@ -376,6 +392,21 @@ angular.mock.$LogProvider = function() {
              * </pre>
              */
             $log.error.logs = [];
+            /**
+             * @ngdoc property
+             * @name ngMock.$log#debug.logs
+             * @propertyOf ngMock.$log
+             *
+             * @description
+             * Array of messages logged using {@link ngMock.$log#debug}.
+             *
+             * @example
+             * <pre>
+             * $log.debug('Some Error');
+             * var first = $log.debug.logs.unshift();
+             * </pre>
+             */
+            $log.debug.logs = [];
         };
 
         /**
@@ -384,20 +415,22 @@ angular.mock.$LogProvider = function() {
          * @methodOf ngMock.$log
          *
          * @description
-         * Assert that the all of the logging methods have no logged messages. If messages present, an exception is thrown.
+         * Assert that the all of the logging methods have no logged messages. If messages present, an
+         * exception is thrown.
          */
         $log.assertEmpty = function() {
             var errors = [];
-            angular.forEach(['error', 'warn', 'info', 'log'], function(logLevel) {
+            angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) {
                 angular.forEach($log[logLevel].logs, function(log) {
                     angular.forEach(log, function (logItem) {
-                        errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' + (logItem.stack || ''));
+                        errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' +
+                                (logItem.stack || ''));
                     });
                 });
             });
             if (errors.length) {
-                errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or an expected " +
-                        "log message was not checked and removed:");
+                errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or "+
+                        "an expected log message was not checked and removed:");
                 errors.push('');
                 throw new Error(errors.join('\n---------\n'));
             }
@@ -409,10 +442,128 @@ angular.mock.$LogProvider = function() {
 };
 
 
+/**
+ * @ngdoc service
+ * @name ngMock.$interval
+ *
+ * @description
+ * Mock implementation of the $interval service.
+ *
+ * Use {@link ngMock.$interval#methods_flush `$interval.flush(millis)`} to
+ * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
+ * time.
+ *
+ * @param {function()} fn A function that should be called repeatedly.
+ * @param {number} delay Number of milliseconds between each function call.
+ * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
+ *   indefinitely.
+ * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
+ *   will invoke `fn` within the {@link ng.$rootScope.Scope#methods_$apply $apply} block.
+ * @returns {promise} A promise which will be notified on each iteration.
+ */
+angular.mock.$IntervalProvider = function() {
+    this.$get = ['$rootScope', '$q',
+        function($rootScope,   $q) {
+            var repeatFns = [],
+                    nextRepeatId = 0,
+                    now = 0;
+
+            var $interval = function(fn, delay, count, invokeApply) {
+                var deferred = $q.defer(),
+                        promise = deferred.promise,
+                        iteration = 0,
+                        skipApply = (angular.isDefined(invokeApply) && !invokeApply);
+
+                count = (angular.isDefined(count)) ? count : 0,
+                        promise.then(null, null, fn);
+
+                promise.$$intervalId = nextRepeatId;
+
+                function tick() {
+                    deferred.notify(iteration++);
+
+                    if (count > 0 && iteration >= count) {
+                        var fnIndex;
+                        deferred.resolve(iteration);
+
+                        angular.forEach(repeatFns, function(fn, index) {
+                            if (fn.id === promise.$$intervalId) fnIndex = index;
+                        });
+
+                        if (fnIndex !== undefined) {
+                            repeatFns.splice(fnIndex, 1);
+                        }
+                    }
+
+                    if (!skipApply) $rootScope.$apply();
+                }
+
+                repeatFns.push({
+                    nextTime:(now + delay),
+                    delay: delay,
+                    fn: tick,
+                    id: nextRepeatId,
+                    deferred: deferred
+                });
+                repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
+
+                nextRepeatId++;
+                return promise;
+            };
+
+            $interval.cancel = function(promise) {
+                var fnIndex;
+
+                angular.forEach(repeatFns, function(fn, index) {
+                    if (fn.id === promise.$$intervalId) fnIndex = index;
+                });
+
+                if (fnIndex !== undefined) {
+                    repeatFns[fnIndex].deferred.reject('canceled');
+                    repeatFns.splice(fnIndex, 1);
+                    return true;
+                }
+
+                return false;
+            };
+
+            /**
+             * @ngdoc method
+             * @name ngMock.$interval#flush
+             * @methodOf ngMock.$interval
+             * @description
+             *
+             * Runs interval tasks scheduled to be run in the next `millis` milliseconds.
+             *
+             * @param {number=} millis maximum timeout amount to flush up until.
+             *
+             * @return {number} The amount of time moved forward.
+             */
+            $interval.flush = function(millis) {
+                now += millis;
+                while (repeatFns.length && repeatFns[0].nextTime <= now) {
+                    var task = repeatFns[0];
+                    task.fn();
+                    task.nextTime += task.delay;
+                    repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
+                }
+                return millis;
+            };
+
+            return $interval;
+        }];
+};
+
+
+/* jshint -W101 */
+/* The R_ISO8061_STR regex is never going to fit into the 100 char limit!
+ * This directive should go inside the anonymous function but a bug in JSHint means that it would
+ * not be enacted early enough to prevent the warning.
+ */
 (function() {
     var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
 
-    function jsonStringToDate(string){
+    function jsonStringToDate(string) {
         var match;
         if (match = string.match(R_ISO8061_STR)) {
             var date = new Date(0),
@@ -423,7 +574,10 @@ angular.mock.$LogProvider = function() {
                 tzMin = int(match[9] + match[11]);
             }
             date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
-            date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
+            date.setUTCHours(int(match[4]||0) - tzHour,
+                    int(match[5]||0) - tzMin,
+                    int(match[6]||0),
+                    int(match[7]||0));
             return date;
         }
         return string;
@@ -480,6 +634,7 @@ angular.mock.$LogProvider = function() {
      * newYearInBratislava.getDate() => 1;
      * newYearInBratislava.getHours() => 0;
      * newYearInBratislava.getMinutes() => 0;
+     * newYearInBratislava.getSeconds() => 0;
      * </pre>
      *
      */
@@ -536,6 +691,10 @@ angular.mock.$LogProvider = function() {
             return self.date.getSeconds();
         };
 
+        self.getMilliseconds = function() {
+            return self.date.getMilliseconds();
+        };
+
         self.getTimezoneOffset = function() {
             return offset * 60;
         };
@@ -581,12 +740,12 @@ angular.mock.$LogProvider = function() {
                         padNumber(self.origDate.getUTCHours(), 2) + ':' +
                         padNumber(self.origDate.getUTCMinutes(), 2) + ':' +
                         padNumber(self.origDate.getUTCSeconds(), 2) + '.' +
-                        padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z'
-            }
+                        padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z';
+            };
         }
 
         //hide all methods not implemented in this mock that the Date prototype exposes
-        var unimplementedMethods = ['getMilliseconds', 'getUTCDay',
+        var unimplementedMethods = ['getUTCDay',
             'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
             'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
             'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
@@ -595,7 +754,7 @@ angular.mock.$LogProvider = function() {
 
         angular.forEach(unimplementedMethods, function(methodName) {
             self[methodName] = function() {
-                throw Error("Method '" + methodName + "' is not implemented in the TzDate mock");
+                throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
             };
         });
 
@@ -605,6 +764,49 @@ angular.mock.$LogProvider = function() {
     //make "tzDateInstance instanceof Date" return true
     angular.mock.TzDate.prototype = Date.prototype;
 })();
+/* jshint +W101 */
+
+angular.mock.animate = angular.module('mock.animate', ['ng'])
+
+        .config(['$provide', function($provide) {
+
+            $provide.decorator('$animate', function($delegate) {
+                var animate = {
+                    queue : [],
+                    enabled : $delegate.enabled,
+                    flushNext : function(name) {
+                        var tick = animate.queue.shift();
+
+                        if (!tick) throw new Error('No animation to be flushed');
+                        if(tick.method !== name) {
+                            throw new Error('The next animation is not "' + name +
+                                    '", but is "' + tick.method + '"');
+                        }
+                        tick.fn();
+                        return tick;
+                    }
+                };
+
+                angular.forEach(['enter','leave','move','addClass','removeClass'], function(method) {
+                    animate[method] = function() {
+                        var params = arguments;
+                        animate.queue.push({
+                            method : method,
+                            params : params,
+                            element : angular.isElement(params[0]) && params[0],
+                            parent  : angular.isElement(params[1]) && params[1],
+                            after   : angular.isElement(params[2]) && params[2],
+                            fn : function() {
+                                $delegate[method].apply($delegate, params);
+                            }
+                        });
+                    };
+                });
+
+                return animate;
+            });
+
+        }]);
 
 
 /**
@@ -614,9 +816,11 @@ angular.mock.$LogProvider = function() {
  *
  * *NOTE*: this is not an injectable instance, just a globally available function.
  *
- * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for debugging.
+ * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for
+ * debugging.
  *
- * This method is also available on window, where it can be used to display objects on debug console.
+ * This method is also available on window, where it can be used to display objects on debug
+ * console.
  *
  * @param {*} object - any object to turn into string.
  * @return {string} a serialized string of the argument
@@ -646,6 +850,8 @@ angular.mock.dump = function(object) {
             } else if (object instanceof Error) {
                 out = object.stack || ('' + object.name + ': ' + object.message);
             } else {
+                // TODO(i): this prevents methods being logged,
+                // we should have a better way to serialize objects
                 out = angular.toJson(object, true);
             }
         } else {
@@ -659,7 +865,7 @@ angular.mock.dump = function(object) {
         offset = offset ||  '  ';
         var log = [offset + 'Scope(' + scope.$id + '): {'];
         for ( var key in scope ) {
-            if (scope.hasOwnProperty(key) && !key.match(/^(\$|this)/)) {
+            if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
                 log.push('  ' + key + ': ' + angular.toJson(scope[key]));
             }
         }
@@ -773,79 +979,104 @@ angular.mock.dump = function(object) {
  *
  *
  * # Unit testing with mock $httpBackend
+ * The following code shows how to setup and use the mock backend in unit testing a controller.
+ * First we create the controller under test
  *
- * <pre>
- // controller
+ <pre>
+ // The controller code
  function MyController($scope, $http) {
-     $http.get('/auth.py').success(function(data) {
-       $scope.user = data;
-     });
-
-     this.saveMessage = function(message) {
-       $scope.status = 'Saving...';
-       $http.post('/add-msg.py', message).success(function(response) {
-         $scope.status = '';
-       }).error(function() {
-         $scope.status = 'ERROR!';
-       });
-     };
-   }
+    var authToken;
+
+    $http.get('/auth.py').success(function(data, status, headers) {
+      authToken = headers('A-Token');
+      $scope.user = data;
+    });
+
+    $scope.saveMessage = function(message) {
+      var headers = { 'Authorization': authToken };
+      $scope.status = 'Saving...';
 
+      $http.post('/add-msg.py', message, { headers: headers } ).success(function(response) {
+        $scope.status = '';
+      }).error(function() {
+        $scope.status = 'ERROR!';
+      });
+    };
+  }
+ </pre>
+ *
+ * Now we setup the mock backend and create the test specs.
+ *
+ <pre>
  // testing controller
- var $httpBackend;
+ describe('MyController', function() {
+       var $httpBackend, $rootScope, createController;
+
+       beforeEach(inject(function($injector) {
+         // Set up the mock http service responses
+         $httpBackend = $injector.get('$httpBackend');
+         // backend definition common for all tests
+         $httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'});
+
+         // Get hold of a scope (i.e. the root scope)
+         $rootScope = $injector.get('$rootScope');
+         // The $controller service is used to create instances of controllers
+         var $controller = $injector.get('$controller');
+
+         createController = function() {
+           return $controller('MyController', {'$scope' : $rootScope });
+         };
+       }));
 
- beforeEach(inject(function($injector) {
-     $httpBackend = $injector.get('$httpBackend');
 
-     // backend definition common for all tests
-     $httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'});
-   }));
+       afterEach(function() {
+         $httpBackend.verifyNoOutstandingExpectation();
+         $httpBackend.verifyNoOutstandingRequest();
+       });
 
 
- afterEach(function() {
-     $httpBackend.verifyNoOutstandingExpectation();
-     $httpBackend.verifyNoOutstandingRequest();
-   });
+       it('should fetch authentication token', function() {
+         $httpBackend.expectGET('/auth.py');
+         var controller = createController();
+         $httpBackend.flush();
+       });
 
 
- it('should fetch authentication token', function() {
-     $httpBackend.expectGET('/auth.py');
-     var controller = scope.$new(MyController);
-     $httpBackend.flush();
-   });
+       it('should send msg to server', function() {
+         var controller = createController();
+         $httpBackend.flush();
 
+         // now you don’t care about the authentication, but
+         // the controller will still send the request and
+         // $httpBackend will respond without you having to
+         // specify the expectation and response for this request
 
- it('should send msg to server', function() {
-     // now you don’t care about the authentication, but
-     // the controller will still send the request and
-     // $httpBackend will respond without you having to
-     // specify the expectation and response for this request
-     $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
+         $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
+         $rootScope.saveMessage('message content');
+         expect($rootScope.status).toBe('Saving...');
+         $httpBackend.flush();
+         expect($rootScope.status).toBe('');
+       });
 
-     var controller = scope.$new(MyController);
-     $httpBackend.flush();
-     controller.saveMessage('message content');
-     expect(controller.status).toBe('Saving...');
-     $httpBackend.flush();
-     expect(controller.status).toBe('');
-   });
 
+       it('should send auth header', function() {
+         var controller = createController();
+         $httpBackend.flush();
 
- it('should send auth header', function() {
-     $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
-       // check if the header was send, if it wasn't the expectation won't
-       // match the request and the test will fail
-       return headers['Authorization'] == 'xxx';
-     }).respond(201, '');
+         $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
+           // check if the header was send, if it wasn't the expectation won't
+           // match the request and the test will fail
+           return headers['Authorization'] == 'xxx';
+         }).respond(201, '');
 
-     var controller = scope.$new(MyController);
-     controller.saveMessage('whatever');
-     $httpBackend.flush();
-   });
+         $rootScope.saveMessage('whatever');
+         $httpBackend.flush();
+       });
+    });
  </pre>
  */
 angular.mock.$HttpBackendProvider = function() {
-    this.$get = [createHttpBackendMock];
+    this.$get = ['$rootScope', createHttpBackendMock];
 };
 
 /**
@@ -862,7 +1093,7 @@ angular.mock.$HttpBackendProvider = function() {
  * @param {Object=} $browser Auto-flushing enabled if specified
  * @return {Object} Instance of $httpBackend mock
  */
-function createHttpBackendMock($delegate, $browser) {
+function createHttpBackendMock($rootScope, $delegate, $browser) {
     var definitions = [],
             expectations = [],
             responses = [],
@@ -879,7 +1110,7 @@ function createHttpBackendMock($delegate, $browser) {
     }
 
     // TODO(vojta): change params to: method, url, data, headers, callback
-    function $httpBackend(method, url, data, callback, headers) {
+    function $httpBackend(method, url, data, callback, headers, timeout, withCredentials) {
         var xhr = new MockXhr(),
                 expectation = expectations[0],
                 wasExpected = false;
@@ -890,24 +1121,42 @@ function createHttpBackendMock($delegate, $browser) {
                     : angular.toJson(data);
         }
 
+        function wrapResponse(wrapped) {
+            if (!$browser && timeout && timeout.then) timeout.then(handleTimeout);
+
+            return handleResponse;
+
+            function handleResponse() {
+                var response = wrapped.response(method, url, data, headers);
+                xhr.$$respHeaders = response[2];
+                callback(response[0], response[1], xhr.getAllResponseHeaders());
+            }
+
+            function handleTimeout() {
+                for (var i = 0, ii = responses.length; i < ii; i++) {
+                    if (responses[i] === handleResponse) {
+                        responses.splice(i, 1);
+                        callback(-1, undefined, '');
+                        break;
+                    }
+                }
+            }
+        }
+
         if (expectation && expectation.match(method, url)) {
             if (!expectation.matchData(data))
-                throw Error('Expected ' + expectation + ' with different data\n' +
+                throw new Error('Expected ' + expectation + ' with different data\n' +
                         'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT:      ' + data);
 
             if (!expectation.matchHeaders(headers))
-                throw Error('Expected ' + expectation + ' with different headers\n' +
+                throw new Error('Expected ' + expectation + ' with different headers\n' +
                         'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT:      ' +
                         prettyPrint(headers));
 
             expectations.shift();
 
             if (expectation.response) {
-                responses.push(function() {
-                    var response = expectation.response(method, url, data, headers);
-                    xhr.$$respHeaders = response[2];
-                    callback(response[0], response[1], xhr.getAllResponseHeaders());
-                });
+                responses.push(wrapResponse(expectation));
                 return;
             }
             wasExpected = true;
@@ -918,20 +1167,16 @@ function createHttpBackendMock($delegate, $browser) {
             if (definition.match(method, url, data, headers || {})) {
                 if (definition.response) {
                     // if $browser specified, we do auto flush all requests
-                    ($browser ? $browser.defer : responsesPush)(function() {
-                        var response = definition.response(method, url, data, headers);
-                        xhr.$$respHeaders = response[2];
-                        callback(response[0], response[1], xhr.getAllResponseHeaders());
-                    });
+                    ($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
                 } else if (definition.passThrough) {
-                    $delegate(method, url, data, callback, headers);
-                } else throw Error('No response defined !');
+                    $delegate(method, url, data, callback, headers, timeout, withCredentials);
+                } else throw new Error('No response defined !');
                 return;
             }
         }
         throw wasExpected ?
-                Error('No response defined !') :
-                Error('Unexpected request: ' + method + ' ' + url + '\n' +
+                new Error('No response defined !') :
+                new Error('Unexpected request: ' + method + ' ' + url + '\n' +
                         (expectation ? 'Expected ' + expectation : 'No more request expected'));
     }
 
@@ -944,13 +1189,15 @@ function createHttpBackendMock($delegate, $browser) {
      *
      * @param {string} method HTTP method.
      * @param {string|RegExp} url HTTP url.
-     * @param {(string|RegExp)=} data HTTP request body.
+     * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
+     *   data string and returns true if the data is as expected.
      * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
      *   object and returns true if the headers match the current definition.
-     * @returns {requestHandler} Returns an object with `respond` method that control how a matched
+     * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
      *   request is handled.
      *
-     *  - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
+     *  - respond –
+     *      `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
      *    – The respond method takes a set of static data to be returned or a function that can return
      *    an array containing response status (number), response data (string) and response headers
      *    (Object).
@@ -1020,7 +1267,8 @@ function createHttpBackendMock($delegate, $browser) {
      * Creates a new backend definition for POST requests. For more info see `when()`.
      *
      * @param {string|RegExp} url HTTP url.
-     * @param {(string|RegExp)=} data HTTP request body.
+     * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
+     *   data string and returns true if the data is as expected.
      * @param {(Object|function(Object))=} headers HTTP headers.
      * @returns {requestHandler} Returns an object with `respond` method that control how a matched
      * request is handled.
@@ -1034,7 +1282,8 @@ function createHttpBackendMock($delegate, $browser) {
      * Creates a new backend definition for PUT requests.  For more info see `when()`.
      *
      * @param {string|RegExp} url HTTP url.
-     * @param {(string|RegExp)=} data HTTP request body.
+     * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
+     *   data string and returns true if the data is as expected.
      * @param {(Object|function(Object))=} headers HTTP headers.
      * @returns {requestHandler} Returns an object with `respond` method that control how a matched
      * request is handled.
@@ -1063,13 +1312,16 @@ function createHttpBackendMock($delegate, $browser) {
      *
      * @param {string} method HTTP method.
      * @param {string|RegExp} url HTTP url.
-     * @param {(string|RegExp)=} data HTTP request body.
+     * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
+     *  receives data string and returns true if the data is as expected, or Object if request body
+     *  is in JSON format.
      * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
      *   object and returns true if the headers match the current expectation.
      * @returns {requestHandler} Returns an object with `respond` method that control how a matched
      *  request is handled.
      *
-     *  - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
+     *  - respond –
+     *    `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
      *    – The respond method takes a set of static data to be returned or a function that can return
      *    an array containing response status (number), response data (string) and response headers
      *    (Object).
@@ -1132,7 +1384,9 @@ function createHttpBackendMock($delegate, $browser) {
      * Creates a new request expectation for POST requests. For more info see `expect()`.
      *
      * @param {string|RegExp} url HTTP url.
-     * @param {(string|RegExp)=} data HTTP request body.
+     * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
+     *  receives data string and returns true if the data is as expected, or Object if request body
+     *  is in JSON format.
      * @param {Object=} headers HTTP headers.
      * @returns {requestHandler} Returns an object with `respond` method that control how a matched
      *   request is handled.
@@ -1146,7 +1400,9 @@ function createHttpBackendMock($delegate, $browser) {
      * Creates a new request expectation for PUT requests. For more info see `expect()`.
      *
      * @param {string|RegExp} url HTTP url.
-     * @param {(string|RegExp)=} data HTTP request body.
+     * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
+     *  receives data string and returns true if the data is as expected, or Object if request body
+     *  is in JSON format.
      * @param {Object=} headers HTTP headers.
      * @returns {requestHandler} Returns an object with `respond` method that control how a matched
      *   request is handled.
@@ -1160,7 +1416,9 @@ function createHttpBackendMock($delegate, $browser) {
      * Creates a new request expectation for PATCH requests. For more info see `expect()`.
      *
      * @param {string|RegExp} url HTTP url.
-     * @param {(string|RegExp)=} data HTTP request body.
+     * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
+     *  receives data string and returns true if the data is as expected, or Object if request body
+     *  is in JSON format.
      * @param {Object=} headers HTTP headers.
      * @returns {requestHandler} Returns an object with `respond` method that control how a matched
      *   request is handled.
@@ -1192,11 +1450,12 @@ function createHttpBackendMock($delegate, $browser) {
      *   is called an exception is thrown (as this typically a sign of programming error).
      */
     $httpBackend.flush = function(count) {
-        if (!responses.length) throw Error('No pending request to flush !');
+        $rootScope.$digest();
+        if (!responses.length) throw new Error('No pending request to flush !');
 
         if (angular.isDefined(count)) {
             while (count--) {
-                if (!responses.length) throw Error('No more pending request to flush !');
+                if (!responses.length) throw new Error('No more pending request to flush !');
                 responses.shift()();
             }
         } else {
@@ -1220,12 +1479,13 @@ function createHttpBackendMock($delegate, $browser) {
      * "afterEach" clause.
      *
      * <pre>
-     *   afterEach($httpBackend.verifyExpectations);
+     *   afterEach($httpBackend.verifyNoOutstandingExpectation);
      * </pre>
      */
     $httpBackend.verifyNoOutstandingExpectation = function() {
+        $rootScope.$digest();
         if (expectations.length) {
-            throw Error('Unsatisfied requests: ' + expectations.join(', '));
+            throw new Error('Unsatisfied requests: ' + expectations.join(', '));
         }
     };
 
@@ -1246,7 +1506,7 @@ function createHttpBackendMock($delegate, $browser) {
      */
     $httpBackend.verifyNoOutstandingRequest = function() {
         if (responses.length) {
-            throw Error('Unflushed requests: ' + responses.length);
+            throw new Error('Unflushed requests: ' + responses.length);
         }
     };
 
@@ -1271,14 +1531,14 @@ function createHttpBackendMock($delegate, $browser) {
     function createShortMethods(prefix) {
         angular.forEach(['GET', 'DELETE', 'JSONP'], function(method) {
             $httpBackend[prefix + method] = function(url, headers) {
-                return $httpBackend[prefix](method, url, undefined, headers)
-            }
+                return $httpBackend[prefix](method, url, undefined, headers);
+            };
         });
 
         angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
             $httpBackend[prefix + method] = function(url, data, headers) {
-                return $httpBackend[prefix](method, url, data, headers)
-            }
+                return $httpBackend[prefix](method, url, data, headers);
+            };
         });
     }
 }
@@ -1311,7 +1571,8 @@ function MockHttpExpectation(method, url, data, headers) {
     this.matchData = function(d) {
         if (angular.isUndefined(data)) return true;
         if (data && angular.isFunction(data.test)) return data.test(d);
-        if (data && !angular.isString(data)) return angular.toJson(data) == d;
+        if (data && angular.isFunction(data)) return data(d);
+        if (data && !angular.isString(data)) return angular.equals(data, angular.fromJson(d));
         return data == d;
     };
 
@@ -1342,7 +1603,8 @@ function MockXhr() {
     };
 
     this.getResponseHeader = function(name) {
-        // the lookup must be case insensitive, that's why we try two quick lookups and full scan at last
+        // the lookup must be case insensitive,
+        // that's why we try two quick lookups first and full scan last
         var header = this.$$respHeaders[name];
         if (header) return header;
 
@@ -1376,17 +1638,51 @@ function MockXhr() {
  * @description
  *
  * This service is just a simple decorator for {@link ng.$timeout $timeout} service
- * that adds a "flush" method.
+ * that adds a "flush" and "verifyNoPendingTasks" methods.
  */
 
-/**
- * @ngdoc method
- * @name ngMock.$timeout#flush
- * @methodOf ngMock.$timeout
- * @description
- *
- * Flushes the queue of pending tasks.
- */
+angular.mock.$TimeoutDecorator = function($delegate, $browser) {
+
+    /**
+     * @ngdoc method
+     * @name ngMock.$timeout#flush
+     * @methodOf ngMock.$timeout
+     * @description
+     *
+     * Flushes the queue of pending tasks.
+     *
+     * @param {number=} delay maximum timeout amount to flush up until
+     */
+    $delegate.flush = function(delay) {
+        $browser.defer.flush(delay);
+    };
+
+    /**
+     * @ngdoc method
+     * @name ngMock.$timeout#verifyNoPendingTasks
+     * @methodOf ngMock.$timeout
+     * @description
+     *
+     * Verifies that there are no pending tasks that need to be flushed.
+     */
+    $delegate.verifyNoPendingTasks = function() {
+        if ($browser.deferredFns.length) {
+            throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
+                    formatPendingTasksAsString($browser.deferredFns));
+        }
+    };
+
+    function formatPendingTasksAsString(tasks) {
+        var result = [];
+        angular.forEach(tasks, function(task) {
+            result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}');
+        });
+
+        return result.join(', ');
+    }
+
+    return $delegate;
+};
 
 /**
  *
@@ -1394,7 +1690,7 @@ function MockXhr() {
 angular.mock.$RootElementProvider = function() {
     this.$get = function() {
         return angular.element('<div ng-app></div>');
-    }
+    };
 };
 
 /**
@@ -1402,24 +1698,27 @@ angular.mock.$RootElementProvider = function() {
  * @name ngMock
  * @description
  *
- * The `ngMock` is an angular module which is used with `ng` module and adds unit-test configuration as well as useful
- * mocks to the {@link AUTO.$injector $injector}.
+ * # ngMock
+ *
+ * The `ngMock` module providers support to inject and mock Angular services into unit tests.
+ * In addition, ngMock also extends various core ng services such that they can be
+ * inspected and controlled in a synchronous manner within test code.
+ *
+ * {@installModule mocks}
+ *
+ * <div doc-module-components="ngMock"></div>
+ *
  */
 angular.module('ngMock', ['ng']).provider({
     $browser: angular.mock.$BrowserProvider,
     $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
     $log: angular.mock.$LogProvider,
+    $interval: angular.mock.$IntervalProvider,
     $httpBackend: angular.mock.$HttpBackendProvider,
     $rootElement: angular.mock.$RootElementProvider
-}).config(function($provide) {
-            $provide.decorator('$timeout', function($delegate, $browser) {
-                $delegate.flush = function() {
-                    $browser.defer.flush();
-                };
-                return $delegate;
-            });
-        });
-
+}).config(['$provide', function($provide) {
+            $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
+        }]);
 
 /**
  * @ngdoc overview
@@ -1430,9 +1729,9 @@ angular.module('ngMock', ['ng']).provider({
  * Currently there is only one mock present in this module -
  * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
  */
-angular.module('ngMockE2E', ['ng']).config(function($provide) {
+angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
     $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
-});
+}]);
 
 /**
  * @ngdoc object
@@ -1472,7 +1771,7 @@ angular.module('ngMockE2E', ['ng']).config(function($provide) {
  *
  *     // adds a new phone to the phones array
  *     $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
- *       phones.push(angular.fromJSON(data));
+ *       phones.push(angular.fromJson(data));
  *     });
  *     $httpBackend.whenGET(/^\/templates\//).passThrough();
  *     //...
@@ -1497,7 +1796,8 @@ angular.module('ngMockE2E', ['ng']).config(function($provide) {
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
  *   control how a matched request is handled.
  *
- *  - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
+ *  - respond –
+ *    `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
  *    – The respond method takes a set of static data to be returned or a function that can return
  *    an array containing response status (number), response data (string) and response headers
  *    (Object).
@@ -1599,7 +1899,8 @@ angular.module('ngMockE2E', ['ng']).config(function($provide) {
  *   control how a matched request is handled.
  */
 angular.mock.e2e = {};
-angular.mock.e2e.$httpBackendDecorator = ['$delegate', '$browser', createHttpBackendMock];
+angular.mock.e2e.$httpBackendDecorator =
+        ['$rootScope', '$delegate', '$browser', createHttpBackendMock];
 
 
 angular.mock.clearDataCache = function() {
@@ -1607,44 +1908,34 @@ angular.mock.clearDataCache = function() {
             cache = angular.element.cache;
 
     for(key in cache) {
-        if (cache.hasOwnProperty(key)) {
+        if (Object.prototype.hasOwnProperty.call(cache,key)) {
             var handle = cache[key].handle;
 
-            handle && angular.element(handle.elem).unbind();
+            handle && angular.element(handle.elem).off();
             delete cache[key];
         }
     }
 };
 
 
-window.jstestdriver && (function(window) {
-    /**
-     * Global method to output any number of objects into JSTD console. Useful for debugging.
-     */
-    window.dump = function() {
-        var args = [];
-        angular.forEach(arguments, function(arg) {
-            args.push(angular.mock.dump(arg));
-        });
-        jstestdriver.console.log.apply(jstestdriver.console, args);
-        if (window.console) {
-            window.console.log.apply(window.console, args);
-        }
-    };
-})(window);
 
+(window.jasmine || window.mocha) && (function(window) {
 
-window.jasmine && (function(window) {
+    var currentSpec = null;
+
+    beforeEach(function() {
+        currentSpec = this;
+    });
 
     afterEach(function() {
-        var spec = getCurrentSpec();
-        var injector = spec.$injector;
+        var injector = currentSpec.$injector;
 
-        spec.$injector = null;
-        spec.$modules = null;
+        currentSpec.$injector = null;
+        currentSpec.$modules = null;
+        currentSpec = null;
 
         if (injector) {
-            injector.get('$rootElement').unbind();
+            injector.get('$rootElement').off();
             injector.get('$browser').pollFns.length = 0;
         }
 
@@ -1663,13 +1954,8 @@ window.jasmine && (function(window) {
         angular.callbacks.counter = 0;
     });
 
-    function getCurrentSpec() {
-        return jasmine.getEnv().currentSpec;
-    }
-
     function isSpecRunning() {
-        var spec = getCurrentSpec();
-        return spec && spec.queue.running;
+        return currentSpec && (window.mocha || currentSpec.queue.running);
     }
 
     /**
@@ -1678,29 +1964,37 @@ window.jasmine && (function(window) {
      * @description
      *
      * *NOTE*: This function is also published on window for easy access.<br>
-     * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}.
      *
      * This function registers a module configuration code. It collects the configuration information
      * which will be used when the injector is created by {@link angular.mock.inject inject}.
      *
      * See {@link angular.mock.inject inject} for usage example
      *
-     * @param {...(string|Function)} fns any number of modules which are represented as string
+     * @param {...(string|Function|Object)} fns any number of modules which are represented as string
      *        aliases or as anonymous module initialization functions. The modules are used to
-     *        configure the injector. The 'ng' and 'ngMock' modules are automatically loaded.
+     *        configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an
+     *        object literal is passed they will be register as values in the module, the key being
+     *        the module name and the value being what is returned.
      */
     window.module = angular.mock.module = function() {
         var moduleFns = Array.prototype.slice.call(arguments, 0);
         return isSpecRunning() ? workFn() : workFn;
         /////////////////////
         function workFn() {
-            var spec = getCurrentSpec();
-            if (spec.$injector) {
-                throw Error('Injector already created, can not register a module!');
+            if (currentSpec.$injector) {
+                throw new Error('Injector already created, can not register a module!');
             } else {
-                var modules = spec.$modules || (spec.$modules = []);
+                var modules = currentSpec.$modules || (currentSpec.$modules = []);
                 angular.forEach(moduleFns, function(module) {
-                    modules.push(module);
+                    if (angular.isObject(module) && !angular.isArray(module)) {
+                        modules.push(function($provide) {
+                            angular.forEach(module, function(value, key) {
+                                $provide.value(key, value);
+                            });
+                        });
+                    } else {
+                        modules.push(module);
+                    }
                 });
             }
         }
@@ -1712,14 +2006,45 @@ window.jasmine && (function(window) {
      * @description
      *
      * *NOTE*: This function is also published on window for easy access.<br>
-     * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}.
      *
      * The inject function wraps a function into an injectable function. The inject() creates new
      * instance of {@link AUTO.$injector $injector} per test, which is then used for
      * resolving references.
      *
-     * See also {@link angular.mock.module module}
      *
+     * ## Resolving References (Underscore Wrapping)
+     * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this
+     * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable
+     * that is declared in the scope of the `describe()` block. Since we would, most likely, want
+     * the variable to have the same name of the reference we have a problem, since the parameter
+     * to the `inject()` function would hide the outer variable.
+     *
+     * To help with this, the injected parameters can, optionally, be enclosed with underscores.
+     * These are ignored by the injector when the reference name is resolved.
+     *
+     * For example, the parameter `_myService_` would be resolved as the reference `myService`.
+     * Since it is available in the function body as _myService_, we can then assign it to a variable
+     * defined in an outer scope.
+     *
+     * ```
+     * // Defined out reference variable outside
+     * var myService;
+     *
+     * // Wrap the parameter in underscores
+     * beforeEach( inject( function(_myService_){
+   *   myService = _myService_;
+   * }));
+     *
+     * // Use myService in a series of tests.
+     * it('makes use of myService', function() {
+   *   myService.doStuff();
+   * });
+     *
+     * ```
+     *
+     * See also {@link angular.mock.module angular.mock.module}
+     *
+     * ## Example
      * Example of what a typical jasmine tests looks like with the inject method.
      * <pre>
      *
@@ -1752,12 +2077,12 @@ window.jasmine && (function(window) {
    *       inject(function(version) {
    *         expect(version).toEqual('overridden');
    *       });
-   *     ));
+   *     });
    *   });
-   *
-   * </pre>
-   *
-   * @param {...Function} fns any number of functions which will be injected using the injector.
+     *
+     * </pre>
+     *
+     * @param {...Function} fns any number of functions which will be injected using the injector.
      */
     window.inject = angular.mock.inject = function() {
         var blockFns = Array.prototype.slice.call(arguments, 0);
@@ -1765,17 +2090,19 @@ window.jasmine && (function(window) {
         return isSpecRunning() ? workFn() : workFn;
         /////////////////////
         function workFn() {
-            var spec = getCurrentSpec();
-            var modules = spec.$modules || [];
+            var modules = currentSpec.$modules || [];
+
             modules.unshift('ngMock');
             modules.unshift('ng');
-            var injector = spec.$injector;
+            var injector = currentSpec.$injector;
             if (!injector) {
-                injector = spec.$injector = angular.injector(modules);
+                injector = currentSpec.$injector = angular.injector(modules);
             }
             for(var i = 0, ii = blockFns.length; i < ii; i++) {
                 try {
+                    /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */
                     injector.invoke(blockFns[i] || angular.noop, this);
+                    /* jshint +W040 */
                 } catch (e) {
                     if(e.stack && errorForStack) e.stack +=  '\n' + errorForStack.stack;
                     throw e;
diff --git a/lib/angular.js b/lib/angular.js
new file mode 100755
index 000000000..5ccd391f8
--- /dev/null
+++ b/lib/angular.js
@@ -0,0 +1,20215 @@
+/**
+ * @license AngularJS v1.2.2
+ * (c) 2010-2012 Google, Inc. http://angularjs.org
+ * License: MIT
+ */
+(function(window, document, undefined) {'use strict';
+
+    /**
+     * @description
+     *
+     * This object provides a utility for producing rich Error messages within
+     * Angular. It can be called as follows:
+     *
+     * var exampleMinErr = minErr('example');
+     * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
+     *
+     * The above creates an instance of minErr in the example namespace. The
+     * resulting error will have a namespaced error code of example.one.  The
+     * resulting error will replace {0} with the value of foo, and {1} with the
+     * value of bar. The object is not restricted in the number of arguments it can
+     * take.
+     *
+     * If fewer arguments are specified than necessary for interpolation, the extra
+     * interpolation markers will be preserved in the final string.
+     *
+     * Since data will be parsed statically during a build step, some restrictions
+     * are applied with respect to how minErr instances are created and called.
+     * Instances should have names of the form namespaceMinErr for a minErr created
+     * using minErr('namespace') . Error codes, namespaces and template strings
+     * should all be static strings, not variables or general expressions.
+     *
+     * @param {string} module The namespace to use for the new minErr instance.
+     * @returns {function(string, string, ...): Error} instance
+     */
+
+    function minErr(module) {
+        return function () {
+            var code = arguments[0],
+                    prefix = '[' + (module ? module + ':' : '') + code + '] ',
+                    template = arguments[1],
+                    templateArgs = arguments,
+                    stringify = function (obj) {
+                        if (typeof obj === 'function') {
+                            return obj.toString().replace(/ \{[\s\S]*$/, '');
+                        } else if (typeof obj === 'undefined') {
+                            return 'undefined';
+                        } else if (typeof obj !== 'string') {
+                            return JSON.stringify(obj);
+                        }
+                        return obj;
+                    },
+                    message, i;
+
+            message = prefix + template.replace(/\{\d+\}/g, function (match) {
+                var index = +match.slice(1, -1), arg;
+
+                if (index + 2 < templateArgs.length) {
+                    arg = templateArgs[index + 2];
+                    if (typeof arg === 'function') {
+                        return arg.toString().replace(/ ?\{[\s\S]*$/, '');
+                    } else if (typeof arg === 'undefined') {
+                        return 'undefined';
+                    } else if (typeof arg !== 'string') {
+                        return toJson(arg);
+                    }
+                    return arg;
+                }
+                return match;
+            });
+
+            message = message + '\nhttp://errors.angularjs.org/1.2.2/' +
+                    (module ? module + '/' : '') + code;
+            for (i = 2; i < arguments.length; i++) {
+                message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
+                        encodeURIComponent(stringify(arguments[i]));
+            }
+
+            return new Error(message);
+        };
+    }
+
+    /* We need to tell jshint what variables are being exported */
+    /* global
+     -angular,
+     -msie,
+     -jqLite,
+     -jQuery,
+     -slice,
+     -push,
+     -toString,
+     -ngMinErr,
+     -_angular,
+     -angularModule,
+     -nodeName_,
+     -uid,
+
+     -lowercase,
+     -uppercase,
+     -manualLowercase,
+     -manualUppercase,
+     -nodeName_,
+     -isArrayLike,
+     -forEach,
+     -sortedKeys,
+     -forEachSorted,
+     -reverseParams,
+     -nextUid,
+     -setHashKey,
+     -extend,
+     -int,
+     -inherit,
+     -noop,
+     -identity,
+     -valueFn,
+     -isUndefined,
+     -isDefined,
+     -isObject,
+     -isString,
+     -isNumber,
+     -isDate,
+     -isArray,
+     -isFunction,
+     -isRegExp,
+     -isWindow,
+     -isScope,
+     -isFile,
+     -isBoolean,
+     -trim,
+     -isElement,
+     -makeMap,
+     -map,
+     -size,
+     -includes,
+     -indexOf,
+     -arrayRemove,
+     -isLeafNode,
+     -copy,
+     -shallowCopy,
+     -equals,
+     -csp,
+     -concat,
+     -sliceArgs,
+     -bind,
+     -toJsonReplacer,
+     -toJson,
+     -fromJson,
+     -toBoolean,
+     -startingTag,
+     -tryDecodeURIComponent,
+     -parseKeyValue,
+     -toKeyValue,
+     -encodeUriSegment,
+     -encodeUriQuery,
+     -angularInit,
+     -bootstrap,
+     -snake_case,
+     -bindJQuery,
+     -assertArg,
+     -assertArgFn,
+     -assertNotHasOwnProperty,
+     -getter,
+     -getBlockElements,
+
+     */
+
+////////////////////////////////////
+
+    /**
+     * @ngdoc function
+     * @name angular.lowercase
+     * @function
+     *
+     * @description Converts the specified string to lowercase.
+     * @param {string} string String to be converted to lowercase.
+     * @returns {string} Lowercased string.
+     */
+    var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;};
+
+
+    /**
+     * @ngdoc function
+     * @name angular.uppercase
+     * @function
+     *
+     * @description Converts the specified string to uppercase.
+     * @param {string} string String to be converted to uppercase.
+     * @returns {string} Uppercased string.
+     */
+    var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;};
+
+
+    var manualLowercase = function(s) {
+        /* jshint bitwise: false */
+        return isString(s)
+                ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
+                : s;
+    };
+    var manualUppercase = function(s) {
+        /* jshint bitwise: false */
+        return isString(s)
+                ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
+                : s;
+    };
+
+
+// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
+// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
+// with correct but slower alternatives.
+    if ('i' !== 'I'.toLowerCase()) {
+        lowercase = manualLowercase;
+        uppercase = manualUppercase;
+    }
+
+
+    var /** holds major version number for IE or NaN for real browsers */
+                    msie,
+            jqLite,           // delay binding since jQuery could be loaded after us.
+            jQuery,           // delay binding
+            slice             = [].slice,
+            push              = [].push,
+            toString          = Object.prototype.toString,
+            ngMinErr          = minErr('ng'),
+
+
+            _angular          = window.angular,
+            /** @name angular */
+                    angular           = window.angular || (window.angular = {}),
+            angularModule,
+            nodeName_,
+            uid               = ['0', '0', '0'];
+
+    /**
+     * IE 11 changed the format of the UserAgent string.
+     * See http://msdn.microsoft.com/en-us/library/ms537503.aspx
+     */
+    msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
+    if (isNaN(msie)) {
+        msie = int((/trident\/.*; rv:(\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
+    }
+
+
+    /**
+     * @private
+     * @param {*} obj
+     * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
+     *                   String ...)
+     */
+    function isArrayLike(obj) {
+        if (obj == null || isWindow(obj)) {
+            return false;
+        }
+
+        var length = obj.length;
+
+        if (obj.nodeType === 1 && length) {
+            return true;
+        }
+
+        return isString(obj) || isArray(obj) || length === 0 ||
+                typeof length === 'number' && length > 0 && (length - 1) in obj;
+    }
+
+    /**
+     * @ngdoc function
+     * @name angular.forEach
+     * @function
+     *
+     * @description
+     * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
+     * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value`
+     * is the value of an object property or an array element and `key` is the object property key or
+     * array element index. Specifying a `context` for the function is optional.
+     *
+     * Note: this function was previously known as `angular.foreach`.
+     *
+     <pre>
+     var values = {name: 'misko', gender: 'male'};
+     var log = [];
+     angular.forEach(values, function(value, key){
+       this.push(key + ': ' + value);
+     }, log);
+     expect(log).toEqual(['name: misko', 'gender:male']);
+     </pre>
+     *
+     * @param {Object|Array} obj Object to iterate over.
+     * @param {Function} iterator Iterator function.
+     * @param {Object=} context Object to become context (`this`) for the iterator function.
+     * @returns {Object|Array} Reference to `obj`.
+     */
+    function forEach(obj, iterator, context) {
+        var key;
+        if (obj) {
+            if (isFunction(obj)){
+                for (key in obj) {
+                    if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) {
+                        iterator.call(context, obj[key], key);
+                    }
+                }
+            } else if (obj.forEach && obj.forEach !== forEach) {
+                obj.forEach(iterator, context);
+            } else if (isArrayLike(obj)) {
+                for (key = 0; key < obj.length; key++)
+                    iterator.call(context, obj[key], key);
+            } else {
+                for (key in obj) {
+                    if (obj.hasOwnProperty(key)) {
+                        iterator.call(context, obj[key], key);
+                    }
+                }
+            }
+        }
+        return obj;
+    }
+
+    function sortedKeys(obj) {
+        var keys = [];
+        for (var key in obj) {
+            if (obj.hasOwnProperty(key)) {
+                keys.push(key);
+            }
+        }
+        return keys.sort();
+    }
+
+    function forEachSorted(obj, iterator, context) {
+        var keys = sortedKeys(obj);
+        for ( var i = 0; i < keys.length; i++) {
+            iterator.call(context, obj[keys[i]], keys[i]);
+        }
+        return keys;
+    }
+
+
+    /**
+     * when using forEach the params are value, key, but it is often useful to have key, value.
+     * @param {function(string, *)} iteratorFn
+     * @returns {function(*, string)}
+     */
+    function reverseParams(iteratorFn) {
+        return function(value, key) { iteratorFn(key, value); };
+    }
+
+    /**
+     * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
+     * characters such as '012ABC'. The reason why we are not using simply a number counter is that
+     * the number string gets longer over time, and it can also overflow, where as the nextId
+     * will grow much slower, it is a string, and it will never overflow.
+     *
+     * @returns an unique alpha-numeric string
+     */
+    function nextUid() {
+        var index = uid.length;
+        var digit;
+
+        while(index) {
+            index--;
+            digit = uid[index].charCodeAt(0);
+            if (digit == 57 /*'9'*/) {
+                uid[index] = 'A';
+                return uid.join('');
+            }
+            if (digit == 90  /*'Z'*/) {
+                uid[index] = '0';
+            } else {
+                uid[index] = String.fromCharCode(digit + 1);
+                return uid.join('');
+            }
+        }
+        uid.unshift('0');
+        return uid.join('');
+    }
+
+
+    /**
+     * Set or clear the hashkey for an object.
+     * @param obj object
+     * @param h the hashkey (!truthy to delete the hashkey)
+     */
+    function setHashKey(obj, h) {
+        if (h) {
+            obj.$$hashKey = h;
+        }
+        else {
+            delete obj.$$hashKey;
+        }
+    }
+
+    /**
+     * @ngdoc function
+     * @name angular.extend
+     * @function
+     *
+     * @description
+     * Extends the destination object `dst` by copying all of the properties from the `src` object(s)
+     * to `dst`. You can specify multiple `src` objects.
+     *
+     * @param {Object} dst Destination object.
+     * @param {...Object} src Source object(s).
+     * @returns {Object} Reference to `dst`.
+     */
+    function extend(dst) {
+        var h = dst.$$hashKey;
+        forEach(arguments, function(obj){
+            if (obj !== dst) {
+                forEach(obj, function(value, key){
+                    dst[key] = value;
+                });
+            }
+        });
+
+        setHashKey(dst,h);
+        return dst;
+    }
+
+    function int(str) {
+        return parseInt(str, 10);
+    }
+
+
+    function inherit(parent, extra) {
+        return extend(new (extend(function() {}, {prototype:parent}))(), extra);
+    }
+
+    /**
+     * @ngdoc function
+     * @name angular.noop
+     * @function
+     *
+     * @description
+     * A function that performs no operations. This function can be useful when writing code in the
+     * functional style.
+     <pre>
+     function foo(callback) {
+       var result = calculateResult();
+       (callback || angular.noop)(result);
+     }
+     </pre>
+     */
+    function noop() {}
+    noop.$inject = [];
+
+
+    /**
+     * @ngdoc function
+     * @name angular.identity
+     * @function
+     *
+     * @description
+     * A function that returns its first argument. This function is useful when writing code in the
+     * functional style.
+     *
+     <pre>
+     function transformer(transformationFn, value) {
+       return (transformationFn || angular.identity)(value);
+     };
+     </pre>
+     */
+    function identity($) {return $;}
+    identity.$inject = [];
+
+
+    function valueFn(value) {return function() {return value;};}
+
+    /**
+     * @ngdoc function
+     * @name angular.isUndefined
+     * @function
+     *
+     * @description
+     * Determines if a reference is undefined.
+     *
+     * @param {*} value Reference to check.
+     * @returns {boolean} True if `value` is undefined.
+     */
+    function isUndefined(value){return typeof value == 'undefined';}
+
+
+    /**
+     * @ngdoc function
+     * @name angular.isDefined
+     * @function
+     *
+     * @description
+     * Determines if a reference is defined.
+     *
+     * @param {*} value Reference to check.
+     * @returns {boolean} True if `value` is defined.
+     */
+    function isDefined(value){return typeof value != 'undefined';}
+
+
+    /**
+     * @ngdoc function
+     * @name angular.isObject
+     * @function
+     *
+     * @description
+     * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
+     * considered to be objects.
+     *
+     * @param {*} value Reference to check.
+     * @returns {boolean} True if `value` is an `Object` but not `null`.
+     */
+    function isObject(value){return value != null && typeof value == 'object';}
+
+
+    /**
+     * @ngdoc function
+     * @name angular.isString
+     * @function
+     *
+     * @description
+     * Determines if a reference is a `String`.
+     *
+     * @param {*} value Reference to check.
+     * @returns {boolean} True if `value` is a `String`.
+     */
+    function isString(value){return typeof value == 'string';}
+
+
+    /**
+     * @ngdoc function
+     * @name angular.isNumber
+     * @function
+     *
+     * @description
+     * Determines if a reference is a `Number`.
+     *
+     * @param {*} value Reference to check.
+     * @returns {boolean} True if `value` is a `Number`.
+     */
+    function isNumber(value){return typeof value == 'number';}
+
+
+    /**
+     * @ngdoc function
+     * @name angular.isDate
+     * @function
+     *
+     * @description
+     * Determines if a value is a date.
+     *
+     * @param {*} value Reference to check.
+     * @returns {boolean} True if `value` is a `Date`.
+     */
+    function isDate(value){
+        return toString.apply(value) == '[object Date]';
+    }
+
+
+    /**
+     * @ngdoc function
+     * @name angular.isArray
+     * @function
+     *
+     * @description
+     * Determines if a reference is an `Array`.
+     *
+     * @param {*} value Reference to check.
+     * @returns {boolean} True if `value` is an `Array`.
+     */
+    function isArray(value) {
+        return toString.apply(value) == '[object Array]';
+    }
+
+
+    /**
+     * @ngdoc function
+     * @name angular.isFunction
+     * @function
+     *
+     * @description
+     * Determines if a reference is a `Function`.
+     *
+     * @param {*} value Reference to check.
+     * @returns {boolean} True if `value` is a `Function`.
+     */
+    function isFunction(value){return typeof value == 'function';}
+
+
+    /**
+     * Determines if a value is a regular expression object.
+     *
+     * @private
+     * @param {*} value Reference to check.
+     * @returns {boolean} True if `value` is a `RegExp`.
+     */
+    function isRegExp(value) {
+        return toString.apply(value) == '[object RegExp]';
+    }
+
+
+    /**
+     * Checks if `obj` is a window object.
+     *
+     * @private
+     * @param {*} obj Object to check
+     * @returns {boolean} True if `obj` is a window obj.
+     */
+    function isWindow(obj) {
+        return obj && obj.document && obj.location && obj.alert && obj.setInterval;
+    }
+
+
+    function isScope(obj) {
+        return obj && obj.$evalAsync && obj.$watch;
+    }
+
+
+    function isFile(obj) {
+        return toString.apply(obj) === '[object File]';
+    }
+
+
+    function isBoolean(value) {
+        return typeof value == 'boolean';
+    }
+
+
+    var trim = (function() {
+        // native trim is way faster: http://jsperf.com/angular-trim-test
+        // but IE doesn't have it... :-(
+        // TODO: we should move this into IE/ES5 polyfill
+        if (!String.prototype.trim) {
+            return function(value) {
+                return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
+            };
+        }
+        return function(value) {
+            return isString(value) ? value.trim() : value;
+        };
+    })();
+
+
+    /**
+     * @ngdoc function
+     * @name angular.isElement
+     * @function
+     *
+     * @description
+     * Determines if a reference is a DOM element (or wrapped jQuery element).
+     *
+     * @param {*} value Reference to check.
+     * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
+     */
+    function isElement(node) {
+        return node &&
+                (node.nodeName  // we are a direct element
+                        || (node.on && node.find));  // we have an on and find method part of jQuery API
+    }
+
+    /**
+     * @param str 'key1,key2,...'
+     * @returns {object} in the form of {key1:true, key2:true, ...}
+     */
+    function makeMap(str){
+        var obj = {}, items = str.split(","), i;
+        for ( i = 0; i < items.length; i++ )
+            obj[ items[i] ] = true;
+        return obj;
+    }
+
+
+    if (msie < 9) {
+        nodeName_ = function(element) {
+            element = element.nodeName ? element : element[0];
+            return (element.scopeName && element.scopeName != 'HTML')
+                    ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName;
+        };
+    } else {
+        nodeName_ = function(element) {
+            return element.nodeName ? element.nodeName : element[0].nodeName;
+        };
+    }
+
+
+    function map(obj, iterator, context) {
+        var results = [];
+        forEach(obj, function(value, index, list) {
+            results.push(iterator.call(context, value, index, list));
+        });
+        return results;
+    }
+
+
+    /**
+     * @description
+     * Determines the number of elements in an array, the number of properties an object has, or
+     * the length of a string.
+     *
+     * Note: This function is used to augment the Object type in Angular expressions. See
+     * {@link angular.Object} for more information about Angular arrays.
+     *
+     * @param {Object|Array|string} obj Object, array, or string to inspect.
+     * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object
+     * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array.
+     */
+    function size(obj, ownPropsOnly) {
+        var count = 0, key;
+
+        if (isArray(obj) || isString(obj)) {
+            return obj.length;
+        } else if (isObject(obj)){
+            for (key in obj)
+                if (!ownPropsOnly || obj.hasOwnProperty(key))
+                    count++;
+        }
+
+        return count;
+    }
+
+
+    function includes(array, obj) {
+        return indexOf(array, obj) != -1;
+    }
+
+    function indexOf(array, obj) {
+        if (array.indexOf) return array.indexOf(obj);
+
+        for ( var i = 0; i < array.length; i++) {
+            if (obj === array[i]) return i;
+        }
+        return -1;
+    }
+
+    function arrayRemove(array, value) {
+        var index = indexOf(array, value);
+        if (index >=0)
+            array.splice(index, 1);
+        return value;
+    }
+
+    function isLeafNode (node) {
+        if (node) {
+            switch (node.nodeName) {
+                case "OPTION":
+                case "PRE":
+                case "TITLE":
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @ngdoc function
+     * @name angular.copy
+     * @function
+     *
+     * @description
+     * Creates a deep copy of `source`, which should be an object or an array.
+     *
+     * * If no destination is supplied, a copy of the object or array is created.
+     * * If a destination is provided, all of its elements (for array) or properties (for objects)
+     *   are deleted and then all elements/properties from the source are copied to it.
+     * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
+     * * If `source` is identical to 'destination' an exception will be thrown.
+     *
+     * @param {*} source The source that will be used to make a copy.
+     *                   Can be any type, including primitives, `null`, and `undefined`.
+     * @param {(Object|Array)=} destination Destination into which the source is copied. If
+     *     provided, must be of the same type as `source`.
+     * @returns {*} The copy or updated `destination`, if `destination` was specified.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <div ng-controller="Controller">
+     <form novalidate class="simple-form">
+     Name: <input type="text" ng-model="user.name" /><br />
+     E-mail: <input type="email" ng-model="user.email" /><br />
+     Gender: <input type="radio" ng-model="user.gender" value="male" />male
+     <input type="radio" ng-model="user.gender" value="female" />female<br />
+     <button ng-click="reset()">RESET</button>
+     <button ng-click="update(user)">SAVE</button>
+     </form>
+     <pre>form = {{user | json}}</pre>
+     <pre>master = {{master | json}}</pre>
+     </div>
+
+     <script>
+     function Controller($scope) {
+    $scope.master= {};
+
+    $scope.update = function(user) {
+      // Example with 1 argument
+      $scope.master= angular.copy(user);
+    };
+
+    $scope.reset = function() {
+      // Example with 2 arguments
+      angular.copy($scope.master, $scope.user);
+    };
+
+    $scope.reset();
+  }
+     </script>
+     </doc:source>
+     </doc:example>
+     */
+    function copy(source, destination){
+        if (isWindow(source) || isScope(source)) {
+            throw ngMinErr('cpws',
+                    "Can't copy! Making copies of Window or Scope instances is not supported.");
+        }
+
+        if (!destination) {
+            destination = source;
+            if (source) {
+                if (isArray(source)) {
+                    destination = copy(source, []);
+                } else if (isDate(source)) {
+                    destination = new Date(source.getTime());
+                } else if (isRegExp(source)) {
+                    destination = new RegExp(source.source);
+                } else if (isObject(source)) {
+                    destination = copy(source, {});
+                }
+            }
+        } else {
+            if (source === destination) throw ngMinErr('cpi',
+                    "Can't copy! Source and destination are identical.");
+            if (isArray(source)) {
+                destination.length = 0;
+                for ( var i = 0; i < source.length; i++) {
+                    destination.push(copy(source[i]));
+                }
+            } else {
+                var h = destination.$$hashKey;
+                forEach(destination, function(value, key){
+                    delete destination[key];
+                });
+                for ( var key in source) {
+                    destination[key] = copy(source[key]);
+                }
+                setHashKey(destination,h);
+            }
+        }
+        return destination;
+    }
+
+    /**
+     * Create a shallow copy of an object
+     */
+    function shallowCopy(src, dst) {
+        dst = dst || {};
+
+        for(var key in src) {
+            // shallowCopy is only ever called by $compile nodeLinkFn, which has control over src
+            // so we don't need to worry hasOwnProperty here
+            if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') {
+                dst[key] = src[key];
+            }
+        }
+
+        return dst;
+    }
+
+
+    /**
+     * @ngdoc function
+     * @name angular.equals
+     * @function
+     *
+     * @description
+     * Determines if two objects or two values are equivalent. Supports value types, regular
+     * expressions, arrays and objects.
+     *
+     * Two objects or values are considered equivalent if at least one of the following is true:
+     *
+     * * Both objects or values pass `===` comparison.
+     * * Both objects or values are of the same type and all of their properties are equal by
+     *   comparing them with `angular.equals`.
+     * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
+     * * Both values represent the same regular expression (In JavasScript,
+     *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
+     *   representation matches).
+     *
+     * During a property comparison, properties of `function` type and properties with names
+     * that begin with `$` are ignored.
+     *
+     * Scope and DOMWindow objects are being compared only by identify (`===`).
+     *
+     * @param {*} o1 Object or value to compare.
+     * @param {*} o2 Object or value to compare.
+     * @returns {boolean} True if arguments are equal.
+     */
+    function equals(o1, o2) {
+        if (o1 === o2) return true;
+        if (o1 === null || o2 === null) return false;
+        if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
+        var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
+        if (t1 == t2) {
+            if (t1 == 'object') {
+                if (isArray(o1)) {
+                    if (!isArray(o2)) return false;
+                    if ((length = o1.length) == o2.length) {
+                        for(key=0; key<length; key++) {
+                            if (!equals(o1[key], o2[key])) return false;
+                        }
+                        return true;
+                    }
+                } else if (isDate(o1)) {
+                    return isDate(o2) && o1.getTime() == o2.getTime();
+                } else if (isRegExp(o1) && isRegExp(o2)) {
+                    return o1.toString() == o2.toString();
+                } else {
+                    if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false;
+                    keySet = {};
+                    for(key in o1) {
+                        if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
+                        if (!equals(o1[key], o2[key])) return false;
+                        keySet[key] = true;
+                    }
+                    for(key in o2) {
+                        if (!keySet.hasOwnProperty(key) &&
+                                key.charAt(0) !== '$' &&
+                                o2[key] !== undefined &&
+                                !isFunction(o2[key])) return false;
+                    }
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+
+    function csp() {
+        return (document.securityPolicy && document.securityPolicy.isActive) ||
+                (document.querySelector &&
+                        !!(document.querySelector('[ng-csp]') || document.querySelector('[data-ng-csp]')));
+    }
+
+
+    function concat(array1, array2, index) {
+        return array1.concat(slice.call(array2, index));
+    }
+
+    function sliceArgs(args, startIndex) {
+        return slice.call(args, startIndex || 0);
+    }
+
+
+    /* jshint -W101 */
+    /**
+     * @ngdoc function
+     * @name angular.bind
+     * @function
+     *
+     * @description
+     * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
+     * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
+     * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
+     * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
+     *
+     * @param {Object} self Context which `fn` should be evaluated in.
+     * @param {function()} fn Function to be bound.
+     * @param {...*} args Optional arguments to be prebound to the `fn` function call.
+     * @returns {function()} Function that wraps the `fn` with all the specified bindings.
+     */
+    /* jshint +W101 */
+    function bind(self, fn) {
+        var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
+        if (isFunction(fn) && !(fn instanceof RegExp)) {
+            return curryArgs.length
+                    ? function() {
+                return arguments.length
+                        ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0)))
+                        : fn.apply(self, curryArgs);
+            }
+                    : function() {
+                return arguments.length
+                        ? fn.apply(self, arguments)
+                        : fn.call(self);
+            };
+        } else {
+            // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
+            return fn;
+        }
+    }
+
+
+    function toJsonReplacer(key, value) {
+        var val = value;
+
+        if (typeof key === 'string' && key.charAt(0) === '$') {
+            val = undefined;
+        } else if (isWindow(value)) {
+            val = '$WINDOW';
+        } else if (value &&  document === value) {
+            val = '$DOCUMENT';
+        } else if (isScope(value)) {
+            val = '$SCOPE';
+        }
+
+        return val;
+    }
+
+
+    /**
+     * @ngdoc function
+     * @name angular.toJson
+     * @function
+     *
+     * @description
+     * Serializes input into a JSON-formatted string. Properties with leading $ characters will be
+     * stripped since angular uses this notation internally.
+     *
+     * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
+     * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
+     * @returns {string|undefined} JSON-ified string representing `obj`.
+     */
+    function toJson(obj, pretty) {
+        if (typeof obj === 'undefined') return undefined;
+        return JSON.stringify(obj, toJsonReplacer, pretty ? '  ' : null);
+    }
+
+
+    /**
+     * @ngdoc function
+     * @name angular.fromJson
+     * @function
+     *
+     * @description
+     * Deserializes a JSON string.
+     *
+     * @param {string} json JSON string to deserialize.
+     * @returns {Object|Array|Date|string|number} Deserialized thingy.
+     */
+    function fromJson(json) {
+        return isString(json)
+                ? JSON.parse(json)
+                : json;
+    }
+
+
+    function toBoolean(value) {
+        if (value && value.length !== 0) {
+            var v = lowercase("" + value);
+            value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
+        } else {
+            value = false;
+        }
+        return value;
+    }
+
+    /**
+     * @returns {string} Returns the string representation of the element.
+     */
+    function startingTag(element) {
+        element = jqLite(element).clone();
+        try {
+            // turns out IE does not let you set .html() on elements which
+            // are not allowed to have children. So we just ignore it.
+            element.html('');
+        } catch(e) {}
+        // As Per DOM Standards
+        var TEXT_NODE = 3;
+        var elemHtml = jqLite('<div>').append(element).html();
+        try {
+            return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
+                    elemHtml.
+                            match(/^(<[^>]+>)/)[1].
+                            replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
+        } catch(e) {
+            return lowercase(elemHtml);
+        }
+
+    }
+
+
+/////////////////////////////////////////////////
+
+    /**
+     * Tries to decode the URI component without throwing an exception.
+     *
+     * @private
+     * @param str value potential URI component to check.
+     * @returns {boolean} True if `value` can be decoded
+     * with the decodeURIComponent function.
+     */
+    function tryDecodeURIComponent(value) {
+        try {
+            return decodeURIComponent(value);
+        } catch(e) {
+            // Ignore any invalid uri component
+        }
+    }
+
+
+    /**
+     * Parses an escaped url query string into key-value pairs.
+     * @returns Object.<(string|boolean)>
+     */
+    function parseKeyValue(/**string*/keyValue) {
+        var obj = {}, key_value, key;
+        forEach((keyValue || "").split('&'), function(keyValue){
+            if ( keyValue ) {
+                key_value = keyValue.split('=');
+                key = tryDecodeURIComponent(key_value[0]);
+                if ( isDefined(key) ) {
+                    var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
+                    if (!obj[key]) {
+                        obj[key] = val;
+                    } else if(isArray(obj[key])) {
+                        obj[key].push(val);
+                    } else {
+                        obj[key] = [obj[key],val];
+                    }
+                }
+            }
+        });
+        return obj;
+    }
+
+    function toKeyValue(obj) {
+        var parts = [];
+        forEach(obj, function(value, key) {
+            if (isArray(value)) {
+                forEach(value, function(arrayValue) {
+                    parts.push(encodeUriQuery(key, true) +
+                            (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
+                });
+            } else {
+                parts.push(encodeUriQuery(key, true) +
+                        (value === true ? '' : '=' + encodeUriQuery(value, true)));
+            }
+        });
+        return parts.length ? parts.join('&') : '';
+    }
+
+
+    /**
+     * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
+     * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
+     * segments:
+     *    segment       = *pchar
+     *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
+     *    pct-encoded   = "%" HEXDIG HEXDIG
+     *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
+     *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
+     *                     / "*" / "+" / "," / ";" / "="
+     */
+    function encodeUriSegment(val) {
+        return encodeUriQuery(val, true).
+                replace(/%26/gi, '&').
+                replace(/%3D/gi, '=').
+                replace(/%2B/gi, '+');
+    }
+
+
+    /**
+     * This method is intended for encoding *key* or *value* parts of query component. We need a custom
+     * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
+     * encoded per http://tools.ietf.org/html/rfc3986:
+     *    query       = *( pchar / "/" / "?" )
+     *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
+     *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
+     *    pct-encoded   = "%" HEXDIG HEXDIG
+     *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
+     *                     / "*" / "+" / "," / ";" / "="
+     */
+    function encodeUriQuery(val, pctEncodeSpaces) {
+        return encodeURIComponent(val).
+                replace(/%40/gi, '@').
+                replace(/%3A/gi, ':').
+                replace(/%24/g, '$').
+                replace(/%2C/gi, ',').
+                replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
+    }
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngApp
+     *
+     * @element ANY
+     * @param {angular.Module} ngApp an optional application
+     *   {@link angular.module module} name to load.
+     *
+     * @description
+     *
+     * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
+     * designates the **root element** of the application and is typically placed near the root element
+     * of the page - e.g. on the `<body>` or `<html>` tags.
+     *
+     * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
+     * found in the document will be used to define the root element to auto-bootstrap as an
+     * application. To run multiple applications in an HTML document you must manually bootstrap them using
+     * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
+     *
+     * You can specify an **AngularJS module** to be used as the root module for the application.  This
+     * module will be loaded into the {@link AUTO.$injector} when the application is bootstrapped and
+     * should contain the application code needed or have dependencies on other modules that will
+     * contain the code. See {@link angular.module} for more information.
+     *
+     * In the example below if the `ngApp` directive were not placed on the `html` element then the
+     * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
+     * would not be resolved to `3`.
+     *
+     * `ngApp` is the easiest, and most common, way to bootstrap an application.
+     *
+     <example module="ngAppDemo">
+     <file name="index.html">
+     <div ng-controller="ngAppDemoController">
+     I can add: {{a}} + {{b}} =  {{ a+b }}
+     </file>
+     <file name="script.js">
+     angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
+     $scope.a = 1;
+     $scope.b = 2;
+   });
+     </file>
+     </example>
+     *
+     */
+    function angularInit(element, bootstrap) {
+        var elements = [element],
+                appElement,
+                module,
+                names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'],
+                NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;
+
+        function append(element) {
+            element && elements.push(element);
+        }
+
+        forEach(names, function(name) {
+            names[name] = true;
+            append(document.getElementById(name));
+            name = name.replace(':', '\\:');
+            if (element.querySelectorAll) {
+                forEach(element.querySelectorAll('.' + name), append);
+                forEach(element.querySelectorAll('.' + name + '\\:'), append);
+                forEach(element.querySelectorAll('[' + name + ']'), append);
+            }
+        });
+
+        forEach(elements, function(element) {
+            if (!appElement) {
+                var className = ' ' + element.className + ' ';
+                var match = NG_APP_CLASS_REGEXP.exec(className);
+                if (match) {
+                    appElement = element;
+                    module = (match[2] || '').replace(/\s+/g, ',');
+                } else {
+                    forEach(element.attributes, function(attr) {
+                        if (!appElement && names[attr.name]) {
+                            appElement = element;
+                            module = attr.value;
+                        }
+                    });
+                }
+            }
+        });
+        if (appElement) {
+            bootstrap(appElement, module ? [module] : []);
+        }
+    }
+
+    /**
+     * @ngdoc function
+     * @name angular.bootstrap
+     * @description
+     * Use this function to manually start up angular application.
+     *
+     * See: {@link guide/bootstrap Bootstrap}
+     *
+     * Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually.
+     * They must use {@link api/ng.directive:ngApp ngApp}.
+     *
+     * @param {Element} element DOM element which is the root of angular application.
+     * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
+     *     Each item in the array should be the name of a predefined module or a (DI annotated)
+     *     function that will be invoked by the injector as a run block.
+     *     See: {@link angular.module modules}
+     * @returns {AUTO.$injector} Returns the newly created injector for this app.
+     */
+    function bootstrap(element, modules) {
+        var doBootstrap = function() {
+            element = jqLite(element);
+
+            if (element.injector()) {
+                var tag = (element[0] === document) ? 'document' : startingTag(element);
+                throw ngMinErr('btstrpd', "App Already Bootstrapped with this Element '{0}'", tag);
+            }
+
+            modules = modules || [];
+            modules.unshift(['$provide', function($provide) {
+                $provide.value('$rootElement', element);
+            }]);
+            modules.unshift('ng');
+            var injector = createInjector(modules);
+            injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate',
+                function(scope, element, compile, injector, animate) {
+                    scope.$apply(function() {
+                        element.data('$injector', injector);
+                        compile(element)(scope);
+                    });
+                }]
+            );
+            return injector;
+        };
+
+        var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
+
+        if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
+            return doBootstrap();
+        }
+
+        window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
+        angular.resumeBootstrap = function(extraModules) {
+            forEach(extraModules, function(module) {
+                modules.push(module);
+            });
+            doBootstrap();
+        };
+    }
+
+    var SNAKE_CASE_REGEXP = /[A-Z]/g;
+    function snake_case(name, separator){
+        separator = separator || '_';
+        return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
+            return (pos ? separator : '') + letter.toLowerCase();
+        });
+    }
+
+    function bindJQuery() {
+        // bind to jQuery if present;
+        jQuery = window.jQuery;
+        // reset to jQuery or default to us.
+        if (jQuery) {
+            jqLite = jQuery;
+            extend(jQuery.fn, {
+                scope: JQLitePrototype.scope,
+                isolateScope: JQLitePrototype.isolateScope,
+                controller: JQLitePrototype.controller,
+                injector: JQLitePrototype.injector,
+                inheritedData: JQLitePrototype.inheritedData
+            });
+            // Method signature:
+            //     jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments)
+            jqLitePatchJQueryRemove('remove', true, true, false);
+            jqLitePatchJQueryRemove('empty', false, false, false);
+            jqLitePatchJQueryRemove('html', false, false, true);
+        } else {
+            jqLite = JQLite;
+        }
+        angular.element = jqLite;
+    }
+
+    /**
+     * throw error if the argument is falsy.
+     */
+    function assertArg(arg, name, reason) {
+        if (!arg) {
+            throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
+        }
+        return arg;
+    }
+
+    function assertArgFn(arg, name, acceptArrayAnnotation) {
+        if (acceptArrayAnnotation && isArray(arg)) {
+            arg = arg[arg.length - 1];
+        }
+
+        assertArg(isFunction(arg), name, 'not a function, got ' +
+                (arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
+        return arg;
+    }
+
+    /**
+     * throw error if the name given is hasOwnProperty
+     * @param  {String} name    the name to test
+     * @param  {String} context the context in which the name is used, such as module or directive
+     */
+    function assertNotHasOwnProperty(name, context) {
+        if (name === 'hasOwnProperty') {
+            throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
+        }
+    }
+
+    /**
+     * Return the value accessible from the object by path. Any undefined traversals are ignored
+     * @param {Object} obj starting object
+     * @param {string} path path to traverse
+     * @param {boolean=true} bindFnToScope
+     * @returns value as accessible by path
+     */
+//TODO(misko): this function needs to be removed
+    function getter(obj, path, bindFnToScope) {
+        if (!path) return obj;
+        var keys = path.split('.');
+        var key;
+        var lastInstance = obj;
+        var len = keys.length;
+
+        for (var i = 0; i < len; i++) {
+            key = keys[i];
+            if (obj) {
+                obj = (lastInstance = obj)[key];
+            }
+        }
+        if (!bindFnToScope && isFunction(obj)) {
+            return bind(lastInstance, obj);
+        }
+        return obj;
+    }
+
+    /**
+     * Return the siblings between `startNode` and `endNode`, inclusive
+     * @param {Object} object with `startNode` and `endNode` properties
+     * @returns jQlite object containing the elements
+     */
+    function getBlockElements(block) {
+        if (block.startNode === block.endNode) {
+            return jqLite(block.startNode);
+        }
+
+        var element = block.startNode;
+        var elements = [element];
+
+        do {
+            element = element.nextSibling;
+            if (!element) break;
+            elements.push(element);
+        } while (element !== block.endNode);
+
+        return jqLite(elements);
+    }
+
+    /**
+     * @ngdoc interface
+     * @name angular.Module
+     * @description
+     *
+     * Interface for configuring angular {@link angular.module modules}.
+     */
+
+    function setupModuleLoader(window) {
+
+        var $injectorMinErr = minErr('$injector');
+        var ngMinErr = minErr('ng');
+
+        function ensure(obj, name, factory) {
+            return obj[name] || (obj[name] = factory());
+        }
+
+        var angular = ensure(window, 'angular', Object);
+
+        // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
+        angular.$$minErr = angular.$$minErr || minErr;
+
+        return ensure(angular, 'module', function() {
+            /** @type {Object.<string, angular.Module>} */
+            var modules = {};
+
+            /**
+             * @ngdoc function
+             * @name angular.module
+             * @description
+             *
+             * The `angular.module` is a global place for creating, registering and retrieving Angular
+             * modules.
+             * All modules (angular core or 3rd party) that should be available to an application must be
+             * registered using this mechanism.
+             *
+             * When passed two or more arguments, a new module is created.  If passed only one argument, an
+             * existing module (the name passed as the first argument to `module`) is retrieved.
+             *
+             *
+             * # Module
+             *
+             * A module is a collection of services, directives, filters, and configuration information.
+             * `angular.module` is used to configure the {@link AUTO.$injector $injector}.
+             *
+             * <pre>
+             * // Create a new module
+             * var myModule = angular.module('myModule', []);
+             *
+             * // register a new service
+             * myModule.value('appName', 'MyCoolApp');
+             *
+             * // configure existing services inside initialization blocks.
+             * myModule.config(function($locationProvider) {
+     *   // Configure existing providers
+     *   $locationProvider.hashPrefix('!');
+     * });
+             * </pre>
+             *
+             * Then you can create an injector and load your modules like this:
+             *
+             * <pre>
+             * var injector = angular.injector(['ng', 'MyModule'])
+             * </pre>
+             *
+             * However it's more likely that you'll just use
+             * {@link ng.directive:ngApp ngApp} or
+             * {@link angular.bootstrap} to simplify this process for you.
+             *
+             * @param {!string} name The name of the module to create or retrieve.
+             * @param {Array.<string>=} requires If specified then new module is being created. If
+             *        unspecified then the the module is being retrieved for further configuration.
+             * @param {Function} configFn Optional configuration function for the module. Same as
+             *        {@link angular.Module#methods_config Module#config()}.
+             * @returns {module} new module with the {@link angular.Module} api.
+             */
+            return function module(name, requires, configFn) {
+                var assertNotHasOwnProperty = function(name, context) {
+                    if (name === 'hasOwnProperty') {
+                        throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
+                    }
+                };
+
+                assertNotHasOwnProperty(name, 'module');
+                if (requires && modules.hasOwnProperty(name)) {
+                    modules[name] = null;
+                }
+                return ensure(modules, name, function() {
+                    if (!requires) {
+                        throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
+                                "the module name or forgot to load it. If registering a module ensure that you " +
+                                "specify the dependencies as the second argument.", name);
+                    }
+
+                    /** @type {!Array.<Array.<*>>} */
+                    var invokeQueue = [];
+
+                    /** @type {!Array.<Function>} */
+                    var runBlocks = [];
+
+                    var config = invokeLater('$injector', 'invoke');
+
+                    /** @type {angular.Module} */
+                    var moduleInstance = {
+                        // Private state
+                        _invokeQueue: invokeQueue,
+                        _runBlocks: runBlocks,
+
+                        /**
+                         * @ngdoc property
+                         * @name angular.Module#requires
+                         * @propertyOf angular.Module
+                         * @returns {Array.<string>} List of module names which must be loaded before this module.
+                         * @description
+                         * Holds the list of modules which the injector will load before the current module is
+                         * loaded.
+                         */
+                        requires: requires,
+
+                        /**
+                         * @ngdoc property
+                         * @name angular.Module#name
+                         * @propertyOf angular.Module
+                         * @returns {string} Name of the module.
+                         * @description
+                         */
+                        name: name,
+
+
+                        /**
+                         * @ngdoc method
+                         * @name angular.Module#provider
+                         * @methodOf angular.Module
+                         * @param {string} name service name
+                         * @param {Function} providerType Construction function for creating new instance of the
+                         *                                service.
+                         * @description
+                         * See {@link AUTO.$provide#provider $provide.provider()}.
+                         */
+                        provider: invokeLater('$provide', 'provider'),
+
+                        /**
+                         * @ngdoc method
+                         * @name angular.Module#factory
+                         * @methodOf angular.Module
+                         * @param {string} name service name
+                         * @param {Function} providerFunction Function for creating new instance of the service.
+                         * @description
+                         * See {@link AUTO.$provide#factory $provide.factory()}.
+                         */
+                        factory: invokeLater('$provide', 'factory'),
+
+                        /**
+                         * @ngdoc method
+                         * @name angular.Module#service
+                         * @methodOf angular.Module
+                         * @param {string} name service name
+                         * @param {Function} constructor A constructor function that will be instantiated.
+                         * @description
+                         * See {@link AUTO.$provide#service $provide.service()}.
+                         */
+                        service: invokeLater('$provide', 'service'),
+
+                        /**
+                         * @ngdoc method
+                         * @name angular.Module#value
+                         * @methodOf angular.Module
+                         * @param {string} name service name
+                         * @param {*} object Service instance object.
+                         * @description
+                         * See {@link AUTO.$provide#value $provide.value()}.
+                         */
+                        value: invokeLater('$provide', 'value'),
+
+                        /**
+                         * @ngdoc method
+                         * @name angular.Module#constant
+                         * @methodOf angular.Module
+                         * @param {string} name constant name
+                         * @param {*} object Constant value.
+                         * @description
+                         * Because the constant are fixed, they get applied before other provide methods.
+                         * See {@link AUTO.$provide#constant $provide.constant()}.
+                         */
+                        constant: invokeLater('$provide', 'constant', 'unshift'),
+
+                        /**
+                         * @ngdoc method
+                         * @name angular.Module#animation
+                         * @methodOf angular.Module
+                         * @param {string} name animation name
+                         * @param {Function} animationFactory Factory function for creating new instance of an
+                         *                                    animation.
+                         * @description
+                         *
+                         * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
+                         *
+                         *
+                         * Defines an animation hook that can be later used with
+                         * {@link ngAnimate.$animate $animate} service and directives that use this service.
+                         *
+                         * <pre>
+                         * module.animation('.animation-name', function($inject1, $inject2) {
+           *   return {
+           *     eventName : function(element, done) {
+           *       //code to run the animation
+           *       //once complete, then run done()
+           *       return function cancellationFunction(element) {
+           *         //code to cancel the animation
+           *       }
+           *     }
+           *   }
+           * })
+                         * </pre>
+                         *
+                         * See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and
+                         * {@link ngAnimate ngAnimate module} for more information.
+                         */
+                        animation: invokeLater('$animateProvider', 'register'),
+
+                        /**
+                         * @ngdoc method
+                         * @name angular.Module#filter
+                         * @methodOf angular.Module
+                         * @param {string} name Filter name.
+                         * @param {Function} filterFactory Factory function for creating new instance of filter.
+                         * @description
+                         * See {@link ng.$filterProvider#register $filterProvider.register()}.
+                         */
+                        filter: invokeLater('$filterProvider', 'register'),
+
+                        /**
+                         * @ngdoc method
+                         * @name angular.Module#controller
+                         * @methodOf angular.Module
+                         * @param {string|Object} name Controller name, or an object map of controllers where the
+                         *    keys are the names and the values are the constructors.
+                         * @param {Function} constructor Controller constructor function.
+                         * @description
+                         * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
+                         */
+                        controller: invokeLater('$controllerProvider', 'register'),
+
+                        /**
+                         * @ngdoc method
+                         * @name angular.Module#directive
+                         * @methodOf angular.Module
+                         * @param {string|Object} name Directive name, or an object map of directives where the
+                         *    keys are the names and the values are the factories.
+                         * @param {Function} directiveFactory Factory function for creating new instance of
+                         * directives.
+                         * @description
+                         * See {@link ng.$compileProvider#methods_directive $compileProvider.directive()}.
+                         */
+                        directive: invokeLater('$compileProvider', 'directive'),
+
+                        /**
+                         * @ngdoc method
+                         * @name angular.Module#config
+                         * @methodOf angular.Module
+                         * @param {Function} configFn Execute this function on module load. Useful for service
+                         *    configuration.
+                         * @description
+                         * Use this method to register work which needs to be performed on module loading.
+                         */
+                        config: config,
+
+                        /**
+                         * @ngdoc method
+                         * @name angular.Module#run
+                         * @methodOf angular.Module
+                         * @param {Function} initializationFn Execute this function after injector creation.
+                         *    Useful for application initialization.
+                         * @description
+                         * Use this method to register work which should be performed when the injector is done
+                         * loading all modules.
+                         */
+                        run: function(block) {
+                            runBlocks.push(block);
+                            return this;
+                        }
+                    };
+
+                    if (configFn) {
+                        config(configFn);
+                    }
+
+                    return  moduleInstance;
+
+                    /**
+                     * @param {string} provider
+                     * @param {string} method
+                     * @param {String=} insertMethod
+                     * @returns {angular.Module}
+                     */
+                    function invokeLater(provider, method, insertMethod) {
+                        return function() {
+                            invokeQueue[insertMethod || 'push']([provider, method, arguments]);
+                            return moduleInstance;
+                        };
+                    }
+                });
+            };
+        });
+
+    }
+
+    /* global
+     angularModule: true,
+     version: true,
+
+     $LocaleProvider,
+     $CompileProvider,
+
+     htmlAnchorDirective,
+     inputDirective,
+     inputDirective,
+     formDirective,
+     scriptDirective,
+     selectDirective,
+     styleDirective,
+     optionDirective,
+     ngBindDirective,
+     ngBindHtmlDirective,
+     ngBindTemplateDirective,
+     ngClassDirective,
+     ngClassEvenDirective,
+     ngClassOddDirective,
+     ngCspDirective,
+     ngCloakDirective,
+     ngControllerDirective,
+     ngFormDirective,
+     ngHideDirective,
+     ngIfDirective,
+     ngIncludeDirective,
+     ngInitDirective,
+     ngNonBindableDirective,
+     ngPluralizeDirective,
+     ngRepeatDirective,
+     ngShowDirective,
+     ngStyleDirective,
+     ngSwitchDirective,
+     ngSwitchWhenDirective,
+     ngSwitchDefaultDirective,
+     ngOptionsDirective,
+     ngTranscludeDirective,
+     ngModelDirective,
+     ngListDirective,
+     ngChangeDirective,
+     requiredDirective,
+     requiredDirective,
+     ngValueDirective,
+     ngAttributeAliasDirectives,
+     ngEventDirectives,
+
+     $AnchorScrollProvider,
+     $AnimateProvider,
+     $BrowserProvider,
+     $CacheFactoryProvider,
+     $ControllerProvider,
+     $DocumentProvider,
+     $ExceptionHandlerProvider,
+     $FilterProvider,
+     $InterpolateProvider,
+     $IntervalProvider,
+     $HttpProvider,
+     $HttpBackendProvider,
+     $LocationProvider,
+     $LogProvider,
+     $ParseProvider,
+     $RootScopeProvider,
+     $QProvider,
+     $SceProvider,
+     $SceDelegateProvider,
+     $SnifferProvider,
+     $TemplateCacheProvider,
+     $TimeoutProvider,
+     $WindowProvider
+     */
+
+
+    /**
+     * @ngdoc property
+     * @name angular.version
+     * @description
+     * An object that contains information about the current AngularJS version. This object has the
+     * following properties:
+     *
+     * - `full` – `{string}` – Full version string, such as "0.9.18".
+     * - `major` – `{number}` – Major version number, such as "0".
+     * - `minor` – `{number}` – Minor version number, such as "9".
+     * - `dot` – `{number}` – Dot version number, such as "18".
+     * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
+     */
+    var version = {
+        full: '1.2.2',    // all of these placeholder strings will be replaced by grunt's
+        major: 1,    // package task
+        minor: 2,
+        dot: 2,
+        codeName: 'consciousness-inertia'
+    };
+
+
+    function publishExternalAPI(angular){
+        extend(angular, {
+            'bootstrap': bootstrap,
+            'copy': copy,
+            'extend': extend,
+            'equals': equals,
+            'element': jqLite,
+            'forEach': forEach,
+            'injector': createInjector,
+            'noop':noop,
+            'bind':bind,
+            'toJson': toJson,
+            'fromJson': fromJson,
+            'identity':identity,
+            'isUndefined': isUndefined,
+            'isDefined': isDefined,
+            'isString': isString,
+            'isFunction': isFunction,
+            'isObject': isObject,
+            'isNumber': isNumber,
+            'isElement': isElement,
+            'isArray': isArray,
+            'version': version,
+            'isDate': isDate,
+            'lowercase': lowercase,
+            'uppercase': uppercase,
+            'callbacks': {counter: 0},
+            '$$minErr': minErr,
+            '$$csp': csp
+        });
+
+        angularModule = setupModuleLoader(window);
+        try {
+            angularModule('ngLocale');
+        } catch (e) {
+            angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
+        }
+
+        angularModule('ng', ['ngLocale'], ['$provide',
+            function ngModule($provide) {
+                $provide.provider('$compile', $CompileProvider).
+                        directive({
+                            a: htmlAnchorDirective,
+                            input: inputDirective,
+                            textarea: inputDirective,
+                            form: formDirective,
+                            script: scriptDirective,
+                            select: selectDirective,
+                            style: styleDirective,
+                            option: optionDirective,
+                            ngBind: ngBindDirective,
+                            ngBindHtml: ngBindHtmlDirective,
+                            ngBindTemplate: ngBindTemplateDirective,
+                            ngClass: ngClassDirective,
+                            ngClassEven: ngClassEvenDirective,
+                            ngClassOdd: ngClassOddDirective,
+                            ngCloak: ngCloakDirective,
+                            ngController: ngControllerDirective,
+                            ngForm: ngFormDirective,
+                            ngHide: ngHideDirective,
+                            ngIf: ngIfDirective,
+                            ngInclude: ngIncludeDirective,
+                            ngInit: ngInitDirective,
+                            ngNonBindable: ngNonBindableDirective,
+                            ngPluralize: ngPluralizeDirective,
+                            ngRepeat: ngRepeatDirective,
+                            ngShow: ngShowDirective,
+                            ngStyle: ngStyleDirective,
+                            ngSwitch: ngSwitchDirective,
+                            ngSwitchWhen: ngSwitchWhenDirective,
+                            ngSwitchDefault: ngSwitchDefaultDirective,
+                            ngOptions: ngOptionsDirective,
+                            ngTransclude: ngTranscludeDirective,
+                            ngModel: ngModelDirective,
+                            ngList: ngListDirective,
+                            ngChange: ngChangeDirective,
+                            required: requiredDirective,
+                            ngRequired: requiredDirective,
+                            ngValue: ngValueDirective
+                        }).
+                        directive(ngAttributeAliasDirectives).
+                        directive(ngEventDirectives);
+                $provide.provider({
+                    $anchorScroll: $AnchorScrollProvider,
+                    $animate: $AnimateProvider,
+                    $browser: $BrowserProvider,
+                    $cacheFactory: $CacheFactoryProvider,
+                    $controller: $ControllerProvider,
+                    $document: $DocumentProvider,
+                    $exceptionHandler: $ExceptionHandlerProvider,
+                    $filter: $FilterProvider,
+                    $interpolate: $InterpolateProvider,
+                    $interval: $IntervalProvider,
+                    $http: $HttpProvider,
+                    $httpBackend: $HttpBackendProvider,
+                    $location: $LocationProvider,
+                    $log: $LogProvider,
+                    $parse: $ParseProvider,
+                    $rootScope: $RootScopeProvider,
+                    $q: $QProvider,
+                    $sce: $SceProvider,
+                    $sceDelegate: $SceDelegateProvider,
+                    $sniffer: $SnifferProvider,
+                    $templateCache: $TemplateCacheProvider,
+                    $timeout: $TimeoutProvider,
+                    $window: $WindowProvider
+                });
+            }
+        ]);
+    }
+
+    /* global
+
+     -JQLitePrototype,
+     -addEventListenerFn,
+     -removeEventListenerFn,
+     -BOOLEAN_ATTR
+     */
+
+//////////////////////////////////
+//JQLite
+//////////////////////////////////
+
+    /**
+     * @ngdoc function
+     * @name angular.element
+     * @function
+     *
+     * @description
+     * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
+     *
+     * If jQuery is available, `angular.element` is an alias for the
+     * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
+     * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
+     *
+     * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
+     * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
+     * commonly needed functionality with the goal of having a very small footprint.</div>
+     *
+     * To use jQuery, simply load it before `DOMContentLoaded` event fired.
+     *
+     * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
+     * jqLite; they are never raw DOM references.</div>
+     *
+     * ## Angular's jqLite
+     * jqLite provides only the following jQuery methods:
+     *
+     * - [`addClass()`](http://api.jquery.com/addClass/)
+     * - [`after()`](http://api.jquery.com/after/)
+     * - [`append()`](http://api.jquery.com/append/)
+     * - [`attr()`](http://api.jquery.com/attr/)
+     * - [`bind()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
+     * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
+     * - [`clone()`](http://api.jquery.com/clone/)
+     * - [`contents()`](http://api.jquery.com/contents/)
+     * - [`css()`](http://api.jquery.com/css/)
+     * - [`data()`](http://api.jquery.com/data/)
+     * - [`eq()`](http://api.jquery.com/eq/)
+     * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
+     * - [`hasClass()`](http://api.jquery.com/hasClass/)
+     * - [`html()`](http://api.jquery.com/html/)
+     * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
+     * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
+     * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
+     * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
+     * - [`prepend()`](http://api.jquery.com/prepend/)
+     * - [`prop()`](http://api.jquery.com/prop/)
+     * - [`ready()`](http://api.jquery.com/ready/)
+     * - [`remove()`](http://api.jquery.com/remove/)
+     * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
+     * - [`removeClass()`](http://api.jquery.com/removeClass/)
+     * - [`removeData()`](http://api.jquery.com/removeData/)
+     * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
+     * - [`text()`](http://api.jquery.com/text/)
+     * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
+     * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
+     * - [`unbind()`](http://api.jquery.com/off/) - Does not support namespaces
+     * - [`val()`](http://api.jquery.com/val/)
+     * - [`wrap()`](http://api.jquery.com/wrap/)
+     *
+     * ## jQuery/jqLite Extras
+     * Angular also provides the following additional methods and events to both jQuery and jqLite:
+     *
+     * ### Events
+     * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
+     *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
+     *    element before it is removed.
+     *
+     * ### Methods
+     * - `controller(name)` - retrieves the controller of the current element or its parent. By default
+     *   retrieves controller associated with the `ngController` directive. If `name` is provided as
+     *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
+     *   `'ngModel'`).
+     * - `injector()` - retrieves the injector of the current element or its parent.
+     * - `scope()` - retrieves the {@link api/ng.$rootScope.Scope scope} of the current
+     *   element or its parent.
+     * - `isolateScope()` - retrieves an isolate {@link api/ng.$rootScope.Scope scope} if one is attached directly to the
+     *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
+     *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
+     * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
+     *   parent element is reached.
+     *
+     * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
+     * @returns {Object} jQuery object.
+     */
+
+    var jqCache = JQLite.cache = {},
+            jqName = JQLite.expando = 'ng-' + new Date().getTime(),
+            jqId = 1,
+            addEventListenerFn = (window.document.addEventListener
+                    ? function(element, type, fn) {element.addEventListener(type, fn, false);}
+                    : function(element, type, fn) {element.attachEvent('on' + type, fn);}),
+            removeEventListenerFn = (window.document.removeEventListener
+                    ? function(element, type, fn) {element.removeEventListener(type, fn, false); }
+                    : function(element, type, fn) {element.detachEvent('on' + type, fn); });
+
+    function jqNextId() { return ++jqId; }
+
+
+    var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
+    var MOZ_HACK_REGEXP = /^moz([A-Z])/;
+    var jqLiteMinErr = minErr('jqLite');
+
+    /**
+     * Converts snake_case to camelCase.
+     * Also there is special case for Moz prefix starting with upper case letter.
+     * @param name Name to normalize
+     */
+    function camelCase(name) {
+        return name.
+                replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
+                    return offset ? letter.toUpperCase() : letter;
+                }).
+                replace(MOZ_HACK_REGEXP, 'Moz$1');
+    }
+
+/////////////////////////////////////////////
+// jQuery mutation patch
+//
+// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
+// $destroy event on all DOM nodes being removed.
+//
+/////////////////////////////////////////////
+
+    function jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) {
+        var originalJqFn = jQuery.fn[name];
+        originalJqFn = originalJqFn.$original || originalJqFn;
+        removePatch.$original = originalJqFn;
+        jQuery.fn[name] = removePatch;
+
+        function removePatch(param) {
+            // jshint -W040
+            var list = filterElems && param ? [this.filter(param)] : [this],
+                    fireEvent = dispatchThis,
+                    set, setIndex, setLength,
+                    element, childIndex, childLength, children;
+
+            if (!getterIfNoArguments || param != null) {
+                while(list.length) {
+                    set = list.shift();
+                    for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
+                        element = jqLite(set[setIndex]);
+                        if (fireEvent) {
+                            element.triggerHandler('$destroy');
+                        } else {
+                            fireEvent = !fireEvent;
+                        }
+                        for(childIndex = 0, childLength = (children = element.children()).length;
+                            childIndex < childLength;
+                            childIndex++) {
+                            list.push(jQuery(children[childIndex]));
+                        }
+                    }
+                }
+            }
+            return originalJqFn.apply(this, arguments);
+        }
+    }
+
+/////////////////////////////////////////////
+    function JQLite(element) {
+        if (element instanceof JQLite) {
+            return element;
+        }
+        if (!(this instanceof JQLite)) {
+            if (isString(element) && element.charAt(0) != '<') {
+                throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
+            }
+            return new JQLite(element);
+        }
+
+        if (isString(element)) {
+            var div = document.createElement('div');
+            // Read about the NoScope elements here:
+            // http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx
+            div.innerHTML = '<div>&#160;</div>' + element; // IE insanity to make NoScope elements work!
+            div.removeChild(div.firstChild); // remove the superfluous div
+            jqLiteAddNodes(this, div.childNodes);
+            var fragment = jqLite(document.createDocumentFragment());
+            fragment.append(this); // detach the elements from the temporary DOM div.
+        } else {
+            jqLiteAddNodes(this, element);
+        }
+    }
+
+    function jqLiteClone(element) {
+        return element.cloneNode(true);
+    }
+
+    function jqLiteDealoc(element){
+        jqLiteRemoveData(element);
+        for ( var i = 0, children = element.childNodes || []; i < children.length; i++) {
+            jqLiteDealoc(children[i]);
+        }
+    }
+
+    function jqLiteOff(element, type, fn, unsupported) {
+        if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
+
+        var events = jqLiteExpandoStore(element, 'events'),
+                handle = jqLiteExpandoStore(element, 'handle');
+
+        if (!handle) return; //no listeners registered
+
+        if (isUndefined(type)) {
+            forEach(events, function(eventHandler, type) {
+                removeEventListenerFn(element, type, eventHandler);
+                delete events[type];
+            });
+        } else {
+            forEach(type.split(' '), function(type) {
+                if (isUndefined(fn)) {
+                    removeEventListenerFn(element, type, events[type]);
+                    delete events[type];
+                } else {
+                    arrayRemove(events[type] || [], fn);
+                }
+            });
+        }
+    }
+
+    function jqLiteRemoveData(element, name) {
+        var expandoId = element[jqName],
+                expandoStore = jqCache[expandoId];
+
+        if (expandoStore) {
+            if (name) {
+                delete jqCache[expandoId].data[name];
+                return;
+            }
+
+            if (expandoStore.handle) {
+                expandoStore.events.$destroy && expandoStore.handle({}, '$destroy');
+                jqLiteOff(element);
+            }
+            delete jqCache[expandoId];
+            element[jqName] = undefined; // ie does not allow deletion of attributes on elements.
+        }
+    }
+
+    function jqLiteExpandoStore(element, key, value) {
+        var expandoId = element[jqName],
+                expandoStore = jqCache[expandoId || -1];
+
+        if (isDefined(value)) {
+            if (!expandoStore) {
+                element[jqName] = expandoId = jqNextId();
+                expandoStore = jqCache[expandoId] = {};
+            }
+            expandoStore[key] = value;
+        } else {
+            return expandoStore && expandoStore[key];
+        }
+    }
+
+    function jqLiteData(element, key, value) {
+        var data = jqLiteExpandoStore(element, 'data'),
+                isSetter = isDefined(value),
+                keyDefined = !isSetter && isDefined(key),
+                isSimpleGetter = keyDefined && !isObject(key);
+
+        if (!data && !isSimpleGetter) {
+            jqLiteExpandoStore(element, 'data', data = {});
+        }
+
+        if (isSetter) {
+            data[key] = value;
+        } else {
+            if (keyDefined) {
+                if (isSimpleGetter) {
+                    // don't create data in this case.
+                    return data && data[key];
+                } else {
+                    extend(data, key);
+                }
+            } else {
+                return data;
+            }
+        }
+    }
+
+    function jqLiteHasClass(element, selector) {
+        if (!element.getAttribute) return false;
+        return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
+                indexOf( " " + selector + " " ) > -1);
+    }
+
+    function jqLiteRemoveClass(element, cssClasses) {
+        if (cssClasses && element.setAttribute) {
+            forEach(cssClasses.split(' '), function(cssClass) {
+                element.setAttribute('class', trim(
+                        (" " + (element.getAttribute('class') || '') + " ")
+                                .replace(/[\n\t]/g, " ")
+                                .replace(" " + trim(cssClass) + " ", " "))
+                );
+            });
+        }
+    }
+
+    function jqLiteAddClass(element, cssClasses) {
+        if (cssClasses && element.setAttribute) {
+            var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
+                    .replace(/[\n\t]/g, " ");
+
+            forEach(cssClasses.split(' '), function(cssClass) {
+                cssClass = trim(cssClass);
+                if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
+                    existingClasses += cssClass + ' ';
+                }
+            });
+
+            element.setAttribute('class', trim(existingClasses));
+        }
+    }
+
+    function jqLiteAddNodes(root, elements) {
+        if (elements) {
+            elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements))
+                    ? elements
+                    : [ elements ];
+            for(var i=0; i < elements.length; i++) {
+                root.push(elements[i]);
+            }
+        }
+    }
+
+    function jqLiteController(element, name) {
+        return jqLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller');
+    }
+
+    function jqLiteInheritedData(element, name, value) {
+        element = jqLite(element);
+
+        // if element is the document object work with the html element instead
+        // this makes $(document).scope() possible
+        if(element[0].nodeType == 9) {
+            element = element.find('html');
+        }
+        var names = isArray(name) ? name : [name];
+
+        while (element.length) {
+
+            for (var i = 0, ii = names.length; i < ii; i++) {
+                if ((value = element.data(names[i])) !== undefined) return value;
+            }
+            element = element.parent();
+        }
+    }
+
+//////////////////////////////////////////
+// Functions which are declared directly.
+//////////////////////////////////////////
+    var JQLitePrototype = JQLite.prototype = {
+        ready: function(fn) {
+            var fired = false;
+
+            function trigger() {
+                if (fired) return;
+                fired = true;
+                fn();
+            }
+
+            // check if document already is loaded
+            if (document.readyState === 'complete'){
+                setTimeout(trigger);
+            } else {
+                this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
+                // we can not use jqLite since we are not done loading and jQuery could be loaded later.
+                // jshint -W064
+                JQLite(window).on('load', trigger); // fallback to window.onload for others
+                // jshint +W064
+            }
+        },
+        toString: function() {
+            var value = [];
+            forEach(this, function(e){ value.push('' + e);});
+            return '[' + value.join(', ') + ']';
+        },
+
+        eq: function(index) {
+            return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
+        },
+
+        length: 0,
+        push: push,
+        sort: [].sort,
+        splice: [].splice
+    };
+
+//////////////////////////////////////////
+// Functions iterating getter/setters.
+// these functions return self on setter and
+// value on get.
+//////////////////////////////////////////
+    var BOOLEAN_ATTR = {};
+    forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
+        BOOLEAN_ATTR[lowercase(value)] = value;
+    });
+    var BOOLEAN_ELEMENTS = {};
+    forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
+        BOOLEAN_ELEMENTS[uppercase(value)] = true;
+    });
+
+    function getBooleanAttrName(element, name) {
+        // check dom last since we will most likely fail on name
+        var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
+
+        // booleanAttr is here twice to minimize DOM access
+        return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr;
+    }
+
+    forEach({
+        data: jqLiteData,
+        inheritedData: jqLiteInheritedData,
+
+        scope: function(element) {
+            // Can't use jqLiteData here directly so we stay compatible with jQuery!
+            return jqLite(element).data('$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
+        },
+
+        isolateScope: function(element) {
+            // Can't use jqLiteData here directly so we stay compatible with jQuery!
+            return jqLite(element).data('$isolateScope') || jqLite(element).data('$isolateScopeNoTemplate');
+        },
+
+        controller: jqLiteController ,
+
+        injector: function(element) {
+            return jqLiteInheritedData(element, '$injector');
+        },
+
+        removeAttr: function(element,name) {
+            element.removeAttribute(name);
+        },
+
+        hasClass: jqLiteHasClass,
+
+        css: function(element, name, value) {
+            name = camelCase(name);
+
+            if (isDefined(value)) {
+                element.style[name] = value;
+            } else {
+                var val;
+
+                if (msie <= 8) {
+                    // this is some IE specific weirdness that jQuery 1.6.4 does not sure why
+                    val = element.currentStyle && element.currentStyle[name];
+                    if (val === '') val = 'auto';
+                }
+
+                val = val || element.style[name];
+
+                if (msie <= 8) {
+                    // jquery weirdness :-/
+                    val = (val === '') ? undefined : val;
+                }
+
+                return  val;
+            }
+        },
+
+        attr: function(element, name, value){
+            var lowercasedName = lowercase(name);
+            if (BOOLEAN_ATTR[lowercasedName]) {
+                if (isDefined(value)) {
+                    if (!!value) {
+                        element[name] = true;
+                        element.setAttribute(name, lowercasedName);
+                    } else {
+                        element[name] = false;
+                        element.removeAttribute(lowercasedName);
+                    }
+                } else {
+                    return (element[name] ||
+                            (element.attributes.getNamedItem(name)|| noop).specified)
+                            ? lowercasedName
+                            : undefined;
+                }
+            } else if (isDefined(value)) {
+                element.setAttribute(name, value);
+            } else if (element.getAttribute) {
+                // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
+                // some elements (e.g. Document) don't have get attribute, so return undefined
+                var ret = element.getAttribute(name, 2);
+                // normalize non-existing attributes to undefined (as jQuery)
+                return ret === null ? undefined : ret;
+            }
+        },
+
+        prop: function(element, name, value) {
+            if (isDefined(value)) {
+                element[name] = value;
+            } else {
+                return element[name];
+            }
+        },
+
+        text: (function() {
+            var NODE_TYPE_TEXT_PROPERTY = [];
+            if (msie < 9) {
+                NODE_TYPE_TEXT_PROPERTY[1] = 'innerText';    /** Element **/
+                NODE_TYPE_TEXT_PROPERTY[3] = 'nodeValue';    /** Text **/
+            } else {
+                NODE_TYPE_TEXT_PROPERTY[1] =                 /** Element **/
+                        NODE_TYPE_TEXT_PROPERTY[3] = 'textContent';  /** Text **/
+            }
+            getText.$dv = '';
+            return getText;
+
+            function getText(element, value) {
+                var textProp = NODE_TYPE_TEXT_PROPERTY[element.nodeType];
+                if (isUndefined(value)) {
+                    return textProp ? element[textProp] : '';
+                }
+                element[textProp] = value;
+            }
+        })(),
+
+        val: function(element, value) {
+            if (isUndefined(value)) {
+                if (nodeName_(element) === 'SELECT' && element.multiple) {
+                    var result = [];
+                    forEach(element.options, function (option) {
+                        if (option.selected) {
+                            result.push(option.value || option.text);
+                        }
+                    });
+                    return result.length === 0 ? null : result;
+                }
+                return element.value;
+            }
+            element.value = value;
+        },
+
+        html: function(element, value) {
+            if (isUndefined(value)) {
+                return element.innerHTML;
+            }
+            for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) {
+                jqLiteDealoc(childNodes[i]);
+            }
+            element.innerHTML = value;
+        }
+    }, function(fn, name){
+        /**
+         * Properties: writes return selection, reads return first value
+         */
+        JQLite.prototype[name] = function(arg1, arg2) {
+            var i, key;
+
+            // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
+            // in a way that survives minification.
+            if (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined) {
+                if (isObject(arg1)) {
+
+                    // we are a write, but the object properties are the key/values
+                    for(i=0; i < this.length; i++) {
+                        if (fn === jqLiteData) {
+                            // data() takes the whole object in jQuery
+                            fn(this[i], arg1);
+                        } else {
+                            for (key in arg1) {
+                                fn(this[i], key, arg1[key]);
+                            }
+                        }
+                    }
+                    // return self for chaining
+                    return this;
+                } else {
+                    // we are a read, so read the first child.
+                    var value = fn.$dv;
+                    // Only if we have $dv do we iterate over all, otherwise it is just the first element.
+                    var jj = (value === undefined) ? Math.min(this.length, 1) : this.length;
+                    for (var j = 0; j < jj; j++) {
+                        var nodeValue = fn(this[j], arg1, arg2);
+                        value = value ? value + nodeValue : nodeValue;
+                    }
+                    return value;
+                }
+            } else {
+                // we are a write, so apply to all children
+                for(i=0; i < this.length; i++) {
+                    fn(this[i], arg1, arg2);
+                }
+                // return self for chaining
+                return this;
+            }
+        };
+    });
+
+    function createEventHandler(element, events) {
+        var eventHandler = function (event, type) {
+            if (!event.preventDefault) {
+                event.preventDefault = function() {
+                    event.returnValue = false; //ie
+                };
+            }
+
+            if (!event.stopPropagation) {
+                event.stopPropagation = function() {
+                    event.cancelBubble = true; //ie
+                };
+            }
+
+            if (!event.target) {
+                event.target = event.srcElement || document;
+            }
+
+            if (isUndefined(event.defaultPrevented)) {
+                var prevent = event.preventDefault;
+                event.preventDefault = function() {
+                    event.defaultPrevented = true;
+                    prevent.call(event);
+                };
+                event.defaultPrevented = false;
+            }
+
+            event.isDefaultPrevented = function() {
+                return event.defaultPrevented || event.returnValue === false;
+            };
+
+            forEach(events[type || event.type], function(fn) {
+                fn.call(element, event);
+            });
+
+            // Remove monkey-patched methods (IE),
+            // as they would cause memory leaks in IE8.
+            if (msie <= 8) {
+                // IE7/8 does not allow to delete property on native object
+                event.preventDefault = null;
+                event.stopPropagation = null;
+                event.isDefaultPrevented = null;
+            } else {
+                // It shouldn't affect normal browsers (native methods are defined on prototype).
+                delete event.preventDefault;
+                delete event.stopPropagation;
+                delete event.isDefaultPrevented;
+            }
+        };
+        eventHandler.elem = element;
+        return eventHandler;
+    }
+
+//////////////////////////////////////////
+// Functions iterating traversal.
+// These functions chain results into a single
+// selector.
+//////////////////////////////////////////
+    forEach({
+        removeData: jqLiteRemoveData,
+
+        dealoc: jqLiteDealoc,
+
+        on: function onFn(element, type, fn, unsupported){
+            if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
+
+            var events = jqLiteExpandoStore(element, 'events'),
+                    handle = jqLiteExpandoStore(element, 'handle');
+
+            if (!events) jqLiteExpandoStore(element, 'events', events = {});
+            if (!handle) jqLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events));
+
+            forEach(type.split(' '), function(type){
+                var eventFns = events[type];
+
+                if (!eventFns) {
+                    if (type == 'mouseenter' || type == 'mouseleave') {
+                        var contains = document.body.contains || document.body.compareDocumentPosition ?
+                                function( a, b ) {
+                                    // jshint bitwise: false
+                                    var adown = a.nodeType === 9 ? a.documentElement : a,
+                                            bup = b && b.parentNode;
+                                    return a === bup || !!( bup && bup.nodeType === 1 && (
+                                            adown.contains ?
+                                                    adown.contains( bup ) :
+                                                    a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+                                            ));
+                                } :
+                                function( a, b ) {
+                                    if ( b ) {
+                                        while ( (b = b.parentNode) ) {
+                                            if ( b === a ) {
+                                                return true;
+                                            }
+                                        }
+                                    }
+                                    return false;
+                                };
+
+                        events[type] = [];
+
+                        // Refer to jQuery's implementation of mouseenter & mouseleave
+                        // Read about mouseenter and mouseleave:
+                        // http://www.quirksmode.org/js/events_mouse.html#link8
+                        var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"};
+
+                        onFn(element, eventmap[type], function(event) {
+                            var target = this, related = event.relatedTarget;
+                            // For mousenter/leave call the handler if related is outside the target.
+                            // NB: No relatedTarget if the mouse left/entered the browser window
+                            if ( !related || (related !== target && !contains(target, related)) ){
+                                handle(event, type);
+                            }
+                        });
+
+                    } else {
+                        addEventListenerFn(element, type, handle);
+                        events[type] = [];
+                    }
+                    eventFns = events[type];
+                }
+                eventFns.push(fn);
+            });
+        },
+
+        off: jqLiteOff,
+
+        replaceWith: function(element, replaceNode) {
+            var index, parent = element.parentNode;
+            jqLiteDealoc(element);
+            forEach(new JQLite(replaceNode), function(node){
+                if (index) {
+                    parent.insertBefore(node, index.nextSibling);
+                } else {
+                    parent.replaceChild(node, element);
+                }
+                index = node;
+            });
+        },
+
+        children: function(element) {
+            var children = [];
+            forEach(element.childNodes, function(element){
+                if (element.nodeType === 1)
+                    children.push(element);
+            });
+            return children;
+        },
+
+        contents: function(element) {
+            return element.childNodes || [];
+        },
+
+        append: function(element, node) {
+            forEach(new JQLite(node), function(child){
+                if (element.nodeType === 1 || element.nodeType === 11) {
+                    element.appendChild(child);
+                }
+            });
+        },
+
+        prepend: function(element, node) {
+            if (element.nodeType === 1) {
+                var index = element.firstChild;
+                forEach(new JQLite(node), function(child){
+                    element.insertBefore(child, index);
+                });
+            }
+        },
+
+        wrap: function(element, wrapNode) {
+            wrapNode = jqLite(wrapNode)[0];
+            var parent = element.parentNode;
+            if (parent) {
+                parent.replaceChild(wrapNode, element);
+            }
+            wrapNode.appendChild(element);
+        },
+
+        remove: function(element) {
+            jqLiteDealoc(element);
+            var parent = element.parentNode;
+            if (parent) parent.removeChild(element);
+        },
+
+        after: function(element, newElement) {
+            var index = element, parent = element.parentNode;
+            forEach(new JQLite(newElement), function(node){
+                parent.insertBefore(node, index.nextSibling);
+                index = node;
+            });
+        },
+
+        addClass: jqLiteAddClass,
+        removeClass: jqLiteRemoveClass,
+
+        toggleClass: function(element, selector, condition) {
+            if (isUndefined(condition)) {
+                condition = !jqLiteHasClass(element, selector);
+            }
+            (condition ? jqLiteAddClass : jqLiteRemoveClass)(element, selector);
+        },
+
+        parent: function(element) {
+            var parent = element.parentNode;
+            return parent && parent.nodeType !== 11 ? parent : null;
+        },
+
+        next: function(element) {
+            if (element.nextElementSibling) {
+                return element.nextElementSibling;
+            }
+
+            // IE8 doesn't have nextElementSibling
+            var elm = element.nextSibling;
+            while (elm != null && elm.nodeType !== 1) {
+                elm = elm.nextSibling;
+            }
+            return elm;
+        },
+
+        find: function(element, selector) {
+            return element.getElementsByTagName(selector);
+        },
+
+        clone: jqLiteClone,
+
+        triggerHandler: function(element, eventName, eventData) {
+            var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName];
+
+            eventData = eventData || [];
+
+            var event = [{
+                preventDefault: noop,
+                stopPropagation: noop
+            }];
+
+            forEach(eventFns, function(fn) {
+                fn.apply(element, event.concat(eventData));
+            });
+        }
+    }, function(fn, name){
+        /**
+         * chaining functions
+         */
+        JQLite.prototype[name] = function(arg1, arg2, arg3) {
+            var value;
+            for(var i=0; i < this.length; i++) {
+                if (isUndefined(value)) {
+                    value = fn(this[i], arg1, arg2, arg3);
+                    if (isDefined(value)) {
+                        // any function which returns a value needs to be wrapped
+                        value = jqLite(value);
+                    }
+                } else {
+                    jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
+                }
+            }
+            return isDefined(value) ? value : this;
+        };
+
+        // bind legacy bind/unbind to on/off
+        JQLite.prototype.bind = JQLite.prototype.on;
+        JQLite.prototype.unbind = JQLite.prototype.off;
+    });
+
+    /**
+     * Computes a hash of an 'obj'.
+     * Hash of a:
+     *  string is string
+     *  number is number as string
+     *  object is either result of calling $$hashKey function on the object or uniquely generated id,
+     *         that is also assigned to the $$hashKey property of the object.
+     *
+     * @param obj
+     * @returns {string} hash string such that the same input will have the same hash string.
+     *         The resulting string key is in 'type:hashKey' format.
+     */
+    function hashKey(obj) {
+        var objType = typeof obj,
+                key;
+
+        if (objType == 'object' && obj !== null) {
+            if (typeof (key = obj.$$hashKey) == 'function') {
+                // must invoke on object to keep the right this
+                key = obj.$$hashKey();
+            } else if (key === undefined) {
+                key = obj.$$hashKey = nextUid();
+            }
+        } else {
+            key = obj;
+        }
+
+        return objType + ':' + key;
+    }
+
+    /**
+     * HashMap which can use objects as keys
+     */
+    function HashMap(array){
+        forEach(array, this.put, this);
+    }
+    HashMap.prototype = {
+        /**
+         * Store key value pair
+         * @param key key to store can be any type
+         * @param value value to store can be any type
+         */
+        put: function(key, value) {
+            this[hashKey(key)] = value;
+        },
+
+        /**
+         * @param key
+         * @returns the value for the key
+         */
+        get: function(key) {
+            return this[hashKey(key)];
+        },
+
+        /**
+         * Remove the key/value pair
+         * @param key
+         */
+        remove: function(key) {
+            var value = this[key = hashKey(key)];
+            delete this[key];
+            return value;
+        }
+    };
+
+    /**
+     * @ngdoc function
+     * @name angular.injector
+     * @function
+     *
+     * @description
+     * Creates an injector function that can be used for retrieving services as well as for
+     * dependency injection (see {@link guide/di dependency injection}).
+     *
+
+     * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
+     *        {@link angular.module}. The `ng` module must be explicitly added.
+     * @returns {function()} Injector function. See {@link AUTO.$injector $injector}.
+     *
+     * @example
+     * Typical usage
+     * <pre>
+     *   // create an injector
+     *   var $injector = angular.injector(['ng']);
+     *
+     *   // use the injector to kick off your application
+     *   // use the type inference to auto inject arguments, or use implicit injection
+     *   $injector.invoke(function($rootScope, $compile, $document){
+ *     $compile($document)($rootScope);
+ *     $rootScope.$digest();
+ *   });
+     * </pre>
+     */
+
+
+    /**
+     * @ngdoc overview
+     * @name AUTO
+     * @description
+     *
+     * Implicit module which gets automatically added to each {@link AUTO.$injector $injector}.
+     */
+
+    var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
+    var FN_ARG_SPLIT = /,/;
+    var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
+    var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
+    var $injectorMinErr = minErr('$injector');
+    function annotate(fn) {
+        var $inject,
+                fnText,
+                argDecl,
+                last;
+
+        if (typeof fn == 'function') {
+            if (!($inject = fn.$inject)) {
+                $inject = [];
+                if (fn.length) {
+                    fnText = fn.toString().replace(STRIP_COMMENTS, '');
+                    argDecl = fnText.match(FN_ARGS);
+                    forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
+                        arg.replace(FN_ARG, function(all, underscore, name){
+                            $inject.push(name);
+                        });
+                    });
+                }
+                fn.$inject = $inject;
+            }
+        } else if (isArray(fn)) {
+            last = fn.length - 1;
+            assertArgFn(fn[last], 'fn');
+            $inject = fn.slice(0, last);
+        } else {
+            assertArgFn(fn, 'fn', true);
+        }
+        return $inject;
+    }
+
+///////////////////////////////////////
+
+    /**
+     * @ngdoc object
+     * @name AUTO.$injector
+     * @function
+     *
+     * @description
+     *
+     * `$injector` is used to retrieve object instances as defined by
+     * {@link AUTO.$provide provider}, instantiate types, invoke methods,
+     * and load modules.
+     *
+     * The following always holds true:
+     *
+     * <pre>
+     *   var $injector = angular.injector();
+     *   expect($injector.get('$injector')).toBe($injector);
+     *   expect($injector.invoke(function($injector){
+ *     return $injector;
+ *   }).toBe($injector);
+     * </pre>
+     *
+     * # Injection Function Annotation
+     *
+     * JavaScript does not have annotations, and annotations are needed for dependency injection. The
+     * following are all valid ways of annotating function with injection arguments and are equivalent.
+     *
+     * <pre>
+     *   // inferred (only works if code not minified/obfuscated)
+     *   $injector.invoke(function(serviceA){});
+     *
+     *   // annotated
+     *   function explicit(serviceA) {};
+     *   explicit.$inject = ['serviceA'];
+     *   $injector.invoke(explicit);
+     *
+     *   // inline
+     *   $injector.invoke(['serviceA', function(serviceA){}]);
+     * </pre>
+     *
+     * ## Inference
+     *
+     * In JavaScript calling `toString()` on a function returns the function definition. The definition
+     * can then be parsed and the function arguments can be extracted. *NOTE:* This does not work with
+     * minification, and obfuscation tools since these tools change the argument names.
+     *
+     * ## `$inject` Annotation
+     * By adding a `$inject` property onto a function the injection parameters can be specified.
+     *
+     * ## Inline
+     * As an array of injection names, where the last item in the array is the function to call.
+     */
+
+    /**
+     * @ngdoc method
+     * @name AUTO.$injector#get
+     * @methodOf AUTO.$injector
+     *
+     * @description
+     * Return an instance of the service.
+     *
+     * @param {string} name The name of the instance to retrieve.
+     * @return {*} The instance.
+     */
+
+    /**
+     * @ngdoc method
+     * @name AUTO.$injector#invoke
+     * @methodOf AUTO.$injector
+     *
+     * @description
+     * Invoke the method and supply the method arguments from the `$injector`.
+     *
+     * @param {!function} fn The function to invoke. Function parameters are injected according to the
+     *   {@link guide/di $inject Annotation} rules.
+     * @param {Object=} self The `this` for the invoked method.
+     * @param {Object=} locals Optional object. If preset then any argument names are read from this
+     *                         object first, before the `$injector` is consulted.
+     * @returns {*} the value returned by the invoked `fn` function.
+     */
+
+    /**
+     * @ngdoc method
+     * @name AUTO.$injector#has
+     * @methodOf AUTO.$injector
+     *
+     * @description
+     * Allows the user to query if the particular service exist.
+     *
+     * @param {string} Name of the service to query.
+     * @returns {boolean} returns true if injector has given service.
+     */
+
+    /**
+     * @ngdoc method
+     * @name AUTO.$injector#instantiate
+     * @methodOf AUTO.$injector
+     * @description
+     * Create a new instance of JS type. The method takes a constructor function invokes the new
+     * operator and supplies all of the arguments to the constructor function as specified by the
+     * constructor annotation.
+     *
+     * @param {function} Type Annotated constructor function.
+     * @param {Object=} locals Optional object. If preset then any argument names are read from this
+     * object first, before the `$injector` is consulted.
+     * @returns {Object} new instance of `Type`.
+     */
+
+    /**
+     * @ngdoc method
+     * @name AUTO.$injector#annotate
+     * @methodOf AUTO.$injector
+     *
+     * @description
+     * Returns an array of service names which the function is requesting for injection. This API is
+     * used by the injector to determine which services need to be injected into the function when the
+     * function is invoked. There are three ways in which the function can be annotated with the needed
+     * dependencies.
+     *
+     * # Argument names
+     *
+     * The simplest form is to extract the dependencies from the arguments of the function. This is done
+     * by converting the function into a string using `toString()` method and extracting the argument
+     * names.
+     * <pre>
+     *   // Given
+     *   function MyController($scope, $route) {
+ *     // ...
+ *   }
+     *
+     *   // Then
+     *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
+     * </pre>
+     *
+     * This method does not work with code minification / obfuscation. For this reason the following
+     * annotation strategies are supported.
+     *
+     * # The `$inject` property
+     *
+     * If a function has an `$inject` property and its value is an array of strings, then the strings
+     * represent names of services to be injected into the function.
+     * <pre>
+     *   // Given
+     *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
+ *     // ...
+ *   }
+     *   // Define function dependencies
+     *   MyController.$inject = ['$scope', '$route'];
+     *
+     *   // Then
+     *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
+     * </pre>
+     *
+     * # The array notation
+     *
+     * It is often desirable to inline Injected functions and that's when setting the `$inject` property
+     * is very inconvenient. In these situations using the array notation to specify the dependencies in
+     * a way that survives minification is a better choice:
+     *
+     * <pre>
+     *   // We wish to write this (not minification / obfuscation safe)
+     *   injector.invoke(function($compile, $rootScope) {
+ *     // ...
+ *   });
+     *
+     *   // We are forced to write break inlining
+     *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
+ *     // ...
+ *   };
+     *   tmpFn.$inject = ['$compile', '$rootScope'];
+     *   injector.invoke(tmpFn);
+     *
+     *   // To better support inline function the inline annotation is supported
+     *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
+ *     // ...
+ *   }]);
+     *
+     *   // Therefore
+     *   expect(injector.annotate(
+     *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
+     *    ).toEqual(['$compile', '$rootScope']);
+     * </pre>
+     *
+     * @param {function|Array.<string|Function>} fn Function for which dependent service names need to
+     * be retrieved as described above.
+     *
+     * @returns {Array.<string>} The names of the services which the function requires.
+     */
+
+
+
+
+    /**
+     * @ngdoc object
+     * @name AUTO.$provide
+     *
+     * @description
+     *
+     * The {@link AUTO.$provide $provide} service has a number of methods for registering components
+     * with the {@link AUTO.$injector $injector}. Many of these functions are also exposed on
+     * {@link angular.Module}.
+     *
+     * An Angular **service** is a singleton object created by a **service factory**.  These **service
+     * factories** are functions which, in turn, are created by a **service provider**.
+     * The **service providers** are constructor functions. When instantiated they must contain a
+     * property called `$get`, which holds the **service factory** function.
+     *
+     * When you request a service, the {@link AUTO.$injector $injector} is responsible for finding the
+     * correct **service provider**, instantiating it and then calling its `$get` **service factory**
+     * function to get the instance of the **service**.
+     *
+     * Often services have no configuration options and there is no need to add methods to the service
+     * provider.  The provider will be no more than a constructor function with a `$get` property. For
+     * these cases the {@link AUTO.$provide $provide} service has additional helper methods to register
+     * services without specifying a provider.
+     *
+     * * {@link AUTO.$provide#methods_provider provider(provider)} - registers a **service provider** with the
+     *     {@link AUTO.$injector $injector}
+     * * {@link AUTO.$provide#methods_constant constant(obj)} - registers a value/object that can be accessed by
+     *     providers and services.
+     * * {@link AUTO.$provide#methods_value value(obj)} - registers a value/object that can only be accessed by
+     *     services, not providers.
+     * * {@link AUTO.$provide#methods_factory factory(fn)} - registers a service **factory function**, `fn`,
+     *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
+     *     given factory function.
+     * * {@link AUTO.$provide#methods_service service(class)} - registers a **constructor function**, `class` that
+     *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
+     *      a new object using the given constructor function.
+     *
+     * See the individual methods for more information and examples.
+     */
+
+    /**
+     * @ngdoc method
+     * @name AUTO.$provide#provider
+     * @methodOf AUTO.$provide
+     * @description
+     *
+     * Register a **provider function** with the {@link AUTO.$injector $injector}. Provider functions
+     * are constructor functions, whose instances are responsible for "providing" a factory for a
+     * service.
+     *
+     * Service provider names start with the name of the service they provide followed by `Provider`.
+     * For example, the {@link ng.$log $log} service has a provider called
+     * {@link ng.$logProvider $logProvider}.
+     *
+     * Service provider objects can have additional methods which allow configuration of the provider
+     * and its service. Importantly, you can configure what kind of service is created by the `$get`
+     * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
+     * method {@link ng.$logProvider#debugEnabled debugEnabled}
+     * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
+     * console or not.
+     *
+     * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
+     'Provider'` key.
+     * @param {(Object|function())} provider If the provider is:
+     *
+     *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
+     *               {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be
+     *               created.
+     *   - `Constructor`: a new instance of the provider will be created using
+     *               {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as
+     *               `object`.
+     *
+     * @returns {Object} registered provider instance
+
+     * @example
+     *
+     * The following example shows how to create a simple event tracking service and register it using
+     * {@link AUTO.$provide#methods_provider $provide.provider()}.
+     *
+     * <pre>
+     *  // Define the eventTracker provider
+     *  function EventTrackerProvider() {
+ *    var trackingUrl = '/track';
+ *
+ *    // A provider method for configuring where the tracked events should been saved
+ *    this.setTrackingUrl = function(url) {
+ *      trackingUrl = url;
+ *    };
+ *
+ *    // The service factory function
+ *    this.$get = ['$http', function($http) {
+ *      var trackedEvents = {};
+ *      return {
+ *        // Call this to track an event
+ *        event: function(event) {
+ *          var count = trackedEvents[event] || 0;
+ *          count += 1;
+ *          trackedEvents[event] = count;
+ *          return count;
+ *        },
+ *        // Call this to save the tracked events to the trackingUrl
+ *        save: function() {
+ *          $http.post(trackingUrl, trackedEvents);
+ *        }
+ *      };
+ *    }];
+ *  }
+     *
+     *  describe('eventTracker', function() {
+ *    var postSpy;
+ *
+ *    beforeEach(module(function($provide) {
+ *      // Register the eventTracker provider
+ *      $provide.provider('eventTracker', EventTrackerProvider);
+ *    }));
+ *
+ *    beforeEach(module(function(eventTrackerProvider) {
+ *      // Configure eventTracker provider
+ *      eventTrackerProvider.setTrackingUrl('/custom-track');
+ *    }));
+ *
+ *    it('tracks events', inject(function(eventTracker) {
+ *      expect(eventTracker.event('login')).toEqual(1);
+ *      expect(eventTracker.event('login')).toEqual(2);
+ *    }));
+ *
+ *    it('saves to the tracking url', inject(function(eventTracker, $http) {
+ *      postSpy = spyOn($http, 'post');
+ *      eventTracker.event('login');
+ *      eventTracker.save();
+ *      expect(postSpy).toHaveBeenCalled();
+ *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
+ *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
+ *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
+ *    }));
+ *  });
+     * </pre>
+     */
+
+    /**
+     * @ngdoc method
+     * @name AUTO.$provide#factory
+     * @methodOf AUTO.$provide
+     * @description
+     *
+     * Register a **service factory**, which will be called to return the service instance.
+     * This is short for registering a service where its provider consists of only a `$get` property,
+     * which is the given service factory function.
+     * You should use {@link AUTO.$provide#factory $provide.factory(getFn)} if you do not need to
+     * configure your service in a provider.
+     *
+     * @param {string} name The name of the instance.
+     * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand
+     *                            for `$provide.provider(name, {$get: $getFn})`.
+     * @returns {Object} registered provider instance
+     *
+     * @example
+     * Here is an example of registering a service
+     * <pre>
+     *   $provide.factory('ping', ['$http', function($http) {
+ *     return function ping() {
+ *       return $http.send('/ping');
+ *     };
+ *   }]);
+     * </pre>
+     * You would then inject and use this service like this:
+     * <pre>
+     *   someModule.controller('Ctrl', ['ping', function(ping) {
+ *     ping();
+ *   }]);
+     * </pre>
+     */
+
+
+    /**
+     * @ngdoc method
+     * @name AUTO.$provide#service
+     * @methodOf AUTO.$provide
+     * @description
+     *
+     * Register a **service constructor**, which will be invoked with `new` to create the service
+     * instance.
+     * This is short for registering a service where its provider's `$get` property is the service
+     * constructor function that will be used to instantiate the service instance.
+     *
+     * You should use {@link AUTO.$provide#methods_service $provide.service(class)} if you define your service
+     * as a type/class. This is common when using {@link http://coffeescript.org CoffeeScript}.
+     *
+     * @param {string} name The name of the instance.
+     * @param {Function} constructor A class (constructor function) that will be instantiated.
+     * @returns {Object} registered provider instance
+     *
+     * @example
+     * Here is an example of registering a service using
+     * {@link AUTO.$provide#methods_service $provide.service(class)} that is defined as a CoffeeScript class.
+     * <pre>
+     *   class Ping
+     *     constructor: (@$http)->
+     *     send: ()=>
+     *       @$http.get('/ping')
+     *
+     *   $provide.service('ping', ['$http', Ping])
+     * </pre>
+     * You would then inject and use this service like this:
+     * <pre>
+     *   someModule.controller 'Ctrl', ['ping', (ping)->
+     *     ping.send()
+     *   ]
+     * </pre>
+     */
+
+
+    /**
+     * @ngdoc method
+     * @name AUTO.$provide#value
+     * @methodOf AUTO.$provide
+     * @description
+     *
+     * Register a **value service** with the {@link AUTO.$injector $injector}, such as a string, a
+     * number, an array, an object or a function.  This is short for registering a service where its
+     * provider's `$get` property is a factory function that takes no arguments and returns the **value
+     * service**.
+     *
+     * Value services are similar to constant services, except that they cannot be injected into a
+     * module configuration function (see {@link angular.Module#config}) but they can be overridden by
+     * an Angular
+     * {@link AUTO.$provide#decorator decorator}.
+     *
+     * @param {string} name The name of the instance.
+     * @param {*} value The value.
+     * @returns {Object} registered provider instance
+     *
+     * @example
+     * Here are some examples of creating value services.
+     * <pre>
+     *   $provide.value('ADMIN_USER', 'admin');
+     *
+     *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
+     *
+     *   $provide.value('halfOf', function(value) {
+ *     return value / 2;
+ *   });
+     * </pre>
+     */
+
+
+    /**
+     * @ngdoc method
+     * @name AUTO.$provide#constant
+     * @methodOf AUTO.$provide
+     * @description
+     *
+     * Register a **constant service**, such as a string, a number, an array, an object or a function,
+     * with the {@link AUTO.$injector $injector}. Unlike {@link AUTO.$provide#value value} it can be
+     * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
+     * be overridden by an Angular {@link AUTO.$provide#decorator decorator}.
+     *
+     * @param {string} name The name of the constant.
+     * @param {*} value The constant value.
+     * @returns {Object} registered instance
+     *
+     * @example
+     * Here a some examples of creating constants:
+     * <pre>
+     *   $provide.constant('SHARD_HEIGHT', 306);
+     *
+     *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
+     *
+     *   $provide.constant('double', function(value) {
+ *     return value * 2;
+ *   });
+     * </pre>
+     */
+
+
+    /**
+     * @ngdoc method
+     * @name AUTO.$provide#decorator
+     * @methodOf AUTO.$provide
+     * @description
+     *
+     * Register a **service decorator** with the {@link AUTO.$injector $injector}. A service decorator
+     * intercepts the creation of a service, allowing it to override or modify the behaviour of the
+     * service. The object returned by the decorator may be the original service, or a new service
+     * object which replaces or wraps and delegates to the original service.
+     *
+     * @param {string} name The name of the service to decorate.
+     * @param {function()} decorator This function will be invoked when the service needs to be
+     *    instantiated and should return the decorated service instance. The function is called using
+     *    the {@link AUTO.$injector#invoke injector.invoke} method and is therefore fully injectable.
+     *    Local injection arguments:
+     *
+     *    * `$delegate` - The original service instance, which can be monkey patched, configured,
+     *      decorated or delegated to.
+     *
+     * @example
+     * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
+     * calls to {@link ng.$log#error $log.warn()}.
+     * <pre>
+     *   $provider.decorator('$log', ['$delegate', function($delegate) {
+ *     $delegate.warn = $delegate.error;
+ *     return $delegate;
+ *   }]);
+     * </pre>
+     */
+
+
+    function createInjector(modulesToLoad) {
+        var INSTANTIATING = {},
+                providerSuffix = 'Provider',
+                path = [],
+                loadedModules = new HashMap(),
+                providerCache = {
+                    $provide: {
+                        provider: supportObject(provider),
+                        factory: supportObject(factory),
+                        service: supportObject(service),
+                        value: supportObject(value),
+                        constant: supportObject(constant),
+                        decorator: decorator
+                    }
+                },
+                providerInjector = (providerCache.$injector =
+                        createInternalInjector(providerCache, function() {
+                            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
+                        })),
+                instanceCache = {},
+                instanceInjector = (instanceCache.$injector =
+                        createInternalInjector(instanceCache, function(servicename) {
+                            var provider = providerInjector.get(servicename + providerSuffix);
+                            return instanceInjector.invoke(provider.$get, provider);
+                        }));
+
+
+        forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
+
+        return instanceInjector;
+
+        ////////////////////////////////////
+        // $provider
+        ////////////////////////////////////
+
+        function supportObject(delegate) {
+            return function(key, value) {
+                if (isObject(key)) {
+                    forEach(key, reverseParams(delegate));
+                } else {
+                    return delegate(key, value);
+                }
+            };
+        }
+
+        function provider(name, provider_) {
+            assertNotHasOwnProperty(name, 'service');
+            if (isFunction(provider_) || isArray(provider_)) {
+                provider_ = providerInjector.instantiate(provider_);
+            }
+            if (!provider_.$get) {
+                throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
+            }
+            return providerCache[name + providerSuffix] = provider_;
+        }
+
+        function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); }
+
+        function service(name, constructor) {
+            return factory(name, ['$injector', function($injector) {
+                return $injector.instantiate(constructor);
+            }]);
+        }
+
+        function value(name, val) { return factory(name, valueFn(val)); }
+
+        function constant(name, value) {
+            assertNotHasOwnProperty(name, 'constant');
+            providerCache[name] = value;
+            instanceCache[name] = value;
+        }
+
+        function decorator(serviceName, decorFn) {
+            var origProvider = providerInjector.get(serviceName + providerSuffix),
+                    orig$get = origProvider.$get;
+
+            origProvider.$get = function() {
+                var origInstance = instanceInjector.invoke(orig$get, origProvider);
+                return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
+            };
+        }
+
+        ////////////////////////////////////
+        // Module Loading
+        ////////////////////////////////////
+        function loadModules(modulesToLoad){
+            var runBlocks = [], moduleFn, invokeQueue, i, ii;
+            forEach(modulesToLoad, function(module) {
+                if (loadedModules.get(module)) return;
+                loadedModules.put(module, true);
+
+                try {
+                    if (isString(module)) {
+                        moduleFn = angularModule(module);
+                        runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
+
+                        for(invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
+                            var invokeArgs = invokeQueue[i],
+                                    provider = providerInjector.get(invokeArgs[0]);
+
+                            provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
+                        }
+                    } else if (isFunction(module)) {
+                        runBlocks.push(providerInjector.invoke(module));
+                    } else if (isArray(module)) {
+                        runBlocks.push(providerInjector.invoke(module));
+                    } else {
+                        assertArgFn(module, 'module');
+                    }
+                } catch (e) {
+                    if (isArray(module)) {
+                        module = module[module.length - 1];
+                    }
+                    if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
+                        // Safari & FF's stack traces don't contain error.message content
+                        // unlike those of Chrome and IE
+                        // So if stack doesn't contain message, we create a new string that contains both.
+                        // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
+                        /* jshint -W022 */
+                        e = e.message + '\n' + e.stack;
+                    }
+                    throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
+                            module, e.stack || e.message || e);
+                }
+            });
+            return runBlocks;
+        }
+
+        ////////////////////////////////////
+        // internal Injector
+        ////////////////////////////////////
+
+        function createInternalInjector(cache, factory) {
+
+            function getService(serviceName) {
+                if (cache.hasOwnProperty(serviceName)) {
+                    if (cache[serviceName] === INSTANTIATING) {
+                        throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- '));
+                    }
+                    return cache[serviceName];
+                } else {
+                    try {
+                        path.unshift(serviceName);
+                        cache[serviceName] = INSTANTIATING;
+                        return cache[serviceName] = factory(serviceName);
+                    } finally {
+                        path.shift();
+                    }
+                }
+            }
+
+            function invoke(fn, self, locals){
+                var args = [],
+                        $inject = annotate(fn),
+                        length, i,
+                        key;
+
+                for(i = 0, length = $inject.length; i < length; i++) {
+                    key = $inject[i];
+                    if (typeof key !== 'string') {
+                        throw $injectorMinErr('itkn',
+                                'Incorrect injection token! Expected service name as string, got {0}', key);
+                    }
+                    args.push(
+                            locals && locals.hasOwnProperty(key)
+                                    ? locals[key]
+                                    : getService(key)
+                    );
+                }
+                if (!fn.$inject) {
+                    // this means that we must be an array.
+                    fn = fn[length];
+                }
+
+
+                // Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke
+                switch (self ? -1 : args.length) {
+                    case  0: return fn();
+                    case  1: return fn(args[0]);
+                    case  2: return fn(args[0], args[1]);
+                    case  3: return fn(args[0], args[1], args[2]);
+                    case  4: return fn(args[0], args[1], args[2], args[3]);
+                    case  5: return fn(args[0], args[1], args[2], args[3], args[4]);
+                    case  6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]);
+                    case  7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+                    case  8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
+                    case  9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7],
+                            args[8]);
+                    case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7],
+                            args[8], args[9]);
+                    default: return fn.apply(self, args);
+                }
+            }
+
+            function instantiate(Type, locals) {
+                var Constructor = function() {},
+                        instance, returnedValue;
+
+                // Check if Type is annotated and use just the given function at n-1 as parameter
+                // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
+                Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
+                instance = new Constructor();
+                returnedValue = invoke(Type, instance, locals);
+
+                return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
+            }
+
+            return {
+                invoke: invoke,
+                instantiate: instantiate,
+                get: getService,
+                annotate: annotate,
+                has: function(name) {
+                    return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
+                }
+            };
+        }
+    }
+
+    /**
+     * @ngdoc function
+     * @name ng.$anchorScroll
+     * @requires $window
+     * @requires $location
+     * @requires $rootScope
+     *
+     * @description
+     * When called, it checks current value of `$location.hash()` and scroll to related element,
+     * according to rules specified in
+     * {@link http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document Html5 spec}.
+     *
+     * It also watches the `$location.hash()` and scrolls whenever it changes to match any anchor.
+     * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`.
+     *
+     * @example
+     <example>
+     <file name="index.html">
+     <div id="scrollArea" ng-controller="ScrollCtrl">
+     <a ng-click="gotoBottom()">Go to bottom</a>
+     <a id="bottom"></a> You're at the bottom!
+     </div>
+     </file>
+     <file name="script.js">
+     function ScrollCtrl($scope, $location, $anchorScroll) {
+         $scope.gotoBottom = function (){
+           // set the location.hash to the id of
+           // the element you wish to scroll to.
+           $location.hash('bottom');
+           
+           // call $anchorScroll()
+           $anchorScroll();
+         }
+       }
+     </file>
+     <file name="style.css">
+     #scrollArea {
+         height: 350px;
+         overflow: auto;
+       }
+
+     #bottom {
+         display: block;
+         margin-top: 2000px;
+       }
+     </file>
+     </example>
+     */
+    function $AnchorScrollProvider() {
+
+        var autoScrollingEnabled = true;
+
+        this.disableAutoScrolling = function() {
+            autoScrollingEnabled = false;
+        };
+
+        this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
+            var document = $window.document;
+
+            // helper function to get first anchor from a NodeList
+            // can't use filter.filter, as it accepts only instances of Array
+            // and IE can't convert NodeList to an array using [].slice
+            // TODO(vojta): use filter if we change it to accept lists as well
+            function getFirstAnchor(list) {
+                var result = null;
+                forEach(list, function(element) {
+                    if (!result && lowercase(element.nodeName) === 'a') result = element;
+                });
+                return result;
+            }
+
+            function scroll() {
+                var hash = $location.hash(), elm;
+
+                // empty hash, scroll to the top of the page
+                if (!hash) $window.scrollTo(0, 0);
+
+                // element with given id
+                else if ((elm = document.getElementById(hash))) elm.scrollIntoView();
+
+                // first anchor with given name :-D
+                else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView();
+
+                // no element and hash == 'top', scroll to the top of the page
+                else if (hash === 'top') $window.scrollTo(0, 0);
+            }
+
+            // does not scroll when user clicks on anchor link that is currently on
+            // (no url change, no $location.hash() change), browser native does scroll
+            if (autoScrollingEnabled) {
+                $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
+                        function autoScrollWatchAction() {
+                            $rootScope.$evalAsync(scroll);
+                        });
+            }
+
+            return scroll;
+        }];
+    }
+
+    var $animateMinErr = minErr('$animate');
+
+    /**
+     * @ngdoc object
+     * @name ng.$animateProvider
+     *
+     * @description
+     * Default implementation of $animate that doesn't perform any animations, instead just
+     * synchronously performs DOM
+     * updates and calls done() callbacks.
+     *
+     * In order to enable animations the ngAnimate module has to be loaded.
+     *
+     * To see the functional implementation check out src/ngAnimate/animate.js
+     */
+    var $AnimateProvider = ['$provide', function($provide) {
+
+
+        this.$$selectors = {};
+
+
+        /**
+         * @ngdoc function
+         * @name ng.$animateProvider#register
+         * @methodOf ng.$animateProvider
+         *
+         * @description
+         * Registers a new injectable animation factory function. The factory function produces the
+         * animation object which contains callback functions for each event that is expected to be
+         * animated.
+         *
+         *   * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction`
+         *   must be called once the element animation is complete. If a function is returned then the
+         *   animation service will use this function to cancel the animation whenever a cancel event is
+         *   triggered.
+         *
+         *
+         *<pre>
+         *   return {
+     *     eventFn : function(element, done) {
+     *       //code to run the animation
+     *       //once complete, then run done()
+     *       return function cancellationFunction() {
+     *         //code to cancel the animation
+     *       }
+     *     }
+     *   }
+         *</pre>
+         *
+         * @param {string} name The name of the animation.
+         * @param {function} factory The factory function that will be executed to return the animation
+         *                           object.
+         */
+        this.register = function(name, factory) {
+            var key = name + '-animation';
+            if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel',
+                    "Expecting class selector starting with '.' got '{0}'.", name);
+            this.$$selectors[name.substr(1)] = key;
+            $provide.factory(key, factory);
+        };
+
+        this.$get = ['$timeout', function($timeout) {
+
+            /**
+             *
+             * @ngdoc object
+             * @name ng.$animate
+             * @description The $animate service provides rudimentary DOM manipulation functions to
+             * insert, remove and move elements within the DOM, as well as adding and removing classes.
+             * This service is the core service used by the ngAnimate $animator service which provides
+             * high-level animation hooks for CSS and JavaScript.
+             *
+             * $animate is available in the AngularJS core, however, the ngAnimate module must be included
+             * to enable full out animation support. Otherwise, $animate will only perform simple DOM
+             * manipulation operations.
+             *
+             * To learn more about enabling animation support, click here to visit the {@link ngAnimate
+             * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service
+             * page}.
+             */
+            return {
+
+                /**
+                 *
+                 * @ngdoc function
+                 * @name ng.$animate#enter
+                 * @methodOf ng.$animate
+                 * @function
+                 * @description Inserts the element into the DOM either after the `after` element or within
+                 *   the `parent` element. Once complete, the done() callback will be fired (if provided).
+                 * @param {jQuery/jqLite element} element the element which will be inserted into the DOM
+                 * @param {jQuery/jqLite element} parent the parent element which will append the element as
+                 *   a child (if the after element is not present)
+                 * @param {jQuery/jqLite element} after the sibling element which will append the element
+                 *   after itself
+                 * @param {function=} done callback function that will be called after the element has been
+                 *   inserted into the DOM
+                 */
+                enter : function(element, parent, after, done) {
+                    if (after) {
+                        after.after(element);
+                    } else {
+                        if (!parent || !parent[0]) {
+                            parent = after.parent();
+                        }
+                        parent.append(element);
+                    }
+                    done && $timeout(done, 0, false);
+                },
+
+                /**
+                 *
+                 * @ngdoc function
+                 * @name ng.$animate#leave
+                 * @methodOf ng.$animate
+                 * @function
+                 * @description Removes the element from the DOM. Once complete, the done() callback will be
+                 *   fired (if provided).
+                 * @param {jQuery/jqLite element} element the element which will be removed from the DOM
+                 * @param {function=} done callback function that will be called after the element has been
+                 *   removed from the DOM
+                 */
+                leave : function(element, done) {
+                    element.remove();
+                    done && $timeout(done, 0, false);
+                },
+
+                /**
+                 *
+                 * @ngdoc function
+                 * @name ng.$animate#move
+                 * @methodOf ng.$animate
+                 * @function
+                 * @description Moves the position of the provided element within the DOM to be placed
+                 * either after the `after` element or inside of the `parent` element. Once complete, the
+                 * done() callback will be fired (if provided).
+                 *
+                 * @param {jQuery/jqLite element} element the element which will be moved around within the
+                 *   DOM
+                 * @param {jQuery/jqLite element} parent the parent element where the element will be
+                 *   inserted into (if the after element is not present)
+                 * @param {jQuery/jqLite element} after the sibling element where the element will be
+                 *   positioned next to
+                 * @param {function=} done the callback function (if provided) that will be fired after the
+                 *   element has been moved to its new position
+                 */
+                move : function(element, parent, after, done) {
+                    // Do not remove element before insert. Removing will cause data associated with the
+                    // element to be dropped. Insert will implicitly do the remove.
+                    this.enter(element, parent, after, done);
+                },
+
+                /**
+                 *
+                 * @ngdoc function
+                 * @name ng.$animate#addClass
+                 * @methodOf ng.$animate
+                 * @function
+                 * @description Adds the provided className CSS class value to the provided element. Once
+                 * complete, the done() callback will be fired (if provided).
+                 * @param {jQuery/jqLite element} element the element which will have the className value
+                 *   added to it
+                 * @param {string} className the CSS class which will be added to the element
+                 * @param {function=} done the callback function (if provided) that will be fired after the
+                 *   className value has been added to the element
+                 */
+                addClass : function(element, className, done) {
+                    className = isString(className) ?
+                            className :
+                            isArray(className) ? className.join(' ') : '';
+                    forEach(element, function (element) {
+                        jqLiteAddClass(element, className);
+                    });
+                    done && $timeout(done, 0, false);
+                },
+
+                /**
+                 *
+                 * @ngdoc function
+                 * @name ng.$animate#removeClass
+                 * @methodOf ng.$animate
+                 * @function
+                 * @description Removes the provided className CSS class value from the provided element.
+                 * Once complete, the done() callback will be fired (if provided).
+                 * @param {jQuery/jqLite element} element the element which will have the className value
+                 *   removed from it
+                 * @param {string} className the CSS class which will be removed from the element
+                 * @param {function=} done the callback function (if provided) that will be fired after the
+                 *   className value has been removed from the element
+                 */
+                removeClass : function(element, className, done) {
+                    className = isString(className) ?
+                            className :
+                            isArray(className) ? className.join(' ') : '';
+                    forEach(element, function (element) {
+                        jqLiteRemoveClass(element, className);
+                    });
+                    done && $timeout(done, 0, false);
+                },
+
+                enabled : noop
+            };
+        }];
+    }];
+
+    /**
+     * ! This is a private undocumented service !
+     *
+     * @name ng.$browser
+     * @requires $log
+     * @description
+     * This object has two goals:
+     *
+     * - hide all the global state in the browser caused by the window object
+     * - abstract away all the browser specific features and inconsistencies
+     *
+     * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
+     * service, which can be used for convenient testing of the application without the interaction with
+     * the real browser apis.
+     */
+    /**
+     * @param {object} window The global window object.
+     * @param {object} document jQuery wrapped document.
+     * @param {function()} XHR XMLHttpRequest constructor.
+     * @param {object} $log console.log or an object with the same interface.
+     * @param {object} $sniffer $sniffer service
+     */
+    function Browser(window, document, $log, $sniffer) {
+        var self = this,
+                rawDocument = document[0],
+                location = window.location,
+                history = window.history,
+                setTimeout = window.setTimeout,
+                clearTimeout = window.clearTimeout,
+                pendingDeferIds = {};
+
+        self.isMock = false;
+
+        var outstandingRequestCount = 0;
+        var outstandingRequestCallbacks = [];
+
+        // TODO(vojta): remove this temporary api
+        self.$$completeOutstandingRequest = completeOutstandingRequest;
+        self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
+
+        /**
+         * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
+         * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
+         */
+        function completeOutstandingRequest(fn) {
+            try {
+                fn.apply(null, sliceArgs(arguments, 1));
+            } finally {
+                outstandingRequestCount--;
+                if (outstandingRequestCount === 0) {
+                    while(outstandingRequestCallbacks.length) {
+                        try {
+                            outstandingRequestCallbacks.pop()();
+                        } catch (e) {
+                            $log.error(e);
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * @private
+         * Note: this method is used only by scenario runner
+         * TODO(vojta): prefix this method with $$ ?
+         * @param {function()} callback Function that will be called when no outstanding request
+         */
+        self.notifyWhenNoOutstandingRequests = function(callback) {
+            // force browser to execute all pollFns - this is needed so that cookies and other pollers fire
+            // at some deterministic time in respect to the test runner's actions. Leaving things up to the
+            // regular poller would result in flaky tests.
+            forEach(pollFns, function(pollFn){ pollFn(); });
+
+            if (outstandingRequestCount === 0) {
+                callback();
+            } else {
+                outstandingRequestCallbacks.push(callback);
+            }
+        };
+
+        //////////////////////////////////////////////////////////////
+        // Poll Watcher API
+        //////////////////////////////////////////////////////////////
+        var pollFns = [],
+                pollTimeout;
+
+        /**
+         * @name ng.$browser#addPollFn
+         * @methodOf ng.$browser
+         *
+         * @param {function()} fn Poll function to add
+         *
+         * @description
+         * Adds a function to the list of functions that poller periodically executes,
+         * and starts polling if not started yet.
+         *
+         * @returns {function()} the added function
+         */
+        self.addPollFn = function(fn) {
+            if (isUndefined(pollTimeout)) startPoller(100, setTimeout);
+            pollFns.push(fn);
+            return fn;
+        };
+
+        /**
+         * @param {number} interval How often should browser call poll functions (ms)
+         * @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
+         *
+         * @description
+         * Configures the poller to run in the specified intervals, using the specified
+         * setTimeout fn and kicks it off.
+         */
+        function startPoller(interval, setTimeout) {
+            (function check() {
+                forEach(pollFns, function(pollFn){ pollFn(); });
+                pollTimeout = setTimeout(check, interval);
+            })();
+        }
+
+        //////////////////////////////////////////////////////////////
+        // URL API
+        //////////////////////////////////////////////////////////////
+
+        var lastBrowserUrl = location.href,
+                baseElement = document.find('base'),
+                newLocation = null;
+
+        /**
+         * @name ng.$browser#url
+         * @methodOf ng.$browser
+         *
+         * @description
+         * GETTER:
+         * Without any argument, this method just returns current value of location.href.
+         *
+         * SETTER:
+         * With at least one argument, this method sets url to new value.
+         * If html5 history api supported, pushState/replaceState is used, otherwise
+         * location.href/location.replace is used.
+         * Returns its own instance to allow chaining
+         *
+         * NOTE: this api is intended for use only by the $location service. Please use the
+         * {@link ng.$location $location service} to change url.
+         *
+         * @param {string} url New url (when used as setter)
+         * @param {boolean=} replace Should new url replace current history record ?
+         */
+        self.url = function(url, replace) {
+            // Android Browser BFCache causes location reference to become stale.
+            if (location !== window.location) location = window.location;
+
+            // setter
+            if (url) {
+                if (lastBrowserUrl == url) return;
+                lastBrowserUrl = url;
+                if ($sniffer.history) {
+                    if (replace) history.replaceState(null, '', url);
+                    else {
+                        history.pushState(null, '', url);
+                        // Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462
+                        baseElement.attr('href', baseElement.attr('href'));
+                    }
+                } else {
+                    newLocation = url;
+                    if (replace) {
+                        location.replace(url);
+                    } else {
+                        location.href = url;
+                    }
+                }
+                return self;
+                // getter
+            } else {
+                // - newLocation is a workaround for an IE7-9 issue with location.replace and location.href
+                //   methods not updating location.href synchronously.
+                // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
+                return newLocation || location.href.replace(/%27/g,"'");
+            }
+        };
+
+        var urlChangeListeners = [],
+                urlChangeInit = false;
+
+        function fireUrlChange() {
+            newLocation = null;
+            if (lastBrowserUrl == self.url()) return;
+
+            lastBrowserUrl = self.url();
+            forEach(urlChangeListeners, function(listener) {
+                listener(self.url());
+            });
+        }
+
+        /**
+         * @name ng.$browser#onUrlChange
+         * @methodOf ng.$browser
+         * @TODO(vojta): refactor to use node's syntax for events
+         *
+         * @description
+         * Register callback function that will be called, when url changes.
+         *
+         * It's only called when the url is changed by outside of angular:
+         * - user types different url into address bar
+         * - user clicks on history (forward/back) button
+         * - user clicks on a link
+         *
+         * It's not called when url is changed by $browser.url() method
+         *
+         * The listener gets called with new url as parameter.
+         *
+         * NOTE: this api is intended for use only by the $location service. Please use the
+         * {@link ng.$location $location service} to monitor url changes in angular apps.
+         *
+         * @param {function(string)} listener Listener function to be called when url changes.
+         * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
+         */
+        self.onUrlChange = function(callback) {
+            if (!urlChangeInit) {
+                // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
+                // don't fire popstate when user change the address bar and don't fire hashchange when url
+                // changed by push/replaceState
+
+                // html5 history api - popstate event
+                if ($sniffer.history) jqLite(window).on('popstate', fireUrlChange);
+                // hashchange event
+                if ($sniffer.hashchange) jqLite(window).on('hashchange', fireUrlChange);
+                // polling
+                else self.addPollFn(fireUrlChange);
+
+                urlChangeInit = true;
+            }
+
+            urlChangeListeners.push(callback);
+            return callback;
+        };
+
+        //////////////////////////////////////////////////////////////
+        // Misc API
+        //////////////////////////////////////////////////////////////
+
+        /**
+         * @name ng.$browser#baseHref
+         * @methodOf ng.$browser
+         *
+         * @description
+         * Returns current <base href>
+         * (always relative - without domain)
+         *
+         * @returns {string=} current <base href>
+         */
+        self.baseHref = function() {
+            var href = baseElement.attr('href');
+            return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
+        };
+
+        //////////////////////////////////////////////////////////////
+        // Cookies API
+        //////////////////////////////////////////////////////////////
+        var lastCookies = {};
+        var lastCookieString = '';
+        var cookiePath = self.baseHref();
+
+        /**
+         * @name ng.$browser#cookies
+         * @methodOf ng.$browser
+         *
+         * @param {string=} name Cookie name
+         * @param {string=} value Cookie value
+         *
+         * @description
+         * The cookies method provides a 'private' low level access to browser cookies.
+         * It is not meant to be used directly, use the $cookie service instead.
+         *
+         * The return values vary depending on the arguments that the method was called with as follows:
+         *
+         * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify
+         *   it
+         * - cookies(name, value) -> set name to value, if value is undefined delete the cookie
+         * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that
+         *   way)
+         *
+         * @returns {Object} Hash of all cookies (if called without any parameter)
+         */
+        self.cookies = function(name, value) {
+            /* global escape: false, unescape: false */
+            var cookieLength, cookieArray, cookie, i, index;
+
+            if (name) {
+                if (value === undefined) {
+                    rawDocument.cookie = escape(name) + "=;path=" + cookiePath +
+                            ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
+                } else {
+                    if (isString(value)) {
+                        cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) +
+                                ';path=' + cookiePath).length + 1;
+
+                        // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
+                        // - 300 cookies
+                        // - 20 cookies per unique domain
+                        // - 4096 bytes per cookie
+                        if (cookieLength > 4096) {
+                            $log.warn("Cookie '"+ name +
+                                    "' possibly not set or overflowed because it was too large ("+
+                                    cookieLength + " > 4096 bytes)!");
+                        }
+                    }
+                }
+            } else {
+                if (rawDocument.cookie !== lastCookieString) {
+                    lastCookieString = rawDocument.cookie;
+                    cookieArray = lastCookieString.split("; ");
+                    lastCookies = {};
+
+                    for (i = 0; i < cookieArray.length; i++) {
+                        cookie = cookieArray[i];
+                        index = cookie.indexOf('=');
+                        if (index > 0) { //ignore nameless cookies
+                            name = unescape(cookie.substring(0, index));
+                            // the first value that is seen for a cookie is the most
+                            // specific one.  values for the same cookie name that
+                            // follow are for less specific paths.
+                            if (lastCookies[name] === undefined) {
+                                lastCookies[name] = unescape(cookie.substring(index + 1));
+                            }
+                        }
+                    }
+                }
+                return lastCookies;
+            }
+        };
+
+
+        /**
+         * @name ng.$browser#defer
+         * @methodOf ng.$browser
+         * @param {function()} fn A function, who's execution should be deferred.
+         * @param {number=} [delay=0] of milliseconds to defer the function execution.
+         * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
+         *
+         * @description
+         * Executes a fn asynchronously via `setTimeout(fn, delay)`.
+         *
+         * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
+         * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
+         * via `$browser.defer.flush()`.
+         *
+         */
+        self.defer = function(fn, delay) {
+            var timeoutId;
+            outstandingRequestCount++;
+            timeoutId = setTimeout(function() {
+                delete pendingDeferIds[timeoutId];
+                completeOutstandingRequest(fn);
+            }, delay || 0);
+            pendingDeferIds[timeoutId] = true;
+            return timeoutId;
+        };
+
+
+        /**
+         * @name ng.$browser#defer.cancel
+         * @methodOf ng.$browser.defer
+         *
+         * @description
+         * Cancels a deferred task identified with `deferId`.
+         *
+         * @param {*} deferId Token returned by the `$browser.defer` function.
+         * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
+         *                    canceled.
+         */
+        self.defer.cancel = function(deferId) {
+            if (pendingDeferIds[deferId]) {
+                delete pendingDeferIds[deferId];
+                clearTimeout(deferId);
+                completeOutstandingRequest(noop);
+                return true;
+            }
+            return false;
+        };
+
+    }
+
+    function $BrowserProvider(){
+        this.$get = ['$window', '$log', '$sniffer', '$document',
+            function( $window,   $log,   $sniffer,   $document){
+                return new Browser($window, $document, $log, $sniffer);
+            }];
+    }
+
+    /**
+     * @ngdoc object
+     * @name ng.$cacheFactory
+     *
+     * @description
+     * Factory that constructs cache objects and gives access to them.
+     *
+     * <pre>
+     *
+     *  var cache = $cacheFactory('cacheId');
+     *  expect($cacheFactory.get('cacheId')).toBe(cache);
+     *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
+     *
+     *  cache.put("key", "value");
+     *  cache.put("another key", "another value");
+     *
+     *  // We've specified no options on creation
+     *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
+     *
+     * </pre>
+     *
+     *
+     * @param {string} cacheId Name or id of the newly created cache.
+     * @param {object=} options Options object that specifies the cache behavior. Properties:
+     *
+     *   - `{number=}` `capacity` — turns the cache into LRU cache.
+     *
+     * @returns {object} Newly created cache object with the following set of methods:
+     *
+     * - `{object}` `info()` — Returns id, size, and options of cache.
+     * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
+     *   it.
+     * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
+     * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
+     * - `{void}` `removeAll()` — Removes all cached values.
+     * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
+     *
+     */
+    function $CacheFactoryProvider() {
+
+        this.$get = function() {
+            var caches = {};
+
+            function cacheFactory(cacheId, options) {
+                if (cacheId in caches) {
+                    throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
+                }
+
+                var size = 0,
+                        stats = extend({}, options, {id: cacheId}),
+                        data = {},
+                        capacity = (options && options.capacity) || Number.MAX_VALUE,
+                        lruHash = {},
+                        freshEnd = null,
+                        staleEnd = null;
+
+                return caches[cacheId] = {
+
+                    put: function(key, value) {
+                        var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
+
+                        refresh(lruEntry);
+
+                        if (isUndefined(value)) return;
+                        if (!(key in data)) size++;
+                        data[key] = value;
+
+                        if (size > capacity) {
+                            this.remove(staleEnd.key);
+                        }
+
+                        return value;
+                    },
+
+
+                    get: function(key) {
+                        var lruEntry = lruHash[key];
+
+                        if (!lruEntry) return;
+
+                        refresh(lruEntry);
+
+                        return data[key];
+                    },
+
+
+                    remove: function(key) {
+                        var lruEntry = lruHash[key];
+
+                        if (!lruEntry) return;
+
+                        if (lruEntry == freshEnd) freshEnd = lruEntry.p;
+                        if (lruEntry == staleEnd) staleEnd = lruEntry.n;
+                        link(lruEntry.n,lruEntry.p);
+
+                        delete lruHash[key];
+                        delete data[key];
+                        size--;
+                    },
+
+
+                    removeAll: function() {
+                        data = {};
+                        size = 0;
+                        lruHash = {};
+                        freshEnd = staleEnd = null;
+                    },
+
+
+                    destroy: function() {
+                        data = null;
+                        stats = null;
+                        lruHash = null;
+                        delete caches[cacheId];
+                    },
+
+
+                    info: function() {
+                        return extend({}, stats, {size: size});
+                    }
+                };
+
+
+                /**
+                 * makes the `entry` the freshEnd of the LRU linked list
+                 */
+                function refresh(entry) {
+                    if (entry != freshEnd) {
+                        if (!staleEnd) {
+                            staleEnd = entry;
+                        } else if (staleEnd == entry) {
+                            staleEnd = entry.n;
+                        }
+
+                        link(entry.n, entry.p);
+                        link(entry, freshEnd);
+                        freshEnd = entry;
+                        freshEnd.n = null;
+                    }
+                }
+
+
+                /**
+                 * bidirectionally links two entries of the LRU linked list
+                 */
+                function link(nextEntry, prevEntry) {
+                    if (nextEntry != prevEntry) {
+                        if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
+                        if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
+                    }
+                }
+            }
+
+
+            /**
+             * @ngdoc method
+             * @name ng.$cacheFactory#info
+             * @methodOf ng.$cacheFactory
+             *
+             * @description
+             * Get information about all the of the caches that have been created
+             *
+             * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
+             */
+            cacheFactory.info = function() {
+                var info = {};
+                forEach(caches, function(cache, cacheId) {
+                    info[cacheId] = cache.info();
+                });
+                return info;
+            };
+
+
+            /**
+             * @ngdoc method
+             * @name ng.$cacheFactory#get
+             * @methodOf ng.$cacheFactory
+             *
+             * @description
+             * Get access to a cache object by the `cacheId` used when it was created.
+             *
+             * @param {string} cacheId Name or id of a cache to access.
+             * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
+             */
+            cacheFactory.get = function(cacheId) {
+                return caches[cacheId];
+            };
+
+
+            return cacheFactory;
+        };
+    }
+
+    /**
+     * @ngdoc object
+     * @name ng.$templateCache
+     *
+     * @description
+     * The first time a template is used, it is loaded in the template cache for quick retrieval. You
+     * can load templates directly into the cache in a `script` tag, or by consuming the
+     * `$templateCache` service directly.
+     *
+     * Adding via the `script` tag:
+     * <pre>
+     * <html ng-app>
+     * <head>
+     * <script type="text/ng-template" id="templateId.html">
+     *   This is the content of the template
+     * </script>
+     * </head>
+     *   ...
+     * </html>
+     * </pre>
+     *
+     * **Note:** the `script` tag containing the template does not need to be included in the `head` of
+     * the document, but it must be below the `ng-app` definition.
+     *
+     * Adding via the $templateCache service:
+     *
+     * <pre>
+     * var myApp = angular.module('myApp', []);
+     * myApp.run(function($templateCache) {
+ *   $templateCache.put('templateId.html', 'This is the content of the template');
+ * });
+     * </pre>
+     *
+     * To retrieve the template later, simply use it in your HTML:
+     * <pre>
+     * <div ng-include=" 'templateId.html' "></div>
+     * </pre>
+     *
+     * or get it via Javascript:
+     * <pre>
+     * $templateCache.get('templateId.html')
+     * </pre>
+     *
+     * See {@link ng.$cacheFactory $cacheFactory}.
+     *
+     */
+    function $TemplateCacheProvider() {
+        this.$get = ['$cacheFactory', function($cacheFactory) {
+            return $cacheFactory('templates');
+        }];
+    }
+
+    /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
+     *
+     * DOM-related variables:
+     *
+     * - "node" - DOM Node
+     * - "element" - DOM Element or Node
+     * - "$node" or "$element" - jqLite-wrapped node or element
+     *
+     *
+     * Compiler related stuff:
+     *
+     * - "linkFn" - linking fn of a single directive
+     * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
+     * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
+     * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
+     */
+
+
+    /**
+     * @ngdoc function
+     * @name ng.$compile
+     * @function
+     *
+     * @description
+     * Compiles a piece of HTML string or DOM into a template and produces a template function, which
+     * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
+     *
+     * The compilation is a process of walking the DOM tree and matching DOM elements to
+     * {@link ng.$compileProvider#methods_directive directives}.
+     *
+     * <div class="alert alert-warning">
+     * **Note:** This document is an in-depth reference of all directive options.
+     * For a gentle introduction to directives with examples of common use cases,
+     * see the {@link guide/directive directive guide}.
+     * </div>
+     *
+     * ## Comprehensive Directive API
+     *
+     * There are many different options for a directive.
+     *
+     * The difference resides in the return value of the factory function.
+     * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
+     * or just the `postLink` function (all other properties will have the default values).
+     *
+     * <div class="alert alert-success">
+     * **Best Practice:** It's recommended to use the "directive definition object" form.
+     * </div>
+     *
+     * Here's an example directive declared with a Directive Definition Object:
+     *
+     * <pre>
+     *   var myModule = angular.module(...);
+     *
+     *   myModule.directive('directiveName', function factory(injectables) {
+ *     var directiveDefinitionObject = {
+ *       priority: 0,
+ *       template: '<div></div>', // or // function(tElement, tAttrs) { ... },
+ *       // or
+ *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
+ *       replace: false,
+ *       transclude: false,
+ *       restrict: 'A',
+ *       scope: false,
+ *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
+ *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
+ *       compile: function compile(tElement, tAttrs, transclude) {
+ *         return {
+ *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
+ *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
+ *         }
+ *         // or
+ *         // return function postLink( ... ) { ... }
+ *       },
+ *       // or
+ *       // link: {
+ *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
+ *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
+ *       // }
+ *       // or
+ *       // link: function postLink( ... ) { ... }
+ *     };
+ *     return directiveDefinitionObject;
+ *   });
+     * </pre>
+     *
+     * <div class="alert alert-warning">
+     * **Note:** Any unspecified options will use the default value. You can see the default values below.
+     * </div>
+     *
+     * Therefore the above can be simplified as:
+     *
+     * <pre>
+     *   var myModule = angular.module(...);
+     *
+     *   myModule.directive('directiveName', function factory(injectables) {
+ *     var directiveDefinitionObject = {
+ *       link: function postLink(scope, iElement, iAttrs) { ... }
+ *     };
+ *     return directiveDefinitionObject;
+ *     // or
+ *     // return function postLink(scope, iElement, iAttrs) { ... }
+ *   });
+     * </pre>
+     *
+     *
+     *
+     * ### Directive Definition Object
+     *
+     * The directive definition object provides instructions to the {@link api/ng.$compile
+     * compiler}. The attributes are:
+     *
+     * #### `priority`
+     * When there are multiple directives defined on a single DOM element, sometimes it
+     * is necessary to specify the order in which the directives are applied. The `priority` is used
+     * to sort the directives before their `compile` functions get called. Priority is defined as a
+     * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
+     * are also run in priority order, but post-link functions are run in reverse order. The order
+     * of directives with the same priority is undefined. The default priority is `0`.
+     *
+     * #### `terminal`
+     * If set to true then the current `priority` will be the last set of directives
+     * which will execute (any directives at the current priority will still execute
+     * as the order of execution on same `priority` is undefined).
+     *
+     * #### `scope`
+     * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
+     * same element request a new scope, only one new scope is created. The new scope rule does not
+     * apply for the root of the template since the root of the template always gets a new scope.
+     *
+     * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
+     * normal scope in that it does not prototypically inherit from the parent scope. This is useful
+     * when creating reusable components, which should not accidentally read or modify data in the
+     * parent scope.
+     *
+     * The 'isolate' scope takes an object hash which defines a set of local scope properties
+     * derived from the parent scope. These local properties are useful for aliasing values for
+     * templates. Locals definition is a hash of local scope property to its source:
+     *
+     * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
+     *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
+     *   attribute name is assumed to be the same as the local name.
+     *   Given `<widget my-attr="hello {{name}}">` and widget definition
+     *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
+     *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
+     *   `localName` property on the widget scope. The `name` is read from the parent scope (not
+     *   component scope).
+     *
+     * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
+     *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
+     *   name is specified then the attribute name is assumed to be the same as the local name.
+     *   Given `<widget my-attr="parentModel">` and widget definition of
+     *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
+     *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
+     *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
+     *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
+     *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional.
+     *
+     * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
+     *   If no `attr` name is specified then the attribute name is assumed to be the same as the
+     *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
+     *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
+     *   a function wrapper for the `count = count + value` expression. Often it's desirable to
+     *   pass data from the isolated scope via an expression and to the parent scope, this can be
+     *   done by passing a map of local variable names and values into the expression wrapper fn.
+     *   For example, if the expression is `increment(amount)` then we can specify the amount value
+     *   by calling the `localFn` as `localFn({amount: 22})`.
+     *
+     *
+     *
+     * #### `controller`
+     * Controller constructor function. The controller is instantiated before the
+     * pre-linking phase and it is shared with other directives (see
+     * `require` attribute). This allows the directives to communicate with each other and augment
+     * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
+     *
+     * * `$scope` - Current scope associated with the element
+     * * `$element` - Current element
+     * * `$attrs` - Current attributes object for the element
+     * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope.
+     *    The scope can be overridden by an optional first argument.
+     *   `function([scope], cloneLinkingFn)`.
+     *
+     *
+     * #### `require`
+     * Require another directive and inject its controller as the fourth argument to the linking function. The
+     * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
+     * injected argument will be an array in corresponding order. If no such directive can be
+     * found, or if the directive does not have a controller, then an error is raised. The name can be prefixed with:
+     *
+     * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
+     * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
+     * * `^` - Locate the required controller by searching the element's parents. Throw an error if not found.
+     * * `?^` - Attempt to locate the required controller by searching the element's parentsor pass `null` to the
+     *   `link` fn if not found.
+     *
+     *
+     * #### `controllerAs`
+     * Controller alias at the directive scope. An alias for the controller so it
+     * can be referenced at the directive template. The directive needs to define a scope for this
+     * configuration to be used. Useful in the case when directive is used as component.
+     *
+     *
+     * #### `restrict`
+     * String of subset of `EACM` which restricts the directive to a specific directive
+     * declaration style. If omitted, the default (attributes only) is used.
+     *
+     * * `E` - Element name: `<my-directive></my-directive>`
+     * * `A` - Attribute (default): `<div my-directive="exp"></div>`
+     * * `C` - Class: `<div class="my-directive: exp;"></div>`
+     * * `M` - Comment: `<!-- directive: my-directive exp -->`
+     *
+     *
+     * #### `template`
+     * replace the current element with the contents of the HTML. The replacement process
+     * migrates all of the attributes / classes from the old element to the new one. See the
+     * {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive
+     * Directives Guide} for an example.
+     *
+     * You can specify `template` as a string representing the template or as a function which takes
+     * two arguments `tElement` and `tAttrs` (described in the `compile` function api below) and
+     * returns a string value representing the template.
+     *
+     *
+     * #### `templateUrl`
+     * Same as `template` but the template is loaded from the specified URL. Because
+     * the template loading is asynchronous the compilation/linking is suspended until the template
+     * is loaded.
+     *
+     * You can specify `templateUrl` as a string representing the URL or as a function which takes two
+     * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
+     * a string value representing the url.  In either case, the template URL is passed through {@link
+     * api/ng.$sce#methods_getTrustedResourceUrl $sce.getTrustedResourceUrl}.
+     *
+     *
+     * #### `replace`
+     * specify where the template should be inserted. Defaults to `false`.
+     *
+     * * `true` - the template will replace the current element.
+     * * `false` - the template will replace the contents of the current element.
+     *
+     *
+     * #### `transclude`
+     * compile the content of the element and make it available to the directive.
+     * Typically used with {@link api/ng.directive:ngTransclude
+     * ngTransclude}. The advantage of transclusion is that the linking function receives a
+     * transclusion function which is pre-bound to the correct scope. In a typical setup the widget
+     * creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate`
+     * scope. This makes it possible for the widget to have private state, and the transclusion to
+     * be bound to the parent (pre-`isolate`) scope.
+     *
+     * * `true` - transclude the content of the directive.
+     * * `'element'` - transclude the whole element including any directives defined at lower priority.
+     *
+     *
+     * #### `compile`
+     *
+     * <pre>
+     *   function compile(tElement, tAttrs, transclude) { ... }
+     * </pre>
+     *
+     * The compile function deals with transforming the template DOM. Since most directives do not do
+     * template transformation, it is not used often. Examples that require compile functions are
+     * directives that transform template DOM, such as {@link
+     * api/ng.directive:ngRepeat ngRepeat}, or load the contents
+     * asynchronously, such as {@link api/ngRoute.directive:ngView ngView}. The
+     * compile function takes the following arguments.
+     *
+     *   * `tElement` - template element - The element where the directive has been declared. It is
+     *     safe to do template transformation on the element and child elements only.
+     *
+     *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
+     *     between all directive compile functions.
+     *
+     *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
+     *
+     * <div class="alert alert-warning">
+     * **Note:** The template instance and the link instance may be different objects if the template has
+     * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
+     * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
+     * should be done in a linking function rather than in a compile function.
+     * </div>
+     *
+     * <div class="alert alert-error">
+     * **Note:** The `transclude` function that is passed to the compile function is deperecated, as it
+     *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
+     *   to the link function instead.
+     * </div>
+
+     * A compile function can have a return value which can be either a function or an object.
+     *
+     * * returning a (post-link) function - is equivalent to registering the linking function via the
+     *   `link` property of the config object when the compile function is empty.
+     *
+     * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
+     *   control when a linking function should be called during the linking phase. See info about
+     *   pre-linking and post-linking functions below.
+     *
+     *
+     * #### `link`
+     * This property is used only if the `compile` property is not defined.
+     *
+     * <pre>
+     *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
+     * </pre>
+     *
+     * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
+     * executed after the template has been cloned. This is where most of the directive logic will be
+     * put.
+     *
+     *   * `scope` - {@link api/ng.$rootScope.Scope Scope} - The scope to be used by the
+     *     directive for registering {@link api/ng.$rootScope.Scope#methods_$watch watches}.
+     *
+     *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
+     *     manipulate the children of the element only in `postLink` function since the children have
+     *     already been linked.
+     *
+     *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
+     *     between all directive linking functions.
+     *
+     *   * `controller` - a controller instance - A controller instance if at least one directive on the
+     *     element defines a controller. The controller is shared among all the directives, which allows
+     *     the directives to use the controllers as a communication channel.
+     *
+     *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
+     *     The scope can be overridden by an optional first argument. This is the same as the `$transclude`
+     *     parameter of directive controllers.
+     *     `function([scope], cloneLinkingFn)`.
+     *
+     *
+     * #### Pre-linking function
+     *
+     * Executed before the child elements are linked. Not safe to do DOM transformation since the
+     * compiler linking function will fail to locate the correct elements for linking.
+     *
+     * #### Post-linking function
+     *
+     * Executed after the child elements are linked. It is safe to do DOM transformation in the post-linking function.
+     *
+     * <a name="Attributes"></a>
+     * ### Attributes
+     *
+     * The {@link api/ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
+     * `link()` or `compile()` functions. It has a variety of uses.
+     *
+     * accessing *Normalized attribute names:*
+     * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
+     * the attributes object allows for normalized access to
+     *   the attributes.
+     *
+     * * *Directive inter-communication:* All directives share the same instance of the attributes
+     *   object which allows the directives to use the attributes object as inter directive
+     *   communication.
+     *
+     * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
+     *   allowing other directives to read the interpolated value.
+     *
+     * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
+     *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
+     *   the only way to easily get the actual value because during the linking phase the interpolation
+     *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
+     *
+     * <pre>
+     * function linkingFn(scope, elm, attrs, ctrl) {
+ *   // get the attribute value
+ *   console.log(attrs.ngModel);
+ *
+ *   // change the attribute
+ *   attrs.$set('ngModel', 'new value');
+ *
+ *   // observe changes to interpolated attribute
+ *   attrs.$observe('ngModel', function(value) {
+ *     console.log('ngModel has changed value to ' + value);
+ *   });
+ * }
+     * </pre>
+     *
+     * Below is an example using `$compileProvider`.
+     *
+     * <div class="alert alert-warning">
+     * **Note**: Typically directives are registered with `module.directive`. The example below is
+     * to illustrate how `$compile` works.
+     * </div>
+     *
+     <doc:example module="compile">
+     <doc:source>
+     <script>
+     angular.module('compile', [], function($compileProvider) {
+        // configure new 'compile' directive by passing a directive
+        // factory function. The factory function injects the '$compile'
+        $compileProvider.directive('compile', function($compile) {
+          // directive factory creates a link function
+          return function(scope, element, attrs) {
+            scope.$watch(
+              function(scope) {
+                 // watch the 'compile' expression for changes
+                return scope.$eval(attrs.compile);
+              },
+              function(value) {
+                // when the 'compile' expression changes
+                // assign it into the current DOM
+                element.html(value);
+
+                // compile the new DOM and link it to the current
+                // scope.
+                // NOTE: we only compile .childNodes so that
+                // we don't get into infinite loop compiling ourselves
+                $compile(element.contents())(scope);
+              }
+            );
+          };
+        })
+      });
+
+     function Ctrl($scope) {
+        $scope.name = 'Angular';
+        $scope.html = 'Hello {{name}}';
+      }
+     </script>
+     <div ng-controller="Ctrl">
+     <input ng-model="name"> <br>
+     <textarea ng-model="html"></textarea> <br>
+     <div compile="html"></div>
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should auto compile', function() {
+       expect(element('div[compile]').text()).toBe('Hello Angular');
+       input('html').enter('{{name}}!');
+       expect(element('div[compile]').text()).toBe('Angular!');
+     });
+     </doc:scenario>
+     </doc:example>
+
+     *
+     *
+     * @param {string|DOMElement} element Element or HTML string to compile into a template function.
+     * @param {function(angular.Scope[, cloneAttachFn]} transclude function available to directives.
+     * @param {number} maxPriority only apply directives lower then given priority (Only effects the
+     *                 root element(s), not their children)
+     * @returns {function(scope[, cloneAttachFn])} a link function which is used to bind template
+     * (a DOM element/tree) to a scope. Where:
+     *
+     *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
+     *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
+     *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
+     *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
+     *  called as: <br> `cloneAttachFn(clonedElement, scope)` where:
+     *
+     *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
+     *      * `scope` - is the current scope with which the linking function is working with.
+     *
+     * Calling the linking function returns the element of the template. It is either the original
+     * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
+     *
+     * After linking the view is not updated until after a call to $digest which typically is done by
+     * Angular automatically.
+     *
+     * If you need access to the bound view, there are two ways to do it:
+     *
+     * - If you are not asking the linking function to clone the template, create the DOM element(s)
+     *   before you send them to the compiler and keep this reference around.
+     *   <pre>
+     *     var element = $compile('<p>{{total}}</p>')(scope);
+     *   </pre>
+     *
+     * - if on the other hand, you need the element to be cloned, the view reference from the original
+     *   example would not point to the clone, but rather to the original template that was cloned. In
+     *   this case, you can access the clone via the cloneAttachFn:
+     *   <pre>
+     *     var templateHTML = angular.element('<p>{{total}}</p>'),
+     *         scope = ....;
+     *
+     *     var clonedElement = $compile(templateHTML)(scope, function(clonedElement, scope) {
+ *       //attach the clone to DOM document at the right place
+ *     });
+     *
+     *     //now we have reference to the cloned DOM via `clone`
+     *   </pre>
+     *
+     *
+     * For information on how the compiler works, see the
+     * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
+     */
+
+    var $compileMinErr = minErr('$compile');
+
+    /**
+     * @ngdoc service
+     * @name ng.$compileProvider
+     * @function
+     *
+     * @description
+     */
+    $CompileProvider.$inject = ['$provide'];
+    function $CompileProvider($provide) {
+        var hasDirectives = {},
+                Suffix = 'Directive',
+                COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
+                CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
+                aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
+                imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
+
+        // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
+        // The assumption is that future DOM event attribute names will begin with
+        // 'on' and be composed of only English letters.
+        var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
+
+        /**
+         * @ngdoc function
+         * @name ng.$compileProvider#directive
+         * @methodOf ng.$compileProvider
+         * @function
+         *
+         * @description
+         * Register a new directive with the compiler.
+         *
+         * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
+         *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
+         *    names and the values are the factories.
+         * @param {function|Array} directiveFactory An injectable directive factory function. See
+         *    {@link guide/directive} for more info.
+         * @returns {ng.$compileProvider} Self for chaining.
+         */
+        this.directive = function registerDirective(name, directiveFactory) {
+            assertNotHasOwnProperty(name, 'directive');
+            if (isString(name)) {
+                assertArg(directiveFactory, 'directiveFactory');
+                if (!hasDirectives.hasOwnProperty(name)) {
+                    hasDirectives[name] = [];
+                    $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
+                        function($injector, $exceptionHandler) {
+                            var directives = [];
+                            forEach(hasDirectives[name], function(directiveFactory, index) {
+                                try {
+                                    var directive = $injector.invoke(directiveFactory);
+                                    if (isFunction(directive)) {
+                                        directive = { compile: valueFn(directive) };
+                                    } else if (!directive.compile && directive.link) {
+                                        directive.compile = valueFn(directive.link);
+                                    }
+                                    directive.priority = directive.priority || 0;
+                                    directive.index = index;
+                                    directive.name = directive.name || name;
+                                    directive.require = directive.require || (directive.controller && directive.name);
+                                    directive.restrict = directive.restrict || 'A';
+                                    directives.push(directive);
+                                } catch (e) {
+                                    $exceptionHandler(e);
+                                }
+                            });
+                            return directives;
+                        }]);
+                }
+                hasDirectives[name].push(directiveFactory);
+            } else {
+                forEach(name, reverseParams(registerDirective));
+            }
+            return this;
+        };
+
+
+        /**
+         * @ngdoc function
+         * @name ng.$compileProvider#aHrefSanitizationWhitelist
+         * @methodOf ng.$compileProvider
+         * @function
+         *
+         * @description
+         * Retrieves or overrides the default regular expression that is used for whitelisting of safe
+         * urls during a[href] sanitization.
+         *
+         * The sanitization is a security measure aimed at prevent XSS attacks via html links.
+         *
+         * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
+         * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
+         * regular expression. If a match is found, the original url is written into the dom. Otherwise,
+         * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
+         *
+         * @param {RegExp=} regexp New regexp to whitelist urls with.
+         * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
+         *    chaining otherwise.
+         */
+        this.aHrefSanitizationWhitelist = function(regexp) {
+            if (isDefined(regexp)) {
+                aHrefSanitizationWhitelist = regexp;
+                return this;
+            }
+            return aHrefSanitizationWhitelist;
+        };
+
+
+        /**
+         * @ngdoc function
+         * @name ng.$compileProvider#imgSrcSanitizationWhitelist
+         * @methodOf ng.$compileProvider
+         * @function
+         *
+         * @description
+         * Retrieves or overrides the default regular expression that is used for whitelisting of safe
+         * urls during img[src] sanitization.
+         *
+         * The sanitization is a security measure aimed at prevent XSS attacks via html links.
+         *
+         * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
+         * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
+         * regular expression. If a match is found, the original url is written into the dom. Otherwise,
+         * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
+         *
+         * @param {RegExp=} regexp New regexp to whitelist urls with.
+         * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
+         *    chaining otherwise.
+         */
+        this.imgSrcSanitizationWhitelist = function(regexp) {
+            if (isDefined(regexp)) {
+                imgSrcSanitizationWhitelist = regexp;
+                return this;
+            }
+            return imgSrcSanitizationWhitelist;
+        };
+
+
+        this.$get = [
+            '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
+            '$controller', '$rootScope', '$document', '$sce', '$animate',
+            function($injector,   $interpolate,   $exceptionHandler,   $http,   $templateCache,   $parse,
+                     $controller,   $rootScope,   $document,   $sce,   $animate) {
+
+                var Attributes = function(element, attr) {
+                    this.$$element = element;
+                    this.$attr = attr || {};
+                };
+
+                Attributes.prototype = {
+                    $normalize: directiveNormalize,
+
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$compile.directive.Attributes#$addClass
+                     * @methodOf ng.$compile.directive.Attributes
+                     * @function
+                     *
+                     * @description
+                     * Adds the CSS class value specified by the classVal parameter to the element. If animations
+                     * are enabled then an animation will be triggered for the class addition.
+                     *
+                     * @param {string} classVal The className value that will be added to the element
+                     */
+                    $addClass : function(classVal) {
+                        if(classVal && classVal.length > 0) {
+                            $animate.addClass(this.$$element, classVal);
+                        }
+                    },
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$compile.directive.Attributes#$removeClass
+                     * @methodOf ng.$compile.directive.Attributes
+                     * @function
+                     *
+                     * @description
+                     * Removes the CSS class value specified by the classVal parameter from the element. If
+                     * animations are enabled then an animation will be triggered for the class removal.
+                     *
+                     * @param {string} classVal The className value that will be removed from the element
+                     */
+                    $removeClass : function(classVal) {
+                        if(classVal && classVal.length > 0) {
+                            $animate.removeClass(this.$$element, classVal);
+                        }
+                    },
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$compile.directive.Attributes#$updateClass
+                     * @methodOf ng.$compile.directive.Attributes
+                     * @function
+                     *
+                     * @description
+                     * Adds and removes the appropriate CSS class values to the element based on the difference
+                     * between the new and old CSS class values (specified as newClasses and oldClasses).
+                     *
+                     * @param {string} newClasses The current CSS className value
+                     * @param {string} oldClasses The former CSS className value
+                     */
+                    $updateClass : function(newClasses, oldClasses) {
+                        this.$removeClass(tokenDifference(oldClasses, newClasses));
+                        this.$addClass(tokenDifference(newClasses, oldClasses));
+                    },
+
+                    /**
+                     * Set a normalized attribute on the element in a way such that all directives
+                     * can share the attribute. This function properly handles boolean attributes.
+                     * @param {string} key Normalized key. (ie ngAttribute)
+                     * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
+                     * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
+                     *     Defaults to true.
+                     * @param {string=} attrName Optional none normalized name. Defaults to key.
+                     */
+                    $set: function(key, value, writeAttr, attrName) {
+                        // TODO: decide whether or not to throw an error if "class"
+                        //is set through this function since it may cause $updateClass to
+                        //become unstable.
+
+                        var booleanKey = getBooleanAttrName(this.$$element[0], key),
+                                normalizedVal,
+                                nodeName;
+
+                        if (booleanKey) {
+                            this.$$element.prop(key, value);
+                            attrName = booleanKey;
+                        }
+
+                        this[key] = value;
+
+                        // translate normalized key to actual key
+                        if (attrName) {
+                            this.$attr[key] = attrName;
+                        } else {
+                            attrName = this.$attr[key];
+                            if (!attrName) {
+                                this.$attr[key] = attrName = snake_case(key, '-');
+                            }
+                        }
+
+                        nodeName = nodeName_(this.$$element);
+
+                        // sanitize a[href] and img[src] values
+                        if ((nodeName === 'A' && key === 'href') ||
+                                (nodeName === 'IMG' && key === 'src')) {
+                            // NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
+                            if (!msie || msie >= 8 ) {
+                                normalizedVal = urlResolve(value).href;
+                                if (normalizedVal !== '') {
+                                    if ((key === 'href' && !normalizedVal.match(aHrefSanitizationWhitelist)) ||
+                                            (key === 'src' && !normalizedVal.match(imgSrcSanitizationWhitelist))) {
+                                        this[key] = value = 'unsafe:' + normalizedVal;
+                                    }
+                                }
+                            }
+                        }
+
+                        if (writeAttr !== false) {
+                            if (value === null || value === undefined) {
+                                this.$$element.removeAttr(attrName);
+                            } else {
+                                this.$$element.attr(attrName, value);
+                            }
+                        }
+
+                        // fire observers
+                        var $$observers = this.$$observers;
+                        $$observers && forEach($$observers[key], function(fn) {
+                            try {
+                                fn(value);
+                            } catch (e) {
+                                $exceptionHandler(e);
+                            }
+                        });
+                    },
+
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$compile.directive.Attributes#$observe
+                     * @methodOf ng.$compile.directive.Attributes
+                     * @function
+                     *
+                     * @description
+                     * Observes an interpolated attribute.
+                     *
+                     * The observer function will be invoked once during the next `$digest` following
+                     * compilation. The observer is then invoked whenever the interpolated value
+                     * changes.
+                     *
+                     * @param {string} key Normalized key. (ie ngAttribute) .
+                     * @param {function(interpolatedValue)} fn Function that will be called whenever
+                     the interpolated value of the attribute changes.
+                     *        See the {@link guide/directive#Attributes Directives} guide for more info.
+                     * @returns {function()} the `fn` parameter.
+                     */
+                    $observe: function(key, fn) {
+                        var attrs = this,
+                                $$observers = (attrs.$$observers || (attrs.$$observers = {})),
+                                listeners = ($$observers[key] || ($$observers[key] = []));
+
+                        listeners.push(fn);
+                        $rootScope.$evalAsync(function() {
+                            if (!listeners.$$inter) {
+                                // no one registered attribute interpolation function, so lets call it manually
+                                fn(attrs[key]);
+                            }
+                        });
+                        return fn;
+                    }
+                };
+
+                var startSymbol = $interpolate.startSymbol(),
+                        endSymbol = $interpolate.endSymbol(),
+                        denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
+                                ? identity
+                                : function denormalizeTemplate(template) {
+                            return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
+                        },
+                        NG_ATTR_BINDING = /^ngAttr[A-Z]/;
+
+
+                return compile;
+
+                //================================
+
+                function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
+                                 previousCompileContext) {
+                    if (!($compileNodes instanceof jqLite)) {
+                        // jquery always rewraps, whereas we need to preserve the original selector so that we can
+                        // modify it.
+                        $compileNodes = jqLite($compileNodes);
+                    }
+                    // We can not compile top level text elements since text nodes can be merged and we will
+                    // not be able to attach scope data to them, so we will wrap them in <span>
+                    forEach($compileNodes, function(node, index){
+                        if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) {
+                            $compileNodes[index] = node = jqLite(node).wrap('<span></span>').parent()[0];
+                        }
+                    });
+                    var compositeLinkFn =
+                            compileNodes($compileNodes, transcludeFn, $compileNodes,
+                                    maxPriority, ignoreDirective, previousCompileContext);
+                    return function publicLinkFn(scope, cloneConnectFn, transcludeControllers){
+                        assertArg(scope, 'scope');
+                        // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
+                        // and sometimes changes the structure of the DOM.
+                        var $linkNode = cloneConnectFn
+                                ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
+                                : $compileNodes;
+
+                        forEach(transcludeControllers, function(instance, name) {
+                            $linkNode.data('$' + name + 'Controller', instance);
+                        });
+
+                        // Attach scope only to non-text nodes.
+                        for(var i = 0, ii = $linkNode.length; i<ii; i++) {
+                            var node = $linkNode[i];
+                            if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
+                                $linkNode.eq(i).data('$scope', scope);
+                            }
+                        }
+                        safeAddClass($linkNode, 'ng-scope');
+                        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
+                        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
+                        return $linkNode;
+                    };
+                }
+
+                function safeAddClass($element, className) {
+                    try {
+                        $element.addClass(className);
+                    } catch(e) {
+                        // ignore, since it means that we are trying to set class on
+                        // SVG element, where class name is read-only.
+                    }
+                }
+
+                /**
+                 * Compile function matches each node in nodeList against the directives. Once all directives
+                 * for a particular node are collected their compile functions are executed. The compile
+                 * functions return values - the linking functions - are combined into a composite linking
+                 * function, which is the a linking function for the node.
+                 *
+                 * @param {NodeList} nodeList an array of nodes or NodeList to compile
+                 * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
+                 *        scope argument is auto-generated to the new child of the transcluded parent scope.
+                 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
+                 *        the rootElement must be set the jqLite collection of the compile root. This is
+                 *        needed so that the jqLite collection items can be replaced with widgets.
+                 * @param {number=} max directive priority
+                 * @returns {?function} A composite linking function of all of the matched directives or null.
+                 */
+                function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
+                                      previousCompileContext) {
+                    var linkFns = [],
+                            nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
+
+                    for(var i = 0; i < nodeList.length; i++) {
+                        attrs = new Attributes();
+
+                        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
+                        directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
+                                ignoreDirective);
+
+                        nodeLinkFn = (directives.length)
+                                ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
+                                null, [], [], previousCompileContext)
+                                : null;
+
+                        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
+                                !nodeList[i].childNodes ||
+                                !nodeList[i].childNodes.length)
+                                ? null
+                                : compileNodes(nodeList[i].childNodes,
+                                nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
+
+                        linkFns.push(nodeLinkFn);
+                        linkFns.push(childLinkFn);
+                        linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
+                        //use the previous context only for the first element in the virtual group
+                        previousCompileContext = null;
+                    }
+
+                    // return a linking function if we have found anything, null otherwise
+                    return linkFnFound ? compositeLinkFn : null;
+
+                    function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
+                        var nodeLinkFn, childLinkFn, node, $node, childScope, childTranscludeFn, i, ii, n;
+
+                        // copy nodeList so that linking doesn't break due to live list updates.
+                        var stableNodeList = [];
+                        for (i = 0, ii = nodeList.length; i < ii; i++) {
+                            stableNodeList.push(nodeList[i]);
+                        }
+
+                        for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
+                            node = stableNodeList[n];
+                            nodeLinkFn = linkFns[i++];
+                            childLinkFn = linkFns[i++];
+                            $node = jqLite(node);
+
+                            if (nodeLinkFn) {
+                                if (nodeLinkFn.scope) {
+                                    childScope = scope.$new();
+                                    $node.data('$scope', childScope);
+                                    safeAddClass($node, 'ng-scope');
+                                } else {
+                                    childScope = scope;
+                                }
+                                childTranscludeFn = nodeLinkFn.transclude;
+                                if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
+                                    nodeLinkFn(childLinkFn, childScope, node, $rootElement,
+                                            createBoundTranscludeFn(scope, childTranscludeFn || transcludeFn)
+                                    );
+                                } else {
+                                    nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
+                                }
+                            } else if (childLinkFn) {
+                                childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
+                            }
+                        }
+                    }
+                }
+
+                function createBoundTranscludeFn(scope, transcludeFn) {
+                    return function boundTranscludeFn(transcludedScope, cloneFn, controllers) {
+                        var scopeCreated = false;
+
+                        if (!transcludedScope) {
+                            transcludedScope = scope.$new();
+                            transcludedScope.$$transcluded = true;
+                            scopeCreated = true;
+                        }
+
+                        var clone = transcludeFn(transcludedScope, cloneFn, controllers);
+                        if (scopeCreated) {
+                            clone.on('$destroy', bind(transcludedScope, transcludedScope.$destroy));
+                        }
+                        return clone;
+                    };
+                }
+
+                /**
+                 * Looks for directives on the given node and adds them to the directive collection which is
+                 * sorted.
+                 *
+                 * @param node Node to search.
+                 * @param directives An array to which the directives are added to. This array is sorted before
+                 *        the function returns.
+                 * @param attrs The shared attrs object which is used to populate the normalized attributes.
+                 * @param {number=} maxPriority Max directive priority.
+                 */
+                function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
+                    var nodeType = node.nodeType,
+                            attrsMap = attrs.$attr,
+                            match,
+                            className;
+
+                    switch(nodeType) {
+                        case 1: /* Element */
+                            // use the node name: <directive>
+                            addDirective(directives,
+                                    directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority, ignoreDirective);
+
+                            // iterate over the attributes
+                            for (var attr, name, nName, ngAttrName, value, nAttrs = node.attributes,
+                                         j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
+                                var attrStartName = false;
+                                var attrEndName = false;
+
+                                attr = nAttrs[j];
+                                if (!msie || msie >= 8 || attr.specified) {
+                                    name = attr.name;
+                                    // support ngAttr attribute binding
+                                    ngAttrName = directiveNormalize(name);
+                                    if (NG_ATTR_BINDING.test(ngAttrName)) {
+                                        name = snake_case(ngAttrName.substr(6), '-');
+                                    }
+
+                                    var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
+                                    if (ngAttrName === directiveNName + 'Start') {
+                                        attrStartName = name;
+                                        attrEndName = name.substr(0, name.length - 5) + 'end';
+                                        name = name.substr(0, name.length - 6);
+                                    }
+
+                                    nName = directiveNormalize(name.toLowerCase());
+                                    attrsMap[nName] = name;
+                                    attrs[nName] = value = trim((msie && name == 'href')
+                                            ? decodeURIComponent(node.getAttribute(name, 2))
+                                            : attr.value);
+                                    if (getBooleanAttrName(node, nName)) {
+                                        attrs[nName] = true; // presence means true
+                                    }
+                                    addAttrInterpolateDirective(node, directives, value, nName);
+                                    addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
+                                            attrEndName);
+                                }
+                            }
+
+                            // use class as directive
+                            className = node.className;
+                            if (isString(className) && className !== '') {
+                                while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
+                                    nName = directiveNormalize(match[2]);
+                                    if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
+                                        attrs[nName] = trim(match[3]);
+                                    }
+                                    className = className.substr(match.index + match[0].length);
+                                }
+                            }
+                            break;
+                        case 3: /* Text Node */
+                            addTextInterpolateDirective(directives, node.nodeValue);
+                            break;
+                        case 8: /* Comment */
+                            try {
+                                match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
+                                if (match) {
+                                    nName = directiveNormalize(match[1]);
+                                    if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
+                                        attrs[nName] = trim(match[2]);
+                                    }
+                                }
+                            } catch (e) {
+                                // turns out that under some circumstances IE9 throws errors when one attempts to read
+                                // comment's node value.
+                                // Just ignore it and continue. (Can't seem to reproduce in test case.)
+                            }
+                            break;
+                    }
+
+                    directives.sort(byPriority);
+                    return directives;
+                }
+
+                /**
+                 * Given a node with an directive-start it collects all of the siblings until it finds
+                 * directive-end.
+                 * @param node
+                 * @param attrStart
+                 * @param attrEnd
+                 * @returns {*}
+                 */
+                function groupScan(node, attrStart, attrEnd) {
+                    var nodes = [];
+                    var depth = 0;
+                    if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
+                        var startNode = node;
+                        do {
+                            if (!node) {
+                                throw $compileMinErr('uterdir',
+                                        "Unterminated attribute, found '{0}' but no matching '{1}' found.",
+                                        attrStart, attrEnd);
+                            }
+                            if (node.nodeType == 1 /** Element **/) {
+                                if (node.hasAttribute(attrStart)) depth++;
+                                if (node.hasAttribute(attrEnd)) depth--;
+                            }
+                            nodes.push(node);
+                            node = node.nextSibling;
+                        } while (depth > 0);
+                    } else {
+                        nodes.push(node);
+                    }
+
+                    return jqLite(nodes);
+                }
+
+                /**
+                 * Wrapper for linking function which converts normal linking function into a grouped
+                 * linking function.
+                 * @param linkFn
+                 * @param attrStart
+                 * @param attrEnd
+                 * @returns {Function}
+                 */
+                function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
+                    return function(scope, element, attrs, controllers, transcludeFn) {
+                        element = groupScan(element[0], attrStart, attrEnd);
+                        return linkFn(scope, element, attrs, controllers, transcludeFn);
+                    };
+                }
+
+                /**
+                 * Once the directives have been collected, their compile functions are executed. This method
+                 * is responsible for inlining directive templates as well as terminating the application
+                 * of the directives if the terminal directive has been reached.
+                 *
+                 * @param {Array} directives Array of collected directives to execute their compile function.
+                 *        this needs to be pre-sorted by priority order.
+                 * @param {Node} compileNode The raw DOM node to apply the compile functions to
+                 * @param {Object} templateAttrs The shared attribute function
+                 * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
+                 *                                                  scope argument is auto-generated to the new
+                 *                                                  child of the transcluded parent scope.
+                 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
+                 *                              argument has the root jqLite array so that we can replace nodes
+                 *                              on it.
+                 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
+                 *                                           compiling the transclusion.
+                 * @param {Array.<Function>} preLinkFns
+                 * @param {Array.<Function>} postLinkFns
+                 * @param {Object} previousCompileContext Context used for previous compilation of the current
+                 *                                        node
+                 * @returns linkFn
+                 */
+                function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
+                                               jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
+                                               previousCompileContext) {
+                    previousCompileContext = previousCompileContext || {};
+
+                    var terminalPriority = -Number.MAX_VALUE,
+                            newScopeDirective,
+                            controllerDirectives = previousCompileContext.controllerDirectives,
+                            newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
+                            templateDirective = previousCompileContext.templateDirective,
+                            nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
+                            hasTranscludeDirective = false,
+                            hasElementTranscludeDirective = false,
+                            $compileNode = templateAttrs.$$element = jqLite(compileNode),
+                            directive,
+                            directiveName,
+                            $template,
+                            replaceDirective = originalReplaceDirective,
+                            childTranscludeFn = transcludeFn,
+                            linkFn,
+                            directiveValue;
+
+                    // executes all directives on the current element
+                    for(var i = 0, ii = directives.length; i < ii; i++) {
+                        directive = directives[i];
+                        var attrStart = directive.$$start;
+                        var attrEnd = directive.$$end;
+
+                        // collect multiblock sections
+                        if (attrStart) {
+                            $compileNode = groupScan(compileNode, attrStart, attrEnd);
+                        }
+                        $template = undefined;
+
+                        if (terminalPriority > directive.priority) {
+                            break; // prevent further processing of directives
+                        }
+
+                        if (directiveValue = directive.scope) {
+                            newScopeDirective = newScopeDirective || directive;
+
+                            // skip the check for directives with async templates, we'll check the derived sync
+                            // directive when the template arrives
+                            if (!directive.templateUrl) {
+                                assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
+                                        $compileNode);
+                                if (isObject(directiveValue)) {
+                                    newIsolateScopeDirective = directive;
+                                }
+                            }
+                        }
+
+                        directiveName = directive.name;
+
+                        if (!directive.templateUrl && directive.controller) {
+                            directiveValue = directive.controller;
+                            controllerDirectives = controllerDirectives || {};
+                            assertNoDuplicate("'" + directiveName + "' controller",
+                                    controllerDirectives[directiveName], directive, $compileNode);
+                            controllerDirectives[directiveName] = directive;
+                        }
+
+                        if (directiveValue = directive.transclude) {
+                            hasTranscludeDirective = true;
+
+                            // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
+                            // This option should only be used by directives that know how to how to safely handle element transclusion,
+                            // where the transcluded nodes are added or replaced after linking.
+                            if (!directive.$$tlb) {
+                                assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
+                                nonTlbTranscludeDirective = directive;
+                            }
+
+                            if (directiveValue == 'element') {
+                                hasElementTranscludeDirective = true;
+                                terminalPriority = directive.priority;
+                                $template = groupScan(compileNode, attrStart, attrEnd);
+                                $compileNode = templateAttrs.$$element =
+                                        jqLite(document.createComment(' ' + directiveName + ': ' +
+                                                templateAttrs[directiveName] + ' '));
+                                compileNode = $compileNode[0];
+                                replaceWith(jqCollection, jqLite(sliceArgs($template)), compileNode);
+
+                                childTranscludeFn = compile($template, transcludeFn, terminalPriority,
+                                        replaceDirective && replaceDirective.name, {
+                                            // Don't pass in:
+                                            // - controllerDirectives - otherwise we'll create duplicates controllers
+                                            // - newIsolateScopeDirective or templateDirective - combining templates with
+                                            //   element transclusion doesn't make sense.
+                                            //
+                                            // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
+                                            // on the same element more than once.
+                                            nonTlbTranscludeDirective: nonTlbTranscludeDirective
+                                        });
+                            } else {
+                                $template = jqLite(jqLiteClone(compileNode)).contents();
+                                $compileNode.html(''); // clear contents
+                                childTranscludeFn = compile($template, transcludeFn);
+                            }
+                        }
+
+                        if (directive.template) {
+                            assertNoDuplicate('template', templateDirective, directive, $compileNode);
+                            templateDirective = directive;
+
+                            directiveValue = (isFunction(directive.template))
+                                    ? directive.template($compileNode, templateAttrs)
+                                    : directive.template;
+
+                            directiveValue = denormalizeTemplate(directiveValue);
+
+                            if (directive.replace) {
+                                replaceDirective = directive;
+                                $template = jqLite('<div>' +
+                                        trim(directiveValue) +
+                                        '</div>').contents();
+                                compileNode = $template[0];
+
+                                if ($template.length != 1 || compileNode.nodeType !== 1) {
+                                    throw $compileMinErr('tplrt',
+                                            "Template for directive '{0}' must have exactly one root element. {1}",
+                                            directiveName, '');
+                                }
+
+                                replaceWith(jqCollection, $compileNode, compileNode);
+
+                                var newTemplateAttrs = {$attr: {}};
+
+                                // combine directives from the original node and from the template:
+                                // - take the array of directives for this element
+                                // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
+                                // - collect directives from the template and sort them by priority
+                                // - combine directives as: processed + template + unprocessed
+                                var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
+                                var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
+
+                                if (newIsolateScopeDirective) {
+                                    markDirectivesAsIsolate(templateDirectives);
+                                }
+                                directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
+                                mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
+
+                                ii = directives.length;
+                            } else {
+                                $compileNode.html(directiveValue);
+                            }
+                        }
+
+                        if (directive.templateUrl) {
+                            assertNoDuplicate('template', templateDirective, directive, $compileNode);
+                            templateDirective = directive;
+
+                            if (directive.replace) {
+                                replaceDirective = directive;
+                            }
+
+                            nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
+                                    templateAttrs, jqCollection, childTranscludeFn, preLinkFns, postLinkFns, {
+                                        controllerDirectives: controllerDirectives,
+                                        newIsolateScopeDirective: newIsolateScopeDirective,
+                                        templateDirective: templateDirective,
+                                        nonTlbTranscludeDirective: nonTlbTranscludeDirective
+                                    });
+                            ii = directives.length;
+                        } else if (directive.compile) {
+                            try {
+                                linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
+                                if (isFunction(linkFn)) {
+                                    addLinkFns(null, linkFn, attrStart, attrEnd);
+                                } else if (linkFn) {
+                                    addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
+                                }
+                            } catch (e) {
+                                $exceptionHandler(e, startingTag($compileNode));
+                            }
+                        }
+
+                        if (directive.terminal) {
+                            nodeLinkFn.terminal = true;
+                            terminalPriority = Math.max(terminalPriority, directive.priority);
+                        }
+
+                    }
+
+                    nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
+                    nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn;
+
+                    // might be normal or delayed nodeLinkFn depending on if templateUrl is present
+                    return nodeLinkFn;
+
+                    ////////////////////
+
+                    function addLinkFns(pre, post, attrStart, attrEnd) {
+                        if (pre) {
+                            if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
+                            pre.require = directive.require;
+                            if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
+                                pre = cloneAndAnnotateFn(pre, {isolateScope: true});
+                            }
+                            preLinkFns.push(pre);
+                        }
+                        if (post) {
+                            if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
+                            post.require = directive.require;
+                            if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
+                                post = cloneAndAnnotateFn(post, {isolateScope: true});
+                            }
+                            postLinkFns.push(post);
+                        }
+                    }
+
+
+                    function getControllers(require, $element, elementControllers) {
+                        var value, retrievalMethod = 'data', optional = false;
+                        if (isString(require)) {
+                            while((value = require.charAt(0)) == '^' || value == '?') {
+                                require = require.substr(1);
+                                if (value == '^') {
+                                    retrievalMethod = 'inheritedData';
+                                }
+                                optional = optional || value == '?';
+                            }
+                            value = null;
+
+                            if (elementControllers && retrievalMethod === 'data') {
+                                value = elementControllers[require];
+                            }
+                            value = value || $element[retrievalMethod]('$' + require + 'Controller');
+
+                            if (!value && !optional) {
+                                throw $compileMinErr('ctreq',
+                                        "Controller '{0}', required by directive '{1}', can't be found!",
+                                        require, directiveName);
+                            }
+                            return value;
+                        } else if (isArray(require)) {
+                            value = [];
+                            forEach(require, function(require) {
+                                value.push(getControllers(require, $element, elementControllers));
+                            });
+                        }
+                        return value;
+                    }
+
+
+                    function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
+                        var attrs, $element, i, ii, linkFn, controller, isolateScope, elementControllers = {}, transcludeFn;
+
+                        if (compileNode === linkNode) {
+                            attrs = templateAttrs;
+                        } else {
+                            attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr));
+                        }
+                        $element = attrs.$$element;
+
+                        if (newIsolateScopeDirective) {
+                            var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/;
+                            var $linkNode = jqLite(linkNode);
+
+                            isolateScope = scope.$new(true);
+
+                            if (templateDirective && (templateDirective === newIsolateScopeDirective.$$originalDirective)) {
+                                $linkNode.data('$isolateScope', isolateScope) ;
+                            } else {
+                                $linkNode.data('$isolateScopeNoTemplate', isolateScope);
+                            }
+
+
+
+                            safeAddClass($linkNode, 'ng-isolate-scope');
+
+                            forEach(newIsolateScopeDirective.scope, function(definition, scopeName) {
+                                var match = definition.match(LOCAL_REGEXP) || [],
+                                        attrName = match[3] || scopeName,
+                                        optional = (match[2] == '?'),
+                                        mode = match[1], // @, =, or &
+                                        lastValue,
+                                        parentGet, parentSet;
+
+                                isolateScope.$$isolateBindings[scopeName] = mode + attrName;
+
+                                switch (mode) {
+
+                                    case '@':
+                                        attrs.$observe(attrName, function(value) {
+                                            isolateScope[scopeName] = value;
+                                        });
+                                        attrs.$$observers[attrName].$$scope = scope;
+                                        if( attrs[attrName] ) {
+                                            // If the attribute has been provided then we trigger an interpolation to ensure
+                                            // the value is there for use in the link fn
+                                            isolateScope[scopeName] = $interpolate(attrs[attrName])(scope);
+                                        }
+                                        break;
+
+                                    case '=':
+                                        if (optional && !attrs[attrName]) {
+                                            return;
+                                        }
+                                        parentGet = $parse(attrs[attrName]);
+                                        parentSet = parentGet.assign || function() {
+                                            // reset the change, or we will throw this exception on every $digest
+                                            lastValue = isolateScope[scopeName] = parentGet(scope);
+                                            throw $compileMinErr('nonassign',
+                                                    "Expression '{0}' used with directive '{1}' is non-assignable!",
+                                                    attrs[attrName], newIsolateScopeDirective.name);
+                                        };
+                                        lastValue = isolateScope[scopeName] = parentGet(scope);
+                                        isolateScope.$watch(function parentValueWatch() {
+                                            var parentValue = parentGet(scope);
+
+                                            if (parentValue !== isolateScope[scopeName]) {
+                                                // we are out of sync and need to copy
+                                                if (parentValue !== lastValue) {
+                                                    // parent changed and it has precedence
+                                                    lastValue = isolateScope[scopeName] = parentValue;
+                                                } else {
+                                                    // if the parent can be assigned then do so
+                                                    parentSet(scope, parentValue = lastValue = isolateScope[scopeName]);
+                                                }
+                                            }
+                                            return parentValue;
+                                        });
+                                        break;
+
+                                    case '&':
+                                        parentGet = $parse(attrs[attrName]);
+                                        isolateScope[scopeName] = function(locals) {
+                                            return parentGet(scope, locals);
+                                        };
+                                        break;
+
+                                    default:
+                                        throw $compileMinErr('iscp',
+                                                "Invalid isolate scope definition for directive '{0}'." +
+                                                        " Definition: {... {1}: '{2}' ...}",
+                                                newIsolateScopeDirective.name, scopeName, definition);
+                                }
+                            });
+                        }
+                        transcludeFn = boundTranscludeFn && controllersBoundTransclude;
+                        if (controllerDirectives) {
+                            forEach(controllerDirectives, function(directive) {
+                                var locals = {
+                                    $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
+                                    $element: $element,
+                                    $attrs: attrs,
+                                    $transclude: transcludeFn
+                                }, controllerInstance;
+
+                                controller = directive.controller;
+                                if (controller == '@') {
+                                    controller = attrs[directive.name];
+                                }
+
+                                controllerInstance = $controller(controller, locals);
+                                // For directives with element transclusion the element is a comment,
+                                // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
+                                // clean up (http://bugs.jquery.com/ticket/8335).
+                                // Instead, we save the controllers for the element in a local hash and attach to .data
+                                // later, once we have the actual element.
+                                elementControllers[directive.name] = controllerInstance;
+                                if (!hasElementTranscludeDirective) {
+                                    $element.data('$' + directive.name + 'Controller', controllerInstance);
+                                }
+
+                                if (directive.controllerAs) {
+                                    locals.$scope[directive.controllerAs] = controllerInstance;
+                                }
+                            });
+                        }
+
+                        // PRELINKING
+                        for(i = 0, ii = preLinkFns.length; i < ii; i++) {
+                            try {
+                                linkFn = preLinkFns[i];
+                                linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs,
+                                        linkFn.require && getControllers(linkFn.require, $element, elementControllers), transcludeFn);
+                            } catch (e) {
+                                $exceptionHandler(e, startingTag($element));
+                            }
+                        }
+
+                        // RECURSION
+                        // We only pass the isolate scope, if the isolate directive has a template,
+                        // otherwise the child elements do not belong to the isolate directive.
+                        var scopeToChild = scope;
+                        if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
+                            scopeToChild = isolateScope;
+                        }
+                        childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
+
+                        // POSTLINKING
+                        for(i = postLinkFns.length - 1; i >= 0; i--) {
+                            try {
+                                linkFn = postLinkFns[i];
+                                linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs,
+                                        linkFn.require && getControllers(linkFn.require, $element, elementControllers), transcludeFn);
+                            } catch (e) {
+                                $exceptionHandler(e, startingTag($element));
+                            }
+                        }
+
+                        // This is the function that is injected as `$transclude`.
+                        function controllersBoundTransclude(scope, cloneAttachFn) {
+                            var transcludeControllers;
+
+                            // no scope passed
+                            if (arguments.length < 2) {
+                                cloneAttachFn = scope;
+                                scope = undefined;
+                            }
+
+                            if (hasElementTranscludeDirective) {
+                                transcludeControllers = elementControllers;
+                            }
+
+                            return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers);
+                        }
+                    }
+                }
+
+                function markDirectivesAsIsolate(directives) {
+                    // mark all directives as needing isolate scope.
+                    for (var j = 0, jj = directives.length; j < jj; j++) {
+                        directives[j] = inherit(directives[j], {$$isolateScope: true});
+                    }
+                }
+
+                /**
+                 * looks up the directive and decorates it with exception handling and proper parameters. We
+                 * call this the boundDirective.
+                 *
+                 * @param {string} name name of the directive to look up.
+                 * @param {string} location The directive must be found in specific format.
+                 *   String containing any of theses characters:
+                 *
+                 *   * `E`: element name
+                 *   * `A': attribute
+                 *   * `C`: class
+                 *   * `M`: comment
+                 * @returns true if directive was added.
+                 */
+                function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
+                                      endAttrName) {
+                    if (name === ignoreDirective) return null;
+                    var match = null;
+                    if (hasDirectives.hasOwnProperty(name)) {
+                        for(var directive, directives = $injector.get(name + Suffix),
+                                    i = 0, ii = directives.length; i<ii; i++) {
+                            try {
+                                directive = directives[i];
+                                if ( (maxPriority === undefined || maxPriority > directive.priority) &&
+                                        directive.restrict.indexOf(location) != -1) {
+                                    if (startAttrName) {
+                                        directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
+                                    }
+                                    tDirectives.push(directive);
+                                    match = directive;
+                                }
+                            } catch(e) { $exceptionHandler(e); }
+                        }
+                    }
+                    return match;
+                }
+
+
+                /**
+                 * When the element is replaced with HTML template then the new attributes
+                 * on the template need to be merged with the existing attributes in the DOM.
+                 * The desired effect is to have both of the attributes present.
+                 *
+                 * @param {object} dst destination attributes (original DOM)
+                 * @param {object} src source attributes (from the directive template)
+                 */
+                function mergeTemplateAttributes(dst, src) {
+                    var srcAttr = src.$attr,
+                            dstAttr = dst.$attr,
+                            $element = dst.$$element;
+
+                    // reapply the old attributes to the new element
+                    forEach(dst, function(value, key) {
+                        if (key.charAt(0) != '$') {
+                            if (src[key]) {
+                                value += (key === 'style' ? ';' : ' ') + src[key];
+                            }
+                            dst.$set(key, value, true, srcAttr[key]);
+                        }
+                    });
+
+                    // copy the new attributes on the old attrs object
+                    forEach(src, function(value, key) {
+                        if (key == 'class') {
+                            safeAddClass($element, value);
+                            dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
+                        } else if (key == 'style') {
+                            $element.attr('style', $element.attr('style') + ';' + value);
+                            dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
+                            // `dst` will never contain hasOwnProperty as DOM parser won't let it.
+                            // You will get an "InvalidCharacterError: DOM Exception 5" error if you
+                            // have an attribute like "has-own-property" or "data-has-own-property", etc.
+                        } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
+                            dst[key] = value;
+                            dstAttr[key] = srcAttr[key];
+                        }
+                    });
+                }
+
+
+                function compileTemplateUrl(directives, $compileNode, tAttrs,
+                                            $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
+                    var linkQueue = [],
+                            afterTemplateNodeLinkFn,
+                            afterTemplateChildLinkFn,
+                            beforeTemplateCompileNode = $compileNode[0],
+                            origAsyncDirective = directives.shift(),
+                    // The fact that we have to copy and patch the directive seems wrong!
+                            derivedSyncDirective = extend({}, origAsyncDirective, {
+                                templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
+                            }),
+                            templateUrl = (isFunction(origAsyncDirective.templateUrl))
+                                    ? origAsyncDirective.templateUrl($compileNode, tAttrs)
+                                    : origAsyncDirective.templateUrl;
+
+                    $compileNode.html('');
+
+                    $http.get($sce.getTrustedResourceUrl(templateUrl), {cache: $templateCache}).
+                            success(function(content) {
+                                var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
+
+                                content = denormalizeTemplate(content);
+
+                                if (origAsyncDirective.replace) {
+                                    $template = jqLite('<div>' + trim(content) + '</div>').contents();
+                                    compileNode = $template[0];
+
+                                    if ($template.length != 1 || compileNode.nodeType !== 1) {
+                                        throw $compileMinErr('tplrt',
+                                                "Template for directive '{0}' must have exactly one root element. {1}",
+                                                origAsyncDirective.name, templateUrl);
+                                    }
+
+                                    tempTemplateAttrs = {$attr: {}};
+                                    replaceWith($rootElement, $compileNode, compileNode);
+                                    var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
+
+                                    if (isObject(origAsyncDirective.scope)) {
+                                        markDirectivesAsIsolate(templateDirectives);
+                                    }
+                                    directives = templateDirectives.concat(directives);
+                                    mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
+                                } else {
+                                    compileNode = beforeTemplateCompileNode;
+                                    $compileNode.html(content);
+                                }
+
+                                directives.unshift(derivedSyncDirective);
+
+                                afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
+                                        childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
+                                        previousCompileContext);
+                                forEach($rootElement, function(node, i) {
+                                    if (node == compileNode) {
+                                        $rootElement[i] = $compileNode[0];
+                                    }
+                                });
+                                afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
+
+
+                                while(linkQueue.length) {
+                                    var scope = linkQueue.shift(),
+                                            beforeTemplateLinkNode = linkQueue.shift(),
+                                            linkRootElement = linkQueue.shift(),
+                                            boundTranscludeFn = linkQueue.shift(),
+                                            linkNode = $compileNode[0];
+
+                                    if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
+                                        // it was cloned therefore we have to clone as well.
+                                        linkNode = jqLiteClone(compileNode);
+                                        replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
+                                    }
+                                    if (afterTemplateNodeLinkFn.transclude) {
+                                        childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude);
+                                    } else {
+                                        childBoundTranscludeFn = boundTranscludeFn;
+                                    }
+                                    afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
+                                            childBoundTranscludeFn);
+                                }
+                                linkQueue = null;
+                            }).
+                            error(function(response, code, headers, config) {
+                                throw $compileMinErr('tpload', 'Failed to load template: {0}', config.url);
+                            });
+
+                    return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
+                        if (linkQueue) {
+                            linkQueue.push(scope);
+                            linkQueue.push(node);
+                            linkQueue.push(rootElement);
+                            linkQueue.push(boundTranscludeFn);
+                        } else {
+                            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, boundTranscludeFn);
+                        }
+                    };
+                }
+
+
+                /**
+                 * Sorting function for bound directives.
+                 */
+                function byPriority(a, b) {
+                    var diff = b.priority - a.priority;
+                    if (diff !== 0) return diff;
+                    if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
+                    return a.index - b.index;
+                }
+
+
+                function assertNoDuplicate(what, previousDirective, directive, element) {
+                    if (previousDirective) {
+                        throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}',
+                                previousDirective.name, directive.name, what, startingTag(element));
+                    }
+                }
+
+
+                function addTextInterpolateDirective(directives, text) {
+                    var interpolateFn = $interpolate(text, true);
+                    if (interpolateFn) {
+                        directives.push({
+                            priority: 0,
+                            compile: valueFn(function textInterpolateLinkFn(scope, node) {
+                                var parent = node.parent(),
+                                        bindings = parent.data('$binding') || [];
+                                bindings.push(interpolateFn);
+                                safeAddClass(parent.data('$binding', bindings), 'ng-binding');
+                                scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
+                                    node[0].nodeValue = value;
+                                });
+                            })
+                        });
+                    }
+                }
+
+
+                function getTrustedContext(node, attrNormalizedName) {
+                    if (attrNormalizedName == "srcdoc") {
+                        return $sce.HTML;
+                    }
+                    var tag = nodeName_(node);
+                    // maction[xlink:href] can source SVG.  It's not limited to <maction>.
+                    if (attrNormalizedName == "xlinkHref" ||
+                            (tag == "FORM" && attrNormalizedName == "action") ||
+                            (tag != "IMG" && (attrNormalizedName == "src" ||
+                                    attrNormalizedName == "ngSrc"))) {
+                        return $sce.RESOURCE_URL;
+                    }
+                }
+
+
+                function addAttrInterpolateDirective(node, directives, value, name) {
+                    var interpolateFn = $interpolate(value, true);
+
+                    // no interpolation found -> ignore
+                    if (!interpolateFn) return;
+
+
+                    if (name === "multiple" && nodeName_(node) === "SELECT") {
+                        throw $compileMinErr("selmulti",
+                                "Binding to the 'multiple' attribute is not supported. Element: {0}",
+                                startingTag(node));
+                    }
+
+                    directives.push({
+                        priority: 100,
+                        compile: function() {
+                            return {
+                                pre: function attrInterpolatePreLinkFn(scope, element, attr) {
+                                    var $$observers = (attr.$$observers || (attr.$$observers = {}));
+
+                                    if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
+                                        throw $compileMinErr('nodomevents',
+                                                "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
+                                                        "ng- versions (such as ng-click instead of onclick) instead.");
+                                    }
+
+                                    // we need to interpolate again, in case the attribute value has been updated
+                                    // (e.g. by another directive's compile function)
+                                    interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name));
+
+                                    // if attribute was updated so that there is no interpolation going on we don't want to
+                                    // register any observers
+                                    if (!interpolateFn) return;
+
+                                    // TODO(i): this should likely be attr.$set(name, iterpolateFn(scope) so that we reset the
+                                    // actual attr value
+                                    attr[name] = interpolateFn(scope);
+                                    ($$observers[name] || ($$observers[name] = [])).$$inter = true;
+                                    (attr.$$observers && attr.$$observers[name].$$scope || scope).
+                                            $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
+                                                //special case for class attribute addition + removal
+                                                //so that class changes can tap into the animation
+                                                //hooks provided by the $animate service. Be sure to
+                                                //skip animations when the first digest occurs (when
+                                                //both the new and the old values are the same) since
+                                                //the CSS classes are the non-interpolated values
+                                                if(name === 'class' && newValue != oldValue) {
+                                                    attr.$updateClass(newValue, oldValue);
+                                                } else {
+                                                    attr.$set(name, newValue);
+                                                }
+                                            });
+                                }
+                            };
+                        }
+                    });
+                }
+
+
+                /**
+                 * This is a special jqLite.replaceWith, which can replace items which
+                 * have no parents, provided that the containing jqLite collection is provided.
+                 *
+                 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
+                 *                               in the root of the tree.
+                 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
+                 *                                  the shell, but replace its DOM node reference.
+                 * @param {Node} newNode The new DOM node.
+                 */
+                function replaceWith($rootElement, elementsToRemove, newNode) {
+                    var firstElementToRemove = elementsToRemove[0],
+                            removeCount = elementsToRemove.length,
+                            parent = firstElementToRemove.parentNode,
+                            i, ii;
+
+                    if ($rootElement) {
+                        for(i = 0, ii = $rootElement.length; i < ii; i++) {
+                            if ($rootElement[i] == firstElementToRemove) {
+                                $rootElement[i++] = newNode;
+                                for (var j = i, j2 = j + removeCount - 1,
+                                             jj = $rootElement.length;
+                                     j < jj; j++, j2++) {
+                                    if (j2 < jj) {
+                                        $rootElement[j] = $rootElement[j2];
+                                    } else {
+                                        delete $rootElement[j];
+                                    }
+                                }
+                                $rootElement.length -= removeCount - 1;
+                                break;
+                            }
+                        }
+                    }
+
+                    if (parent) {
+                        parent.replaceChild(newNode, firstElementToRemove);
+                    }
+                    var fragment = document.createDocumentFragment();
+                    fragment.appendChild(firstElementToRemove);
+                    newNode[jqLite.expando] = firstElementToRemove[jqLite.expando];
+                    for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
+                        var element = elementsToRemove[k];
+                        jqLite(element).remove(); // must do this way to clean up expando
+                        fragment.appendChild(element);
+                        delete elementsToRemove[k];
+                    }
+
+                    elementsToRemove[0] = newNode;
+                    elementsToRemove.length = 1;
+                }
+
+
+                function cloneAndAnnotateFn(fn, annotation) {
+                    return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
+                }
+            }];
+    }
+
+    var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i;
+    /**
+     * Converts all accepted directives format into proper directive name.
+     * All of these will become 'myDirective':
+     *   my:Directive
+     *   my-directive
+     *   x-my-directive
+     *   data-my:directive
+     *
+     * Also there is special case for Moz prefix starting with upper case letter.
+     * @param name Name to normalize
+     */
+    function directiveNormalize(name) {
+        return camelCase(name.replace(PREFIX_REGEXP, ''));
+    }
+
+    /**
+     * @ngdoc object
+     * @name ng.$compile.directive.Attributes
+     *
+     * @description
+     * A shared object between directive compile / linking functions which contains normalized DOM
+     * element attributes. The values reflect current binding state `{{ }}`. The normalization is
+     * needed since all of these are treated as equivalent in Angular:
+     *
+     *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
+     */
+
+    /**
+     * @ngdoc property
+     * @name ng.$compile.directive.Attributes#$attr
+     * @propertyOf ng.$compile.directive.Attributes
+     * @returns {object} A map of DOM element attribute names to the normalized name. This is
+     *                   needed to do reverse lookup from normalized name back to actual name.
+     */
+
+
+    /**
+     * @ngdoc function
+     * @name ng.$compile.directive.Attributes#$set
+     * @methodOf ng.$compile.directive.Attributes
+     * @function
+     *
+     * @description
+     * Set DOM element attribute value.
+     *
+     *
+     * @param {string} name Normalized element attribute name of the property to modify. The name is
+     *          revers translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
+     *          property to the original name.
+     * @param {string} value Value to set the attribute to. The value can be an interpolated string.
+     */
+
+
+
+    /**
+     * Closure compiler type information
+     */
+
+    function nodesetLinkingFn(
+        /* angular.Scope */ scope,
+        /* NodeList */ nodeList,
+        /* Element */ rootElement,
+        /* function(Function) */ boundTranscludeFn
+            ){}
+
+    function directiveLinkingFn(
+        /* nodesetLinkingFn */ nodesetLinkingFn,
+        /* angular.Scope */ scope,
+        /* Node */ node,
+        /* Element */ rootElement,
+        /* function(Function) */ boundTranscludeFn
+            ){}
+
+    function tokenDifference(str1, str2) {
+        var values = '',
+                tokens1 = str1.split(/\s+/),
+                tokens2 = str2.split(/\s+/);
+
+        outer:
+                for(var i = 0; i < tokens1.length; i++) {
+                    var token = tokens1[i];
+                    for(var j = 0; j < tokens2.length; j++) {
+                        if(token == tokens2[j]) continue outer;
+                    }
+                    values += (values.length > 0 ? ' ' : '') + token;
+                }
+        return values;
+    }
+
+    /**
+     * @ngdoc object
+     * @name ng.$controllerProvider
+     * @description
+     * The {@link ng.$controller $controller service} is used by Angular to create new
+     * controllers.
+     *
+     * This provider allows controller registration via the
+     * {@link ng.$controllerProvider#methods_register register} method.
+     */
+    function $ControllerProvider() {
+        var controllers = {},
+                CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
+
+
+        /**
+         * @ngdoc function
+         * @name ng.$controllerProvider#register
+         * @methodOf ng.$controllerProvider
+         * @param {string|Object} name Controller name, or an object map of controllers where the keys are
+         *    the names and the values are the constructors.
+         * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
+         *    annotations in the array notation).
+         */
+        this.register = function(name, constructor) {
+            assertNotHasOwnProperty(name, 'controller');
+            if (isObject(name)) {
+                extend(controllers, name);
+            } else {
+                controllers[name] = constructor;
+            }
+        };
+
+
+        this.$get = ['$injector', '$window', function($injector, $window) {
+
+            /**
+             * @ngdoc function
+             * @name ng.$controller
+             * @requires $injector
+             *
+             * @param {Function|string} constructor If called with a function then it's considered to be the
+             *    controller constructor function. Otherwise it's considered to be a string which is used
+             *    to retrieve the controller constructor using the following steps:
+             *
+             *    * check if a controller with given name is registered via `$controllerProvider`
+             *    * check if evaluating the string on the current scope returns a constructor
+             *    * check `window[constructor]` on the global `window` object
+             *
+             * @param {Object} locals Injection locals for Controller.
+             * @return {Object} Instance of given controller.
+             *
+             * @description
+             * `$controller` service is responsible for instantiating controllers.
+             *
+             * It's just a simple call to {@link AUTO.$injector $injector}, but extracted into
+             * a service, so that one can override this service with {@link https://gist.github.com/1649788
+             * BC version}.
+             */
+            return function(expression, locals) {
+                var instance, match, constructor, identifier;
+
+                if(isString(expression)) {
+                    match = expression.match(CNTRL_REG),
+                            constructor = match[1],
+                            identifier = match[3];
+                    expression = controllers.hasOwnProperty(constructor)
+                            ? controllers[constructor]
+                            : getter(locals.$scope, constructor, true) || getter($window, constructor, true);
+
+                    assertArgFn(expression, constructor, true);
+                }
+
+                instance = $injector.instantiate(expression, locals);
+
+                if (identifier) {
+                    if (!(locals && typeof locals.$scope == 'object')) {
+                        throw minErr('$controller')('noscp',
+                                "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
+                                constructor || expression.name, identifier);
+                    }
+
+                    locals.$scope[identifier] = instance;
+                }
+
+                return instance;
+            };
+        }];
+    }
+
+    /**
+     * @ngdoc object
+     * @name ng.$document
+     * @requires $window
+     *
+     * @description
+     * A {@link angular.element jQuery (lite)}-wrapped reference to the browser's `window.document`
+     * element.
+     */
+    function $DocumentProvider(){
+        this.$get = ['$window', function(window){
+            return jqLite(window.document);
+        }];
+    }
+
+    /**
+     * @ngdoc function
+     * @name ng.$exceptionHandler
+     * @requires $log
+     *
+     * @description
+     * Any uncaught exception in angular expressions is delegated to this service.
+     * The default implementation simply delegates to `$log.error` which logs it into
+     * the browser console.
+     *
+     * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
+     * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
+     *
+     * ## Example:
+     *
+     * <pre>
+     *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function () {
+ *     return function (exception, cause) {
+ *       exception.message += ' (caused by "' + cause + '")';
+ *       throw exception;
+ *     };
+ *   });
+     * </pre>
+     *
+     * This example will override the normal action of `$exceptionHandler`, to make angular
+     * exceptions fail hard when they happen, instead of just logging to the console.
+     *
+     * @param {Error} exception Exception associated with the error.
+     * @param {string=} cause optional information about the context in which
+     *       the error was thrown.
+     *
+     */
+    function $ExceptionHandlerProvider() {
+        this.$get = ['$log', function($log) {
+            return function(exception, cause) {
+                $log.error.apply($log, arguments);
+            };
+        }];
+    }
+
+    /**
+     * Parse headers into key value object
+     *
+     * @param {string} headers Raw headers as a string
+     * @returns {Object} Parsed headers as key value object
+     */
+    function parseHeaders(headers) {
+        var parsed = {}, key, val, i;
+
+        if (!headers) return parsed;
+
+        forEach(headers.split('\n'), function(line) {
+            i = line.indexOf(':');
+            key = lowercase(trim(line.substr(0, i)));
+            val = trim(line.substr(i + 1));
+
+            if (key) {
+                if (parsed[key]) {
+                    parsed[key] += ', ' + val;
+                } else {
+                    parsed[key] = val;
+                }
+            }
+        });
+
+        return parsed;
+    }
+
+
+    /**
+     * Returns a function that provides access to parsed headers.
+     *
+     * Headers are lazy parsed when first requested.
+     * @see parseHeaders
+     *
+     * @param {(string|Object)} headers Headers to provide access to.
+     * @returns {function(string=)} Returns a getter function which if called with:
+     *
+     *   - if called with single an argument returns a single header value or null
+     *   - if called with no arguments returns an object containing all headers.
+     */
+    function headersGetter(headers) {
+        var headersObj = isObject(headers) ? headers : undefined;
+
+        return function(name) {
+            if (!headersObj) headersObj =  parseHeaders(headers);
+
+            if (name) {
+                return headersObj[lowercase(name)] || null;
+            }
+
+            return headersObj;
+        };
+    }
+
+
+    /**
+     * Chain all given functions
+     *
+     * This function is used for both request and response transforming
+     *
+     * @param {*} data Data to transform.
+     * @param {function(string=)} headers Http headers getter fn.
+     * @param {(function|Array.<function>)} fns Function or an array of functions.
+     * @returns {*} Transformed data.
+     */
+    function transformData(data, headers, fns) {
+        if (isFunction(fns))
+            return fns(data, headers);
+
+        forEach(fns, function(fn) {
+            data = fn(data, headers);
+        });
+
+        return data;
+    }
+
+
+    function isSuccess(status) {
+        return 200 <= status && status < 300;
+    }
+
+
+    function $HttpProvider() {
+        var JSON_START = /^\s*(\[|\{[^\{])/,
+                JSON_END = /[\}\]]\s*$/,
+                PROTECTION_PREFIX = /^\)\]\}',?\n/,
+                CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': 'application/json;charset=utf-8'};
+
+        var defaults = this.defaults = {
+            // transform incoming response data
+            transformResponse: [function(data) {
+                if (isString(data)) {
+                    // strip json vulnerability protection prefix
+                    data = data.replace(PROTECTION_PREFIX, '');
+                    if (JSON_START.test(data) && JSON_END.test(data))
+                        data = fromJson(data);
+                }
+                return data;
+            }],
+
+            // transform outgoing request data
+            transformRequest: [function(d) {
+                return isObject(d) && !isFile(d) ? toJson(d) : d;
+            }],
+
+            // default headers
+            headers: {
+                common: {
+                    'Accept': 'application/json, text/plain, */*'
+                },
+                post:   CONTENT_TYPE_APPLICATION_JSON,
+                put:    CONTENT_TYPE_APPLICATION_JSON,
+                patch:  CONTENT_TYPE_APPLICATION_JSON
+            },
+
+            xsrfCookieName: 'XSRF-TOKEN',
+            xsrfHeaderName: 'X-XSRF-TOKEN'
+        };
+
+        /**
+         * Are ordered by request, i.e. they are applied in the same order as the
+         * array, on request, but reverse order, on response.
+         */
+        var interceptorFactories = this.interceptors = [];
+
+        /**
+         * For historical reasons, response interceptors are ordered by the order in which
+         * they are applied to the response. (This is the opposite of interceptorFactories)
+         */
+        var responseInterceptorFactories = this.responseInterceptors = [];
+
+        this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
+            function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
+
+                var defaultCache = $cacheFactory('$http');
+
+                /**
+                 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
+                 * The reversal is needed so that we can build up the interception chain around the
+                 * server request.
+                 */
+                var reversedInterceptors = [];
+
+                forEach(interceptorFactories, function(interceptorFactory) {
+                    reversedInterceptors.unshift(isString(interceptorFactory)
+                            ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
+                });
+
+                forEach(responseInterceptorFactories, function(interceptorFactory, index) {
+                    var responseFn = isString(interceptorFactory)
+                            ? $injector.get(interceptorFactory)
+                            : $injector.invoke(interceptorFactory);
+
+                    /**
+                     * Response interceptors go before "around" interceptors (no real reason, just
+                     * had to pick one.) But they are already reversed, so we can't use unshift, hence
+                     * the splice.
+                     */
+                    reversedInterceptors.splice(index, 0, {
+                        response: function(response) {
+                            return responseFn($q.when(response));
+                        },
+                        responseError: function(response) {
+                            return responseFn($q.reject(response));
+                        }
+                    });
+                });
+
+
+                /**
+                 * @ngdoc function
+                 * @name ng.$http
+                 * @requires $httpBackend
+                 * @requires $browser
+                 * @requires $cacheFactory
+                 * @requires $rootScope
+                 * @requires $q
+                 * @requires $injector
+                 *
+                 * @description
+                 * The `$http` service is a core Angular service that facilitates communication with the remote
+                 * HTTP servers via the browser's {@link https://developer.mozilla.org/en/xmlhttprequest
+                 * XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}.
+                 *
+                 * For unit testing applications that use `$http` service, see
+                 * {@link ngMock.$httpBackend $httpBackend mock}.
+                 *
+                 * For a higher level of abstraction, please check out the {@link ngResource.$resource
+                 * $resource} service.
+                 *
+                 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
+                 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
+                 * it is important to familiarize yourself with these APIs and the guarantees they provide.
+                 *
+                 *
+                 * # General usage
+                 * The `$http` service is a function which takes a single argument — a configuration object —
+                 * that is used to generate an HTTP request and returns  a {@link ng.$q promise}
+                 * with two $http specific methods: `success` and `error`.
+                 *
+                 * <pre>
+                 *   $http({method: 'GET', url: '/someUrl'}).
+                 *     success(function(data, status, headers, config) {
+     *       // this callback will be called asynchronously
+     *       // when the response is available
+     *     }).
+                 *     error(function(data, status, headers, config) {
+     *       // called asynchronously if an error occurs
+     *       // or server returns response with an error status.
+     *     });
+                 * </pre>
+                 *
+                 * Since the returned value of calling the $http function is a `promise`, you can also use
+                 * the `then` method to register callbacks, and these callbacks will receive a single argument –
+                 * an object representing the response. See the API signature and type info below for more
+                 * details.
+                 *
+                 * A response status code between 200 and 299 is considered a success status and
+                 * will result in the success callback being called. Note that if the response is a redirect,
+                 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
+                 * called for such responses.
+                 *
+                 * # Calling $http from outside AngularJS
+                 * The `$http` service will not actually send the request until the next `$digest()` is
+                 * executed. Normally this is not an issue, since almost all the time your call to `$http` will
+                 * be from within a `$apply()` block.
+                 * If you are calling `$http` from outside Angular, then you should wrap it in a call to
+                 * `$apply` to cause a $digest to occur and also to handle errors in the block correctly.
+                 *
+                 * ```
+                 * $scope.$apply(function() {
+     *   $http(...);
+     * });
+                 * ```
+                 *
+                 * # Writing Unit Tests that use $http
+                 * When unit testing you are mostly responsible for scheduling the `$digest` cycle. If you do
+                 * not trigger a `$digest` before calling `$httpBackend.flush()` then the request will not have
+                 * been made and `$httpBackend.expect(...)` expectations will fail.  The solution is to run the
+                 * code that calls the `$http()` method inside a $apply block as explained in the previous
+                 * section.
+                 *
+                 * ```
+                 * $httpBackend.expectGET(...);
+                 * $scope.$apply(function() {
+     *   $http.get(...);
+     * });
+                 * $httpBackend.flush();
+                 * ```
+                 *
+                 * # Shortcut methods
+                 *
+                 * Since all invocations of the $http service require passing in an HTTP method and URL, and
+                 * POST/PUT requests require request data to be provided as well, shortcut methods
+                 * were created:
+                 *
+                 * <pre>
+                 *   $http.get('/someUrl').success(successCallback);
+                 *   $http.post('/someUrl', data).success(successCallback);
+                 * </pre>
+                 *
+                 * Complete list of shortcut methods:
+                 *
+                 * - {@link ng.$http#methods_get $http.get}
+                 * - {@link ng.$http#methods_head $http.head}
+                 * - {@link ng.$http#methods_post $http.post}
+                 * - {@link ng.$http#methods_put $http.put}
+                 * - {@link ng.$http#methods_delete $http.delete}
+                 * - {@link ng.$http#methods_jsonp $http.jsonp}
+                 *
+                 *
+                 * # Setting HTTP Headers
+                 *
+                 * The $http service will automatically add certain HTTP headers to all requests. These defaults
+                 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
+                 * object, which currently contains this default configuration:
+                 *
+                 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
+                 *   - `Accept: application/json, text/plain, * / *`
+                 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
+                 *   - `Content-Type: application/json`
+                 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
+                 *   - `Content-Type: application/json`
+                 *
+                 * To add or overwrite these defaults, simply add or remove a property from these configuration
+                 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
+                 * with the lowercased HTTP method name as the key, e.g.
+                 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }.
+                 *
+                 * The defaults can also be set at runtime via the `$http.defaults` object in the same
+                 * fashion. In addition, you can supply a `headers` property in the config object passed when
+                 * calling `$http(config)`, which overrides the defaults without changing them globally.
+                 *
+                 *
+                 * # Transforming Requests and Responses
+                 *
+                 * Both requests and responses can be transformed using transform functions. By default, Angular
+                 * applies these transformations:
+                 *
+                 * Request transformations:
+                 *
+                 * - If the `data` property of the request configuration object contains an object, serialize it
+                 *   into JSON format.
+                 *
+                 * Response transformations:
+                 *
+                 *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
+                 *  - If JSON response is detected, deserialize it using a JSON parser.
+                 *
+                 * To globally augment or override the default transforms, modify the
+                 * `$httpProvider.defaults.transformRequest` and `$httpProvider.defaults.transformResponse`
+                 * properties. These properties are by default an array of transform functions, which allows you
+                 * to `push` or `unshift` a new transformation function into the transformation chain. You can
+                 * also decide to completely override any default transformations by assigning your
+                 * transformation functions to these properties directly without the array wrapper.
+                 *
+                 * Similarly, to locally override the request/response transforms, augment the
+                 * `transformRequest` and/or `transformResponse` properties of the configuration object passed
+                 * into `$http`.
+                 *
+                 *
+                 * # Caching
+                 *
+                 * To enable caching, set the request configuration `cache` property to `true` (to use default
+                 * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
+                 * When the cache is enabled, `$http` stores the response from the server in the specified
+                 * cache. The next time the same request is made, the response is served from the cache without
+                 * sending a request to the server.
+                 *
+                 * Note that even if the response is served from cache, delivery of the data is asynchronous in
+                 * the same way that real requests are.
+                 *
+                 * If there are multiple GET requests for the same URL that should be cached using the same
+                 * cache, but the cache is not populated yet, only one request to the server will be made and
+                 * the remaining requests will be fulfilled using the response from the first request.
+                 *
+                 * You can change the default cache to a new object (built with
+                 * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
+                 * {@link ng.$http#properties_defaults `$http.defaults.cache`} property. All requests who set
+                 * their `cache` property to `true` will now use this cache object.
+                 *
+                 * If you set the default cache to `false` then only requests that specify their own custom
+                 * cache object will be cached.
+                 *
+                 * # Interceptors
+                 *
+                 * Before you start creating interceptors, be sure to understand the
+                 * {@link ng.$q $q and deferred/promise APIs}.
+                 *
+                 * For purposes of global error handling, authentication, or any kind of synchronous or
+                 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
+                 * able to intercept requests before they are handed to the server and
+                 * responses before they are handed over to the application code that
+                 * initiated these requests. The interceptors leverage the {@link ng.$q
+                 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
+                 *
+                 * The interceptors are service factories that are registered with the `$httpProvider` by
+                 * adding them to the `$httpProvider.interceptors` array. The factory is called and
+                 * injected with dependencies (if specified) and returns the interceptor.
+                 *
+                 * There are two kinds of interceptors (and two kinds of rejection interceptors):
+                 *
+                 *   * `request`: interceptors get called with http `config` object. The function is free to
+                 *     modify the `config` or create a new one. The function needs to return the `config`
+                 *     directly or as a promise.
+                 *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
+                 *     resolved with a rejection.
+                 *   * `response`: interceptors get called with http `response` object. The function is free to
+                 *     modify the `response` or create a new one. The function needs to return the `response`
+                 *     directly or as a promise.
+                 *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
+                 *     resolved with a rejection.
+                 *
+                 *
+                 * <pre>
+                 *   // register the interceptor as a service
+                 *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
+     *     return {
+     *       // optional method
+     *       'request': function(config) {
+     *         // do something on success
+     *         return config || $q.when(config);
+     *       },
+     *
+     *       // optional method
+     *      'requestError': function(rejection) {
+     *         // do something on error
+     *         if (canRecover(rejection)) {
+     *           return responseOrNewPromise
+     *         }
+     *         return $q.reject(rejection);
+     *       },
+     *
+     *
+     *
+     *       // optional method
+     *       'response': function(response) {
+     *         // do something on success
+     *         return response || $q.when(response);
+     *       },
+     *
+     *       // optional method
+     *      'responseError': function(rejection) {
+     *         // do something on error
+     *         if (canRecover(rejection)) {
+     *           return responseOrNewPromise
+     *         }
+     *         return $q.reject(rejection);
+     *       };
+     *     }
+     *   });
+                 *
+                 *   $httpProvider.interceptors.push('myHttpInterceptor');
+                 *
+                 *
+                 *   // register the interceptor via an anonymous factory
+                 *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
+     *     return {
+     *      'request': function(config) {
+     *          // same as above
+     *       },
+     *       'response': function(response) {
+     *          // same as above
+     *       }
+     *     };
+     *   });
+                 * </pre>
+                 *
+                 * # Response interceptors (DEPRECATED)
+                 *
+                 * Before you start creating interceptors, be sure to understand the
+                 * {@link ng.$q $q and deferred/promise APIs}.
+                 *
+                 * For purposes of global error handling, authentication or any kind of synchronous or
+                 * asynchronous preprocessing of received responses, it is desirable to be able to intercept
+                 * responses for http requests before they are handed over to the application code that
+                 * initiated these requests. The response interceptors leverage the {@link ng.$q
+                 * promise apis} to fulfil this need for both synchronous and asynchronous preprocessing.
+                 *
+                 * The interceptors are service factories that are registered with the $httpProvider by
+                 * adding them to the `$httpProvider.responseInterceptors` array. The factory is called and
+                 * injected with dependencies (if specified) and returns the interceptor  — a function that
+                 * takes a {@link ng.$q promise} and returns the original or a new promise.
+                 *
+                 * <pre>
+                 *   // register the interceptor as a service
+                 *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
+     *     return function(promise) {
+     *       return promise.then(function(response) {
+     *         // do something on success
+     *         return response;
+     *       }, function(response) {
+     *         // do something on error
+     *         if (canRecover(response)) {
+     *           return responseOrNewPromise
+     *         }
+     *         return $q.reject(response);
+     *       });
+     *     }
+     *   });
+                 *
+                 *   $httpProvider.responseInterceptors.push('myHttpInterceptor');
+                 *
+                 *
+                 *   // register the interceptor via an anonymous factory
+                 *   $httpProvider.responseInterceptors.push(function($q, dependency1, dependency2) {
+     *     return function(promise) {
+     *       // same as above
+     *     }
+     *   });
+                 * </pre>
+                 *
+                 *
+                 * # Security Considerations
+                 *
+                 * When designing web applications, consider security threats from:
+                 *
+                 * - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
+                 *   JSON vulnerability}
+                 * - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF}
+                 *
+                 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
+                 * pre-configured with strategies that address these issues, but for this to work backend server
+                 * cooperation is required.
+                 *
+                 * ## JSON Vulnerability Protection
+                 *
+                 * A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
+                 * JSON vulnerability} allows third party website to turn your JSON resource URL into
+                 * {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To
+                 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
+                 * Angular will automatically strip the prefix before processing it as JSON.
+                 *
+                 * For example if your server needs to return:
+                 * <pre>
+                 * ['one','two']
+                 * </pre>
+                 *
+                 * which is vulnerable to attack, your server can return:
+                 * <pre>
+                 * )]}',
+                 * ['one','two']
+                 * </pre>
+                 *
+                 * Angular will strip the prefix, before processing the JSON.
+                 *
+                 *
+                 * ## Cross Site Request Forgery (XSRF) Protection
+                 *
+                 * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which
+                 * an unauthorized site can gain your user's private data. Angular provides a mechanism
+                 * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
+                 * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
+                 * JavaScript that runs on your domain could read the cookie, your server can be assured that
+                 * the XHR came from JavaScript running on your domain. The header will not be set for
+                 * cross-domain requests.
+                 *
+                 * To take advantage of this, your server needs to set a token in a JavaScript readable session
+                 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
+                 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
+                 * that only JavaScript running on your domain could have sent the request. The token must be
+                 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
+                 * making up its own tokens). We recommend that the token is a digest of your site's
+                 * authentication cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt}
+                 * for added security.
+                 *
+                 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
+                 * properties of either $httpProvider.defaults, or the per-request config object.
+                 *
+                 *
+                 * @param {object} config Object describing the request to be made and how it should be
+                 *    processed. The object has following properties:
+                 *
+                 *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
+                 *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
+                 *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned
+                 *      to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be
+                 *      JSONified.
+                 *    - **data** – `{string|Object}` – Data to be sent as the request message data.
+                 *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
+                 *      HTTP headers to send to the server. If the return value of a function is null, the
+                 *      header will not be sent.
+                 *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
+                 *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
+                 *    - **transformRequest** –
+                 *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
+                 *      transform function or an array of such functions. The transform function takes the http
+                 *      request body and headers and returns its transformed (typically serialized) version.
+                 *    - **transformResponse** –
+                 *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
+                 *      transform function or an array of such functions. The transform function takes the http
+                 *      response body and headers and returns its transformed (typically deserialized) version.
+                 *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
+                 *      GET request, otherwise if a cache instance built with
+                 *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
+                 *      caching.
+                 *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
+                 *      that should abort the request when resolved.
+                 *    - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the
+                 *      XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
+                 *      requests with credentials} for more information.
+                 *    - **responseType** - `{string}` - see {@link
+                 *      https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
+                 *
+                 * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
+                 *   standard `then` method and two http specific methods: `success` and `error`. The `then`
+                 *   method takes two arguments a success and an error callback which will be called with a
+                 *   response object. The `success` and `error` methods take a single argument - a function that
+                 *   will be called when the request succeeds or fails respectively. The arguments passed into
+                 *   these functions are destructured representation of the response object passed into the
+                 *   `then` method. The response object has these properties:
+                 *
+                 *   - **data** – `{string|Object}` – The response body transformed with the transform
+                 *     functions.
+                 *   - **status** – `{number}` – HTTP status code of the response.
+                 *   - **headers** – `{function([headerName])}` – Header getter function.
+                 *   - **config** – `{Object}` – The configuration object that was used to generate the request.
+                 *
+                 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
+                 *   requests. This is primarily meant to be used for debugging purposes.
+                 *
+                 *
+                 * @example
+                 <example>
+                 <file name="index.html">
+                 <div ng-controller="FetchCtrl">
+                 <select ng-model="method">
+                 <option>GET</option>
+                 <option>JSONP</option>
+                 </select>
+                 <input type="text" ng-model="url" size="80"/>
+                 <button ng-click="fetch()">fetch</button><br>
+                 <button ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
+                 <button
+                 ng-click="updateModel('JSONP',
+                 'http://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
+                 Sample JSONP
+                 </button>
+                 <button
+                 ng-click="updateModel('JSONP', 'http://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
+                 Invalid JSONP
+                 </button>
+                 <pre>http status code: {{status}}</pre>
+                 <pre>http response data: {{data}}</pre>
+                 </div>
+                 </file>
+                 <file name="script.js">
+                 function FetchCtrl($scope, $http, $templateCache) {
+    $scope.method = 'GET';
+    $scope.url = 'http-hello.html';
+
+    $scope.fetch = function() {
+      $scope.code = null;
+      $scope.response = null;
+
+      $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
+        success(function(data, status) {
+          $scope.status = status;
+          $scope.data = data;
+        }).
+        error(function(data, status) {
+          $scope.data = data || "Request failed";
+          $scope.status = status;
+      });
+    };
+
+    $scope.updateModel = function(method, url) {
+      $scope.method = method;
+      $scope.url = url;
+    };
+  }
+                 </file>
+                 <file name="http-hello.html">
+                 Hello, $http!
+                 </file>
+                 <file name="scenario.js">
+                 it('should make an xhr GET request', function() {
+    element(':button:contains("Sample GET")').click();
+    element(':button:contains("fetch")').click();
+    expect(binding('status')).toBe('200');
+    expect(binding('data')).toMatch(/Hello, \$http!/);
+  });
+
+                 it('should make a JSONP request to angularjs.org', function() {
+    element(':button:contains("Sample JSONP")').click();
+    element(':button:contains("fetch")').click();
+    expect(binding('status')).toBe('200');
+    expect(binding('data')).toMatch(/Super Hero!/);
+  });
+
+                 it('should make JSONP request to invalid URL and invoke the error handler',
+                 function() {
+    element(':button:contains("Invalid JSONP")').click();
+    element(':button:contains("fetch")').click();
+    expect(binding('status')).toBe('0');
+    expect(binding('data')).toBe('Request failed');
+  });
+                 </file>
+                 </example>
+                 */
+                function $http(requestConfig) {
+                    var config = {
+                        transformRequest: defaults.transformRequest,
+                        transformResponse: defaults.transformResponse
+                    };
+                    var headers = mergeHeaders(requestConfig);
+
+                    extend(config, requestConfig);
+                    config.headers = headers;
+                    config.method = uppercase(config.method);
+
+                    var xsrfValue = urlIsSameOrigin(config.url)
+                            ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]
+                            : undefined;
+                    if (xsrfValue) {
+                        headers[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
+                    }
+
+
+                    var serverRequest = function(config) {
+                        headers = config.headers;
+                        var reqData = transformData(config.data, headersGetter(headers), config.transformRequest);
+
+                        // strip content-type if data is undefined
+                        if (isUndefined(config.data)) {
+                            forEach(headers, function(value, header) {
+                                if (lowercase(header) === 'content-type') {
+                                    delete headers[header];
+                                }
+                            });
+                        }
+
+                        if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
+                            config.withCredentials = defaults.withCredentials;
+                        }
+
+                        // send request
+                        return sendReq(config, reqData, headers).then(transformResponse, transformResponse);
+                    };
+
+                    var chain = [serverRequest, undefined];
+                    var promise = $q.when(config);
+
+                    // apply interceptors
+                    forEach(reversedInterceptors, function(interceptor) {
+                        if (interceptor.request || interceptor.requestError) {
+                            chain.unshift(interceptor.request, interceptor.requestError);
+                        }
+                        if (interceptor.response || interceptor.responseError) {
+                            chain.push(interceptor.response, interceptor.responseError);
+                        }
+                    });
+
+                    while(chain.length) {
+                        var thenFn = chain.shift();
+                        var rejectFn = chain.shift();
+
+                        promise = promise.then(thenFn, rejectFn);
+                    }
+
+                    promise.success = function(fn) {
+                        promise.then(function(response) {
+                            fn(response.data, response.status, response.headers, config);
+                        });
+                        return promise;
+                    };
+
+                    promise.error = function(fn) {
+                        promise.then(null, function(response) {
+                            fn(response.data, response.status, response.headers, config);
+                        });
+                        return promise;
+                    };
+
+                    return promise;
+
+                    function transformResponse(response) {
+                        // make a copy since the response must be cacheable
+                        var resp = extend({}, response, {
+                            data: transformData(response.data, response.headers, config.transformResponse)
+                        });
+                        return (isSuccess(response.status))
+                                ? resp
+                                : $q.reject(resp);
+                    }
+
+                    function mergeHeaders(config) {
+                        var defHeaders = defaults.headers,
+                                reqHeaders = extend({}, config.headers),
+                                defHeaderName, lowercaseDefHeaderName, reqHeaderName;
+
+                        defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
+
+                        // execute if header value is function
+                        execHeaders(defHeaders);
+                        execHeaders(reqHeaders);
+
+                        // using for-in instead of forEach to avoid unecessary iteration after header has been found
+                        defaultHeadersIteration:
+                                for (defHeaderName in defHeaders) {
+                                    lowercaseDefHeaderName = lowercase(defHeaderName);
+
+                                    for (reqHeaderName in reqHeaders) {
+                                        if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
+                                            continue defaultHeadersIteration;
+                                        }
+                                    }
+
+                                    reqHeaders[defHeaderName] = defHeaders[defHeaderName];
+                                }
+
+                        return reqHeaders;
+
+                        function execHeaders(headers) {
+                            var headerContent;
+
+                            forEach(headers, function(headerFn, header) {
+                                if (isFunction(headerFn)) {
+                                    headerContent = headerFn();
+                                    if (headerContent != null) {
+                                        headers[header] = headerContent;
+                                    } else {
+                                        delete headers[header];
+                                    }
+                                }
+                            });
+                        }
+                    }
+                }
+
+                $http.pendingRequests = [];
+
+                /**
+                 * @ngdoc method
+                 * @name ng.$http#get
+                 * @methodOf ng.$http
+                 *
+                 * @description
+                 * Shortcut method to perform `GET` request.
+                 *
+                 * @param {string} url Relative or absolute URL specifying the destination of the request
+                 * @param {Object=} config Optional configuration object
+                 * @returns {HttpPromise} Future object
+                 */
+
+                /**
+                 * @ngdoc method
+                 * @name ng.$http#delete
+                 * @methodOf ng.$http
+                 *
+                 * @description
+                 * Shortcut method to perform `DELETE` request.
+                 *
+                 * @param {string} url Relative or absolute URL specifying the destination of the request
+                 * @param {Object=} config Optional configuration object
+                 * @returns {HttpPromise} Future object
+                 */
+
+                /**
+                 * @ngdoc method
+                 * @name ng.$http#head
+                 * @methodOf ng.$http
+                 *
+                 * @description
+                 * Shortcut method to perform `HEAD` request.
+                 *
+                 * @param {string} url Relative or absolute URL specifying the destination of the request
+                 * @param {Object=} config Optional configuration object
+                 * @returns {HttpPromise} Future object
+                 */
+
+                /**
+                 * @ngdoc method
+                 * @name ng.$http#jsonp
+                 * @methodOf ng.$http
+                 *
+                 * @description
+                 * Shortcut method to perform `JSONP` request.
+                 *
+                 * @param {string} url Relative or absolute URL specifying the destination of the request.
+                 *                     Should contain `JSON_CALLBACK` string.
+                 * @param {Object=} config Optional configuration object
+                 * @returns {HttpPromise} Future object
+                 */
+                createShortMethods('get', 'delete', 'head', 'jsonp');
+
+                /**
+                 * @ngdoc method
+                 * @name ng.$http#post
+                 * @methodOf ng.$http
+                 *
+                 * @description
+                 * Shortcut method to perform `POST` request.
+                 *
+                 * @param {string} url Relative or absolute URL specifying the destination of the request
+                 * @param {*} data Request content
+                 * @param {Object=} config Optional configuration object
+                 * @returns {HttpPromise} Future object
+                 */
+
+                /**
+                 * @ngdoc method
+                 * @name ng.$http#put
+                 * @methodOf ng.$http
+                 *
+                 * @description
+                 * Shortcut method to perform `PUT` request.
+                 *
+                 * @param {string} url Relative or absolute URL specifying the destination of the request
+                 * @param {*} data Request content
+                 * @param {Object=} config Optional configuration object
+                 * @returns {HttpPromise} Future object
+                 */
+                createShortMethodsWithData('post', 'put');
+
+                /**
+                 * @ngdoc property
+                 * @name ng.$http#defaults
+                 * @propertyOf ng.$http
+                 *
+                 * @description
+                 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
+                 * default headers, withCredentials as well as request and response transformations.
+                 *
+                 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
+                 */
+                $http.defaults = defaults;
+
+
+                return $http;
+
+
+                function createShortMethods(names) {
+                    forEach(arguments, function(name) {
+                        $http[name] = function(url, config) {
+                            return $http(extend(config || {}, {
+                                method: name,
+                                url: url
+                            }));
+                        };
+                    });
+                }
+
+
+                function createShortMethodsWithData(name) {
+                    forEach(arguments, function(name) {
+                        $http[name] = function(url, data, config) {
+                            return $http(extend(config || {}, {
+                                method: name,
+                                url: url,
+                                data: data
+                            }));
+                        };
+                    });
+                }
+
+
+                /**
+                 * Makes the request.
+                 *
+                 * !!! ACCESSES CLOSURE VARS:
+                 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
+                 */
+                function sendReq(config, reqData, reqHeaders) {
+                    var deferred = $q.defer(),
+                            promise = deferred.promise,
+                            cache,
+                            cachedResp,
+                            url = buildUrl(config.url, config.params);
+
+                    $http.pendingRequests.push(config);
+                    promise.then(removePendingReq, removePendingReq);
+
+
+                    if ((config.cache || defaults.cache) && config.cache !== false && config.method == 'GET') {
+                        cache = isObject(config.cache) ? config.cache
+                                : isObject(defaults.cache) ? defaults.cache
+                                : defaultCache;
+                    }
+
+                    if (cache) {
+                        cachedResp = cache.get(url);
+                        if (isDefined(cachedResp)) {
+                            if (cachedResp.then) {
+                                // cached request has already been sent, but there is no response yet
+                                cachedResp.then(removePendingReq, removePendingReq);
+                                return cachedResp;
+                            } else {
+                                // serving from cache
+                                if (isArray(cachedResp)) {
+                                    resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2]));
+                                } else {
+                                    resolvePromise(cachedResp, 200, {});
+                                }
+                            }
+                        } else {
+                            // put the promise for the non-transformed response into cache as a placeholder
+                            cache.put(url, promise);
+                        }
+                    }
+
+                    // if we won't have the response in cache, send the request to the backend
+                    if (isUndefined(cachedResp)) {
+                        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
+                                config.withCredentials, config.responseType);
+                    }
+
+                    return promise;
+
+
+                    /**
+                     * Callback registered to $httpBackend():
+                     *  - caches the response if desired
+                     *  - resolves the raw $http promise
+                     *  - calls $apply
+                     */
+                    function done(status, response, headersString) {
+                        if (cache) {
+                            if (isSuccess(status)) {
+                                cache.put(url, [status, response, parseHeaders(headersString)]);
+                            } else {
+                                // remove promise from the cache
+                                cache.remove(url);
+                            }
+                        }
+
+                        resolvePromise(response, status, headersString);
+                        if (!$rootScope.$$phase) $rootScope.$apply();
+                    }
+
+
+                    /**
+                     * Resolves the raw $http promise.
+                     */
+                    function resolvePromise(response, status, headers) {
+                        // normalize internal statuses to 0
+                        status = Math.max(status, 0);
+
+                        (isSuccess(status) ? deferred.resolve : deferred.reject)({
+                            data: response,
+                            status: status,
+                            headers: headersGetter(headers),
+                            config: config
+                        });
+                    }
+
+
+                    function removePendingReq() {
+                        var idx = indexOf($http.pendingRequests, config);
+                        if (idx !== -1) $http.pendingRequests.splice(idx, 1);
+                    }
+                }
+
+
+                function buildUrl(url, params) {
+                    if (!params) return url;
+                    var parts = [];
+                    forEachSorted(params, function(value, key) {
+                        if (value === null || isUndefined(value)) return;
+                        if (!isArray(value)) value = [value];
+
+                        forEach(value, function(v) {
+                            if (isObject(v)) {
+                                v = toJson(v);
+                            }
+                            parts.push(encodeUriQuery(key) + '=' +
+                                    encodeUriQuery(v));
+                        });
+                    });
+                    return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
+                }
+
+
+            }];
+    }
+
+    var XHR = window.XMLHttpRequest || function() {
+        /* global ActiveXObject */
+        try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
+        try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
+        try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {}
+        throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest.");
+    };
+
+
+    /**
+     * @ngdoc object
+     * @name ng.$httpBackend
+     * @requires $browser
+     * @requires $window
+     * @requires $document
+     *
+     * @description
+     * HTTP backend used by the {@link ng.$http service} that delegates to
+     * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
+     *
+     * You should never need to use this service directly, instead use the higher-level abstractions:
+     * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
+     *
+     * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
+     * $httpBackend} which can be trained with responses.
+     */
+    function $HttpBackendProvider() {
+        this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
+            return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks,
+                    $document[0], $window.location.protocol.replace(':', ''));
+        }];
+    }
+
+    function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) {
+        var ABORTED = -1;
+
+        // TODO(vojta): fix the signature
+        return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
+            var status;
+            $browser.$$incOutstandingRequestCount();
+            url = url || $browser.url();
+
+            if (lowercase(method) == 'jsonp') {
+                var callbackId = '_' + (callbacks.counter++).toString(36);
+                callbacks[callbackId] = function(data) {
+                    callbacks[callbackId].data = data;
+                };
+
+                var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
+                        function() {
+                            if (callbacks[callbackId].data) {
+                                completeRequest(callback, 200, callbacks[callbackId].data);
+                            } else {
+                                completeRequest(callback, status || -2);
+                            }
+                            delete callbacks[callbackId];
+                        });
+            } else {
+                var xhr = new XHR();
+                xhr.open(method, url, true);
+                forEach(headers, function(value, key) {
+                    if (isDefined(value)) {
+                        xhr.setRequestHeader(key, value);
+                    }
+                });
+
+                // In IE6 and 7, this might be called synchronously when xhr.send below is called and the
+                // response is in the cache. the promise api will ensure that to the app code the api is
+                // always async
+                xhr.onreadystatechange = function() {
+                    if (xhr.readyState == 4) {
+                        var responseHeaders = null,
+                                response = null;
+
+                        if(status !== ABORTED) {
+                            responseHeaders = xhr.getAllResponseHeaders();
+                            response = xhr.responseType ? xhr.response : xhr.responseText;
+                        }
+
+                        // responseText is the old-school way of retrieving response (supported by IE8 & 9)
+                        // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
+                        completeRequest(callback,
+                                status || xhr.status,
+                                response,
+                                responseHeaders);
+                    }
+                };
+
+                if (withCredentials) {
+                    xhr.withCredentials = true;
+                }
+
+                if (responseType) {
+                    xhr.responseType = responseType;
+                }
+
+                xhr.send(post || null);
+            }
+
+            if (timeout > 0) {
+                var timeoutId = $browserDefer(timeoutRequest, timeout);
+            } else if (timeout && timeout.then) {
+                timeout.then(timeoutRequest);
+            }
+
+
+            function timeoutRequest() {
+                status = ABORTED;
+                jsonpDone && jsonpDone();
+                xhr && xhr.abort();
+            }
+
+            function completeRequest(callback, status, response, headersString) {
+                var protocol = locationProtocol || urlResolve(url).protocol;
+
+                // cancel timeout and subsequent timeout promise resolution
+                timeoutId && $browserDefer.cancel(timeoutId);
+                jsonpDone = xhr = null;
+
+                // fix status code for file protocol (it's always 0)
+                status = (protocol == 'file') ? (response ? 200 : 404) : status;
+
+                // normalize IE bug (http://bugs.jquery.com/ticket/1450)
+                status = status == 1223 ? 204 : status;
+
+                callback(status, response, headersString);
+                $browser.$$completeOutstandingRequest(noop);
+            }
+        };
+
+        function jsonpReq(url, done) {
+            // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
+            // - fetches local scripts via XHR and evals them
+            // - adds and immediately removes script elements from the document
+            var script = rawDocument.createElement('script'),
+                    doneWrapper = function() {
+                        script.onreadystatechange = script.onload = script.onerror = null;
+                        rawDocument.body.removeChild(script);
+                        if (done) done();
+                    };
+
+            script.type = 'text/javascript';
+            script.src = url;
+
+            if (msie && msie <= 8) {
+                script.onreadystatechange = function() {
+                    if (/loaded|complete/.test(script.readyState)) {
+                        doneWrapper();
+                    }
+                };
+            } else {
+                script.onload = script.onerror = function() {
+                    doneWrapper();
+                };
+            }
+
+            rawDocument.body.appendChild(script);
+            return doneWrapper;
+        }
+    }
+
+    var $interpolateMinErr = minErr('$interpolate');
+
+    /**
+     * @ngdoc object
+     * @name ng.$interpolateProvider
+     * @function
+     *
+     * @description
+     *
+     * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
+     *
+     * @example
+     <doc:example module="customInterpolationApp">
+     <doc:source>
+     <script>
+     var customInterpolationApp = angular.module('customInterpolationApp', []);
+
+     customInterpolationApp.config(function($interpolateProvider) {
+    $interpolateProvider.startSymbol('//');
+    $interpolateProvider.endSymbol('//');
+  });
+
+
+     customInterpolationApp.controller('DemoController', function DemoController() {
+      this.label = "This binding is brought you by // interpolation symbols.";
+  });
+     </script>
+     <div ng-app="App" ng-controller="DemoController as demo">
+     //demo.label//
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should interpolate binding with custom symbols', function() {
+  expect(binding('demo.label')).toBe('This binding is brought you by // interpolation symbols.');
+ });
+     </doc:scenario>
+     </doc:example>
+     */
+    function $InterpolateProvider() {
+        var startSymbol = '{{';
+        var endSymbol = '}}';
+
+        /**
+         * @ngdoc method
+         * @name ng.$interpolateProvider#startSymbol
+         * @methodOf ng.$interpolateProvider
+         * @description
+         * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
+   *
+   * @param {string=} value new value to set the starting symbol to.
+         * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
+         */
+        this.startSymbol = function(value){
+            if (value) {
+                startSymbol = value;
+                return this;
+            } else {
+                return startSymbol;
+            }
+        };
+
+        /**
+         * @ngdoc method
+         * @name ng.$interpolateProvider#endSymbol
+         * @methodOf ng.$interpolateProvider
+         * @description
+         * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
+         *
+         * @param {string=} value new value to set the ending symbol to.
+         * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
+         */
+        this.endSymbol = function(value){
+            if (value) {
+                endSymbol = value;
+                return this;
+            } else {
+                return endSymbol;
+            }
+        };
+
+
+        this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
+            var startSymbolLength = startSymbol.length,
+                    endSymbolLength = endSymbol.length;
+
+            /**
+             * @ngdoc function
+             * @name ng.$interpolate
+             * @function
+             *
+             * @requires $parse
+             * @requires $sce
+             *
+             * @description
+             *
+             * Compiles a string with markup into an interpolation function. This service is used by the
+             * HTML {@link ng.$compile $compile} service for data binding. See
+             * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
+             * interpolation markup.
+             *
+             *
+             <pre>
+             var $interpolate = ...; // injected
+             var exp = $interpolate('Hello {{name}}!');
+             expect(exp({name:'Angular'}).toEqual('Hello Angular!');
+             </pre>
+             *
+             *
+             * @param {string} text The text with markup to interpolate.
+             * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
+             *    embedded expression in order to return an interpolation function. Strings with no
+             *    embedded expression will return null for the interpolation function.
+             * @param {string=} trustedContext when provided, the returned function passes the interpolated
+             *    result through {@link ng.$sce#methods_getTrusted $sce.getTrusted(interpolatedResult,
+             *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
+             *    provides Strict Contextual Escaping for details.
+             * @returns {function(context)} an interpolation function which is used to compute the
+             *    interpolated string. The function has these parameters:
+             *
+             *    * `context`: an object against which any expressions embedded in the strings are evaluated
+             *      against.
+             *
+             */
+            function $interpolate(text, mustHaveExpression, trustedContext) {
+                var startIndex,
+                        endIndex,
+                        index = 0,
+                        parts = [],
+                        length = text.length,
+                        hasInterpolation = false,
+                        fn,
+                        exp,
+                        concat = [];
+
+                while(index < length) {
+                    if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) &&
+                            ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) {
+                        (index != startIndex) && parts.push(text.substring(index, startIndex));
+                        parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex)));
+                        fn.exp = exp;
+                        index = endIndex + endSymbolLength;
+                        hasInterpolation = true;
+                    } else {
+                        // we did not find anything, so we have to add the remainder to the parts array
+                        (index != length) && parts.push(text.substring(index));
+                        index = length;
+                    }
+                }
+
+                if (!(length = parts.length)) {
+                    // we added, nothing, must have been an empty string.
+                    parts.push('');
+                    length = 1;
+                }
+
+                // Concatenating expressions makes it hard to reason about whether some combination of
+                // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
+                // single expression be used for iframe[src], object[src], etc., we ensure that the value
+                // that's used is assigned or constructed by some JS code somewhere that is more testable or
+                // make it obvious that you bound the value to some user controlled value.  This helps reduce
+                // the load when auditing for XSS issues.
+                if (trustedContext && parts.length > 1) {
+                    throw $interpolateMinErr('noconcat',
+                            "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
+                                    "interpolations that concatenate multiple expressions when a trusted value is " +
+                                    "required.  See http://docs.angularjs.org/api/ng.$sce", text);
+                }
+
+                if (!mustHaveExpression  || hasInterpolation) {
+                    concat.length = length;
+                    fn = function(context) {
+                        try {
+                            for(var i = 0, ii = length, part; i<ii; i++) {
+                                if (typeof (part = parts[i]) == 'function') {
+                                    part = part(context);
+                                    if (trustedContext) {
+                                        part = $sce.getTrusted(trustedContext, part);
+                                    } else {
+                                        part = $sce.valueOf(part);
+                                    }
+                                    if (part === null || isUndefined(part)) {
+                                        part = '';
+                                    } else if (typeof part != 'string') {
+                                        part = toJson(part);
+                                    }
+                                }
+                                concat[i] = part;
+                            }
+                            return concat.join('');
+                        }
+                        catch(err) {
+                            var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
+                                    err.toString());
+                            $exceptionHandler(newErr);
+                        }
+                    };
+                    fn.exp = text;
+                    fn.parts = parts;
+                    return fn;
+                }
+            }
+
+
+            /**
+             * @ngdoc method
+             * @name ng.$interpolate#startSymbol
+             * @methodOf ng.$interpolate
+             * @description
+             * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
+     *
+     * Use {@link ng.$interpolateProvider#startSymbol $interpolateProvider#startSymbol} to change
+             * the symbol.
+             *
+             * @returns {string} start symbol.
+             */
+            $interpolate.startSymbol = function() {
+                return startSymbol;
+            };
+
+
+            /**
+             * @ngdoc method
+             * @name ng.$interpolate#endSymbol
+             * @methodOf ng.$interpolate
+             * @description
+             * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
+             *
+             * Use {@link ng.$interpolateProvider#endSymbol $interpolateProvider#endSymbol} to change
+             * the symbol.
+             *
+             * @returns {string} start symbol.
+             */
+            $interpolate.endSymbol = function() {
+                return endSymbol;
+            };
+
+            return $interpolate;
+        }];
+    }
+
+    function $IntervalProvider() {
+        this.$get = ['$rootScope', '$window', '$q',
+            function($rootScope,   $window,   $q) {
+                var intervals = {};
+
+
+                /**
+                 * @ngdoc function
+                 * @name ng.$interval
+                 *
+                 * @description
+                 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
+                 * milliseconds.
+                 *
+                 * The return value of registering an interval function is a promise. This promise will be
+                 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
+                 * run indefinitely if `count` is not defined. The value of the notification will be the
+                 * number of iterations that have run.
+                 * To cancel an interval, call `$interval.cancel(promise)`.
+                 *
+                 * In tests you can use {@link ngMock.$interval#methods_flush `$interval.flush(millis)`} to
+                 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
+                 * time.
+                 *
+                 * @param {function()} fn A function that should be called repeatedly.
+                 * @param {number} delay Number of milliseconds between each function call.
+                 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
+                 *   indefinitely.
+                 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
+                 *   will invoke `fn` within the {@link ng.$rootScope.Scope#methods_$apply $apply} block.
+                 * @returns {promise} A promise which will be notified on each iteration.
+                 */
+                function interval(fn, delay, count, invokeApply) {
+                    var setInterval = $window.setInterval,
+                            clearInterval = $window.clearInterval,
+                            deferred = $q.defer(),
+                            promise = deferred.promise,
+                            iteration = 0,
+                            skipApply = (isDefined(invokeApply) && !invokeApply);
+
+                    count = isDefined(count) ? count : 0,
+
+                            promise.then(null, null, fn);
+
+                    promise.$$intervalId = setInterval(function tick() {
+                        deferred.notify(iteration++);
+
+                        if (count > 0 && iteration >= count) {
+                            deferred.resolve(iteration);
+                            clearInterval(promise.$$intervalId);
+                            delete intervals[promise.$$intervalId];
+                        }
+
+                        if (!skipApply) $rootScope.$apply();
+
+                    }, delay);
+
+                    intervals[promise.$$intervalId] = deferred;
+
+                    return promise;
+                }
+
+
+                /**
+                 * @ngdoc function
+                 * @name ng.$interval#cancel
+                 * @methodOf ng.$interval
+                 *
+                 * @description
+                 * Cancels a task associated with the `promise`.
+                 *
+                 * @param {number} promise Promise returned by the `$interval` function.
+                 * @returns {boolean} Returns `true` if the task was successfully canceled.
+                 */
+                interval.cancel = function(promise) {
+                    if (promise && promise.$$intervalId in intervals) {
+                        intervals[promise.$$intervalId].reject('canceled');
+                        clearInterval(promise.$$intervalId);
+                        delete intervals[promise.$$intervalId];
+                        return true;
+                    }
+                    return false;
+                };
+
+                return interval;
+            }];
+    }
+
+    /**
+     * @ngdoc object
+     * @name ng.$locale
+     *
+     * @description
+     * $locale service provides localization rules for various Angular components. As of right now the
+     * only public api is:
+     *
+     * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
+     */
+    function $LocaleProvider(){
+        this.$get = function() {
+            return {
+                id: 'en-us',
+
+                NUMBER_FORMATS: {
+                    DECIMAL_SEP: '.',
+                    GROUP_SEP: ',',
+                    PATTERNS: [
+                        { // Decimal Pattern
+                            minInt: 1,
+                            minFrac: 0,
+                            maxFrac: 3,
+                            posPre: '',
+                            posSuf: '',
+                            negPre: '-',
+                            negSuf: '',
+                            gSize: 3,
+                            lgSize: 3
+                        },{ //Currency Pattern
+                            minInt: 1,
+                            minFrac: 2,
+                            maxFrac: 2,
+                            posPre: '\u00A4',
+                            posSuf: '',
+                            negPre: '(\u00A4',
+                            negSuf: ')',
+                            gSize: 3,
+                            lgSize: 3
+                        }
+                    ],
+                    CURRENCY_SYM: '$'
+                },
+
+                DATETIME_FORMATS: {
+                    MONTH:
+                            'January,February,March,April,May,June,July,August,September,October,November,December'
+                                    .split(','),
+                    SHORTMONTH:  'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
+                    DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
+                    SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
+                    AMPMS: ['AM','PM'],
+                    medium: 'MMM d, y h:mm:ss a',
+                    short: 'M/d/yy h:mm a',
+                    fullDate: 'EEEE, MMMM d, y',
+                    longDate: 'MMMM d, y',
+                    mediumDate: 'MMM d, y',
+                    shortDate: 'M/d/yy',
+                    mediumTime: 'h:mm:ss a',
+                    shortTime: 'h:mm a'
+                },
+
+                pluralCat: function(num) {
+                    if (num === 1) {
+                        return 'one';
+                    }
+                    return 'other';
+                }
+            };
+        };
+    }
+
+    var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
+            DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
+    var $locationMinErr = minErr('$location');
+
+
+    /**
+     * Encode path using encodeUriSegment, ignoring forward slashes
+     *
+     * @param {string} path Path to encode
+     * @returns {string}
+     */
+    function encodePath(path) {
+        var segments = path.split('/'),
+                i = segments.length;
+
+        while (i--) {
+            segments[i] = encodeUriSegment(segments[i]);
+        }
+
+        return segments.join('/');
+    }
+
+    function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) {
+        var parsedUrl = urlResolve(absoluteUrl, appBase);
+
+        locationObj.$$protocol = parsedUrl.protocol;
+        locationObj.$$host = parsedUrl.hostname;
+        locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
+    }
+
+
+    function parseAppUrl(relativeUrl, locationObj, appBase) {
+        var prefixed = (relativeUrl.charAt(0) !== '/');
+        if (prefixed) {
+            relativeUrl = '/' + relativeUrl;
+        }
+        var match = urlResolve(relativeUrl, appBase);
+        locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
+                match.pathname.substring(1) : match.pathname);
+        locationObj.$$search = parseKeyValue(match.search);
+        locationObj.$$hash = decodeURIComponent(match.hash);
+
+        // make sure path starts with '/';
+        if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
+            locationObj.$$path = '/' + locationObj.$$path;
+        }
+    }
+
+
+    /**
+     *
+     * @param {string} begin
+     * @param {string} whole
+     * @returns {string} returns text from whole after begin or undefined if it does not begin with
+     *                   expected string.
+     */
+    function beginsWith(begin, whole) {
+        if (whole.indexOf(begin) === 0) {
+            return whole.substr(begin.length);
+        }
+    }
+
+
+    function stripHash(url) {
+        var index = url.indexOf('#');
+        return index == -1 ? url : url.substr(0, index);
+    }
+
+
+    function stripFile(url) {
+        return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
+    }
+
+    /* return the server only (scheme://host:port) */
+    function serverBase(url) {
+        return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
+    }
+
+
+    /**
+     * LocationHtml5Url represents an url
+     * This object is exposed as $location service when HTML5 mode is enabled and supported
+     *
+     * @constructor
+     * @param {string} appBase application base URL
+     * @param {string} basePrefix url path prefix
+     */
+    function LocationHtml5Url(appBase, basePrefix) {
+        this.$$html5 = true;
+        basePrefix = basePrefix || '';
+        var appBaseNoFile = stripFile(appBase);
+        parseAbsoluteUrl(appBase, this, appBase);
+
+
+        /**
+         * Parse given html5 (regular) url string into properties
+         * @param {string} newAbsoluteUrl HTML5 url
+         * @private
+         */
+        this.$$parse = function(url) {
+            var pathUrl = beginsWith(appBaseNoFile, url);
+            if (!isString(pathUrl)) {
+                throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
+                        appBaseNoFile);
+            }
+
+            parseAppUrl(pathUrl, this, appBase);
+
+            if (!this.$$path) {
+                this.$$path = '/';
+            }
+
+            this.$$compose();
+        };
+
+        /**
+         * Compose url and update `absUrl` property
+         * @private
+         */
+        this.$$compose = function() {
+            var search = toKeyValue(this.$$search),
+                    hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
+
+            this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
+            this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
+        };
+
+        this.$$rewrite = function(url) {
+            var appUrl, prevAppUrl;
+
+            if ( (appUrl = beginsWith(appBase, url)) !== undefined ) {
+                prevAppUrl = appUrl;
+                if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) {
+                    return appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
+                } else {
+                    return appBase + prevAppUrl;
+                }
+            } else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) {
+                return appBaseNoFile + appUrl;
+            } else if (appBaseNoFile == url + '/') {
+                return appBaseNoFile;
+            }
+        };
+    }
+
+
+    /**
+     * LocationHashbangUrl represents url
+     * This object is exposed as $location service when developer doesn't opt into html5 mode.
+     * It also serves as the base class for html5 mode fallback on legacy browsers.
+     *
+     * @constructor
+     * @param {string} appBase application base URL
+     * @param {string} hashPrefix hashbang prefix
+     */
+    function LocationHashbangUrl(appBase, hashPrefix) {
+        var appBaseNoFile = stripFile(appBase);
+
+        parseAbsoluteUrl(appBase, this, appBase);
+
+
+        /**
+         * Parse given hashbang url into properties
+         * @param {string} url Hashbang url
+         * @private
+         */
+        this.$$parse = function(url) {
+            var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
+            var withoutHashUrl = withoutBaseUrl.charAt(0) == '#'
+                    ? beginsWith(hashPrefix, withoutBaseUrl)
+                    : (this.$$html5)
+                    ? withoutBaseUrl
+                    : '';
+
+            if (!isString(withoutHashUrl)) {
+                throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url,
+                        hashPrefix);
+            }
+            parseAppUrl(withoutHashUrl, this, appBase);
+            this.$$compose();
+        };
+
+        /**
+         * Compose hashbang url and update `absUrl` property
+         * @private
+         */
+        this.$$compose = function() {
+            var search = toKeyValue(this.$$search),
+                    hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
+
+            this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
+            this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
+        };
+
+        this.$$rewrite = function(url) {
+            if(stripHash(appBase) == stripHash(url)) {
+                return url;
+            }
+        };
+    }
+
+
+    /**
+     * LocationHashbangUrl represents url
+     * This object is exposed as $location service when html5 history api is enabled but the browser
+     * does not support it.
+     *
+     * @constructor
+     * @param {string} appBase application base URL
+     * @param {string} hashPrefix hashbang prefix
+     */
+    function LocationHashbangInHtml5Url(appBase, hashPrefix) {
+        this.$$html5 = true;
+        LocationHashbangUrl.apply(this, arguments);
+
+        var appBaseNoFile = stripFile(appBase);
+
+        this.$$rewrite = function(url) {
+            var appUrl;
+
+            if ( appBase == stripHash(url) ) {
+                return url;
+            } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) {
+                return appBase + hashPrefix + appUrl;
+            } else if ( appBaseNoFile === url + '/') {
+                return appBaseNoFile;
+            }
+        };
+    }
+
+
+    LocationHashbangInHtml5Url.prototype =
+            LocationHashbangUrl.prototype =
+                    LocationHtml5Url.prototype = {
+
+                        /**
+                         * Are we in html5 mode?
+                         * @private
+                         */
+                        $$html5: false,
+
+                        /**
+                         * Has any change been replacing ?
+                         * @private
+                         */
+                        $$replace: false,
+
+                        /**
+                         * @ngdoc method
+                         * @name ng.$location#absUrl
+                         * @methodOf ng.$location
+                         *
+                         * @description
+                         * This method is getter only.
+                         *
+                         * Return full url representation with all segments encoded according to rules specified in
+                         * {@link http://www.ietf.org/rfc/rfc3986.txt RFC 3986}.
+                         *
+                         * @return {string} full url
+                         */
+                        absUrl: locationGetter('$$absUrl'),
+
+                        /**
+                         * @ngdoc method
+                         * @name ng.$location#url
+                         * @methodOf ng.$location
+                         *
+                         * @description
+                         * This method is getter / setter.
+                         *
+                         * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
+                         *
+                         * Change path, search and hash, when called with parameter and return `$location`.
+                         *
+                         * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
+                         * @param {string=} replace The path that will be changed
+                         * @return {string} url
+                         */
+                        url: function(url, replace) {
+                            if (isUndefined(url))
+                                return this.$$url;
+
+                            var match = PATH_MATCH.exec(url);
+                            if (match[1]) this.path(decodeURIComponent(match[1]));
+                            if (match[2] || match[1]) this.search(match[3] || '');
+                            this.hash(match[5] || '', replace);
+
+                            return this;
+                        },
+
+                        /**
+                         * @ngdoc method
+                         * @name ng.$location#protocol
+                         * @methodOf ng.$location
+                         *
+                         * @description
+                         * This method is getter only.
+                         *
+                         * Return protocol of current url.
+                         *
+                         * @return {string} protocol of current url
+                         */
+                        protocol: locationGetter('$$protocol'),
+
+                        /**
+                         * @ngdoc method
+                         * @name ng.$location#host
+                         * @methodOf ng.$location
+                         *
+                         * @description
+                         * This method is getter only.
+                         *
+                         * Return host of current url.
+                         *
+                         * @return {string} host of current url.
+                         */
+                        host: locationGetter('$$host'),
+
+                        /**
+                         * @ngdoc method
+                         * @name ng.$location#port
+                         * @methodOf ng.$location
+                         *
+                         * @description
+                         * This method is getter only.
+                         *
+                         * Return port of current url.
+                         *
+                         * @return {Number} port
+                         */
+                        port: locationGetter('$$port'),
+
+                        /**
+                         * @ngdoc method
+                         * @name ng.$location#path
+                         * @methodOf ng.$location
+                         *
+                         * @description
+                         * This method is getter / setter.
+                         *
+                         * Return path of current url when called without any parameter.
+                         *
+                         * Change path when called with parameter and return `$location`.
+                         *
+                         * Note: Path should always begin with forward slash (/), this method will add the forward slash
+                         * if it is missing.
+                         *
+                         * @param {string=} path New path
+                         * @return {string} path
+                         */
+                        path: locationGetterSetter('$$path', function(path) {
+                            return path.charAt(0) == '/' ? path : '/' + path;
+                        }),
+
+                        /**
+                         * @ngdoc method
+                         * @name ng.$location#search
+                         * @methodOf ng.$location
+                         *
+                         * @description
+                         * This method is getter / setter.
+                         *
+                         * Return search part (as object) of current url when called without any parameter.
+                         *
+                         * Change search part when called with parameter and return `$location`.
+                         *
+                         * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
+                         * hash object. Hash object may contain an array of values, which will be decoded as duplicates in
+                         * the url.
+                         *
+                         * @param {(string|Array<string>)=} paramValue If `search` is a string, then `paramValue` will override only a
+                         * single search parameter. If `paramValue` is an array, it will set the parameter as a
+                         * comma-separated value. If `paramValue` is `null`, the parameter will be deleted.
+                         *
+                         * @return {string} search
+                         */
+                        search: function(search, paramValue) {
+                            switch (arguments.length) {
+                                case 0:
+                                    return this.$$search;
+                                case 1:
+                                    if (isString(search)) {
+                                        this.$$search = parseKeyValue(search);
+                                    } else if (isObject(search)) {
+                                        this.$$search = search;
+                                    } else {
+                                        throw $locationMinErr('isrcharg',
+                                                'The first argument of the `$location#search()` call must be a string or an object.');
+                                    }
+                                    break;
+                                default:
+                                    if (isUndefined(paramValue) || paramValue === null) {
+                                        delete this.$$search[search];
+                                    } else {
+                                        this.$$search[search] = paramValue;
+                                    }
+                            }
+
+                            this.$$compose();
+                            return this;
+                        },
+
+                        /**
+                         * @ngdoc method
+                         * @name ng.$location#hash
+                         * @methodOf ng.$location
+                         *
+                         * @description
+                         * This method is getter / setter.
+                         *
+                         * Return hash fragment when called without any parameter.
+                         *
+                         * Change hash fragment when called with parameter and return `$location`.
+                         *
+                         * @param {string=} hash New hash fragment
+                         * @return {string} hash
+                         */
+                        hash: locationGetterSetter('$$hash', identity),
+
+                        /**
+                         * @ngdoc method
+                         * @name ng.$location#replace
+                         * @methodOf ng.$location
+                         *
+                         * @description
+                         * If called, all changes to $location during current `$digest` will be replacing current history
+                         * record, instead of adding new one.
+                         */
+                        replace: function() {
+                            this.$$replace = true;
+                            return this;
+                        }
+                    };
+
+    function locationGetter(property) {
+        return function() {
+            return this[property];
+        };
+    }
+
+
+    function locationGetterSetter(property, preprocess) {
+        return function(value) {
+            if (isUndefined(value))
+                return this[property];
+
+            this[property] = preprocess(value);
+            this.$$compose();
+
+            return this;
+        };
+    }
+
+
+    /**
+     * @ngdoc object
+     * @name ng.$location
+     *
+     * @requires $browser
+     * @requires $sniffer
+     * @requires $rootElement
+     *
+     * @description
+     * The $location service parses the URL in the browser address bar (based on the
+     * {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL
+     * available to your application. Changes to the URL in the address bar are reflected into
+     * $location service and changes to $location are reflected into the browser address bar.
+     *
+     * **The $location service:**
+     *
+     * - Exposes the current URL in the browser address bar, so you can
+     *   - Watch and observe the URL.
+     *   - Change the URL.
+     * - Synchronizes the URL with the browser when the user
+     *   - Changes the address bar.
+     *   - Clicks the back or forward button (or clicks a History link).
+     *   - Clicks on a link.
+     * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
+     *
+     * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular
+     * Services: Using $location}
+     */
+
+    /**
+     * @ngdoc object
+     * @name ng.$locationProvider
+     * @description
+     * Use the `$locationProvider` to configure how the application deep linking paths are stored.
+     */
+    function $LocationProvider(){
+        var hashPrefix = '',
+                html5Mode = false;
+
+        /**
+         * @ngdoc property
+         * @name ng.$locationProvider#hashPrefix
+         * @methodOf ng.$locationProvider
+         * @description
+         * @param {string=} prefix Prefix for hash part (containing path and search)
+         * @returns {*} current value if used as getter or itself (chaining) if used as setter
+         */
+        this.hashPrefix = function(prefix) {
+            if (isDefined(prefix)) {
+                hashPrefix = prefix;
+                return this;
+            } else {
+                return hashPrefix;
+            }
+        };
+
+        /**
+         * @ngdoc property
+         * @name ng.$locationProvider#html5Mode
+         * @methodOf ng.$locationProvider
+         * @description
+         * @param {boolean=} mode Use HTML5 strategy if available.
+         * @returns {*} current value if used as getter or itself (chaining) if used as setter
+         */
+        this.html5Mode = function(mode) {
+            if (isDefined(mode)) {
+                html5Mode = mode;
+                return this;
+            } else {
+                return html5Mode;
+            }
+        };
+
+        /**
+         * @ngdoc event
+         * @name ng.$location#$locationChangeStart
+         * @eventOf ng.$location
+         * @eventType broadcast on root scope
+         * @description
+         * Broadcasted before a URL will change. This change can be prevented by calling
+         * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
+         * details about event object. Upon successful change
+         * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
+         *
+         * @param {Object} angularEvent Synthetic event object.
+         * @param {string} newUrl New URL
+         * @param {string=} oldUrl URL that was before it was changed.
+         */
+
+        /**
+         * @ngdoc event
+         * @name ng.$location#$locationChangeSuccess
+         * @eventOf ng.$location
+         * @eventType broadcast on root scope
+         * @description
+         * Broadcasted after a URL was changed.
+         *
+         * @param {Object} angularEvent Synthetic event object.
+         * @param {string} newUrl New URL
+         * @param {string=} oldUrl URL that was before it was changed.
+         */
+
+        this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
+            function( $rootScope,   $browser,   $sniffer,   $rootElement) {
+                var $location,
+                        LocationMode,
+                        baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
+                        initialUrl = $browser.url(),
+                        appBase;
+
+                if (html5Mode) {
+                    appBase = serverBase(initialUrl) + (baseHref || '/');
+                    LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
+                } else {
+                    appBase = stripHash(initialUrl);
+                    LocationMode = LocationHashbangUrl;
+                }
+                $location = new LocationMode(appBase, '#' + hashPrefix);
+                $location.$$parse($location.$$rewrite(initialUrl));
+
+                $rootElement.on('click', function(event) {
+                    // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
+                    // currently we open nice url link and redirect then
+
+                    if (event.ctrlKey || event.metaKey || event.which == 2) return;
+
+                    var elm = jqLite(event.target);
+
+                    // traverse the DOM up to find first A tag
+                    while (lowercase(elm[0].nodeName) !== 'a') {
+                        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
+                        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
+                    }
+
+                    var absHref = elm.prop('href');
+                    var rewrittenUrl = $location.$$rewrite(absHref);
+
+                    if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) {
+                        event.preventDefault();
+                        if (rewrittenUrl != $browser.url()) {
+                            // update location manually
+                            $location.$$parse(rewrittenUrl);
+                            $rootScope.$apply();
+                            // hack to work around FF6 bug 684208 when scenario runner clicks on links
+                            window.angular['ff-684208-preventDefault'] = true;
+                        }
+                    }
+                });
+
+
+                // rewrite hashbang url <> html5 url
+                if ($location.absUrl() != initialUrl) {
+                    $browser.url($location.absUrl(), true);
+                }
+
+                // update $location when $browser url changes
+                $browser.onUrlChange(function(newUrl) {
+                    if ($location.absUrl() != newUrl) {
+                        if ($rootScope.$broadcast('$locationChangeStart', newUrl,
+                                $location.absUrl()).defaultPrevented) {
+                            $browser.url($location.absUrl());
+                            return;
+                        }
+                        $rootScope.$evalAsync(function() {
+                            var oldUrl = $location.absUrl();
+
+                            $location.$$parse(newUrl);
+                            afterLocationChange(oldUrl);
+                        });
+                        if (!$rootScope.$$phase) $rootScope.$digest();
+                    }
+                });
+
+                // update browser
+                var changeCounter = 0;
+                $rootScope.$watch(function $locationWatch() {
+                    var oldUrl = $browser.url();
+                    var currentReplace = $location.$$replace;
+
+                    if (!changeCounter || oldUrl != $location.absUrl()) {
+                        changeCounter++;
+                        $rootScope.$evalAsync(function() {
+                            if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl).
+                                    defaultPrevented) {
+                                $location.$$parse(oldUrl);
+                            } else {
+                                $browser.url($location.absUrl(), currentReplace);
+                                afterLocationChange(oldUrl);
+                            }
+                        });
+                    }
+                    $location.$$replace = false;
+
+                    return changeCounter;
+                });
+
+                return $location;
+
+                function afterLocationChange(oldUrl) {
+                    $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl);
+                }
+            }];
+    }
+
+    /**
+     * @ngdoc object
+     * @name ng.$log
+     * @requires $window
+     *
+     * @description
+     * Simple service for logging. Default implementation safely writes the message
+     * into the browser's console (if present).
+     *
+     * The main purpose of this service is to simplify debugging and troubleshooting.
+     *
+     * The default is to log `debug` messages. You can use
+     * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
+     *
+     * @example
+     <example>
+     <file name="script.js">
+     function LogCtrl($scope, $log) {
+         $scope.$log = $log;
+         $scope.message = 'Hello World!';
+       }
+     </file>
+     <file name="index.html">
+     <div ng-controller="LogCtrl">
+     <p>Reload this page with open console, enter text and hit the log button...</p>
+     Message:
+     <input type="text" ng-model="message"/>
+     <button ng-click="$log.log(message)">log</button>
+     <button ng-click="$log.warn(message)">warn</button>
+     <button ng-click="$log.info(message)">info</button>
+     <button ng-click="$log.error(message)">error</button>
+     </div>
+     </file>
+     </example>
+     */
+
+    /**
+     * @ngdoc object
+     * @name ng.$logProvider
+     * @description
+     * Use the `$logProvider` to configure how the application logs messages
+     */
+    function $LogProvider(){
+        var debug = true,
+                self = this;
+
+        /**
+         * @ngdoc property
+         * @name ng.$logProvider#debugEnabled
+         * @methodOf ng.$logProvider
+         * @description
+         * @param {string=} flag enable or disable debug level messages
+         * @returns {*} current value if used as getter or itself (chaining) if used as setter
+         */
+        this.debugEnabled = function(flag) {
+            if (isDefined(flag)) {
+                debug = flag;
+                return this;
+            } else {
+                return debug;
+            }
+        };
+
+        this.$get = ['$window', function($window){
+            return {
+                /**
+                 * @ngdoc method
+                 * @name ng.$log#log
+                 * @methodOf ng.$log
+                 *
+                 * @description
+                 * Write a log message
+                 */
+                log: consoleLog('log'),
+
+                /**
+                 * @ngdoc method
+                 * @name ng.$log#info
+                 * @methodOf ng.$log
+                 *
+                 * @description
+                 * Write an information message
+                 */
+                info: consoleLog('info'),
+
+                /**
+                 * @ngdoc method
+                 * @name ng.$log#warn
+                 * @methodOf ng.$log
+                 *
+                 * @description
+                 * Write a warning message
+                 */
+                warn: consoleLog('warn'),
+
+                /**
+                 * @ngdoc method
+                 * @name ng.$log#error
+                 * @methodOf ng.$log
+                 *
+                 * @description
+                 * Write an error message
+                 */
+                error: consoleLog('error'),
+
+                /**
+                 * @ngdoc method
+                 * @name ng.$log#debug
+                 * @methodOf ng.$log
+                 *
+                 * @description
+                 * Write a debug message
+                 */
+                debug: (function () {
+                    var fn = consoleLog('debug');
+
+                    return function() {
+                        if (debug) {
+                            fn.apply(self, arguments);
+                        }
+                    };
+                }())
+            };
+
+            function formatError(arg) {
+                if (arg instanceof Error) {
+                    if (arg.stack) {
+                        arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
+                                ? 'Error: ' + arg.message + '\n' + arg.stack
+                                : arg.stack;
+                    } else if (arg.sourceURL) {
+                        arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
+                    }
+                }
+                return arg;
+            }
+
+            function consoleLog(type) {
+                var console = $window.console || {},
+                        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);
+                    };
+                }
+
+                // we are IE which either doesn't have window.console => this is noop and we do nothing,
+                // or we are IE where console.log doesn't have apply so we log at least first 2 args
+                return function(arg1, arg2) {
+                    logFn(arg1, arg2 == null ? '' : arg2);
+                };
+            }
+        }];
+    }
+
+    var $parseMinErr = minErr('$parse');
+    var promiseWarningCache = {};
+    var promiseWarning;
+
+// Sandboxing Angular Expressions
+// ------------------------------
+// Angular expressions are generally considered safe because these expressions only have direct
+// access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by
+// obtaining a reference to native JS functions such as the Function constructor.
+//
+// As an example, consider the following Angular expression:
+//
+//   {}.toString.constructor(alert("evil JS code"))
+//
+// We want to prevent this type of access. For the sake of performance, during the lexing phase we
+// disallow any "dotted" access to any member named "constructor".
+//
+// For reflective calls (a[b]) we check that the value of the lookup is not the Function constructor
+// while evaluating the expression, which is a stronger but more expensive test. Since reflective
+// calls are expensive anyway, this is not such a big deal compared to static dereferencing.
+//
+// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
+// against the expression language, but not to prevent exploits that were enabled by exposing
+// sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good
+// practice and therefore we are not even trying to protect against interaction with an object
+// explicitly exposed in this way.
+//
+// A developer could foil the name check by aliasing the Function constructor under a different
+// name on the scope.
+//
+// In general, it is not possible to access a Window object from an angular expression unless a
+// window or some DOM object that has a reference to window is published onto a Scope.
+
+    function ensureSafeMemberName(name, fullExpression) {
+        if (name === "constructor") {
+            throw $parseMinErr('isecfld',
+                    'Referencing "constructor" field in Angular expressions is disallowed! Expression: {0}',
+                    fullExpression);
+        }
+        return name;
+    }
+
+    function ensureSafeObject(obj, fullExpression) {
+        // nifty check if obj is Function that is fast and works across iframes and other contexts
+        if (obj && obj.constructor === obj) {
+            throw $parseMinErr('isecfn',
+                    'Referencing Function in Angular expressions is disallowed! Expression: {0}',
+                    fullExpression);
+        } else if (// isWindow(obj)
+                obj && obj.document && obj.location && obj.alert && obj.setInterval) {
+            throw $parseMinErr('isecwindow',
+                    'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
+                    fullExpression);
+        } else if (// isElement(obj)
+                obj && (obj.nodeName || (obj.on && obj.find))) {
+            throw $parseMinErr('isecdom',
+                    'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
+                    fullExpression);
+        } else {
+            return obj;
+        }
+    }
+
+    var OPERATORS = {
+        /* jshint bitwise : false */
+        'null':function(){return null;},
+        'true':function(){return true;},
+        'false':function(){return false;},
+        undefined:noop,
+        '+':function(self, locals, a,b){
+            a=a(self, locals); b=b(self, locals);
+            if (isDefined(a)) {
+                if (isDefined(b)) {
+                    return a + b;
+                }
+                return a;
+            }
+            return isDefined(b)?b:undefined;},
+        '-':function(self, locals, a,b){
+            a=a(self, locals); b=b(self, locals);
+            return (isDefined(a)?a:0)-(isDefined(b)?b:0);
+        },
+        '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);},
+        '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);},
+        '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);},
+        '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);},
+        '=':noop,
+        '===':function(self, locals, a, b){return a(self, locals)===b(self, locals);},
+        '!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);},
+        '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);},
+        '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);},
+        '<':function(self, locals, a,b){return a(self, locals)<b(self, locals);},
+        '>':function(self, locals, a,b){return a(self, locals)>b(self, locals);},
+        '<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);},
+        '>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);},
+        '&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);},
+        '||':function(self, locals, a,b){return a(self, locals)||b(self, locals);},
+        '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);},
+//    '|':function(self, locals, a,b){return a|b;},
+        '|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));},
+        '!':function(self, locals, a){return !a(self, locals);}
+    };
+    /* jshint bitwise: true */
+    var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
+
+
+/////////////////////////////////////////
+
+
+    /**
+     * @constructor
+     */
+    var Lexer = function (options) {
+        this.options = options;
+    };
+
+    Lexer.prototype = {
+        constructor: Lexer,
+
+        lex: function (text) {
+            this.text = text;
+
+            this.index = 0;
+            this.ch = undefined;
+            this.lastCh = ':'; // can start regexp
+
+            this.tokens = [];
+
+            var token;
+            var json = [];
+
+            while (this.index < this.text.length) {
+                this.ch = this.text.charAt(this.index);
+                if (this.is('"\'')) {
+                    this.readString(this.ch);
+                } else if (this.isNumber(this.ch) || this.is('.') && this.isNumber(this.peek())) {
+                    this.readNumber();
+                } else if (this.isIdent(this.ch)) {
+                    this.readIdent();
+                    // identifiers can only be if the preceding char was a { or ,
+                    if (this.was('{,') && json[0] === '{' &&
+                            (token = this.tokens[this.tokens.length - 1])) {
+                        token.json = token.text.indexOf('.') === -1;
+                    }
+                } else if (this.is('(){}[].,;:?')) {
+                    this.tokens.push({
+                        index: this.index,
+                        text: this.ch,
+                        json: (this.was(':[,') && this.is('{[')) || this.is('}]:,')
+                    });
+                    if (this.is('{[')) json.unshift(this.ch);
+                    if (this.is('}]')) json.shift();
+                    this.index++;
+                } else if (this.isWhitespace(this.ch)) {
+                    this.index++;
+                    continue;
+                } else {
+                    var ch2 = this.ch + this.peek();
+                    var ch3 = ch2 + this.peek(2);
+                    var fn = OPERATORS[this.ch];
+                    var fn2 = OPERATORS[ch2];
+                    var fn3 = OPERATORS[ch3];
+                    if (fn3) {
+                        this.tokens.push({index: this.index, text: ch3, fn: fn3});
+                        this.index += 3;
+                    } else if (fn2) {
+                        this.tokens.push({index: this.index, text: ch2, fn: fn2});
+                        this.index += 2;
+                    } else if (fn) {
+                        this.tokens.push({
+                            index: this.index,
+                            text: this.ch,
+                            fn: fn,
+                            json: (this.was('[,:') && this.is('+-'))
+                        });
+                        this.index += 1;
+                    } else {
+                        this.throwError('Unexpected next character ', this.index, this.index + 1);
+                    }
+                }
+                this.lastCh = this.ch;
+            }
+            return this.tokens;
+        },
+
+        is: function(chars) {
+            return chars.indexOf(this.ch) !== -1;
+        },
+
+        was: function(chars) {
+            return chars.indexOf(this.lastCh) !== -1;
+        },
+
+        peek: function(i) {
+            var num = i || 1;
+            return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
+        },
+
+        isNumber: function(ch) {
+            return ('0' <= ch && ch <= '9');
+        },
+
+        isWhitespace: function(ch) {
+            // IE treats non-breaking space as \u00A0
+            return (ch === ' ' || ch === '\r' || ch === '\t' ||
+                    ch === '\n' || ch === '\v' || ch === '\u00A0');
+        },
+
+        isIdent: function(ch) {
+            return ('a' <= ch && ch <= 'z' ||
+                    'A' <= ch && ch <= 'Z' ||
+                    '_' === ch || ch === '$');
+        },
+
+        isExpOperator: function(ch) {
+            return (ch === '-' || ch === '+' || this.isNumber(ch));
+        },
+
+        throwError: function(error, start, end) {
+            end = end || this.index;
+            var colStr = (isDefined(start)
+                    ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
+                    : ' ' + end);
+            throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
+                    error, colStr, this.text);
+        },
+
+        readNumber: function() {
+            var number = '';
+            var start = this.index;
+            while (this.index < this.text.length) {
+                var ch = lowercase(this.text.charAt(this.index));
+                if (ch == '.' || this.isNumber(ch)) {
+                    number += ch;
+                } else {
+                    var peekCh = this.peek();
+                    if (ch == 'e' && this.isExpOperator(peekCh)) {
+                        number += ch;
+                    } else if (this.isExpOperator(ch) &&
+                            peekCh && this.isNumber(peekCh) &&
+                            number.charAt(number.length - 1) == 'e') {
+                        number += ch;
+                    } else if (this.isExpOperator(ch) &&
+                            (!peekCh || !this.isNumber(peekCh)) &&
+                            number.charAt(number.length - 1) == 'e') {
+                        this.throwError('Invalid exponent');
+                    } else {
+                        break;
+                    }
+                }
+                this.index++;
+            }
+            number = 1 * number;
+            this.tokens.push({
+                index: start,
+                text: number,
+                json: true,
+                fn: function() { return number; }
+            });
+        },
+
+        readIdent: function() {
+            var parser = this;
+
+            var ident = '';
+            var start = this.index;
+
+            var lastDot, peekIndex, methodName, ch;
+
+            while (this.index < this.text.length) {
+                ch = this.text.charAt(this.index);
+                if (ch === '.' || this.isIdent(ch) || this.isNumber(ch)) {
+                    if (ch === '.') lastDot = this.index;
+                    ident += ch;
+                } else {
+                    break;
+                }
+                this.index++;
+            }
+
+            //check if this is not a method invocation and if it is back out to last dot
+            if (lastDot) {
+                peekIndex = this.index;
+                while (peekIndex < this.text.length) {
+                    ch = this.text.charAt(peekIndex);
+                    if (ch === '(') {
+                        methodName = ident.substr(lastDot - start + 1);
+                        ident = ident.substr(0, lastDot - start);
+                        this.index = peekIndex;
+                        break;
+                    }
+                    if (this.isWhitespace(ch)) {
+                        peekIndex++;
+                    } else {
+                        break;
+                    }
+                }
+            }
+
+
+            var token = {
+                index: start,
+                text: ident
+            };
+
+            // OPERATORS is our own object so we don't need to use special hasOwnPropertyFn
+            if (OPERATORS.hasOwnProperty(ident)) {
+                token.fn = OPERATORS[ident];
+                token.json = OPERATORS[ident];
+            } else {
+                var getter = getterFn(ident, this.options, this.text);
+                token.fn = extend(function(self, locals) {
+                    return (getter(self, locals));
+                }, {
+                    assign: function(self, value) {
+                        return setter(self, ident, value, parser.text, parser.options);
+                    }
+                });
+            }
+
+            this.tokens.push(token);
+
+            if (methodName) {
+                this.tokens.push({
+                    index:lastDot,
+                    text: '.',
+                    json: false
+                });
+                this.tokens.push({
+                    index: lastDot + 1,
+                    text: methodName,
+                    json: false
+                });
+            }
+        },
+
+        readString: function(quote) {
+            var start = this.index;
+            this.index++;
+            var string = '';
+            var rawString = quote;
+            var escape = false;
+            while (this.index < this.text.length) {
+                var ch = this.text.charAt(this.index);
+                rawString += ch;
+                if (escape) {
+                    if (ch === 'u') {
+                        var hex = this.text.substring(this.index + 1, this.index + 5);
+                        if (!hex.match(/[\da-f]{4}/i))
+                            this.throwError('Invalid unicode escape [\\u' + hex + ']');
+                        this.index += 4;
+                        string += String.fromCharCode(parseInt(hex, 16));
+                    } else {
+                        var rep = ESCAPE[ch];
+                        if (rep) {
+                            string += rep;
+                        } else {
+                            string += ch;
+                        }
+                    }
+                    escape = false;
+                } else if (ch === '\\') {
+                    escape = true;
+                } else if (ch === quote) {
+                    this.index++;
+                    this.tokens.push({
+                        index: start,
+                        text: rawString,
+                        string: string,
+                        json: true,
+                        fn: function() { return string; }
+                    });
+                    return;
+                } else {
+                    string += ch;
+                }
+                this.index++;
+            }
+            this.throwError('Unterminated quote', start);
+        }
+    };
+
+
+    /**
+     * @constructor
+     */
+    var Parser = function (lexer, $filter, options) {
+        this.lexer = lexer;
+        this.$filter = $filter;
+        this.options = options;
+    };
+
+    Parser.ZERO = function () { return 0; };
+
+    Parser.prototype = {
+        constructor: Parser,
+
+        parse: function (text, json) {
+            this.text = text;
+
+            //TODO(i): strip all the obsolte json stuff from this file
+            this.json = json;
+
+            this.tokens = this.lexer.lex(text);
+
+            if (json) {
+                // The extra level of aliasing is here, just in case the lexer misses something, so that
+                // we prevent any accidental execution in JSON.
+                this.assignment = this.logicalOR;
+
+                this.functionCall =
+                        this.fieldAccess =
+                                this.objectIndex =
+                                        this.filterChain = function() {
+                                            this.throwError('is not valid json', {text: text, index: 0});
+                                        };
+            }
+
+            var value = json ? this.primary() : this.statements();
+
+            if (this.tokens.length !== 0) {
+                this.throwError('is an unexpected token', this.tokens[0]);
+            }
+
+            value.literal = !!value.literal;
+            value.constant = !!value.constant;
+
+            return value;
+        },
+
+        primary: function () {
+            var primary;
+            if (this.expect('(')) {
+                primary = this.filterChain();
+                this.consume(')');
+            } else if (this.expect('[')) {
+                primary = this.arrayDeclaration();
+            } else if (this.expect('{')) {
+                primary = this.object();
+            } else {
+                var token = this.expect();
+                primary = token.fn;
+                if (!primary) {
+                    this.throwError('not a primary expression', token);
+                }
+                if (token.json) {
+                    primary.constant = true;
+                    primary.literal = true;
+                }
+            }
+
+            var next, context;
+            while ((next = this.expect('(', '[', '.'))) {
+                if (next.text === '(') {
+                    primary = this.functionCall(primary, context);
+                    context = null;
+                } else if (next.text === '[') {
+                    context = primary;
+                    primary = this.objectIndex(primary);
+                } else if (next.text === '.') {
+                    context = primary;
+                    primary = this.fieldAccess(primary);
+                } else {
+                    this.throwError('IMPOSSIBLE');
+                }
+            }
+            return primary;
+        },
+
+        throwError: function(msg, token) {
+            throw $parseMinErr('syntax',
+                    'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
+                    token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
+        },
+
+        peekToken: function() {
+            if (this.tokens.length === 0)
+                throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
+            return this.tokens[0];
+        },
+
+        peek: function(e1, e2, e3, e4) {
+            if (this.tokens.length > 0) {
+                var token = this.tokens[0];
+                var t = token.text;
+                if (t === e1 || t === e2 || t === e3 || t === e4 ||
+                        (!e1 && !e2 && !e3 && !e4)) {
+                    return token;
+                }
+            }
+            return false;
+        },
+
+        expect: function(e1, e2, e3, e4){
+            var token = this.peek(e1, e2, e3, e4);
+            if (token) {
+                if (this.json && !token.json) {
+                    this.throwError('is not valid json', token);
+                }
+                this.tokens.shift();
+                return token;
+            }
+            return false;
+        },
+
+        consume: function(e1){
+            if (!this.expect(e1)) {
+                this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
+            }
+        },
+
+        unaryFn: function(fn, right) {
+            return extend(function(self, locals) {
+                return fn(self, locals, right);
+            }, {
+                constant:right.constant
+            });
+        },
+
+        ternaryFn: function(left, middle, right){
+            return extend(function(self, locals){
+                return left(self, locals) ? middle(self, locals) : right(self, locals);
+            }, {
+                constant: left.constant && middle.constant && right.constant
+            });
+        },
+
+        binaryFn: function(left, fn, right) {
+            return extend(function(self, locals) {
+                return fn(self, locals, left, right);
+            }, {
+                constant:left.constant && right.constant
+            });
+        },
+
+        statements: function() {
+            var statements = [];
+            while (true) {
+                if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
+                    statements.push(this.filterChain());
+                if (!this.expect(';')) {
+                    // optimize for the common case where there is only one statement.
+                    // TODO(size): maybe we should not support multiple statements?
+                    return (statements.length === 1)
+                            ? statements[0]
+                            : function(self, locals) {
+                        var value;
+                        for (var i = 0; i < statements.length; i++) {
+                            var statement = statements[i];
+                            if (statement) {
+                                value = statement(self, locals);
+                            }
+                        }
+                        return value;
+                    };
+                }
+            }
+        },
+
+        filterChain: function() {
+            var left = this.expression();
+            var token;
+            while (true) {
+                if ((token = this.expect('|'))) {
+                    left = this.binaryFn(left, token.fn, this.filter());
+                } else {
+                    return left;
+                }
+            }
+        },
+
+        filter: function() {
+            var token = this.expect();
+            var fn = this.$filter(token.text);
+            var argsFn = [];
+            while (true) {
+                if ((token = this.expect(':'))) {
+                    argsFn.push(this.expression());
+                } else {
+                    var fnInvoke = function(self, locals, input) {
+                        var args = [input];
+                        for (var i = 0; i < argsFn.length; i++) {
+                            args.push(argsFn[i](self, locals));
+                        }
+                        return fn.apply(self, args);
+                    };
+                    return function() {
+                        return fnInvoke;
+                    };
+                }
+            }
+        },
+
+        expression: function() {
+            return this.assignment();
+        },
+
+        assignment: function() {
+            var left = this.ternary();
+            var right;
+            var token;
+            if ((token = this.expect('='))) {
+                if (!left.assign) {
+                    this.throwError('implies assignment but [' +
+                            this.text.substring(0, token.index) + '] can not be assigned to', token);
+                }
+                right = this.ternary();
+                return function(scope, locals) {
+                    return left.assign(scope, right(scope, locals), locals);
+                };
+            }
+            return left;
+        },
+
+        ternary: function() {
+            var left = this.logicalOR();
+            var middle;
+            var token;
+            if ((token = this.expect('?'))) {
+                middle = this.ternary();
+                if ((token = this.expect(':'))) {
+                    return this.ternaryFn(left, middle, this.ternary());
+                } else {
+                    this.throwError('expected :', token);
+                }
+            } else {
+                return left;
+            }
+        },
+
+        logicalOR: function() {
+            var left = this.logicalAND();
+            var token;
+            while (true) {
+                if ((token = this.expect('||'))) {
+                    left = this.binaryFn(left, token.fn, this.logicalAND());
+                } else {
+                    return left;
+                }
+            }
+        },
+
+        logicalAND: function() {
+            var left = this.equality();
+            var token;
+            if ((token = this.expect('&&'))) {
+                left = this.binaryFn(left, token.fn, this.logicalAND());
+            }
+            return left;
+        },
+
+        equality: function() {
+            var left = this.relational();
+            var token;
+            if ((token = this.expect('==','!=','===','!=='))) {
+                left = this.binaryFn(left, token.fn, this.equality());
+            }
+            return left;
+        },
+
+        relational: function() {
+            var left = this.additive();
+            var token;
+            if ((token = this.expect('<', '>', '<=', '>='))) {
+                left = this.binaryFn(left, token.fn, this.relational());
+            }
+            return left;
+        },
+
+        additive: function() {
+            var left = this.multiplicative();
+            var token;
+            while ((token = this.expect('+','-'))) {
+                left = this.binaryFn(left, token.fn, this.multiplicative());
+            }
+            return left;
+        },
+
+        multiplicative: function() {
+            var left = this.unary();
+            var token;
+            while ((token = this.expect('*','/','%'))) {
+                left = this.binaryFn(left, token.fn, this.unary());
+            }
+            return left;
+        },
+
+        unary: function() {
+            var token;
+            if (this.expect('+')) {
+                return this.primary();
+            } else if ((token = this.expect('-'))) {
+                return this.binaryFn(Parser.ZERO, token.fn, this.unary());
+            } else if ((token = this.expect('!'))) {
+                return this.unaryFn(token.fn, this.unary());
+            } else {
+                return this.primary();
+            }
+        },
+
+        fieldAccess: function(object) {
+            var parser = this;
+            var field = this.expect().text;
+            var getter = getterFn(field, this.options, this.text);
+
+            return extend(function(scope, locals, self) {
+                return getter(self || object(scope, locals), locals);
+            }, {
+                assign: function(scope, value, locals) {
+                    return setter(object(scope, locals), field, value, parser.text, parser.options);
+                }
+            });
+        },
+
+        objectIndex: function(obj) {
+            var parser = this;
+
+            var indexFn = this.expression();
+            this.consume(']');
+
+            return extend(function(self, locals) {
+                var o = obj(self, locals),
+                        i = indexFn(self, locals),
+                        v, p;
+
+                if (!o) return undefined;
+                v = ensureSafeObject(o[i], parser.text);
+                if (v && v.then && parser.options.unwrapPromises) {
+                    p = v;
+                    if (!('$$v' in v)) {
+                        p.$$v = undefined;
+                        p.then(function(val) { p.$$v = val; });
+                    }
+                    v = v.$$v;
+                }
+                return v;
+            }, {
+                assign: function(self, value, locals) {
+                    var key = indexFn(self, locals);
+                    // prevent overwriting of Function.constructor which would break ensureSafeObject check
+                    var safe = ensureSafeObject(obj(self, locals), parser.text);
+                    return safe[key] = value;
+                }
+            });
+        },
+
+        functionCall: function(fn, contextGetter) {
+            var argsFn = [];
+            if (this.peekToken().text !== ')') {
+                do {
+                    argsFn.push(this.expression());
+                } while (this.expect(','));
+            }
+            this.consume(')');
+
+            var parser = this;
+
+            return function(scope, locals) {
+                var args = [];
+                var context = contextGetter ? contextGetter(scope, locals) : scope;
+
+                for (var i = 0; i < argsFn.length; i++) {
+                    args.push(argsFn[i](scope, locals));
+                }
+                var fnPtr = fn(scope, locals, context) || noop;
+
+                ensureSafeObject(context, parser.text);
+                ensureSafeObject(fnPtr, parser.text);
+
+                // IE stupidity! (IE doesn't have apply for some native functions)
+                var v = fnPtr.apply
+                        ? fnPtr.apply(context, args)
+                        : fnPtr(args[0], args[1], args[2], args[3], args[4]);
+
+                return ensureSafeObject(v, parser.text);
+            };
+        },
+
+        // This is used with json array declaration
+        arrayDeclaration: function () {
+            var elementFns = [];
+            var allConstant = true;
+            if (this.peekToken().text !== ']') {
+                do {
+                    var elementFn = this.expression();
+                    elementFns.push(elementFn);
+                    if (!elementFn.constant) {
+                        allConstant = false;
+                    }
+                } while (this.expect(','));
+            }
+            this.consume(']');
+
+            return extend(function(self, locals) {
+                var array = [];
+                for (var i = 0; i < elementFns.length; i++) {
+                    array.push(elementFns[i](self, locals));
+                }
+                return array;
+            }, {
+                literal: true,
+                constant: allConstant
+            });
+        },
+
+        object: function () {
+            var keyValues = [];
+            var allConstant = true;
+            if (this.peekToken().text !== '}') {
+                do {
+                    var token = this.expect(),
+                            key = token.string || token.text;
+                    this.consume(':');
+                    var value = this.expression();
+                    keyValues.push({key: key, value: value});
+                    if (!value.constant) {
+                        allConstant = false;
+                    }
+                } while (this.expect(','));
+            }
+            this.consume('}');
+
+            return extend(function(self, locals) {
+                var object = {};
+                for (var i = 0; i < keyValues.length; i++) {
+                    var keyValue = keyValues[i];
+                    object[keyValue.key] = keyValue.value(self, locals);
+                }
+                return object;
+            }, {
+                literal: true,
+                constant: allConstant
+            });
+        }
+    };
+
+
+//////////////////////////////////////////////////
+// Parser helper functions
+//////////////////////////////////////////////////
+
+    function setter(obj, path, setValue, fullExp, options) {
+        //needed?
+        options = options || {};
+
+        var element = path.split('.'), key;
+        for (var i = 0; element.length > 1; i++) {
+            key = ensureSafeMemberName(element.shift(), fullExp);
+            var propertyObj = obj[key];
+            if (!propertyObj) {
+                propertyObj = {};
+                obj[key] = propertyObj;
+            }
+            obj = propertyObj;
+            if (obj.then && options.unwrapPromises) {
+                promiseWarning(fullExp);
+                if (!("$$v" in obj)) {
+                    (function(promise) {
+                        promise.then(function(val) { promise.$$v = val; }); }
+                            )(obj);
+                }
+                if (obj.$$v === undefined) {
+                    obj.$$v = {};
+                }
+                obj = obj.$$v;
+            }
+        }
+        key = ensureSafeMemberName(element.shift(), fullExp);
+        obj[key] = setValue;
+        return setValue;
+    }
+
+    var getterFnCache = {};
+
+    /**
+     * Implementation of the "Black Hole" variant from:
+     * - http://jsperf.com/angularjs-parse-getter/4
+     * - http://jsperf.com/path-evaluation-simplified/7
+     */
+    function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
+        ensureSafeMemberName(key0, fullExp);
+        ensureSafeMemberName(key1, fullExp);
+        ensureSafeMemberName(key2, fullExp);
+        ensureSafeMemberName(key3, fullExp);
+        ensureSafeMemberName(key4, fullExp);
+
+        return !options.unwrapPromises
+                ? function cspSafeGetter(scope, locals) {
+            var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
+
+            if (pathVal === null || pathVal === undefined) return pathVal;
+            pathVal = pathVal[key0];
+
+            if (!key1 || pathVal === null || pathVal === undefined) return pathVal;
+            pathVal = pathVal[key1];
+
+            if (!key2 || pathVal === null || pathVal === undefined) return pathVal;
+            pathVal = pathVal[key2];
+
+            if (!key3 || pathVal === null || pathVal === undefined) return pathVal;
+            pathVal = pathVal[key3];
+
+            if (!key4 || pathVal === null || pathVal === undefined) return pathVal;
+            pathVal = pathVal[key4];
+
+            return pathVal;
+        }
+                : function cspSafePromiseEnabledGetter(scope, locals) {
+            var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope,
+                    promise;
+
+            if (pathVal === null || pathVal === undefined) return pathVal;
+
+            pathVal = pathVal[key0];
+            if (pathVal && pathVal.then) {
+                promiseWarning(fullExp);
+                if (!("$$v" in pathVal)) {
+                    promise = pathVal;
+                    promise.$$v = undefined;
+                    promise.then(function(val) { promise.$$v = val; });
+                }
+                pathVal = pathVal.$$v;
+            }
+            if (!key1 || pathVal === null || pathVal === undefined) return pathVal;
+
+            pathVal = pathVal[key1];
+            if (pathVal && pathVal.then) {
+                promiseWarning(fullExp);
+                if (!("$$v" in pathVal)) {
+                    promise = pathVal;
+                    promise.$$v = undefined;
+                    promise.then(function(val) { promise.$$v = val; });
+                }
+                pathVal = pathVal.$$v;
+            }
+            if (!key2 || pathVal === null || pathVal === undefined) return pathVal;
+
+            pathVal = pathVal[key2];
+            if (pathVal && pathVal.then) {
+                promiseWarning(fullExp);
+                if (!("$$v" in pathVal)) {
+                    promise = pathVal;
+                    promise.$$v = undefined;
+                    promise.then(function(val) { promise.$$v = val; });
+                }
+                pathVal = pathVal.$$v;
+            }
+            if (!key3 || pathVal === null || pathVal === undefined) return pathVal;
+
+            pathVal = pathVal[key3];
+            if (pathVal && pathVal.then) {
+                promiseWarning(fullExp);
+                if (!("$$v" in pathVal)) {
+                    promise = pathVal;
+                    promise.$$v = undefined;
+                    promise.then(function(val) { promise.$$v = val; });
+                }
+                pathVal = pathVal.$$v;
+            }
+            if (!key4 || pathVal === null || pathVal === undefined) return pathVal;
+
+            pathVal = pathVal[key4];
+            if (pathVal && pathVal.then) {
+                promiseWarning(fullExp);
+                if (!("$$v" in pathVal)) {
+                    promise = pathVal;
+                    promise.$$v = undefined;
+                    promise.then(function(val) { promise.$$v = val; });
+                }
+                pathVal = pathVal.$$v;
+            }
+            return pathVal;
+        };
+    }
+
+    function getterFn(path, options, fullExp) {
+        // Check whether the cache has this getter already.
+        // We can use hasOwnProperty directly on the cache because we ensure,
+        // see below, that the cache never stores a path called 'hasOwnProperty'
+        if (getterFnCache.hasOwnProperty(path)) {
+            return getterFnCache[path];
+        }
+
+        var pathKeys = path.split('.'),
+                pathKeysLength = pathKeys.length,
+                fn;
+
+        if (options.csp) {
+            if (pathKeysLength < 6) {
+                fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp,
+                        options);
+            } else {
+                fn = function(scope, locals) {
+                    var i = 0, val;
+                    do {
+                        val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++],
+                                pathKeys[i++], fullExp, options)(scope, locals);
+
+                        locals = undefined; // clear after first iteration
+                        scope = val;
+                    } while (i < pathKeysLength);
+                    return val;
+                };
+            }
+        } else {
+            var code = 'var l, fn, p;\n';
+            forEach(pathKeys, function(key, index) {
+                ensureSafeMemberName(key, fullExp);
+                code += 'if(s === null || s === undefined) return s;\n' +
+                        'l=s;\n' +
+                        's='+ (index
+                    // we simply dereference 's' on any .dot notation
+                        ? 's'
+                    // but if we are first then we check locals first, and if so read it first
+                        : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' +
+                        (options.unwrapPromises
+                                ? 'if (s && s.then) {\n' +
+                                ' pw("' + fullExp.replace(/(["\r\n])/g, '\\$1') + '");\n' +
+                                ' if (!("$$v" in s)) {\n' +
+                                ' p=s;\n' +
+                                ' p.$$v = undefined;\n' +
+                                ' p.then(function(v) {p.$$v=v;});\n' +
+                                '}\n' +
+                                ' s=s.$$v\n' +
+                                '}\n'
+                                : '');
+            });
+            code += 'return s;';
+
+            /* jshint -W054 */
+            var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning
+            /* jshint +W054 */
+            evaledFnGetter.toString = function() { return code; };
+            fn = function(scope, locals) {
+                return evaledFnGetter(scope, locals, promiseWarning);
+            };
+        }
+
+        // Only cache the value if it's not going to mess up the cache object
+        // This is more performant that using Object.prototype.hasOwnProperty.call
+        if (path !== 'hasOwnProperty') {
+            getterFnCache[path] = fn;
+        }
+        return fn;
+    }
+
+///////////////////////////////////
+
+    /**
+     * @ngdoc function
+     * @name ng.$parse
+     * @function
+     *
+     * @description
+     *
+     * Converts Angular {@link guide/expression expression} into a function.
+     *
+     * <pre>
+     *   var getter = $parse('user.name');
+     *   var setter = getter.assign;
+     *   var context = {user:{name:'angular'}};
+     *   var locals = {user:{name:'local'}};
+     *
+     *   expect(getter(context)).toEqual('angular');
+     *   setter(context, 'newValue');
+     *   expect(context.user.name).toEqual('newValue');
+     *   expect(getter(context, locals)).toEqual('local');
+     * </pre>
+     *
+     *
+     * @param {string} expression String expression to compile.
+     * @returns {function(context, locals)} a function which represents the compiled expression:
+     *
+     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
+     *      are evaluated against (typically a scope object).
+     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
+     *      `context`.
+     *
+     *    The returned function also has the following properties:
+     *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
+     *        literal.
+     *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
+     *        constant literals.
+     *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
+     *        set to a function to change its value on the given context.
+     *
+     */
+
+
+    /**
+     * @ngdoc object
+     * @name ng.$parseProvider
+     * @function
+     *
+     * @description
+     * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
+     *  service.
+     */
+    function $ParseProvider() {
+        var cache = {};
+
+        var $parseOptions = {
+            csp: false,
+            unwrapPromises: false,
+            logPromiseWarnings: true
+        };
+
+
+        /**
+         * @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future.
+         *
+         * @ngdoc method
+         * @name ng.$parseProvider#unwrapPromises
+         * @methodOf ng.$parseProvider
+         * @description
+         *
+         * **This feature is deprecated, see deprecation notes below for more info**
+         *
+         * If set to true (default is false), $parse will unwrap promises automatically when a promise is
+         * found at any part of the expression. In other words, if set to true, the expression will always
+         * result in a non-promise value.
+         *
+         * While the promise is unresolved, it's treated as undefined, but once resolved and fulfilled,
+         * the fulfillment value is used in place of the promise while evaluating the expression.
+         *
+         * **Deprecation notice**
+         *
+         * This is a feature that didn't prove to be wildly useful or popular, primarily because of the
+         * dichotomy between data access in templates (accessed as raw values) and controller code
+         * (accessed as promises).
+         *
+         * In most code we ended up resolving promises manually in controllers anyway and thus unifying
+         * the model access there.
+         *
+         * Other downsides of automatic promise unwrapping:
+         *
+         * - when building components it's often desirable to receive the raw promises
+         * - adds complexity and slows down expression evaluation
+         * - makes expression code pre-generation unattractive due to the amount of code that needs to be
+         *   generated
+         * - makes IDE auto-completion and tool support hard
+         *
+         * **Warning Logs**
+         *
+         * If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a
+         * promise (to reduce the noise, each expression is logged only once). To disable this logging use
+         * `$parseProvider.logPromiseWarnings(false)` api.
+         *
+         *
+         * @param {boolean=} value New value.
+         * @returns {boolean|self} Returns the current setting when used as getter and self if used as
+         *                         setter.
+         */
+        this.unwrapPromises = function(value) {
+            if (isDefined(value)) {
+                $parseOptions.unwrapPromises = !!value;
+                return this;
+            } else {
+                return $parseOptions.unwrapPromises;
+            }
+        };
+
+
+        /**
+         * @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future.
+         *
+         * @ngdoc method
+         * @name ng.$parseProvider#logPromiseWarnings
+         * @methodOf ng.$parseProvider
+         * @description
+         *
+         * Controls whether Angular should log a warning on any encounter of a promise in an expression.
+         *
+         * The default is set to `true`.
+         *
+         * This setting applies only if `$parseProvider.unwrapPromises` setting is set to true as well.
+         *
+         * @param {boolean=} value New value.
+         * @returns {boolean|self} Returns the current setting when used as getter and self if used as
+         *                         setter.
+         */
+        this.logPromiseWarnings = function(value) {
+            if (isDefined(value)) {
+                $parseOptions.logPromiseWarnings = value;
+                return this;
+            } else {
+                return $parseOptions.logPromiseWarnings;
+            }
+        };
+
+
+        this.$get = ['$filter', '$sniffer', '$log', function($filter, $sniffer, $log) {
+            $parseOptions.csp = $sniffer.csp;
+
+            promiseWarning = function promiseWarningFn(fullExp) {
+                if (!$parseOptions.logPromiseWarnings || promiseWarningCache.hasOwnProperty(fullExp)) return;
+                promiseWarningCache[fullExp] = true;
+                $log.warn('[$parse] Promise found in the expression `' + fullExp + '`. ' +
+                        'Automatic unwrapping of promises in Angular expressions is deprecated.');
+            };
+
+            return function(exp) {
+                var parsedExpression;
+
+                switch (typeof exp) {
+                    case 'string':
+
+                        if (cache.hasOwnProperty(exp)) {
+                            return cache[exp];
+                        }
+
+                        var lexer = new Lexer($parseOptions);
+                        var parser = new Parser(lexer, $filter, $parseOptions);
+                        parsedExpression = parser.parse(exp, false);
+
+                        if (exp !== 'hasOwnProperty') {
+                            // Only cache the value if it's not going to mess up the cache object
+                            // This is more performant that using Object.prototype.hasOwnProperty.call
+                            cache[exp] = parsedExpression;
+                        }
+
+                        return parsedExpression;
+
+                    case 'function':
+                        return exp;
+
+                    default:
+                        return noop;
+                }
+            };
+        }];
+    }
+
+    /**
+     * @ngdoc service
+     * @name ng.$q
+     * @requires $rootScope
+     *
+     * @description
+     * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q).
+     *
+     * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
+     * interface for interacting with an object that represents the result of an action that is
+     * performed asynchronously, and may or may not be finished at any given point in time.
+     *
+     * From the perspective of dealing with error handling, deferred and promise APIs are to
+     * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
+     *
+     * <pre>
+     *   // for the purpose of this example let's assume that variables `$q` and `scope` are
+     *   // available in the current lexical scope (they could have been injected or passed in).
+     *
+     *   function asyncGreet(name) {
+ *     var deferred = $q.defer();
+ *
+ *     setTimeout(function() {
+ *       // since this fn executes async in a future turn of the event loop, we need to wrap
+ *       // our code into an $apply call so that the model changes are properly observed.
+ *       scope.$apply(function() {
+ *         deferred.notify('About to greet ' + name + '.');
+ *
+ *         if (okToGreet(name)) {
+ *           deferred.resolve('Hello, ' + name + '!');
+ *         } else {
+ *           deferred.reject('Greeting ' + name + ' is not allowed.');
+ *         }
+ *       });
+ *     }, 1000);
+ *
+ *     return deferred.promise;
+ *   }
+     *
+     *   var promise = asyncGreet('Robin Hood');
+     *   promise.then(function(greeting) {
+ *     alert('Success: ' + greeting);
+ *   }, function(reason) {
+ *     alert('Failed: ' + reason);
+ *   }, function(update) {
+ *     alert('Got notification: ' + update);
+ *   });
+     * </pre>
+     *
+     * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
+     * comes in the way of guarantees that promise and deferred APIs make, see
+     * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
+     *
+     * Additionally the promise api allows for composition that is very hard to do with the
+     * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
+     * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
+     * section on serial or parallel joining of promises.
+     *
+     *
+     * # The Deferred API
+     *
+     * A new instance of deferred is constructed by calling `$q.defer()`.
+     *
+     * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
+     * that can be used for signaling the successful or unsuccessful completion, as well as the status
+     * of the task.
+     *
+     * **Methods**
+     *
+     * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
+     *   constructed via `$q.reject`, the promise will be rejected instead.
+     * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
+     *   resolving it with a rejection constructed via `$q.reject`.
+     * - `notify(value)` - provides updates on the status of the promises execution. This may be called
+     *   multiple times before the promise is either resolved or rejected.
+     *
+     * **Properties**
+     *
+     * - promise – `{Promise}` – promise object associated with this deferred.
+     *
+     *
+     * # The Promise API
+     *
+     * A new promise instance is created when a deferred instance is created and can be retrieved by
+     * calling `deferred.promise`.
+     *
+     * The purpose of the promise object is to allow for interested parties to get access to the result
+     * of the deferred task when it completes.
+     *
+     * **Methods**
+     *
+     * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
+     *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
+     *   as soon as the result is available. The callbacks are called with a single argument: the result
+     *   or rejection reason. Additionally, the notify callback may be called zero or more times to
+     *   provide a progress indication, before the promise is resolved or rejected.
+     *
+     *   This method *returns a new promise* which is resolved or rejected via the return value of the
+     *   `successCallback`, `errorCallback`. It also notifies via the return value of the
+     *   `notifyCallback` method. The promise can not be resolved or rejected from the notifyCallback
+     *   method.
+     *
+     * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
+     *
+     * - `finally(callback)` – allows you to observe either the fulfillment or rejection of a promise,
+     *   but to do so without modifying the final value. This is useful to release resources or do some
+     *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
+     *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
+     *   more information.
+     *
+     *   Because `finally` is a reserved word in JavaScript and reserved keywords are not supported as
+     *   property names by ES3, you'll need to invoke the method like `promise['finally'](callback)` to
+     *   make your code IE8 compatible.
+     *
+     * # Chaining promises
+     *
+     * Because calling the `then` method of a promise returns a new derived promise, it is easily
+     * possible to create a chain of promises:
+     *
+     * <pre>
+     *   promiseB = promiseA.then(function(result) {
+ *     return result + 1;
+ *   });
+     *
+     *   // promiseB will be resolved immediately after promiseA is resolved and its value
+     *   // will be the result of promiseA incremented by 1
+     * </pre>
+     *
+     * It is possible to create chains of any length and since a promise can be resolved with another
+     * promise (which will defer its resolution further), it is possible to pause/defer resolution of
+     * the promises at any point in the chain. This makes it possible to implement powerful APIs like
+     * $http's response interceptors.
+     *
+     *
+     * # Differences between Kris Kowal's Q and $q
+     *
+     *  There are three main differences:
+     *
+     * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
+     *   mechanism in angular, which means faster propagation of resolution or rejection into your
+     *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
+     * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
+     *   all the important functionality needed for common async tasks.
+     *
+     *  # Testing
+     *
+     *  <pre>
+     *    it('should simulate promise', inject(function($q, $rootScope) {
+ *      var deferred = $q.defer();
+ *      var promise = deferred.promise;
+ *      var resolvedValue;
+ *
+ *      promise.then(function(value) { resolvedValue = value; });
+ *      expect(resolvedValue).toBeUndefined();
+ *
+ *      // Simulate resolving of promise
+ *      deferred.resolve(123);
+ *      // Note that the 'then' function does not get called synchronously.
+ *      // This is because we want the promise API to always be async, whether or not
+ *      // it got called synchronously or asynchronously.
+ *      expect(resolvedValue).toBeUndefined();
+ *
+ *      // Propagate promise resolution to 'then' functions using $apply().
+ *      $rootScope.$apply();
+ *      expect(resolvedValue).toEqual(123);
+ *    }));
+     *  </pre>
+     */
+    function $QProvider() {
+
+        this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
+            return qFactory(function(callback) {
+                $rootScope.$evalAsync(callback);
+            }, $exceptionHandler);
+        }];
+    }
+
+
+    /**
+     * Constructs a promise manager.
+     *
+     * @param {function(function)} nextTick Function for executing functions in the next turn.
+     * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
+     *     debugging purposes.
+     * @returns {object} Promise manager.
+     */
+    function qFactory(nextTick, exceptionHandler) {
+
+        /**
+         * @ngdoc
+         * @name ng.$q#defer
+         * @methodOf ng.$q
+         * @description
+         * Creates a `Deferred` object which represents a task which will finish in the future.
+         *
+         * @returns {Deferred} Returns a new instance of deferred.
+         */
+        var defer = function() {
+            var pending = [],
+                    value, deferred;
+
+            deferred = {
+
+                resolve: function(val) {
+                    if (pending) {
+                        var callbacks = pending;
+                        pending = undefined;
+                        value = ref(val);
+
+                        if (callbacks.length) {
+                            nextTick(function() {
+                                var callback;
+                                for (var i = 0, ii = callbacks.length; i < ii; i++) {
+                                    callback = callbacks[i];
+                                    value.then(callback[0], callback[1], callback[2]);
+                                }
+                            });
+                        }
+                    }
+                },
+
+
+                reject: function(reason) {
+                    deferred.resolve(reject(reason));
+                },
+
+
+                notify: function(progress) {
+                    if (pending) {
+                        var callbacks = pending;
+
+                        if (pending.length) {
+                            nextTick(function() {
+                                var callback;
+                                for (var i = 0, ii = callbacks.length; i < ii; i++) {
+                                    callback = callbacks[i];
+                                    callback[2](progress);
+                                }
+                            });
+                        }
+                    }
+                },
+
+
+                promise: {
+                    then: function(callback, errback, progressback) {
+                        var result = defer();
+
+                        var wrappedCallback = function(value) {
+                            try {
+                                result.resolve((isFunction(callback) ? callback : defaultCallback)(value));
+                            } catch(e) {
+                                result.reject(e);
+                                exceptionHandler(e);
+                            }
+                        };
+
+                        var wrappedErrback = function(reason) {
+                            try {
+                                result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));
+                            } catch(e) {
+                                result.reject(e);
+                                exceptionHandler(e);
+                            }
+                        };
+
+                        var wrappedProgressback = function(progress) {
+                            try {
+                                result.notify((isFunction(progressback) ? progressback : defaultCallback)(progress));
+                            } catch(e) {
+                                exceptionHandler(e);
+                            }
+                        };
+
+                        if (pending) {
+                            pending.push([wrappedCallback, wrappedErrback, wrappedProgressback]);
+                        } else {
+                            value.then(wrappedCallback, wrappedErrback, wrappedProgressback);
+                        }
+
+                        return result.promise;
+                    },
+
+                    "catch": function(callback) {
+                        return this.then(null, callback);
+                    },
+
+                    "finally": function(callback) {
+
+                        function makePromise(value, resolved) {
+                            var result = defer();
+                            if (resolved) {
+                                result.resolve(value);
+                            } else {
+                                result.reject(value);
+                            }
+                            return result.promise;
+                        }
+
+                        function handleCallback(value, isResolved) {
+                            var callbackOutput = null;
+                            try {
+                                callbackOutput = (callback ||defaultCallback)();
+                            } catch(e) {
+                                return makePromise(e, false);
+                            }
+                            if (callbackOutput && isFunction(callbackOutput.then)) {
+                                return callbackOutput.then(function() {
+                                    return makePromise(value, isResolved);
+                                }, function(error) {
+                                    return makePromise(error, false);
+                                });
+                            } else {
+                                return makePromise(value, isResolved);
+                            }
+                        }
+
+                        return this.then(function(value) {
+                            return handleCallback(value, true);
+                        }, function(error) {
+                            return handleCallback(error, false);
+                        });
+                    }
+                }
+            };
+
+            return deferred;
+        };
+
+
+        var ref = function(value) {
+            if (value && isFunction(value.then)) return value;
+            return {
+                then: function(callback) {
+                    var result = defer();
+                    nextTick(function() {
+                        result.resolve(callback(value));
+                    });
+                    return result.promise;
+                }
+            };
+        };
+
+
+        /**
+         * @ngdoc
+         * @name ng.$q#reject
+         * @methodOf ng.$q
+         * @description
+         * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
+         * used to forward rejection in a chain of promises. If you are dealing with the last promise in
+         * a promise chain, you don't need to worry about it.
+         *
+         * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
+         * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
+         * a promise error callback and you want to forward the error to the promise derived from the
+         * current promise, you have to "rethrow" the error by returning a rejection constructed via
+         * `reject`.
+         *
+         * <pre>
+         *   promiseB = promiseA.then(function(result) {
+   *     // success: do something and resolve promiseB
+   *     //          with the old or a new result
+   *     return result;
+   *   }, function(reason) {
+   *     // error: handle the error if possible and
+   *     //        resolve promiseB with newPromiseOrValue,
+   *     //        otherwise forward the rejection to promiseB
+   *     if (canHandle(reason)) {
+   *      // handle the error and recover
+   *      return newPromiseOrValue;
+   *     }
+   *     return $q.reject(reason);
+   *   });
+         * </pre>
+         *
+         * @param {*} reason Constant, message, exception or an object representing the rejection reason.
+         * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
+         */
+        var reject = function(reason) {
+            return {
+                then: function(callback, errback) {
+                    var result = defer();
+                    nextTick(function() {
+                        try {
+                            result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));
+                        } catch(e) {
+                            result.reject(e);
+                            exceptionHandler(e);
+                        }
+                    });
+                    return result.promise;
+                }
+            };
+        };
+
+
+        /**
+         * @ngdoc
+         * @name ng.$q#when
+         * @methodOf ng.$q
+         * @description
+         * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
+         * This is useful when you are dealing with an object that might or might not be a promise, or if
+         * the promise comes from a source that can't be trusted.
+         *
+         * @param {*} value Value or a promise
+         * @returns {Promise} Returns a promise of the passed value or promise
+         */
+        var when = function(value, callback, errback, progressback) {
+            var result = defer(),
+                    done;
+
+            var wrappedCallback = function(value) {
+                try {
+                    return (isFunction(callback) ? callback : defaultCallback)(value);
+                } catch (e) {
+                    exceptionHandler(e);
+                    return reject(e);
+                }
+            };
+
+            var wrappedErrback = function(reason) {
+                try {
+                    return (isFunction(errback) ? errback : defaultErrback)(reason);
+                } catch (e) {
+                    exceptionHandler(e);
+                    return reject(e);
+                }
+            };
+
+            var wrappedProgressback = function(progress) {
+                try {
+                    return (isFunction(progressback) ? progressback : defaultCallback)(progress);
+                } catch (e) {
+                    exceptionHandler(e);
+                }
+            };
+
+            nextTick(function() {
+                ref(value).then(function(value) {
+                    if (done) return;
+                    done = true;
+                    result.resolve(ref(value).then(wrappedCallback, wrappedErrback, wrappedProgressback));
+                }, function(reason) {
+                    if (done) return;
+                    done = true;
+                    result.resolve(wrappedErrback(reason));
+                }, function(progress) {
+                    if (done) return;
+                    result.notify(wrappedProgressback(progress));
+                });
+            });
+
+            return result.promise;
+        };
+
+
+        function defaultCallback(value) {
+            return value;
+        }
+
+
+        function defaultErrback(reason) {
+            return reject(reason);
+        }
+
+
+        /**
+         * @ngdoc
+         * @name ng.$q#all
+         * @methodOf ng.$q
+         * @description
+         * Combines multiple promises into a single promise that is resolved when all of the input
+         * promises are resolved.
+         *
+         * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
+         * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
+         *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
+         *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
+         *   with the same rejection value.
+         */
+        function all(promises) {
+            var deferred = defer(),
+                    counter = 0,
+                    results = isArray(promises) ? [] : {};
+
+            forEach(promises, function(promise, key) {
+                counter++;
+                ref(promise).then(function(value) {
+                    if (results.hasOwnProperty(key)) return;
+                    results[key] = value;
+                    if (!(--counter)) deferred.resolve(results);
+                }, function(reason) {
+                    if (results.hasOwnProperty(key)) return;
+                    deferred.reject(reason);
+                });
+            });
+
+            if (counter === 0) {
+                deferred.resolve(results);
+            }
+
+            return deferred.promise;
+        }
+
+        return {
+            defer: defer,
+            reject: reject,
+            when: when,
+            all: all
+        };
+    }
+
+    /**
+     * DESIGN NOTES
+     *
+     * The design decisions behind the scope are heavily favored for speed and memory consumption.
+     *
+     * The typical use of scope is to watch the expressions, which most of the time return the same
+     * value as last time so we optimize the operation.
+     *
+     * Closures construction is expensive in terms of speed as well as memory:
+     *   - No closures, instead use prototypical inheritance for API
+     *   - Internal state needs to be stored on scope directly, which means that private state is
+     *     exposed as $$____ properties
+     *
+     * Loop operations are optimized by using while(count--) { ... }
+     *   - this means that in order to keep the same order of execution as addition we have to add
+     *     items to the array at the beginning (shift) instead of at the end (push)
+     *
+     * Child scopes are created and removed often
+     *   - Using an array would be slow since inserts in middle are expensive so we use linked list
+     *
+     * There are few watches then a lot of observers. This is why you don't want the observer to be
+     * implemented in the same way as watch. Watch requires return of initialization function which
+     * are expensive to construct.
+     */
+
+
+    /**
+     * @ngdoc object
+     * @name ng.$rootScopeProvider
+     * @description
+     *
+     * Provider for the $rootScope service.
+     */
+
+    /**
+     * @ngdoc function
+     * @name ng.$rootScopeProvider#digestTtl
+     * @methodOf ng.$rootScopeProvider
+     * @description
+     *
+     * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
+     * assuming that the model is unstable.
+     *
+     * The current default is 10 iterations.
+     *
+     * In complex applications it's possible that the dependencies between `$watch`s will result in
+     * several digest iterations. However if an application needs more than the default 10 digest
+     * iterations for its model to stabilize then you should investigate what is causing the model to
+     * continuously change during the digest.
+     *
+     * Increasing the TTL could have performance implications, so you should not change it without
+     * proper justification.
+     *
+     * @param {number} limit The number of digest iterations.
+     */
+
+
+    /**
+     * @ngdoc object
+     * @name ng.$rootScope
+     * @description
+     *
+     * Every application has a single root {@link ng.$rootScope.Scope scope}.
+     * All other scopes are descendant scopes of the root scope. Scopes provide separation
+     * between the model and the view, via a mechanism for watching the model for changes.
+     * They also provide an event emission/broadcast and subscription facility. See the
+     * {@link guide/scope developer guide on scopes}.
+     */
+    function $RootScopeProvider(){
+        var TTL = 10;
+        var $rootScopeMinErr = minErr('$rootScope');
+
+        this.digestTtl = function(value) {
+            if (arguments.length) {
+                TTL = value;
+            }
+            return TTL;
+        };
+
+        this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
+            function( $injector,   $exceptionHandler,   $parse,   $browser) {
+
+                /**
+                 * @ngdoc function
+                 * @name ng.$rootScope.Scope
+                 *
+                 * @description
+                 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
+                 * {@link AUTO.$injector $injector}. Child scopes are created using the
+                 * {@link ng.$rootScope.Scope#methods_$new $new()} method. (Most scopes are created automatically when
+                 * compiled HTML template is executed.)
+                 *
+                 * Here is a simple scope snippet to show how you can interact with the scope.
+                 * <pre>
+                 * <file src="./test/ng/rootScopeSpec.js" tag="docs1" />
+                 * </pre>
+                 *
+                 * # Inheritance
+                 * A scope can inherit from a parent scope, as in this example:
+                 * <pre>
+                 var parent = $rootScope;
+                 var child = parent.$new();
+
+                 parent.salutation = "Hello";
+                 child.name = "World";
+                 expect(child.salutation).toEqual('Hello');
+
+                 child.salutation = "Welcome";
+                 expect(child.salutation).toEqual('Welcome');
+                 expect(parent.salutation).toEqual('Hello');
+                 * </pre>
+                 *
+                 *
+                 * @param {Object.<string, function()>=} providers Map of service factory which need to be
+                 *                                       provided for the current scope. Defaults to {@link ng}.
+                 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
+                 *                              append/override services provided by `providers`. This is handy
+                 *                              when unit-testing and having the need to override a default
+                 *                              service.
+                 * @returns {Object} Newly created scope.
+                 *
+                 */
+                function Scope() {
+                    this.$id = nextUid();
+                    this.$$phase = this.$parent = this.$$watchers =
+                            this.$$nextSibling = this.$$prevSibling =
+                                    this.$$childHead = this.$$childTail = null;
+                    this['this'] = this.$root =  this;
+                    this.$$destroyed = false;
+                    this.$$asyncQueue = [];
+                    this.$$postDigestQueue = [];
+                    this.$$listeners = {};
+                    this.$$isolateBindings = {};
+                }
+
+                /**
+                 * @ngdoc property
+                 * @name ng.$rootScope.Scope#$id
+                 * @propertyOf ng.$rootScope.Scope
+                 * @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for
+                 *   debugging.
+                 */
+
+
+                Scope.prototype = {
+                    constructor: Scope,
+                    /**
+                     * @ngdoc function
+                     * @name ng.$rootScope.Scope#$new
+                     * @methodOf ng.$rootScope.Scope
+                     * @function
+                     *
+                     * @description
+                     * Creates a new child {@link ng.$rootScope.Scope scope}.
+                     *
+                     * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and
+                     * {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the
+                     * scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
+                     *
+                     * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
+                     * desired for the scope and its child scopes to be permanently detached from the parent and
+                     * thus stop participating in model change detection and listener notification by invoking.
+                     *
+                     * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
+                     *         parent scope. The scope is isolated, as it can not see parent scope properties.
+                     *         When creating widgets, it is useful for the widget to not accidentally read parent
+                     *         state.
+                     *
+                     * @returns {Object} The newly created child scope.
+                     *
+                     */
+                    $new: function(isolate) {
+                        var Child,
+                                child;
+
+                        if (isolate) {
+                            child = new Scope();
+                            child.$root = this.$root;
+                            // ensure that there is just one async queue per $rootScope and its children
+                            child.$$asyncQueue = this.$$asyncQueue;
+                            child.$$postDigestQueue = this.$$postDigestQueue;
+                        } else {
+                            Child = function() {}; // should be anonymous; This is so that when the minifier munges
+                            // the name it does not become random set of chars. This will then show up as class
+                            // name in the debugger.
+                            Child.prototype = this;
+                            child = new Child();
+                            child.$id = nextUid();
+                        }
+                        child['this'] = child;
+                        child.$$listeners = {};
+                        child.$parent = this;
+                        child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null;
+                        child.$$prevSibling = this.$$childTail;
+                        if (this.$$childHead) {
+                            this.$$childTail.$$nextSibling = child;
+                            this.$$childTail = child;
+                        } else {
+                            this.$$childHead = this.$$childTail = child;
+                        }
+                        return child;
+                    },
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$rootScope.Scope#$watch
+                     * @methodOf ng.$rootScope.Scope
+                     * @function
+                     *
+                     * @description
+                     * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
+                     *
+                     * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
+                     *   $digest()} and should return the value that will be watched. (Since
+                     *   {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
+                     *   `watchExpression` can execute multiple times per
+                     *   {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
+                     * - The `listener` is called only when the value from the current `watchExpression` and the
+                     *   previous call to `watchExpression` are not equal (with the exception of the initial run,
+                     *   see below). The inequality is determined according to
+                     *   {@link angular.equals} function. To save the value of the object for later comparison,
+                     *   the {@link angular.copy} function is used. It also means that watching complex options
+                     *   will have adverse memory and performance implications.
+                     * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
+                     *   This is achieved by rerunning the watchers until no changes are detected. The rerun
+                     *   iteration limit is 10 to prevent an infinite loop deadlock.
+                     *
+                     *
+                     * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
+                     * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
+                     * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
+                     * change is detected, be prepared for multiple calls to your listener.)
+                     *
+                     * After a watcher is registered with the scope, the `listener` fn is called asynchronously
+                     * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
+                     * watcher. In rare cases, this is undesirable because the listener is called when the result
+                     * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
+                     * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
+                     * listener was called due to initialization.
+                     *
+                     * The example below contains an illustration of using a function as your $watch listener
+                     *
+                     *
+                     * # Example
+                     * <pre>
+                     // let's assume that scope was dependency injected as the $rootScope
+                     var scope = $rootScope;
+                     scope.name = 'misko';
+                     scope.counter = 0;
+
+                     expect(scope.counter).toEqual(0);
+                     scope.$watch('name', function(newValue, oldValue) {
+             scope.counter = scope.counter + 1;
+           });
+                     expect(scope.counter).toEqual(0);
+
+                     scope.$digest();
+                     // no variable change
+                     expect(scope.counter).toEqual(0);
+
+                     scope.name = 'adam';
+                     scope.$digest();
+                     expect(scope.counter).toEqual(1);
+
+
+
+                     // Using a listener function
+                     var food;
+                     scope.foodCounter = 0;
+                     expect(scope.foodCounter).toEqual(0);
+                     scope.$watch(
+                     // This is the listener function
+                     function() { return food; },
+                     // This is the change handler
+                     function(newValue, oldValue) {
+               if ( newValue !== oldValue ) {
+                 // Only increment the counter if the value changed
+                 scope.foodCounter = scope.foodCounter + 1;
+               }
+             }
+                     );
+                     // No digest has been run so the counter will be zero
+                     expect(scope.foodCounter).toEqual(0);
+
+                     // Run the digest but since food has not changed cout will still be zero
+                     scope.$digest();
+                     expect(scope.foodCounter).toEqual(0);
+
+                     // Update food and run digest.  Now the counter will increment
+                     food = 'cheeseburger';
+                     scope.$digest();
+                     expect(scope.foodCounter).toEqual(1);
+
+                     * </pre>
+                     *
+                     *
+                     *
+                     * @param {(function()|string)} watchExpression Expression that is evaluated on each
+                     *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
+                     *    a call to the `listener`.
+                     *
+                     *    - `string`: Evaluated as {@link guide/expression expression}
+                     *    - `function(scope)`: called with current `scope` as a parameter.
+                     * @param {(function()|string)=} listener Callback called whenever the return value of
+                     *   the `watchExpression` changes.
+                     *
+                     *    - `string`: Evaluated as {@link guide/expression expression}
+                     *    - `function(newValue, oldValue, scope)`: called with current and previous values as
+                     *      parameters.
+                     *
+                     * @param {boolean=} objectEquality Compare object for equality rather than for reference.
+                     * @returns {function()} Returns a deregistration function for this listener.
+                     */
+                    $watch: function(watchExp, listener, objectEquality) {
+                        var scope = this,
+                                get = compileToFn(watchExp, 'watch'),
+                                array = scope.$$watchers,
+                                watcher = {
+                                    fn: listener,
+                                    last: initWatchVal,
+                                    get: get,
+                                    exp: watchExp,
+                                    eq: !!objectEquality
+                                };
+
+                        // in the case user pass string, we need to compile it, do we really need this ?
+                        if (!isFunction(listener)) {
+                            var listenFn = compileToFn(listener || noop, 'listener');
+                            watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};
+                        }
+
+                        if (typeof watchExp == 'string' && get.constant) {
+                            var originalFn = watcher.fn;
+                            watcher.fn = function(newVal, oldVal, scope) {
+                                originalFn.call(this, newVal, oldVal, scope);
+                                arrayRemove(array, watcher);
+                            };
+                        }
+
+                        if (!array) {
+                            array = scope.$$watchers = [];
+                        }
+                        // we use unshift since we use a while loop in $digest for speed.
+                        // the while loop reads in reverse order.
+                        array.unshift(watcher);
+
+                        return function() {
+                            arrayRemove(array, watcher);
+                        };
+                    },
+
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$rootScope.Scope#$watchCollection
+                     * @methodOf ng.$rootScope.Scope
+                     * @function
+                     *
+                     * @description
+                     * Shallow watches the properties of an object and fires whenever any of the properties change
+                     * (for arrays, this implies watching the array items; for object maps, this implies watching
+                     * the properties). If a change is detected, the `listener` callback is fired.
+                     *
+                     * - The `obj` collection is observed via standard $watch operation and is examined on every
+                     *   call to $digest() to see if any items have been added, removed, or moved.
+                     * - The `listener` is called whenever anything within the `obj` has changed. Examples include
+                     *   adding, removing, and moving items belonging to an object or array.
+                     *
+                     *
+                     * # Example
+                     * <pre>
+                     $scope.names = ['igor', 'matias', 'misko', 'james'];
+                     $scope.dataCount = 4;
+
+                     $scope.$watchCollection('names', function(newNames, oldNames) {
+            $scope.dataCount = newNames.length;
+          });
+
+                     expect($scope.dataCount).toEqual(4);
+                     $scope.$digest();
+
+                     //still at 4 ... no changes
+                     expect($scope.dataCount).toEqual(4);
+
+                     $scope.names.pop();
+                     $scope.$digest();
+
+                     //now there's been a change
+                     expect($scope.dataCount).toEqual(3);
+                     * </pre>
+                     *
+                     *
+                     * @param {string|Function(scope)} obj Evaluated as {@link guide/expression expression}. The
+                     *    expression value should evaluate to an object or an array which is observed on each
+                     *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
+                     *    collection will trigger a call to the `listener`.
+                     *
+                     * @param {function(newCollection, oldCollection, scope)} listener a callback function that is
+                     *    fired with both the `newCollection` and `oldCollection` as parameters.
+                     *    The `newCollection` object is the newly modified data obtained from the `obj` expression
+                     *    and the `oldCollection` object is a copy of the former collection data.
+                     *    The `scope` refers to the current scope.
+                     *
+                     * @returns {function()} Returns a de-registration function for this listener. When the
+                     *    de-registration function is executed, the internal watch operation is terminated.
+                     */
+                    $watchCollection: function(obj, listener) {
+                        var self = this;
+                        var oldValue;
+                        var newValue;
+                        var changeDetected = 0;
+                        var objGetter = $parse(obj);
+                        var internalArray = [];
+                        var internalObject = {};
+                        var oldLength = 0;
+
+                        function $watchCollectionWatch() {
+                            newValue = objGetter(self);
+                            var newLength, key;
+
+                            if (!isObject(newValue)) {
+                                if (oldValue !== newValue) {
+                                    oldValue = newValue;
+                                    changeDetected++;
+                                }
+                            } else if (isArrayLike(newValue)) {
+                                if (oldValue !== internalArray) {
+                                    // we are transitioning from something which was not an array into array.
+                                    oldValue = internalArray;
+                                    oldLength = oldValue.length = 0;
+                                    changeDetected++;
+                                }
+
+                                newLength = newValue.length;
+
+                                if (oldLength !== newLength) {
+                                    // if lengths do not match we need to trigger change notification
+                                    changeDetected++;
+                                    oldValue.length = oldLength = newLength;
+                                }
+                                // copy the items to oldValue and look for changes.
+                                for (var i = 0; i < newLength; i++) {
+                                    if (oldValue[i] !== newValue[i]) {
+                                        changeDetected++;
+                                        oldValue[i] = newValue[i];
+                                    }
+                                }
+                            } else {
+                                if (oldValue !== internalObject) {
+                                    // we are transitioning from something which was not an object into object.
+                                    oldValue = internalObject = {};
+                                    oldLength = 0;
+                                    changeDetected++;
+                                }
+                                // copy the items to oldValue and look for changes.
+                                newLength = 0;
+                                for (key in newValue) {
+                                    if (newValue.hasOwnProperty(key)) {
+                                        newLength++;
+                                        if (oldValue.hasOwnProperty(key)) {
+                                            if (oldValue[key] !== newValue[key]) {
+                                                changeDetected++;
+                                                oldValue[key] = newValue[key];
+                                            }
+                                        } else {
+                                            oldLength++;
+                                            oldValue[key] = newValue[key];
+                                            changeDetected++;
+                                        }
+                                    }
+                                }
+                                if (oldLength > newLength) {
+                                    // we used to have more keys, need to find them and destroy them.
+                                    changeDetected++;
+                                    for(key in oldValue) {
+                                        if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) {
+                                            oldLength--;
+                                            delete oldValue[key];
+                                        }
+                                    }
+                                }
+                            }
+                            return changeDetected;
+                        }
+
+                        function $watchCollectionAction() {
+                            listener(newValue, oldValue, self);
+                        }
+
+                        return this.$watch($watchCollectionWatch, $watchCollectionAction);
+                    },
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$rootScope.Scope#$digest
+                     * @methodOf ng.$rootScope.Scope
+                     * @function
+                     *
+                     * @description
+                     * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
+                     * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
+                     * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
+                     * until no more listeners are firing. This means that it is possible to get into an infinite
+                     * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
+                     * iterations exceeds 10.
+                     *
+                     * Usually, you don't call `$digest()` directly in
+                     * {@link ng.directive:ngController controllers} or in
+                     * {@link ng.$compileProvider#methods_directive directives}.
+                     * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
+                     * a {@link ng.$compileProvider#methods_directive directives}), which will force a `$digest()`.
+                     *
+                     * If you want to be notified whenever `$digest()` is called,
+                     * you can register a `watchExpression` function with
+                     * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
+                     *
+                     * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
+                     *
+                     * # Example
+                     * <pre>
+                     var scope = ...;
+                     scope.name = 'misko';
+                     scope.counter = 0;
+
+                     expect(scope.counter).toEqual(0);
+                     scope.$watch('name', function(newValue, oldValue) {
+             scope.counter = scope.counter + 1;
+           });
+                     expect(scope.counter).toEqual(0);
+
+                     scope.$digest();
+                     // no variable change
+                     expect(scope.counter).toEqual(0);
+
+                     scope.name = 'adam';
+                     scope.$digest();
+                     expect(scope.counter).toEqual(1);
+                     * </pre>
+                     *
+                     */
+                    $digest: function() {
+                        var watch, value, last,
+                                watchers,
+                                asyncQueue = this.$$asyncQueue,
+                                postDigestQueue = this.$$postDigestQueue,
+                                length,
+                                dirty, ttl = TTL,
+                                next, current, target = this,
+                                watchLog = [],
+                                logIdx, logMsg, asyncTask;
+
+                        beginPhase('$digest');
+
+                        do { // "while dirty" loop
+                            dirty = false;
+                            current = target;
+
+                            while(asyncQueue.length) {
+                                try {
+                                    asyncTask = asyncQueue.shift();
+                                    asyncTask.scope.$eval(asyncTask.expression);
+                                } catch (e) {
+                                    $exceptionHandler(e);
+                                }
+                            }
+
+                            do { // "traverse the scopes" loop
+                                if ((watchers = current.$$watchers)) {
+                                    // process our watches
+                                    length = watchers.length;
+                                    while (length--) {
+                                        try {
+                                            watch = watchers[length];
+                                            // Most common watches are on primitives, in which case we can short
+                                            // circuit it with === operator, only when === fails do we use .equals
+                                            if (watch && (value = watch.get(current)) !== (last = watch.last) &&
+                                                    !(watch.eq
+                                                            ? equals(value, last)
+                                                            : (typeof value == 'number' && typeof last == 'number'
+                                                            && isNaN(value) && isNaN(last)))) {
+                                                dirty = true;
+                                                watch.last = watch.eq ? copy(value) : value;
+                                                watch.fn(value, ((last === initWatchVal) ? value : last), current);
+                                                if (ttl < 5) {
+                                                    logIdx = 4 - ttl;
+                                                    if (!watchLog[logIdx]) watchLog[logIdx] = [];
+                                                    logMsg = (isFunction(watch.exp))
+                                                            ? 'fn: ' + (watch.exp.name || watch.exp.toString())
+                                                            : watch.exp;
+                                                    logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
+                                                    watchLog[logIdx].push(logMsg);
+                                                }
+                                            }
+                                        } catch (e) {
+                                            $exceptionHandler(e);
+                                        }
+                                    }
+                                }
+
+                                // Insanity Warning: scope depth-first traversal
+                                // yes, this code is a bit crazy, but it works and we have tests to prove it!
+                                // this piece should be kept in sync with the traversal in $broadcast
+                                if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
+                                    while(current !== target && !(next = current.$$nextSibling)) {
+                                        current = current.$parent;
+                                    }
+                                }
+                            } while ((current = next));
+
+                            if(dirty && !(ttl--)) {
+                                clearPhase();
+                                throw $rootScopeMinErr('infdig',
+                                        '{0} $digest() iterations reached. Aborting!\n' +
+                                                'Watchers fired in the last 5 iterations: {1}',
+                                        TTL, toJson(watchLog));
+                            }
+                        } while (dirty || asyncQueue.length);
+
+                        clearPhase();
+
+                        while(postDigestQueue.length) {
+                            try {
+                                postDigestQueue.shift()();
+                            } catch (e) {
+                                $exceptionHandler(e);
+                            }
+                        }
+                    },
+
+
+                    /**
+                     * @ngdoc event
+                     * @name ng.$rootScope.Scope#$destroy
+                     * @eventOf ng.$rootScope.Scope
+                     * @eventType broadcast on scope being destroyed
+                     *
+                     * @description
+                     * Broadcasted when a scope and its children are being destroyed.
+                     *
+                     * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
+                     * clean up DOM bindings before an element is removed from the DOM.
+                     */
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$rootScope.Scope#$destroy
+                     * @methodOf ng.$rootScope.Scope
+                     * @function
+                     *
+                     * @description
+                     * Removes the current scope (and all of its children) from the parent scope. Removal implies
+                     * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
+                     * propagate to the current scope and its children. Removal also implies that the current
+                     * scope is eligible for garbage collection.
+                     *
+                     * The `$destroy()` is usually used by directives such as
+                     * {@link ng.directive:ngRepeat ngRepeat} for managing the
+                     * unrolling of the loop.
+                     *
+                     * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
+                     * Application code can register a `$destroy` event handler that will give it a chance to
+                     * perform any necessary cleanup.
+                     *
+                     * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
+                     * clean up DOM bindings before an element is removed from the DOM.
+                     */
+                    $destroy: function() {
+                        // we can't destroy the root scope or a scope that has been already destroyed
+                        if ($rootScope == this || this.$$destroyed) return;
+                        var parent = this.$parent;
+
+                        this.$broadcast('$destroy');
+                        this.$$destroyed = true;
+
+                        if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
+                        if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
+                        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
+                        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
+
+                        // This is bogus code that works around Chrome's GC leak
+                        // see: https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
+                        this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
+                                this.$$childTail = null;
+                    },
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$rootScope.Scope#$eval
+                     * @methodOf ng.$rootScope.Scope
+                     * @function
+                     *
+                     * @description
+                     * Executes the `expression` on the current scope and returns the result. Any exceptions in
+                     * the expression are propagated (uncaught). This is useful when evaluating Angular
+                     * expressions.
+                     *
+                     * # Example
+                     * <pre>
+                     var scope = ng.$rootScope.Scope();
+                     scope.a = 1;
+                     scope.b = 2;
+
+                     expect(scope.$eval('a+b')).toEqual(3);
+                     expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
+                     * </pre>
+                     *
+                     * @param {(string|function())=} expression An angular expression to be executed.
+                     *
+                     *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
+                     *    - `function(scope)`: execute the function with the current `scope` parameter.
+                     *
+                     * @param {(object)=} locals Local variables object, useful for overriding values in scope.
+                     * @returns {*} The result of evaluating the expression.
+                     */
+                    $eval: function(expr, locals) {
+                        return $parse(expr)(this, locals);
+                    },
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$rootScope.Scope#$evalAsync
+                     * @methodOf ng.$rootScope.Scope
+                     * @function
+                     *
+                     * @description
+                     * Executes the expression on the current scope at a later point in time.
+                     *
+                     * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
+                     * that:
+                     *
+                     *   - it will execute after the function that scheduled the evaluation (preferably before DOM
+                     *     rendering).
+                     *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
+                     *     `expression` execution.
+                     *
+                     * Any exceptions from the execution of the expression are forwarded to the
+                     * {@link ng.$exceptionHandler $exceptionHandler} service.
+                     *
+                     * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
+                     * will be scheduled. However, it is encouraged to always call code that changes the model
+                     * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
+                     *
+                     * @param {(string|function())=} expression An angular expression to be executed.
+                     *
+                     *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
+                     *    - `function(scope)`: execute the function with the current `scope` parameter.
+                     *
+                     */
+                    $evalAsync: function(expr) {
+                        // if we are outside of an $digest loop and this is the first time we are scheduling async
+                        // task also schedule async auto-flush
+                        if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) {
+                            $browser.defer(function() {
+                                if ($rootScope.$$asyncQueue.length) {
+                                    $rootScope.$digest();
+                                }
+                            });
+                        }
+
+                        this.$$asyncQueue.push({scope: this, expression: expr});
+                    },
+
+                    $$postDigest : function(fn) {
+                        this.$$postDigestQueue.push(fn);
+                    },
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$rootScope.Scope#$apply
+                     * @methodOf ng.$rootScope.Scope
+                     * @function
+                     *
+                     * @description
+                     * `$apply()` is used to execute an expression in angular from outside of the angular
+                     * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
+                     * Because we are calling into the angular framework we need to perform proper scope life
+                     * cycle of {@link ng.$exceptionHandler exception handling},
+                     * {@link ng.$rootScope.Scope#$digest executing watches}.
+                     *
+                     * ## Life cycle
+                     *
+                     * # Pseudo-Code of `$apply()`
+                     * <pre>
+                     function $apply(expr) {
+             try {
+               return $eval(expr);
+             } catch (e) {
+               $exceptionHandler(e);
+             } finally {
+               $root.$digest();
+             }
+           }
+                     * </pre>
+                     *
+                     *
+                     * Scope's `$apply()` method transitions through the following stages:
+                     *
+                     * 1. The {@link guide/expression expression} is executed using the
+                     *    {@link ng.$rootScope.Scope#$eval $eval()} method.
+                     * 2. Any exceptions from the execution of the expression are forwarded to the
+                     *    {@link ng.$exceptionHandler $exceptionHandler} service.
+                     * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
+                     *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
+                     *
+                     *
+                     * @param {(string|function())=} exp An angular expression to be executed.
+                     *
+                     *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
+                     *    - `function(scope)`: execute the function with current `scope` parameter.
+                     *
+                     * @returns {*} The result of evaluating the expression.
+                     */
+                    $apply: function(expr) {
+                        try {
+                            beginPhase('$apply');
+                            return this.$eval(expr);
+                        } catch (e) {
+                            $exceptionHandler(e);
+                        } finally {
+                            clearPhase();
+                            try {
+                                $rootScope.$digest();
+                            } catch (e) {
+                                $exceptionHandler(e);
+                                throw e;
+                            }
+                        }
+                    },
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$rootScope.Scope#$on
+                     * @methodOf ng.$rootScope.Scope
+                     * @function
+                     *
+                     * @description
+                     * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
+                     * discussion of event life cycle.
+                     *
+                     * The event listener function format is: `function(event, args...)`. The `event` object
+                     * passed into the listener has the following attributes:
+                     *
+                     *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
+                     *     `$broadcast`-ed.
+                     *   - `currentScope` - `{Scope}`: the current scope which is handling the event.
+                     *   - `name` - `{string}`: name of the event.
+                     *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
+                     *     further event propagation (available only for events that were `$emit`-ed).
+                     *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
+                     *     to true.
+                     *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
+                     *
+                     * @param {string} name Event name to listen on.
+                     * @param {function(event, args...)} listener Function to call when the event is emitted.
+                     * @returns {function()} Returns a deregistration function for this listener.
+                     */
+                    $on: function(name, listener) {
+                        var namedListeners = this.$$listeners[name];
+                        if (!namedListeners) {
+                            this.$$listeners[name] = namedListeners = [];
+                        }
+                        namedListeners.push(listener);
+
+                        return function() {
+                            namedListeners[indexOf(namedListeners, listener)] = null;
+                        };
+                    },
+
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$rootScope.Scope#$emit
+                     * @methodOf ng.$rootScope.Scope
+                     * @function
+                     *
+                     * @description
+                     * Dispatches an event `name` upwards through the scope hierarchy notifying the
+                     * registered {@link ng.$rootScope.Scope#$on} listeners.
+                     *
+                     * The event life cycle starts at the scope on which `$emit` was called. All
+                     * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
+                     * notified. Afterwards, the event traverses upwards toward the root scope and calls all
+                     * registered listeners along the way. The event will stop propagating if one of the listeners
+                     * cancels it.
+                     *
+                     * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
+                     * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
+                     *
+                     * @param {string} name Event name to emit.
+                     * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
+                     * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
+                     */
+                    $emit: function(name, args) {
+                        var empty = [],
+                                namedListeners,
+                                scope = this,
+                                stopPropagation = false,
+                                event = {
+                                    name: name,
+                                    targetScope: scope,
+                                    stopPropagation: function() {stopPropagation = true;},
+                                    preventDefault: function() {
+                                        event.defaultPrevented = true;
+                                    },
+                                    defaultPrevented: false
+                                },
+                                listenerArgs = concat([event], arguments, 1),
+                                i, length;
+
+                        do {
+                            namedListeners = scope.$$listeners[name] || empty;
+                            event.currentScope = scope;
+                            for (i=0, length=namedListeners.length; i<length; i++) {
+
+                                // if listeners were deregistered, defragment the array
+                                if (!namedListeners[i]) {
+                                    namedListeners.splice(i, 1);
+                                    i--;
+                                    length--;
+                                    continue;
+                                }
+                                try {
+                                    //allow all listeners attached to the current scope to run
+                                    namedListeners[i].apply(null, listenerArgs);
+                                } catch (e) {
+                                    $exceptionHandler(e);
+                                }
+                            }
+                            //if any listener on the current scope stops propagation, prevent bubbling
+                            if (stopPropagation) return event;
+                            //traverse upwards
+                            scope = scope.$parent;
+                        } while (scope);
+
+                        return event;
+                    },
+
+
+                    /**
+                     * @ngdoc function
+                     * @name ng.$rootScope.Scope#$broadcast
+                     * @methodOf ng.$rootScope.Scope
+                     * @function
+                     *
+                     * @description
+                     * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
+                     * registered {@link ng.$rootScope.Scope#$on} listeners.
+                     *
+                     * The event life cycle starts at the scope on which `$broadcast` was called. All
+                     * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
+                     * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
+                     * scope and calls all registered listeners along the way. The event cannot be canceled.
+                     *
+                     * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
+                     * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
+                     *
+                     * @param {string} name Event name to broadcast.
+                     * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
+                     * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
+                     */
+                    $broadcast: function(name, args) {
+                        var target = this,
+                                current = target,
+                                next = target,
+                                event = {
+                                    name: name,
+                                    targetScope: target,
+                                    preventDefault: function() {
+                                        event.defaultPrevented = true;
+                                    },
+                                    defaultPrevented: false
+                                },
+                                listenerArgs = concat([event], arguments, 1),
+                                listeners, i, length;
+
+                        //down while you can, then up and next sibling or up and next sibling until back at root
+                        do {
+                            current = next;
+                            event.currentScope = current;
+                            listeners = current.$$listeners[name] || [];
+                            for (i=0, length = listeners.length; i<length; i++) {
+                                // if listeners were deregistered, defragment the array
+                                if (!listeners[i]) {
+                                    listeners.splice(i, 1);
+                                    i--;
+                                    length--;
+                                    continue;
+                                }
+
+                                try {
+                                    listeners[i].apply(null, listenerArgs);
+                                } catch(e) {
+                                    $exceptionHandler(e);
+                                }
+                            }
+
+                            // Insanity Warning: scope depth-first traversal
+                            // yes, this code is a bit crazy, but it works and we have tests to prove it!
+                            // this piece should be kept in sync with the traversal in $digest
+                            if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
+                                while(current !== target && !(next = current.$$nextSibling)) {
+                                    current = current.$parent;
+                                }
+                            }
+                        } while ((current = next));
+
+                        return event;
+                    }
+                };
+
+                var $rootScope = new Scope();
+
+                return $rootScope;
+
+
+                function beginPhase(phase) {
+                    if ($rootScope.$$phase) {
+                        throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
+                    }
+
+                    $rootScope.$$phase = phase;
+                }
+
+                function clearPhase() {
+                    $rootScope.$$phase = null;
+                }
+
+                function compileToFn(exp, name) {
+                    var fn = $parse(exp);
+                    assertArgFn(fn, name);
+                    return fn;
+                }
+
+                /**
+                 * function used as an initial value for watchers.
+                 * because it's unique we can easily tell it apart from other values
+                 */
+                function initWatchVal() {}
+            }];
+    }
+
+    var $sceMinErr = minErr('$sce');
+
+    var SCE_CONTEXTS = {
+        HTML: 'html',
+        CSS: 'css',
+        URL: 'url',
+        // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
+        // url.  (e.g. ng-include, script src, templateUrl)
+        RESOURCE_URL: 'resourceUrl',
+        JS: 'js'
+    };
+
+// Helper functions follow.
+
+// Copied from:
+// http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962
+// Prereq: s is a string.
+    function escapeForRegexp(s) {
+        return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
+                replace(/\x08/g, '\\x08');
+    }
+
+
+    function adjustMatcher(matcher) {
+        if (matcher === 'self') {
+            return matcher;
+        } else if (isString(matcher)) {
+            // Strings match exactly except for 2 wildcards - '*' and '**'.
+            // '*' matches any character except those from the set ':/.?&'.
+            // '**' matches any character (like .* in a RegExp).
+            // More than 2 *'s raises an error as it's ill defined.
+            if (matcher.indexOf('***') > -1) {
+                throw $sceMinErr('iwcard',
+                        'Illegal sequence *** in string matcher.  String: {0}', matcher);
+            }
+            matcher = escapeForRegexp(matcher).
+                    replace('\\*\\*', '.*').
+                    replace('\\*', '[^:/.?&;]*');
+            return new RegExp('^' + matcher + '$');
+        } else if (isRegExp(matcher)) {
+            // The only other type of matcher allowed is a Regexp.
+            // Match entire URL / disallow partial matches.
+            // Flags are reset (i.e. no global, ignoreCase or multiline)
+            return new RegExp('^' + matcher.source + '$');
+        } else {
+            throw $sceMinErr('imatcher',
+                    'Matchers may only be "self", string patterns or RegExp objects');
+        }
+    }
+
+
+    function adjustMatchers(matchers) {
+        var adjustedMatchers = [];
+        if (isDefined(matchers)) {
+            forEach(matchers, function(matcher) {
+                adjustedMatchers.push(adjustMatcher(matcher));
+            });
+        }
+        return adjustedMatchers;
+    }
+
+
+    /**
+     * @ngdoc service
+     * @name ng.$sceDelegate
+     * @function
+     *
+     * @description
+     *
+     * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
+     * Contextual Escaping (SCE)} services to AngularJS.
+     *
+     * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
+     * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
+     * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
+     * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
+     * work because `$sce` delegates to `$sceDelegate` for these operations.
+     *
+     * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
+     *
+     * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
+     * can override it completely to change the behavior of `$sce`, the common case would
+     * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
+     * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
+     * templates.  Refer {@link ng.$sceDelegateProvider#methods_resourceUrlWhitelist
+     * $sceDelegateProvider.resourceUrlWhitelist} and {@link
+     * ng.$sceDelegateProvider#methods_resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
+     */
+
+    /**
+     * @ngdoc object
+     * @name ng.$sceDelegateProvider
+     * @description
+     *
+     * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
+     * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
+     * that the URLs used for sourcing Angular templates are safe.  Refer {@link
+     * ng.$sceDelegateProvider#methods_resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
+     * {@link ng.$sceDelegateProvider#methods_resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
+     *
+     * For the general details about this service in Angular, read the main page for {@link ng.$sce
+     * Strict Contextual Escaping (SCE)}.
+     *
+     * **Example**:  Consider the following case. <a name="example"></a>
+     *
+     * - your app is hosted at url `http://myapp.example.com/`
+     * - but some of your templates are hosted on other domains you control such as
+     *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
+     * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
+     *
+     * Here is what a secure configuration for this scenario might look like:
+     *
+     * <pre class="prettyprint">
+     *    angular.module('myApp', []).config(function($sceDelegateProvider) {
+ *      $sceDelegateProvider.resourceUrlWhitelist([
+ *        // Allow same origin resource loads.
+ *        'self',
+ *        // Allow loading from our assets domain.  Notice the difference between * and **.
+ *        'http://srv*.assets.example.com/**']);
+ *
+ *      // The blacklist overrides the whitelist so the open redirect here is blocked.
+ *      $sceDelegateProvider.resourceUrlBlacklist([
+ *        'http://myapp.example.com/clickThru**']);
+ *      });
+     * </pre>
+     */
+
+    function $SceDelegateProvider() {
+        this.SCE_CONTEXTS = SCE_CONTEXTS;
+
+        // Resource URLs can also be trusted by policy.
+        var resourceUrlWhitelist = ['self'],
+                resourceUrlBlacklist = [];
+
+        /**
+         * @ngdoc function
+         * @name ng.sceDelegateProvider#resourceUrlWhitelist
+         * @methodOf ng.$sceDelegateProvider
+         * @function
+         *
+         * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
+         *     provided.  This must be an array or null.  A snapshot of this array is used so further
+         *     changes to the array are ignored.
+         *
+         *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
+         *     allowed in this array.
+         *
+         *     Note: **an empty whitelist array will block all URLs**!
+         *
+         * @return {Array} the currently set whitelist array.
+         *
+         * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
+         * same origin resource requests.
+         *
+         * @description
+         * Sets/Gets the whitelist of trusted resource URLs.
+         */
+        this.resourceUrlWhitelist = function (value) {
+            if (arguments.length) {
+                resourceUrlWhitelist = adjustMatchers(value);
+            }
+            return resourceUrlWhitelist;
+        };
+
+        /**
+         * @ngdoc function
+         * @name ng.sceDelegateProvider#resourceUrlBlacklist
+         * @methodOf ng.$sceDelegateProvider
+         * @function
+         *
+         * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
+         *     provided.  This must be an array or null.  A snapshot of this array is used so further
+         *     changes to the array are ignored.
+         *
+         *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
+         *     allowed in this array.
+         *
+         *     The typical usage for the blacklist is to **block
+         *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
+         *     these would otherwise be trusted but actually return content from the redirected domain.
+         *
+         *     Finally, **the blacklist overrides the whitelist** and has the final say.
+         *
+         * @return {Array} the currently set blacklist array.
+         *
+         * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
+         * is no blacklist.)
+         *
+         * @description
+         * Sets/Gets the blacklist of trusted resource URLs.
+         */
+
+        this.resourceUrlBlacklist = function (value) {
+            if (arguments.length) {
+                resourceUrlBlacklist = adjustMatchers(value);
+            }
+            return resourceUrlBlacklist;
+        };
+
+        this.$get = ['$injector', function($injector) {
+
+            var htmlSanitizer = function htmlSanitizer(html) {
+                throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
+            };
+
+            if ($injector.has('$sanitize')) {
+                htmlSanitizer = $injector.get('$sanitize');
+            }
+
+
+            function matchUrl(matcher, parsedUrl) {
+                if (matcher === 'self') {
+                    return urlIsSameOrigin(parsedUrl);
+                } else {
+                    // definitely a regex.  See adjustMatchers()
+                    return !!matcher.exec(parsedUrl.href);
+                }
+            }
+
+            function isResourceUrlAllowedByPolicy(url) {
+                var parsedUrl = urlResolve(url.toString());
+                var i, n, allowed = false;
+                // Ensure that at least one item from the whitelist allows this url.
+                for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
+                    if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
+                        allowed = true;
+                        break;
+                    }
+                }
+                if (allowed) {
+                    // Ensure that no item from the blacklist blocked this url.
+                    for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
+                        if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
+                            allowed = false;
+                            break;
+                        }
+                    }
+                }
+                return allowed;
+            }
+
+            function generateHolderType(Base) {
+                var holderType = function TrustedValueHolderType(trustedValue) {
+                    this.$$unwrapTrustedValue = function() {
+                        return trustedValue;
+                    };
+                };
+                if (Base) {
+                    holderType.prototype = new Base();
+                }
+                holderType.prototype.valueOf = function sceValueOf() {
+                    return this.$$unwrapTrustedValue();
+                };
+                holderType.prototype.toString = function sceToString() {
+                    return this.$$unwrapTrustedValue().toString();
+                };
+                return holderType;
+            }
+
+            var trustedValueHolderBase = generateHolderType(),
+                    byType = {};
+
+            byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
+            byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
+            byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
+            byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
+            byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
+
+            /**
+             * @ngdoc method
+             * @name ng.$sceDelegate#trustAs
+             * @methodOf ng.$sceDelegate
+             *
+             * @description
+             * Returns an object that is trusted by angular for use in specified strict
+             * contextual escaping contexts (such as ng-html-bind-unsafe, ng-include, any src
+             * attribute interpolation, any dom event binding attribute interpolation
+             * such as for onclick,  etc.) that uses the provided value.
+             * See {@link ng.$sce $sce} for enabling strict contextual escaping.
+             *
+             * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
+             *   resourceUrl, html, js and css.
+             * @param {*} value The value that that should be considered trusted/safe.
+             * @returns {*} A value that can be used to stand in for the provided `value` in places
+             * where Angular expects a $sce.trustAs() return value.
+             */
+            function trustAs(type, trustedValue) {
+                var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
+                if (!Constructor) {
+                    throw $sceMinErr('icontext',
+                            'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
+                            type, trustedValue);
+                }
+                if (trustedValue === null || trustedValue === undefined || trustedValue === '') {
+                    return trustedValue;
+                }
+                // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
+                // mutable objects, we ensure here that the value passed in is actually a string.
+                if (typeof trustedValue !== 'string') {
+                    throw $sceMinErr('itype',
+                            'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
+                            type);
+                }
+                return new Constructor(trustedValue);
+            }
+
+            /**
+             * @ngdoc method
+             * @name ng.$sceDelegate#valueOf
+             * @methodOf ng.$sceDelegate
+             *
+             * @description
+             * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#methods_trustAs
+             * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
+             * ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}.
+             *
+             * If the passed parameter is not a value that had been returned by {@link
+             * ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}, returns it as-is.
+             *
+             * @param {*} value The result of a prior {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}
+             *      call or anything else.
+             * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#methods_trustAs
+             *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
+             *     `value` unchanged.
+             */
+            function valueOf(maybeTrusted) {
+                if (maybeTrusted instanceof trustedValueHolderBase) {
+                    return maybeTrusted.$$unwrapTrustedValue();
+                } else {
+                    return maybeTrusted;
+                }
+            }
+
+            /**
+             * @ngdoc method
+             * @name ng.$sceDelegate#getTrusted
+             * @methodOf ng.$sceDelegate
+             *
+             * @description
+             * Takes the result of a {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`} call and
+             * returns the originally supplied value if the queried context type is a supertype of the
+             * created type.  If this condition isn't satisfied, throws an exception.
+             *
+             * @param {string} type The kind of context in which this value is to be used.
+             * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#methods_trustAs
+             *     `$sceDelegate.trustAs`} call.
+             * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#methods_trustAs
+             *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
+             */
+            function getTrusted(type, maybeTrusted) {
+                if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') {
+                    return maybeTrusted;
+                }
+                var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
+                if (constructor && maybeTrusted instanceof constructor) {
+                    return maybeTrusted.$$unwrapTrustedValue();
+                }
+                // If we get here, then we may only take one of two actions.
+                // 1. sanitize the value for the requested type, or
+                // 2. throw an exception.
+                if (type === SCE_CONTEXTS.RESOURCE_URL) {
+                    if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
+                        return maybeTrusted;
+                    } else {
+                        throw $sceMinErr('insecurl',
+                                'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
+                                maybeTrusted.toString());
+                    }
+                } else if (type === SCE_CONTEXTS.HTML) {
+                    return htmlSanitizer(maybeTrusted);
+                }
+                throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
+            }
+
+            return { trustAs: trustAs,
+                getTrusted: getTrusted,
+                valueOf: valueOf };
+        }];
+    }
+
+
+    /**
+     * @ngdoc object
+     * @name ng.$sceProvider
+     * @description
+     *
+     * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
+     * -   enable/disable Strict Contextual Escaping (SCE) in a module
+     * -   override the default implementation with a custom delegate
+     *
+     * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
+     */
+
+    /* jshint maxlen: false*/
+
+    /**
+     * @ngdoc service
+     * @name ng.$sce
+     * @function
+     *
+     * @description
+     *
+     * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
+     *
+     * # Strict Contextual Escaping
+     *
+     * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
+     * contexts to result in a value that is marked as safe to use for that context.  One example of
+     * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
+     * to these contexts as privileged or SCE contexts.
+     *
+     * As of version 1.2, Angular ships with SCE enabled by default.
+     *
+     * Note:  When enabled (the default), IE8 in quirks mode is not supported.  In this mode, IE8 allows
+     * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
+     * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
+     * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
+     * to the top of your HTML document.
+     *
+     * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
+     * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
+     *
+     * Here's an example of a binding in a privileged context:
+     *
+     * <pre class="prettyprint">
+     *     <input ng-model="userHtml">
+     *     <div ng-bind-html="userHtml">
+     * </pre>
+     *
+     * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
+     * disabled, this application allows the user to render arbitrary HTML into the DIV.
+     * In a more realistic example, one may be rendering user comments, blog articles, etc. via
+     * bindings.  (HTML is just one example of a context where rendering user controlled input creates
+     * security vulnerabilities.)
+     *
+     * For the case of HTML, you might use a library, either on the client side, or on the server side,
+     * to sanitize unsafe HTML before binding to the value and rendering it in the document.
+     *
+     * How would you ensure that every place that used these types of bindings was bound to a value that
+     * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
+     * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
+     * properties/fields and forgot to update the binding to the sanitized value?
+     *
+     * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
+     * determine that something explicitly says it's safe to use a value for binding in that
+     * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
+     * for those values that you can easily tell are safe - because they were received from your server,
+     * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
+     * allowing only the files in a specific directory to do this.  Ensuring that the internal API
+     * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
+     *
+     * In the case of AngularJS' SCE service, one uses {@link ng.$sce#methods_trustAs $sce.trustAs}
+     * (and shorthand methods such as {@link ng.$sce#methods_trustAsHtml $sce.trustAsHtml}, etc.) to
+     * obtain values that will be accepted by SCE / privileged contexts.
+     *
+     *
+     * ## How does it work?
+     *
+     * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#methods_getTrusted
+     * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
+     * ng.$sce#methods_parse $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
+     * {@link ng.$sce#methods_getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
+     *
+     * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
+     * ng.$sce#methods_parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
+     * simplified):
+     *
+     * <pre class="prettyprint">
+     *   var ngBindHtmlDirective = ['$sce', function($sce) {
+ *     return function(scope, element, attr) {
+ *       scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
+ *         element.html(value || '');
+ *       });
+ *     };
+ *   }];
+     * </pre>
+     *
+     * ## Impact on loading templates
+     *
+     * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
+     * `templateUrl`'s specified by {@link guide/directive directives}.
+     *
+     * By default, Angular only loads templates from the same domain and protocol as the application
+     * document.  This is done by calling {@link ng.$sce#methods_getTrustedResourceUrl
+     * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
+     * protocols, you may either either {@link ng.$sceDelegateProvider#methods_resourceUrlWhitelist whitelist
+     * them} or {@link ng.$sce#methods_trustAsResourceUrl wrap it} into a trusted value.
+     *
+     * *Please note*:
+     * The browser's
+     * {@link https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest
+     * Same Origin Policy} and {@link http://www.w3.org/TR/cors/ Cross-Origin Resource Sharing (CORS)}
+     * policy apply in addition to this and may further restrict whether the template is successfully
+     * loaded.  This means that without the right CORS policy, loading templates from a different domain
+     * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
+     * browsers.
+     *
+     * ## This feels like too much overhead for the developer?
+     *
+     * It's important to remember that SCE only applies to interpolation expressions.
+     *
+     * If your expressions are constant literals, they're automatically trusted and you don't need to
+     * call `$sce.trustAs` on them.  (e.g.
+     * `<div ng-html-bind-unsafe="'<b>implicitly trusted</b>'"></div>`) just works.
+     *
+     * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
+     * through {@link ng.$sce#methods_getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
+     *
+     * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
+     * templates in `ng-include` from your application's domain without having to even know about SCE.
+     * It blocks loading templates from other domains or loading templates over http from an https
+     * served document.  You can change these by setting your own custom {@link
+     * ng.$sceDelegateProvider#methods_resourceUrlWhitelist whitelists} and {@link
+     * ng.$sceDelegateProvider#methods_resourceUrlBlacklist blacklists} for matching such URLs.
+     *
+     * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
+     * application that's secure and can be audited to verify that with much more ease than bolting
+     * security onto an application later.
+     *
+     * <a name="contexts"></a>
+     * ## What trusted context types are supported?
+     *
+     * | Context             | Notes          |
+     * |---------------------|----------------|
+     * | `$sce.HTML`         | For HTML that's safe to source into the application.  The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. |
+     * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
+     * | `$sce.URL`          | For URLs that are safe to follow as links.  Currently unused (`<a href=` and `<img src=` sanitize their urls and don't consititute an SCE context. |
+     * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contens are also safe to include in your application.  Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.)  <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
+     * | `$sce.JS`           | For JavaScript that is safe to execute in your application's context.  Currently unused.  Feel free to use it in your own directives. |
+     *
+     * ## Format of items in {@link ng.$sceDelegateProvider#methods_resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#methods_resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
+     *
+     *  Each element in these arrays must be one of the following:
+     *
+     *  - **'self'**
+     *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
+     *      domain** as the application document using the **same protocol**.
+     *  - **String** (except the special value `'self'`)
+     *    - The string is matched against the full *normalized / absolute URL* of the resource
+     *      being tested (substring matches are not good enough.)
+     *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
+     *      match themselves.
+     *    - `*`: matches zero or more occurances of any character other than one of the following 6
+     *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'.  It's a useful wildcard for use
+     *      in a whitelist.
+     *    - `**`: matches zero or more occurances of *any* character.  As such, it's not
+     *      not appropriate to use in for a scheme, domain, etc. as it would match too much.  (e.g.
+     *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
+     *      not have been the intention.)  It's usage at the very end of the path is ok.  (e.g.
+     *      http://foo.example.com/templates/**).
+     *  - **RegExp** (*see caveat below*)
+     *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
+     *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
+     *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
+     *      have good test coverage.).  For instance, the use of `.` in the regex is correct only in a
+     *      small number of cases.  A `.` character in the regex used when matching the scheme or a
+     *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
+     *      is highly recommended to use the string patterns and only fall back to regular expressions
+     *      if they as a last resort.
+     *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
+     *      matched against the **entire** *normalized / absolute URL* of the resource being tested
+     *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
+     *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
+     *    - If you are generating your Javascript from some other templating engine (not
+     *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
+     *      remember to escape your regular expression (and be aware that you might need more than
+     *      one level of escaping depending on your templating engine and the way you interpolated
+     *      the value.)  Do make use of your platform's escaping mechanism as it might be good
+     *      enough before coding your own.  e.g. Ruby has
+     *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
+     *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
+     *      Javascript lacks a similar built in function for escaping.  Take a look at Google
+     *      Closure library's [goog.string.regExpEscape(s)](
+     *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
+     *
+     * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
+     *
+     * ## Show me an example using SCE.
+     *
+     * @example
+     <example module="mySceApp">
+     <file name="index.html">
+     <div ng-controller="myAppController as myCtrl">
+     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
+     <b>User comments</b><br>
+     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
+     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
+     exploit.
+     <div class="well">
+     <div ng-repeat="userComment in myCtrl.userComments">
+     <b>{{userComment.name}}</b>:
+     <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
+     <br>
+     </div>
+     </div>
+     </div>
+     </file>
+
+     <file name="script.js">
+     var mySceApp = angular.module('mySceApp', ['ngSanitize']);
+
+     mySceApp.controller("myAppController", function myAppController($http, $templateCache, $sce) {
+    var self = this;
+    $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
+      self.userComments = userComments;
+    });
+    self.explicitlyTrustedHtml = $sce.trustAsHtml(
+        '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
+        'sanitization.&quot;">Hover over this text.</span>');
+  });
+     </file>
+
+     <file name="test_data.json">
+     [
+     { "name": "Alice",
+    "htmlComment":
+        "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
+  },
+     { "name": "Bob",
+    "htmlComment": "<i>Yes!</i>  Am I the only other one?"
+  }
+     ]
+     </file>
+
+     <file name="scenario.js">
+     describe('SCE doc demo', function() {
+    it('should sanitize untrusted values', function() {
+      expect(element('.htmlComment').html()).toBe('<span>Is <i>anyone</i> reading this?</span>');
+    });
+    it('should NOT sanitize explicitly trusted values', function() {
+      expect(element('#explicitlyTrustedHtml').html()).toBe(
+          '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
+          'sanitization.&quot;">Hover over this text.</span>');
+    });
+  });
+     </file>
+     </example>
+     *
+     *
+     *
+     * ## Can I disable SCE completely?
+     *
+     * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
+     * for little coding overhead.  It will be much harder to take an SCE disabled application and
+     * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
+     * for cases where you have a lot of existing code that was written before SCE was introduced and
+     * you're migrating them a module at a time.
+     *
+     * That said, here's how you can completely disable SCE:
+     *
+     * <pre class="prettyprint">
+     *   angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
+ *     // Completely disable SCE.  For demonstration purposes only!
+ *     // Do not use in new projects.
+ *     $sceProvider.enabled(false);
+ *   });
+     * </pre>
+     *
+     */
+    /* jshint maxlen: 100 */
+
+    function $SceProvider() {
+        var enabled = true;
+
+        /**
+         * @ngdoc function
+         * @name ng.sceProvider#enabled
+         * @methodOf ng.$sceProvider
+         * @function
+         *
+         * @param {boolean=} value If provided, then enables/disables SCE.
+         * @return {boolean} true if SCE is enabled, false otherwise.
+         *
+         * @description
+         * Enables/disables SCE and returns the current value.
+         */
+        this.enabled = function (value) {
+            if (arguments.length) {
+                enabled = !!value;
+            }
+            return enabled;
+        };
+
+
+        /* Design notes on the default implementation for SCE.
+         *
+         * The API contract for the SCE delegate
+         * -------------------------------------
+         * The SCE delegate object must provide the following 3 methods:
+         *
+         * - trustAs(contextEnum, value)
+         *     This method is used to tell the SCE service that the provided value is OK to use in the
+         *     contexts specified by contextEnum.  It must return an object that will be accepted by
+         *     getTrusted() for a compatible contextEnum and return this value.
+         *
+         * - valueOf(value)
+         *     For values that were not produced by trustAs(), return them as is.  For values that were
+         *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
+         *     trustAs is wrapping the given values into some type, this operation unwraps it when given
+         *     such a value.
+         *
+         * - getTrusted(contextEnum, value)
+         *     This function should return the a value that is safe to use in the context specified by
+         *     contextEnum or throw and exception otherwise.
+         *
+         * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
+         * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
+         * instance, an implementation could maintain a registry of all trusted objects by context.  In
+         * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
+         * return the same object passed in if it was found in the registry under a compatible context or
+         * throw an exception otherwise.  An implementation might only wrap values some of the time based
+         * on some criteria.  getTrusted() might return a value and not throw an exception for special
+         * constants or objects even if not wrapped.  All such implementations fulfill this contract.
+         *
+         *
+         * A note on the inheritance model for SCE contexts
+         * ------------------------------------------------
+         * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
+         * is purely an implementation details.
+         *
+         * The contract is simply this:
+         *
+         *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
+         *     will also succeed.
+         *
+         * Inheritance happens to capture this in a natural way.  In some future, we
+         * may not use inheritance anymore.  That is OK because no code outside of
+         * sce.js and sceSpecs.js would need to be aware of this detail.
+         */
+
+        this.$get = ['$parse', '$sniffer', '$sceDelegate', function(
+                $parse,   $sniffer,   $sceDelegate) {
+            // Prereq: Ensure that we're not running in IE8 quirks mode.  In that mode, IE allows
+            // the "expression(javascript expression)" syntax which is insecure.
+            if (enabled && $sniffer.msie && $sniffer.msieDocumentMode < 8) {
+                throw $sceMinErr('iequirks',
+                        'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' +
+                                'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
+                                'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
+            }
+
+            var sce = copy(SCE_CONTEXTS);
+
+            /**
+             * @ngdoc function
+             * @name ng.sce#isEnabled
+             * @methodOf ng.$sce
+             * @function
+             *
+             * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
+             * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
+             *
+             * @description
+             * Returns a boolean indicating if SCE is enabled.
+             */
+            sce.isEnabled = function () {
+                return enabled;
+            };
+            sce.trustAs = $sceDelegate.trustAs;
+            sce.getTrusted = $sceDelegate.getTrusted;
+            sce.valueOf = $sceDelegate.valueOf;
+
+            if (!enabled) {
+                sce.trustAs = sce.getTrusted = function(type, value) { return value; };
+                sce.valueOf = identity;
+            }
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#parse
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
+             * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
+             * wraps the expression in a call to {@link ng.$sce#methods_getTrusted $sce.getTrusted(*type*,
+             * *result*)}
+             *
+             * @param {string} type The kind of SCE context in which this result will be used.
+             * @param {string} expression String expression to compile.
+             * @returns {function(context, locals)} a function which represents the compiled expression:
+             *
+             *    * `context` – `{object}` – an object against which any expressions embedded in the strings
+             *      are evaluated against (typically a scope object).
+             *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
+             *      `context`.
+             */
+            sce.parseAs = function sceParseAs(type, expr) {
+                var parsed = $parse(expr);
+                if (parsed.literal && parsed.constant) {
+                    return parsed;
+                } else {
+                    return function sceParseAsTrusted(self, locals) {
+                        return sce.getTrusted(type, parsed(self, locals));
+                    };
+                }
+            };
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#trustAs
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Delegates to {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}.  As such,
+             * returns an objectthat is trusted by angular for use in specified strict contextual
+             * escaping contexts (such as ng-html-bind-unsafe, ng-include, any src attribute
+             * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
+             * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
+             * escaping.
+             *
+             * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
+             *   resource_url, html, js and css.
+             * @param {*} value The value that that should be considered trusted/safe.
+             * @returns {*} A value that can be used to stand in for the provided `value` in places
+             * where Angular expects a $sce.trustAs() return value.
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#trustAsHtml
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.trustAsHtml(value)` →
+             *     {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
+             *
+             * @param {*} value The value to trustAs.
+             * @returns {*} An object that can be passed to {@link ng.$sce#methods_getTrustedHtml
+             *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
+             *     only accept expressions that are either literal constants or are the
+             *     return value of {@link ng.$sce#methods_trustAs $sce.trustAs}.)
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#trustAsUrl
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.trustAsUrl(value)` →
+             *     {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs($sce.URL, value)`}
+             *
+             * @param {*} value The value to trustAs.
+             * @returns {*} An object that can be passed to {@link ng.$sce#methods_getTrustedUrl
+             *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
+             *     only accept expressions that are either literal constants or are the
+             *     return value of {@link ng.$sce#methods_trustAs $sce.trustAs}.)
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#trustAsResourceUrl
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
+             *     {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
+             *
+             * @param {*} value The value to trustAs.
+             * @returns {*} An object that can be passed to {@link ng.$sce#methods_getTrustedResourceUrl
+             *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
+             *     only accept expressions that are either literal constants or are the return
+             *     value of {@link ng.$sce#methods_trustAs $sce.trustAs}.)
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#trustAsJs
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.trustAsJs(value)` →
+             *     {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs($sce.JS, value)`}
+             *
+             * @param {*} value The value to trustAs.
+             * @returns {*} An object that can be passed to {@link ng.$sce#methods_getTrustedJs
+             *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
+             *     only accept expressions that are either literal constants or are the
+             *     return value of {@link ng.$sce#methods_trustAs $sce.trustAs}.)
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#getTrusted
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Delegates to {@link ng.$sceDelegate#methods_getTrusted `$sceDelegate.getTrusted`}.  As such,
+             * takes the result of a {@link ng.$sce#methods_trustAs `$sce.trustAs`}() call and returns the
+             * originally supplied value if the queried context type is a supertype of the created type.
+             * If this condition isn't satisfied, throws an exception.
+             *
+             * @param {string} type The kind of context in which this value is to be used.
+             * @param {*} maybeTrusted The result of a prior {@link ng.$sce#methods_trustAs `$sce.trustAs`}
+             *                         call.
+             * @returns {*} The value the was originally provided to
+             *              {@link ng.$sce#methods_trustAs `$sce.trustAs`} if valid in this context.
+             *              Otherwise, throws an exception.
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#getTrustedHtml
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.getTrustedHtml(value)` →
+             *     {@link ng.$sceDelegate#methods_getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
+             *
+             * @param {*} value The value to pass to `$sce.getTrusted`.
+             * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#getTrustedCss
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.getTrustedCss(value)` →
+             *     {@link ng.$sceDelegate#methods_getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
+             *
+             * @param {*} value The value to pass to `$sce.getTrusted`.
+             * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#getTrustedUrl
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.getTrustedUrl(value)` →
+             *     {@link ng.$sceDelegate#methods_getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
+             *
+             * @param {*} value The value to pass to `$sce.getTrusted`.
+             * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#getTrustedResourceUrl
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
+             *     {@link ng.$sceDelegate#methods_getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
+             *
+             * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
+             * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#getTrustedJs
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.getTrustedJs(value)` →
+             *     {@link ng.$sceDelegate#methods_getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
+             *
+             * @param {*} value The value to pass to `$sce.getTrusted`.
+             * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#parseAsHtml
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.parseAsHtml(expression string)` →
+             *     {@link ng.$sce#methods_parse `$sce.parseAs($sce.HTML, value)`}
+             *
+             * @param {string} expression String expression to compile.
+             * @returns {function(context, locals)} a function which represents the compiled expression:
+             *
+             *    * `context` – `{object}` – an object against which any expressions embedded in the strings
+             *      are evaluated against (typically a scope object).
+             *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
+             *      `context`.
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#parseAsCss
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.parseAsCss(value)` →
+             *     {@link ng.$sce#methods_parse `$sce.parseAs($sce.CSS, value)`}
+             *
+             * @param {string} expression String expression to compile.
+             * @returns {function(context, locals)} a function which represents the compiled expression:
+             *
+             *    * `context` – `{object}` – an object against which any expressions embedded in the strings
+             *      are evaluated against (typically a scope object).
+             *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
+             *      `context`.
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#parseAsUrl
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.parseAsUrl(value)` →
+             *     {@link ng.$sce#methods_parse `$sce.parseAs($sce.URL, value)`}
+             *
+             * @param {string} expression String expression to compile.
+             * @returns {function(context, locals)} a function which represents the compiled expression:
+             *
+             *    * `context` – `{object}` – an object against which any expressions embedded in the strings
+             *      are evaluated against (typically a scope object).
+             *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
+             *      `context`.
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#parseAsResourceUrl
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
+             *     {@link ng.$sce#methods_parse `$sce.parseAs($sce.RESOURCE_URL, value)`}
+             *
+             * @param {string} expression String expression to compile.
+             * @returns {function(context, locals)} a function which represents the compiled expression:
+             *
+             *    * `context` – `{object}` – an object against which any expressions embedded in the strings
+             *      are evaluated against (typically a scope object).
+             *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
+             *      `context`.
+             */
+
+            /**
+             * @ngdoc method
+             * @name ng.$sce#parseAsJs
+             * @methodOf ng.$sce
+             *
+             * @description
+             * Shorthand method.  `$sce.parseAsJs(value)` →
+             *     {@link ng.$sce#methods_parse `$sce.parseAs($sce.JS, value)`}
+             *
+             * @param {string} expression String expression to compile.
+             * @returns {function(context, locals)} a function which represents the compiled expression:
+             *
+             *    * `context` – `{object}` – an object against which any expressions embedded in the strings
+             *      are evaluated against (typically a scope object).
+             *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
+             *      `context`.
+             */
+
+            // Shorthand delegations.
+            var parse = sce.parseAs,
+                    getTrusted = sce.getTrusted,
+                    trustAs = sce.trustAs;
+
+            forEach(SCE_CONTEXTS, function (enumValue, name) {
+                var lName = lowercase(name);
+                sce[camelCase("parse_as_" + lName)] = function (expr) {
+                    return parse(enumValue, expr);
+                };
+                sce[camelCase("get_trusted_" + lName)] = function (value) {
+                    return getTrusted(enumValue, value);
+                };
+                sce[camelCase("trust_as_" + lName)] = function (value) {
+                    return trustAs(enumValue, value);
+                };
+            });
+
+            return sce;
+        }];
+    }
+
+    /**
+     * !!! This is an undocumented "private" service !!!
+     *
+     * @name ng.$sniffer
+     * @requires $window
+     * @requires $document
+     *
+     * @property {boolean} history Does the browser support html5 history api ?
+     * @property {boolean} hashchange Does the browser support hashchange event ?
+     * @property {boolean} transitions Does the browser support CSS transition events ?
+     * @property {boolean} animations Does the browser support CSS animation events ?
+     *
+     * @description
+     * This is very simple implementation of testing browser's features.
+     */
+    function $SnifferProvider() {
+        this.$get = ['$window', '$document', function($window, $document) {
+            var eventSupport = {},
+                    android =
+                            int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
+                    boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
+                    document = $document[0] || {},
+                    documentMode = document.documentMode,
+                    vendorPrefix,
+                    vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/,
+                    bodyStyle = document.body && document.body.style,
+                    transitions = false,
+                    animations = false,
+                    match;
+
+            if (bodyStyle) {
+                for(var prop in bodyStyle) {
+                    if(match = vendorRegex.exec(prop)) {
+                        vendorPrefix = match[0];
+                        vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
+                        break;
+                    }
+                }
+
+                if(!vendorPrefix) {
+                    vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
+                }
+
+                transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
+                animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
+
+                if (android && (!transitions||!animations)) {
+                    transitions = isString(document.body.style.webkitTransition);
+                    animations = isString(document.body.style.webkitAnimation);
+                }
+            }
+
+
+            return {
+                // Android has history.pushState, but it does not update location correctly
+                // so let's not use the history API at all.
+                // http://code.google.com/p/android/issues/detail?id=17471
+                // https://github.com/angular/angular.js/issues/904
+
+                // older webit browser (533.9) on Boxee box has exactly the same problem as Android has
+                // so let's not use the history API also
+                // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
+                // jshint -W018
+                history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
+                // jshint +W018
+                hashchange: 'onhashchange' in $window &&
+                    // IE8 compatible mode lies
+                        (!documentMode || documentMode > 7),
+                hasEvent: function(event) {
+                    // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
+                    // it. In particular the event is not fired when backspace or delete key are pressed or
+                    // when cut operation is performed.
+                    if (event == 'input' && msie == 9) return false;
+
+                    if (isUndefined(eventSupport[event])) {
+                        var divElm = document.createElement('div');
+                        eventSupport[event] = 'on' + event in divElm;
+                    }
+
+                    return eventSupport[event];
+                },
+                csp: csp(),
+                vendorPrefix: vendorPrefix,
+                transitions : transitions,
+                animations : animations,
+                msie : msie,
+                msieDocumentMode: documentMode
+            };
+        }];
+    }
+
+    function $TimeoutProvider() {
+        this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler',
+            function($rootScope,   $browser,   $q,   $exceptionHandler) {
+                var deferreds = {};
+
+
+                /**
+                 * @ngdoc function
+                 * @name ng.$timeout
+                 * @requires $browser
+                 *
+                 * @description
+                 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
+                 * block and delegates any exceptions to
+                 * {@link ng.$exceptionHandler $exceptionHandler} service.
+                 *
+                 * The return value of registering a timeout function is a promise, which will be resolved when
+                 * the timeout is reached and the timeout function is executed.
+                 *
+                 * To cancel a timeout request, call `$timeout.cancel(promise)`.
+                 *
+                 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
+                 * synchronously flush the queue of deferred functions.
+                 *
+                 * @param {function()} fn A function, whose execution should be delayed.
+                 * @param {number=} [delay=0] Delay in milliseconds.
+                 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
+                 *   will invoke `fn` within the {@link ng.$rootScope.Scope#methods_$apply $apply} block.
+                 * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
+                 *   promise will be resolved with is the return value of the `fn` function.
+                 *
+                 * @example
+                 <doc:example module="time">
+                 <doc:source>
+                 <script>
+                 function Ctrl2($scope,$timeout) {
+              $scope.format = 'M/d/yy h:mm:ss a';
+              $scope.blood_1 = 100;
+              $scope.blood_2 = 120;
+
+              var stop;
+              $scope.fight = function() {
+                stop = $timeout(function() {
+                  if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
+                      $scope.blood_1 = $scope.blood_1 - 3;
+                      $scope.blood_2 = $scope.blood_2 - 4;
+                      $scope.fight();
+                  } else {
+                      $timeout.cancel(stop);
+                  }
+                }, 100);
+              };
+
+              $scope.stopFight = function() {
+                $timeout.cancel(stop);
+              };
+
+              $scope.resetFight = function() {
+                $scope.blood_1 = 100;
+                $scope.blood_2 = 120;
+              }
+            }
+
+                 angular.module('time', [])
+                 // Register the 'myCurrentTime' directive factory method.
+                 // We inject $timeout and dateFilter service since the factory method is DI.
+                 .directive('myCurrentTime', function($timeout, dateFilter) {
+                // return the directive link function. (compile function not needed)
+                return function(scope, element, attrs) {
+                  var format,  // date format
+                  timeoutId; // timeoutId, so that we can cancel the time updates
+
+                  // used to update the UI
+                  function updateTime() {
+                    element.text(dateFilter(new Date(), format));
+                  }
+
+                  // watch the expression, and update the UI on change.
+                  scope.$watch(attrs.myCurrentTime, function(value) {
+                    format = value;
+                    updateTime();
+                  });
+
+                  // schedule update in one second
+                  function updateLater() {
+                    // save the timeoutId for canceling
+                    timeoutId = $timeout(function() {
+                      updateTime(); // update DOM
+                      updateLater(); // schedule another update
+                    }, 1000);
+                  }
+
+                  // listen on DOM destroy (removal) event, and cancel the next UI update
+                  // to prevent updating time ofter the DOM element was removed.
+                  element.bind('$destroy', function() {
+                    $timeout.cancel(timeoutId);
+                  });
+
+                  updateLater(); // kick off the UI update process.
+                }
+              });
+                 </script>
+
+                 <div>
+                 <div ng-controller="Ctrl2">
+                 Date format: <input ng-model="format"> <hr/>
+                 Current time is: <span my-current-time="format"></span>
+                 <hr/>
+                 Blood 1 : <font color='red'>{{blood_1}}</font>
+                 Blood 2 : <font color='red'>{{blood_2}}</font>
+                 <button type="button" data-ng-click="fight()">Fight</button>
+                 <button type="button" data-ng-click="stopFight()">StopFight</button>
+                 <button type="button" data-ng-click="resetFight()">resetFight</button>
+                 </div>
+                 </div>
+
+                 </doc:source>
+                 </doc:example>
+                 */
+                function timeout(fn, delay, invokeApply) {
+                    var deferred = $q.defer(),
+                            promise = deferred.promise,
+                            skipApply = (isDefined(invokeApply) && !invokeApply),
+                            timeoutId;
+
+                    timeoutId = $browser.defer(function() {
+                        try {
+                            deferred.resolve(fn());
+                        } catch(e) {
+                            deferred.reject(e);
+                            $exceptionHandler(e);
+                        }
+                        finally {
+                            delete deferreds[promise.$$timeoutId];
+                        }
+
+                        if (!skipApply) $rootScope.$apply();
+                    }, delay);
+
+                    promise.$$timeoutId = timeoutId;
+                    deferreds[timeoutId] = deferred;
+
+                    return promise;
+                }
+
+
+                /**
+                 * @ngdoc function
+                 * @name ng.$timeout#cancel
+                 * @methodOf ng.$timeout
+                 *
+                 * @description
+                 * Cancels a task associated with the `promise`. As a result of this, the promise will be
+                 * resolved with a rejection.
+                 *
+                 * @param {Promise=} promise Promise returned by the `$timeout` function.
+                 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
+                 *   canceled.
+                 */
+                timeout.cancel = function(promise) {
+                    if (promise && promise.$$timeoutId in deferreds) {
+                        deferreds[promise.$$timeoutId].reject('canceled');
+                        delete deferreds[promise.$$timeoutId];
+                        return $browser.defer.cancel(promise.$$timeoutId);
+                    }
+                    return false;
+                };
+
+                return timeout;
+            }];
+    }
+
+// NOTE:  The usage of window and document instead of $window and $document here is
+// deliberate.  This service depends on the specific behavior of anchor nodes created by the
+// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
+// cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
+// doesn't know about mocked locations and resolves URLs to the real document - which is
+// exactly the behavior needed here.  There is little value is mocking these out for this
+// service.
+    var urlParsingNode = document.createElement("a");
+    /*
+     Matches paths for file protocol on windows,
+     such as /C:/foo/bar, and captures only /foo/bar.
+     */
+    var windowsFilePathExp = /^\/?.*?:(\/.*)/;
+    var originUrl = urlResolve(window.location.href, true);
+
+
+    /**
+     *
+     * Implementation Notes for non-IE browsers
+     * ----------------------------------------
+     * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
+     * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
+     * URL will be resolved into an absolute URL in the context of the application document.
+     * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
+     * properties are all populated to reflect the normalized URL.  This approach has wide
+     * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
+     * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
+     *
+     * Implementation Notes for IE
+     * ---------------------------
+     * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other
+     * browsers.  However, the parsed components will not be set if the URL assigned did not specify
+     * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
+     * work around that by performing the parsing in a 2nd step by taking a previously normalized
+     * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
+     * properties such as protocol, hostname, port, etc.
+     *
+     * IE7 does not normalize the URL when assigned to an anchor node.  (Apparently, it does, if one
+     * uses the inner HTML approach to assign the URL as part of an HTML snippet -
+     * http://stackoverflow.com/a/472729)  However, setting img[src] does normalize the URL.
+     * Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception.
+     * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that
+     * method and IE < 8 is unsupported.
+     *
+     * References:
+     *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
+     *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
+     *   http://url.spec.whatwg.org/#urlutils
+     *   https://github.com/angular/angular.js/pull/2902
+     *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
+     *
+     * @function
+     * @param {string} url The URL to be parsed.
+     * @description Normalizes and parses a URL.
+     * @returns {object} Returns the normalized URL as a dictionary.
+     *
+     *   | member name   | Description    |
+     *   |---------------|----------------|
+     *   | href          | A normalized version of the provided URL if it was not an absolute URL |
+     *   | protocol      | The protocol including the trailing colon                              |
+     *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
+     *   | search        | The search params, minus the question mark                             |
+     *   | hash          | The hash string, minus the hash symbol
+     *   | hostname      | The hostname
+     *   | port          | The port, without ":"
+     *   | pathname      | The pathname, beginning with "/"
+     *
+     */
+    function urlResolve(url, base) {
+        var href = url,
+                pathname;
+
+        if (msie) {
+            // Normalize before parse.  Refer Implementation Notes on why this is
+            // done in two steps on IE.
+            urlParsingNode.setAttribute("href", href);
+            href = urlParsingNode.href;
+        }
+
+        urlParsingNode.setAttribute('href', href);
+
+        /*
+         * In Windows, on an anchor node on documents loaded from
+         * the filesystem, the browser will return a pathname
+         * prefixed with the drive name ('/C:/path') when a
+         * pathname without a drive is set:
+         *  * a.setAttribute('href', '/foo')
+         *   * a.pathname === '/C:/foo' //true
+         *
+         * Inside of Angular, we're always using pathnames that
+         * do not include drive names for routing.
+         */
+
+        pathname = removeWindowsDriveName(urlParsingNode.pathname, url, base);
+        pathname = (pathname.charAt(0) === '/') ? pathname : '/' + pathname;
+
+        // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
+        return {
+            href: urlParsingNode.href,
+            protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
+            host: urlParsingNode.host,
+            search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
+            hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
+            hostname: urlParsingNode.hostname,
+            port: urlParsingNode.port,
+            pathname: pathname
+        };
+    }
+
+
+    /**
+     * Parse a request URL and determine whether this is a same-origin request as the application document.
+     *
+     * @param {string|object} requestUrl The url of the request as a string that will be resolved
+     * or a parsed URL object.
+     * @returns {boolean} Whether the request is for the same origin as the application document.
+     */
+    function urlIsSameOrigin(requestUrl) {
+        var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
+        return (parsed.protocol === originUrl.protocol &&
+                parsed.host === originUrl.host);
+    }
+
+    function removeWindowsDriveName (path, url, base) {
+        var firstPathSegmentMatch;
+
+        //Get the relative path from the input URL.
+        if (url.indexOf(base) === 0) {
+            url = url.replace(base, '');
+        }
+
+        /*
+         * The input URL intentionally contains a
+         * first path segment that ends with a colon.
+         */
+        if (windowsFilePathExp.exec(url)) {
+            return path;
+        }
+
+        firstPathSegmentMatch = windowsFilePathExp.exec(path);
+        return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
+    }
+
+    /**
+     * @ngdoc object
+     * @name ng.$window
+     *
+     * @description
+     * A reference to the browser's `window` object. While `window`
+     * is globally available in JavaScript, it causes testability problems, because
+     * it is a global variable. In angular we always refer to it through the
+     * `$window` service, so it may be overridden, removed or mocked for testing.
+     *
+     * Expressions, like the one defined for the `ngClick` directive in the example
+     * below, are evaluated with respect to the current scope.  Therefore, there is
+     * no risk of inadvertently coding in a dependency on a global value in such an
+     * expression.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope, $window) {
+           $scope.$window = $window;
+           $scope.greeting = 'Hello, World!';
+         }
+     </script>
+     <div ng-controller="Ctrl">
+     <input type="text" ng-model="greeting" />
+     <button ng-click="$window.alert(greeting)">ALERT</button>
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should display the greeting in the input box', function() {
+       input('greeting').enter('Hello, E2E Tests');
+       // If we click the button it will block the test runner
+       // element(':button').click();
+      });
+     </doc:scenario>
+     </doc:example>
+     */
+    function $WindowProvider(){
+        this.$get = valueFn(window);
+    }
+
+    /**
+     * @ngdoc object
+     * @name ng.$filterProvider
+     * @description
+     *
+     * Filters are just functions which transform input to an output. However filters need to be
+     * Dependency Injected. To achieve this a filter definition consists of a factory function which is
+     * annotated with dependencies and is responsible for creating a filter function.
+     *
+     * <pre>
+     *   // Filter registration
+     *   function MyModule($provide, $filterProvider) {
+ *     // create a service to demonstrate injection (not always needed)
+ *     $provide.value('greet', function(name){
+ *       return 'Hello ' + name + '!';
+ *     });
+ *
+ *     // register a filter factory which uses the
+ *     // greet service to demonstrate DI.
+ *     $filterProvider.register('greet', function(greet){
+ *       // return the filter function which uses the greet service
+ *       // to generate salutation
+ *       return function(text) {
+ *         // filters need to be forgiving so check input validity
+ *         return text && greet(text) || text;
+ *       };
+ *     });
+ *   }
+     * </pre>
+     *
+     * The filter function is registered with the `$injector` under the filter name suffix with
+     * `Filter`.
+     *
+     * <pre>
+     *   it('should be the same instance', inject(
+     *     function($filterProvider) {
+ *       $filterProvider.register('reverse', function(){
+ *         return ...;
+ *       });
+ *     },
+     *     function($filter, reverseFilter) {
+ *       expect($filter('reverse')).toBe(reverseFilter);
+ *     });
+     * </pre>
+     *
+     *
+     * For more information about how angular filters work, and how to create your own filters, see
+     * {@link guide/filter Filters} in the Angular Developer Guide.
+     */
+    /**
+     * @ngdoc method
+     * @name ng.$filterProvider#register
+     * @methodOf ng.$filterProvider
+     * @description
+     * Register filter factory function.
+     *
+     * @param {String} name Name of the filter.
+     * @param {function} fn The filter factory function which is injectable.
+     */
+
+
+    /**
+     * @ngdoc function
+     * @name ng.$filter
+     * @function
+     * @description
+     * Filters are used for formatting data displayed to the user.
+     *
+     * The general syntax in templates is as follows:
+     *
+     *         {{ expression [| filter_name[:parameter_value] ... ] }}
+     *
+     * @param {String} name Name of the filter function to retrieve
+     * @return {Function} the filter function
+     */
+    $FilterProvider.$inject = ['$provide'];
+    function $FilterProvider($provide) {
+        var suffix = 'Filter';
+
+        /**
+         * @ngdoc function
+         * @name ng.$controllerProvider#register
+         * @methodOf ng.$controllerProvider
+         * @param {string|Object} name Name of the filter function, or an object map of filters where
+         *    the keys are the filter names and the values are the filter factories.
+         * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
+         *    of the registered filter instances.
+         */
+        function register(name, factory) {
+            if(isObject(name)) {
+                var filters = {};
+                forEach(name, function(filter, key) {
+                    filters[key] = register(key, filter);
+                });
+                return filters;
+            } else {
+                return $provide.factory(name + suffix, factory);
+            }
+        }
+        this.register = register;
+
+        this.$get = ['$injector', function($injector) {
+            return function(name) {
+                return $injector.get(name + suffix);
+            };
+        }];
+
+        ////////////////////////////////////////
+
+        /* global
+         currencyFilter: false,
+         dateFilter: false,
+         filterFilter: false,
+         jsonFilter: false,
+         limitToFilter: false,
+         lowercaseFilter: false,
+         numberFilter: false,
+         orderByFilter: false,
+         uppercaseFilter: false,
+         */
+
+        register('currency', currencyFilter);
+        register('date', dateFilter);
+        register('filter', filterFilter);
+        register('json', jsonFilter);
+        register('limitTo', limitToFilter);
+        register('lowercase', lowercaseFilter);
+        register('number', numberFilter);
+        register('orderBy', orderByFilter);
+        register('uppercase', uppercaseFilter);
+    }
+
+    /**
+     * @ngdoc filter
+     * @name ng.filter:filter
+     * @function
+     *
+     * @description
+     * Selects a subset of items from `array` and returns it as a new array.
+     *
+     * @param {Array} array The source array.
+     * @param {string|Object|function()} expression The predicate to be used for selecting items from
+     *   `array`.
+     *
+     *   Can be one of:
+     *
+     *   - `string`: Predicate that results in a substring match using the value of `expression`
+     *     string. All strings or objects with string properties in `array` that contain this string
+     *     will be returned. The predicate can be negated by prefixing the string with `!`.
+     *
+     *   - `Object`: A pattern object can be used to filter specific properties on objects contained
+     *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
+     *     which have property `name` containing "M" and property `phone` containing "1". A special
+     *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
+     *     property of the object. That's equivalent to the simple substring match with a `string`
+     *     as described above.
+     *
+     *   - `function`: A predicate function can be used to write arbitrary filters. The function is
+     *     called for each element of `array`. The final result is an array of those elements that
+     *     the predicate returned true for.
+     *
+     * @param {function(expected, actual)|true|undefined} comparator Comparator which is used in
+     *     determining if the expected value (from the filter expression) and actual value (from
+     *     the object in the array) should be considered a match.
+     *
+     *   Can be one of:
+     *
+     *     - `function(expected, actual)`:
+     *       The function will be given the object value and the predicate value to compare and
+     *       should return true if the item should be included in filtered result.
+     *
+     *     - `true`: A shorthand for `function(expected, actual) { return angular.equals(expected, actual)}`.
+     *       this is essentially strict comparison of expected and actual.
+     *
+     *     - `false|undefined`: A short hand for a function which will look for a substring match in case
+     *       insensitive way.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <div ng-init="friends = [{name:'John', phone:'555-1276'},
+     {name:'Mary', phone:'800-BIG-MARY'},
+     {name:'Mike', phone:'555-4321'},
+     {name:'Adam', phone:'555-5678'},
+     {name:'Julie', phone:'555-8765'},
+     {name:'Juliette', phone:'555-5678'}]"></div>
+
+     Search: <input ng-model="searchText">
+     <table id="searchTextResults">
+     <tr><th>Name</th><th>Phone</th></tr>
+     <tr ng-repeat="friend in friends | filter:searchText">
+     <td>{{friend.name}}</td>
+     <td>{{friend.phone}}</td>
+     </tr>
+     </table>
+     <hr>
+     Any: <input ng-model="search.$"> <br>
+     Name only <input ng-model="search.name"><br>
+     Phone only <input ng-model="search.phone"><br>
+     Equality <input type="checkbox" ng-model="strict"><br>
+     <table id="searchObjResults">
+     <tr><th>Name</th><th>Phone</th></tr>
+     <tr ng-repeat="friend in friends | filter:search:strict">
+     <td>{{friend.name}}</td>
+     <td>{{friend.phone}}</td>
+     </tr>
+     </table>
+     </doc:source>
+     <doc:scenario>
+     it('should search across all fields when filtering with a string', function() {
+         input('searchText').enter('m');
+         expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')).
+           toEqual(['Mary', 'Mike', 'Adam']);
+
+         input('searchText').enter('76');
+         expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')).
+           toEqual(['John', 'Julie']);
+       });
+
+     it('should search in specific fields when filtering with a predicate object', function() {
+         input('search.$').enter('i');
+         expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')).
+           toEqual(['Mary', 'Mike', 'Julie', 'Juliette']);
+       });
+     it('should use a equal comparison when comparator is true', function() {
+         input('search.name').enter('Julie');
+         input('strict').check();
+         expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')).
+           toEqual(['Julie']);
+       });
+     </doc:scenario>
+     </doc:example>
+     */
+    function filterFilter() {
+        return function(array, expression, comparator) {
+            if (!isArray(array)) return array;
+
+            var comparatorType = typeof(comparator),
+                    predicates = [];
+
+            predicates.check = function(value) {
+                for (var j = 0; j < predicates.length; j++) {
+                    if(!predicates[j](value)) {
+                        return false;
+                    }
+                }
+                return true;
+            };
+
+            if (comparatorType !== 'function') {
+                if (comparatorType === 'boolean' && comparator) {
+                    comparator = function(obj, text) {
+                        return angular.equals(obj, text);
+                    };
+                } else {
+                    comparator = function(obj, text) {
+                        text = (''+text).toLowerCase();
+                        return (''+obj).toLowerCase().indexOf(text) > -1;
+                    };
+                }
+            }
+
+            var search = function(obj, text){
+                if (typeof text == 'string' && text.charAt(0) === '!') {
+                    return !search(obj, text.substr(1));
+                }
+                switch (typeof obj) {
+                    case "boolean":
+                    case "number":
+                    case "string":
+                        return comparator(obj, text);
+                    case "object":
+                        switch (typeof text) {
+                            case "object":
+                                return comparator(obj, text);
+                            default:
+                                for ( var objKey in obj) {
+                                    if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
+                                        return true;
+                                    }
+                                }
+                                break;
+                        }
+                        return false;
+                    case "array":
+                        for ( var i = 0; i < obj.length; i++) {
+                            if (search(obj[i], text)) {
+                                return true;
+                            }
+                        }
+                        return false;
+                    default:
+                        return false;
+                }
+            };
+            switch (typeof expression) {
+                case "boolean":
+                case "number":
+                case "string":
+                    // Set up expression object and fall through
+                    expression = {$:expression};
+                // jshint -W086
+                case "object":
+                    // jshint +W086
+                    for (var key in expression) {
+                        if (key == '$') {
+                            (function() {
+                                if (!expression[key]) return;
+                                var path = key;
+                                predicates.push(function(value) {
+                                    return search(value, expression[path]);
+                                });
+                            })();
+                        } else {
+                            (function() {
+                                if (typeof(expression[key]) == 'undefined') { return; }
+                                var path = key;
+                                predicates.push(function(value) {
+                                    return search(getter(value,path), expression[path]);
+                                });
+                            })();
+                        }
+                    }
+                    break;
+                case 'function':
+                    predicates.push(expression);
+                    break;
+                default:
+                    return array;
+            }
+            var filtered = [];
+            for ( var j = 0; j < array.length; j++) {
+                var value = array[j];
+                if (predicates.check(value)) {
+                    filtered.push(value);
+                }
+            }
+            return filtered;
+        };
+    }
+
+    /**
+     * @ngdoc filter
+     * @name ng.filter:currency
+     * @function
+     *
+     * @description
+     * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
+     * symbol for current locale is used.
+     *
+     * @param {number} amount Input to filter.
+     * @param {string=} symbol Currency symbol or identifier to be displayed.
+     * @returns {string} Formatted number.
+     *
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+           $scope.amount = 1234.56;
+         }
+     </script>
+     <div ng-controller="Ctrl">
+     <input type="number" ng-model="amount"> <br>
+     default currency symbol ($): {{amount | currency}}<br>
+     custom currency identifier (USD$): {{amount | currency:"USD$"}}
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should init with 1234.56', function() {
+         expect(binding('amount | currency')).toBe('$1,234.56');
+         expect(binding('amount | currency:"USD$"')).toBe('USD$1,234.56');
+       });
+     it('should update', function() {
+         input('amount').enter('-1234');
+         expect(binding('amount | currency')).toBe('($1,234.00)');
+         expect(binding('amount | currency:"USD$"')).toBe('(USD$1,234.00)');
+       });
+     </doc:scenario>
+     </doc:example>
+     */
+    currencyFilter.$inject = ['$locale'];
+    function currencyFilter($locale) {
+        var formats = $locale.NUMBER_FORMATS;
+        return function(amount, currencySymbol){
+            if (isUndefined(currencySymbol)) currencySymbol = formats.CURRENCY_SYM;
+            return formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2).
+                    replace(/\u00A4/g, currencySymbol);
+        };
+    }
+
+    /**
+     * @ngdoc filter
+     * @name ng.filter:number
+     * @function
+     *
+     * @description
+     * Formats a number as text.
+     *
+     * If the input is not a number an empty string is returned.
+     *
+     * @param {number|string} number Number to format.
+     * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
+     * If this is not provided then the fraction size is computed from the current locale's number
+     * formatting pattern. In the case of the default locale, it will be 3.
+     * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+           $scope.val = 1234.56789;
+         }
+     </script>
+     <div ng-controller="Ctrl">
+     Enter number: <input ng-model='val'><br>
+     Default formatting: {{val | number}}<br>
+     No fractions: {{val | number:0}}<br>
+     Negative number: {{-val | number:4}}
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should format numbers', function() {
+         expect(binding('val | number')).toBe('1,234.568');
+         expect(binding('val | number:0')).toBe('1,235');
+         expect(binding('-val | number:4')).toBe('-1,234.5679');
+       });
+
+     it('should update', function() {
+         input('val').enter('3374.333');
+         expect(binding('val | number')).toBe('3,374.333');
+         expect(binding('val | number:0')).toBe('3,374');
+         expect(binding('-val | number:4')).toBe('-3,374.3330');
+       });
+     </doc:scenario>
+     </doc:example>
+     */
+
+
+    numberFilter.$inject = ['$locale'];
+    function numberFilter($locale) {
+        var formats = $locale.NUMBER_FORMATS;
+        return function(number, fractionSize) {
+            return formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
+                    fractionSize);
+        };
+    }
+
+    var DECIMAL_SEP = '.';
+    function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
+        if (isNaN(number) || !isFinite(number)) return '';
+
+        var isNegative = number < 0;
+        number = Math.abs(number);
+        var numStr = number + '',
+                formatedText = '',
+                parts = [];
+
+        var hasExponent = false;
+        if (numStr.indexOf('e') !== -1) {
+            var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
+            if (match && match[2] == '-' && match[3] > fractionSize + 1) {
+                numStr = '0';
+            } else {
+                formatedText = numStr;
+                hasExponent = true;
+            }
+        }
+
+        if (!hasExponent) {
+            var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
+
+            // determine fractionSize if it is not specified
+            if (isUndefined(fractionSize)) {
+                fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
+            }
+
+            var pow = Math.pow(10, fractionSize);
+            number = Math.round(number * pow) / pow;
+            var fraction = ('' + number).split(DECIMAL_SEP);
+            var whole = fraction[0];
+            fraction = fraction[1] || '';
+
+            var i, pos = 0,
+                    lgroup = pattern.lgSize,
+                    group = pattern.gSize;
+
+            if (whole.length >= (lgroup + group)) {
+                pos = whole.length - lgroup;
+                for (i = 0; i < pos; i++) {
+                    if ((pos - i)%group === 0 && i !== 0) {
+                        formatedText += groupSep;
+                    }
+                    formatedText += whole.charAt(i);
+                }
+            }
+
+            for (i = pos; i < whole.length; i++) {
+                if ((whole.length - i)%lgroup === 0 && i !== 0) {
+                    formatedText += groupSep;
+                }
+                formatedText += whole.charAt(i);
+            }
+
+            // format fraction part.
+            while(fraction.length < fractionSize) {
+                fraction += '0';
+            }
+
+            if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
+        } else {
+
+            if (fractionSize > 0 && number > -1 && number < 1) {
+                formatedText = number.toFixed(fractionSize);
+            }
+        }
+
+        parts.push(isNegative ? pattern.negPre : pattern.posPre);
+        parts.push(formatedText);
+        parts.push(isNegative ? pattern.negSuf : pattern.posSuf);
+        return parts.join('');
+    }
+
+    function padNumber(num, digits, trim) {
+        var neg = '';
+        if (num < 0) {
+            neg =  '-';
+            num = -num;
+        }
+        num = '' + num;
+        while(num.length < digits) num = '0' + num;
+        if (trim)
+            num = num.substr(num.length - digits);
+        return neg + num;
+    }
+
+
+    function dateGetter(name, size, offset, trim) {
+        offset = offset || 0;
+        return function(date) {
+            var value = date['get' + name]();
+            if (offset > 0 || value > -offset)
+                value += offset;
+            if (value === 0 && offset == -12 ) value = 12;
+            return padNumber(value, size, trim);
+        };
+    }
+
+    function dateStrGetter(name, shortForm) {
+        return function(date, formats) {
+            var value = date['get' + name]();
+            var get = uppercase(shortForm ? ('SHORT' + name) : name);
+
+            return formats[get][value];
+        };
+    }
+
+    function timeZoneGetter(date) {
+        var zone = -1 * date.getTimezoneOffset();
+        var paddedZone = (zone >= 0) ? "+" : "";
+
+        paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
+                padNumber(Math.abs(zone % 60), 2);
+
+        return paddedZone;
+    }
+
+    function ampmGetter(date, formats) {
+        return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
+    }
+
+    var DATE_FORMATS = {
+        yyyy: dateGetter('FullYear', 4),
+        yy: dateGetter('FullYear', 2, 0, true),
+        y: dateGetter('FullYear', 1),
+        MMMM: dateStrGetter('Month'),
+        MMM: dateStrGetter('Month', true),
+        MM: dateGetter('Month', 2, 1),
+        M: dateGetter('Month', 1, 1),
+        dd: dateGetter('Date', 2),
+        d: dateGetter('Date', 1),
+        HH: dateGetter('Hours', 2),
+        H: dateGetter('Hours', 1),
+        hh: dateGetter('Hours', 2, -12),
+        h: dateGetter('Hours', 1, -12),
+        mm: dateGetter('Minutes', 2),
+        m: dateGetter('Minutes', 1),
+        ss: dateGetter('Seconds', 2),
+        s: dateGetter('Seconds', 1),
+        // while ISO 8601 requires fractions to be prefixed with `.` or `,`
+        // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
+        sss: dateGetter('Milliseconds', 3),
+        EEEE: dateStrGetter('Day'),
+        EEE: dateStrGetter('Day', true),
+        a: ampmGetter,
+        Z: timeZoneGetter
+    };
+
+    var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,
+            NUMBER_STRING = /^\-?\d+$/;
+
+    /**
+     * @ngdoc filter
+     * @name ng.filter:date
+     * @function
+     *
+     * @description
+     *   Formats `date` to a string based on the requested `format`.
+     *
+     *   `format` string can be composed of the following elements:
+     *
+     *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
+     *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
+     *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
+     *   * `'MMMM'`: Month in year (January-December)
+     *   * `'MMM'`: Month in year (Jan-Dec)
+     *   * `'MM'`: Month in year, padded (01-12)
+     *   * `'M'`: Month in year (1-12)
+     *   * `'dd'`: Day in month, padded (01-31)
+     *   * `'d'`: Day in month (1-31)
+     *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
+     *   * `'EEE'`: Day in Week, (Sun-Sat)
+     *   * `'HH'`: Hour in day, padded (00-23)
+     *   * `'H'`: Hour in day (0-23)
+     *   * `'hh'`: Hour in am/pm, padded (01-12)
+     *   * `'h'`: Hour in am/pm, (1-12)
+     *   * `'mm'`: Minute in hour, padded (00-59)
+     *   * `'m'`: Minute in hour (0-59)
+     *   * `'ss'`: Second in minute, padded (00-59)
+     *   * `'s'`: Second in minute (0-59)
+     *   * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
+     *   * `'a'`: am/pm marker
+     *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
+     *
+     *   `format` string can also be one of the following predefined
+     *   {@link guide/i18n localizable formats}:
+     *
+     *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
+     *     (e.g. Sep 3, 2010 12:05:08 pm)
+     *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 pm)
+     *   * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US  locale
+     *     (e.g. Friday, September 3, 2010)
+     *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
+     *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
+     *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
+     *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm)
+     *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm)
+     *
+     *   `format` string can contain literal values. These need to be quoted with single quotes (e.g.
+     *   `"h 'in the morning'"`). In order to output single quote, use two single quotes in a sequence
+     *   (e.g. `"h 'o''clock'"`).
+     *
+     * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
+     *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
+     *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
+     *    specified in the string input, the time is considered to be in the local timezone.
+     * @param {string=} format Formatting rules (see Description). If not specified,
+     *    `mediumDate` is used.
+     * @returns {string} Formatted string or the input if input is not recognized as date/millis.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
+     {{1288323623006 | date:'medium'}}<br>
+     <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
+     {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}<br>
+     <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
+     {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}<br>
+     </doc:source>
+     <doc:scenario>
+     it('should format date', function() {
+         expect(binding("1288323623006 | date:'medium'")).
+            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
+         expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).
+            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
+         expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).
+     toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
+     });
+     </doc:scenario>
+     </doc:example>
+     */
+    dateFilter.$inject = ['$locale'];
+    function dateFilter($locale) {
+
+
+        var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
+        // 1        2       3         4          5          6          7          8  9     10      11
+        function jsonStringToDate(string) {
+            var match;
+            if (match = string.match(R_ISO8601_STR)) {
+                var date = new Date(0),
+                        tzHour = 0,
+                        tzMin  = 0,
+                        dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
+                        timeSetter = match[8] ? date.setUTCHours : date.setHours;
+
+                if (match[9]) {
+                    tzHour = int(match[9] + match[10]);
+                    tzMin = int(match[9] + match[11]);
+                }
+                dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
+                var h = int(match[4]||0) - tzHour;
+                var m = int(match[5]||0) - tzMin;
+                var s = int(match[6]||0);
+                var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000);
+                timeSetter.call(date, h, m, s, ms);
+                return date;
+            }
+            return string;
+        }
+
+
+        return function(date, format) {
+            var text = '',
+                    parts = [],
+                    fn, match;
+
+            format = format || 'mediumDate';
+            format = $locale.DATETIME_FORMATS[format] || format;
+            if (isString(date)) {
+                if (NUMBER_STRING.test(date)) {
+                    date = int(date);
+                } else {
+                    date = jsonStringToDate(date);
+                }
+            }
+
+            if (isNumber(date)) {
+                date = new Date(date);
+            }
+
+            if (!isDate(date)) {
+                return date;
+            }
+
+            while(format) {
+                match = DATE_FORMATS_SPLIT.exec(format);
+                if (match) {
+                    parts = concat(parts, match, 1);
+                    format = parts.pop();
+                } else {
+                    parts.push(format);
+                    format = null;
+                }
+            }
+
+            forEach(parts, function(value){
+                fn = DATE_FORMATS[value];
+                text += fn ? fn(date, $locale.DATETIME_FORMATS)
+                        : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
+            });
+
+            return text;
+        };
+    }
+
+
+    /**
+     * @ngdoc filter
+     * @name ng.filter:json
+     * @function
+     *
+     * @description
+     *   Allows you to convert a JavaScript object into JSON string.
+     *
+     *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
+     *   the binding is automatically converted to JSON.
+     *
+     * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
+     * @returns {string} JSON string.
+     *
+     *
+     * @example:
+     <doc:example>
+     <doc:source>
+     <pre>{{ {'name':'value'} | json }}</pre>
+     </doc:source>
+     <doc:scenario>
+     it('should jsonify filtered objects', function() {
+         expect(binding("{'name':'value'}")).toMatch(/\{\n  "name": ?"value"\n}/);
+       });
+     </doc:scenario>
+     </doc:example>
+     *
+     */
+    function jsonFilter() {
+        return function(object) {
+            return toJson(object, true);
+        };
+    }
+
+
+    /**
+     * @ngdoc filter
+     * @name ng.filter:lowercase
+     * @function
+     * @description
+     * Converts string to lowercase.
+     * @see angular.lowercase
+     */
+    var lowercaseFilter = valueFn(lowercase);
+
+
+    /**
+     * @ngdoc filter
+     * @name ng.filter:uppercase
+     * @function
+     * @description
+     * Converts string to uppercase.
+     * @see angular.uppercase
+     */
+    var uppercaseFilter = valueFn(uppercase);
+
+    /**
+     * @ngdoc function
+     * @name ng.filter:limitTo
+     * @function
+     *
+     * @description
+     * Creates a new array or string containing only a specified number of elements. The elements
+     * are taken from either the beginning or the end of the source array or string, as specified by
+     * the value and sign (positive or negative) of `limit`.
+     *
+     * @param {Array|string} input Source array or string to be limited.
+     * @param {string|number} limit The length of the returned array or string. If the `limit` number
+     *     is positive, `limit` number of items from the beginning of the source array/string are copied.
+     *     If the number is negative, `limit` number  of items from the end of the source array/string
+     *     are copied. The `limit` will be trimmed if it exceeds `array.length`
+     * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
+     *     had less than `limit` elements.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+           $scope.numbers = [1,2,3,4,5,6,7,8,9];
+           $scope.letters = "abcdefghi";
+           $scope.numLimit = 3;
+           $scope.letterLimit = 3;
+         }
+     </script>
+     <div ng-controller="Ctrl">
+     Limit {{numbers}} to: <input type="integer" ng-model="numLimit">
+     <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
+     Limit {{letters}} to: <input type="integer" ng-model="letterLimit">
+     <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should limit the number array to first three items', function() {
+         expect(element('.doc-example-live input[ng-model=numLimit]').val()).toBe('3');
+         expect(element('.doc-example-live input[ng-model=letterLimit]').val()).toBe('3');
+         expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3]');
+         expect(binding('letters | limitTo:letterLimit')).toEqual('abc');
+       });
+
+     it('should update the output when -3 is entered', function() {
+         input('numLimit').enter(-3);
+         input('letterLimit').enter(-3);
+         expect(binding('numbers | limitTo:numLimit')).toEqual('[7,8,9]');
+         expect(binding('letters | limitTo:letterLimit')).toEqual('ghi');
+       });
+
+     it('should not exceed the maximum size of input array', function() {
+         input('numLimit').enter(100);
+         input('letterLimit').enter(100);
+         expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3,4,5,6,7,8,9]');
+         expect(binding('letters | limitTo:letterLimit')).toEqual('abcdefghi');
+       });
+     </doc:scenario>
+     </doc:example>
+     */
+    function limitToFilter(){
+        return function(input, limit) {
+            if (!isArray(input) && !isString(input)) return input;
+
+            limit = int(limit);
+
+            if (isString(input)) {
+                //NaN check on limit
+                if (limit) {
+                    return limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length);
+                } else {
+                    return "";
+                }
+            }
+
+            var out = [],
+                    i, n;
+
+            // if abs(limit) exceeds maximum length, trim it
+            if (limit > input.length)
+                limit = input.length;
+            else if (limit < -input.length)
+                limit = -input.length;
+
+            if (limit > 0) {
+                i = 0;
+                n = limit;
+            } else {
+                i = input.length + limit;
+                n = input.length;
+            }
+
+            for (; i<n; i++) {
+                out.push(input[i]);
+            }
+
+            return out;
+        };
+    }
+
+    /**
+     * @ngdoc function
+     * @name ng.filter:orderBy
+     * @function
+     *
+     * @description
+     * Orders a specified `array` by the `expression` predicate.
+     *
+     * @param {Array} array The array to sort.
+     * @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be
+     *    used by the comparator to determine the order of elements.
+     *
+     *    Can be one of:
+     *
+     *    - `function`: Getter function. The result of this function will be sorted using the
+     *      `<`, `=`, `>` operator.
+     *    - `string`: An Angular expression which evaluates to an object to order by, such as 'name'
+     *      to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control
+     *      ascending or descending sort order (for example, +name or -name).
+     *    - `Array`: An array of function or string predicates. The first predicate in the array
+     *      is used for sorting, but when two items are equivalent, the next predicate is used.
+     *
+     * @param {boolean=} reverse Reverse the order the array.
+     * @returns {Array} Sorted copy of the source array.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+           $scope.friends =
+               [{name:'John', phone:'555-1212', age:10},
+                {name:'Mary', phone:'555-9876', age:19},
+                {name:'Mike', phone:'555-4321', age:21},
+                {name:'Adam', phone:'555-5678', age:35},
+                {name:'Julie', phone:'555-8765', age:29}]
+           $scope.predicate = '-age';
+         }
+     </script>
+     <div ng-controller="Ctrl">
+     <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
+     <hr/>
+     [ <a href="" ng-click="predicate=''">unsorted</a> ]
+     <table class="friend">
+     <tr>
+     <th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a>
+     (<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th>
+     <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
+     <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
+     </tr>
+     <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
+     <td>{{friend.name}}</td>
+     <td>{{friend.phone}}</td>
+     <td>{{friend.age}}</td>
+     </tr>
+     </table>
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should be reverse ordered by aged', function() {
+         expect(binding('predicate')).toBe('-age');
+         expect(repeater('table.friend', 'friend in friends').column('friend.age')).
+           toEqual(['35', '29', '21', '19', '10']);
+         expect(repeater('table.friend', 'friend in friends').column('friend.name')).
+           toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']);
+       });
+
+     it('should reorder the table when user selects different predicate', function() {
+         element('.doc-example-live a:contains("Name")').click();
+         expect(repeater('table.friend', 'friend in friends').column('friend.name')).
+           toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']);
+         expect(repeater('table.friend', 'friend in friends').column('friend.age')).
+           toEqual(['35', '10', '29', '19', '21']);
+
+         element('.doc-example-live a:contains("Phone")').click();
+         expect(repeater('table.friend', 'friend in friends').column('friend.phone')).
+           toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']);
+         expect(repeater('table.friend', 'friend in friends').column('friend.name')).
+           toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']);
+       });
+     </doc:scenario>
+     </doc:example>
+     */
+    orderByFilter.$inject = ['$parse'];
+    function orderByFilter($parse){
+        return function(array, sortPredicate, reverseOrder) {
+            if (!isArray(array)) return array;
+            if (!sortPredicate) return array;
+            sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
+            sortPredicate = map(sortPredicate, function(predicate){
+                var descending = false, get = predicate || identity;
+                if (isString(predicate)) {
+                    if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
+                        descending = predicate.charAt(0) == '-';
+                        predicate = predicate.substring(1);
+                    }
+                    get = $parse(predicate);
+                }
+                return reverseComparator(function(a,b){
+                    return compare(get(a),get(b));
+                }, descending);
+            });
+            var arrayCopy = [];
+            for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
+            return arrayCopy.sort(reverseComparator(comparator, reverseOrder));
+
+            function comparator(o1, o2){
+                for ( var i = 0; i < sortPredicate.length; i++) {
+                    var comp = sortPredicate[i](o1, o2);
+                    if (comp !== 0) return comp;
+                }
+                return 0;
+            }
+            function reverseComparator(comp, descending) {
+                return toBoolean(descending)
+                        ? function(a,b){return comp(b,a);}
+                        : comp;
+            }
+            function compare(v1, v2){
+                var t1 = typeof v1;
+                var t2 = typeof v2;
+                if (t1 == t2) {
+                    if (t1 == "string") {
+                        v1 = v1.toLowerCase();
+                        v2 = v2.toLowerCase();
+                    }
+                    if (v1 === v2) return 0;
+                    return v1 < v2 ? -1 : 1;
+                } else {
+                    return t1 < t2 ? -1 : 1;
+                }
+            }
+        };
+    }
+
+    function ngDirective(directive) {
+        if (isFunction(directive)) {
+            directive = {
+                link: directive
+            };
+        }
+        directive.restrict = directive.restrict || 'AC';
+        return valueFn(directive);
+    }
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:a
+     * @restrict E
+     *
+     * @description
+     * Modifies the default behavior of the html A tag so that the default action is prevented when
+     * the href attribute is empty.
+     *
+     * This change permits the easy creation of action links with the `ngClick` directive
+     * without changing the location or causing page reloads, e.g.:
+     * `<a href="" ng-click="list.addItem()">Add Item</a>`
+     */
+    var htmlAnchorDirective = valueFn({
+        restrict: 'E',
+        compile: function(element, attr) {
+
+            if (msie <= 8) {
+
+                // turn <a href ng-click="..">link</a> into a stylable link in IE
+                // but only if it doesn't have name attribute, in which case it's an anchor
+                if (!attr.href && !attr.name) {
+                    attr.$set('href', '');
+                }
+
+                // add a comment node to anchors to workaround IE bug that causes element content to be reset
+                // to new attribute content if attribute is updated with value containing @ and element also
+                // contains value with @
+                // see issue #1949
+                element.append(document.createComment('IE fix'));
+            }
+
+            return function(scope, element) {
+                element.on('click', function(event){
+                    // if we have no href url, then don't navigate anywhere.
+                    if (!element.attr('href')) {
+                        event.preventDefault();
+                    }
+                });
+            };
+        }
+    });
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngHref
+     * @restrict A
+     *
+     * @description
+     * Using Angular markup like `{{hash}}` in an href attribute will
+     * make the link go to the wrong URL if the user clicks it before
+     * Angular has a chance to replace the `{{hash}}` markup with its
+     * value. Until Angular replaces the markup the link will be broken
+     * and will most likely return a 404 error.
+     *
+     * The `ngHref` directive solves this problem.
+     *
+     * The wrong way to write it:
+     * <pre>
+     * <a href="http://www.gravatar.com/avatar/{{hash}}"/>
+     * </pre>
+     *
+     * The correct way to write it:
+     * <pre>
+     * <a ng-href="http://www.gravatar.com/avatar/{{hash}}"/>
+     * </pre>
+     *
+     * @element A
+     * @param {template} ngHref any string which can contain `{{}}` markup.
+     *
+     * @example
+     * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
+     * in links and their different behaviors:
+     <doc:example>
+     <doc:source>
+     <input ng-model="value" /><br />
+     <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
+     <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
+     <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
+     <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
+     <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
+     <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
+     </doc:source>
+     <doc:scenario>
+     it('should execute ng-click but not reload when href without value', function() {
+          element('#link-1').click();
+          expect(input('value').val()).toEqual('1');
+          expect(element('#link-1').attr('href')).toBe("");
+        });
+
+     it('should execute ng-click but not reload when href empty string', function() {
+          element('#link-2').click();
+          expect(input('value').val()).toEqual('2');
+          expect(element('#link-2').attr('href')).toBe("");
+        });
+
+     it('should execute ng-click and change url when ng-href specified', function() {
+          expect(element('#link-3').attr('href')).toBe("/123");
+
+          element('#link-3').click();
+          expect(browser().window().path()).toEqual('/123');
+        });
+
+     it('should execute ng-click but not reload when href empty string and name specified', function() {
+          element('#link-4').click();
+          expect(input('value').val()).toEqual('4');
+          expect(element('#link-4').attr('href')).toBe('');
+        });
+
+     it('should execute ng-click but not reload when no href but name specified', function() {
+          element('#link-5').click();
+          expect(input('value').val()).toEqual('5');
+          expect(element('#link-5').attr('href')).toBe(undefined);
+        });
+
+     it('should only change url when only ng-href', function() {
+          input('value').enter('6');
+          expect(element('#link-6').attr('href')).toBe('6');
+
+          element('#link-6').click();
+          expect(browser().location().url()).toEqual('/6');
+        });
+     </doc:scenario>
+     </doc:example>
+     */
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngSrc
+     * @restrict A
+     *
+     * @description
+     * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
+     * work right: The browser will fetch from the URL with the literal
+     * text `{{hash}}` until Angular replaces the expression inside
+     * `{{hash}}`. The `ngSrc` directive solves this problem.
+     *
+     * The buggy way to write it:
+     * <pre>
+     * <img src="http://www.gravatar.com/avatar/{{hash}}"/>
+     * </pre>
+     *
+     * The correct way to write it:
+     * <pre>
+     * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>
+     * </pre>
+     *
+     * @element IMG
+     * @param {template} ngSrc any string which can contain `{{}}` markup.
+     */
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngSrcset
+     * @restrict A
+     *
+     * @description
+     * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
+     * work right: The browser will fetch from the URL with the literal
+     * text `{{hash}}` until Angular replaces the expression inside
+     * `{{hash}}`. The `ngSrcset` directive solves this problem.
+     *
+     * The buggy way to write it:
+     * <pre>
+     * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
+     * </pre>
+     *
+     * The correct way to write it:
+     * <pre>
+     * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
+     * </pre>
+     *
+     * @element IMG
+     * @param {template} ngSrcset any string which can contain `{{}}` markup.
+     */
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngDisabled
+     * @restrict A
+     *
+     * @description
+     *
+     * The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
+     * <pre>
+     * <div ng-init="scope = { isDisabled: false }">
+     *  <button disabled="{{scope.isDisabled}}">Disabled</button>
+     * </div>
+     * </pre>
+     *
+     * The HTML specification does not require browsers to preserve the values of boolean attributes
+     * such as disabled. (Their presence means true and their absence means false.)
+     * If we put an Angular interpolation expression into such an attribute then the
+     * binding information would be lost when the browser removes the attribute.
+     * The `ngDisabled` directive solves this problem for the `disabled` attribute.
+     * This complementary directive is not removed by the browser and so provides
+     * a permanent reliable place to store the binding information.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
+     <button ng-model="button" ng-disabled="checked">Button</button>
+     </doc:source>
+     <doc:scenario>
+     it('should toggle button', function() {
+          expect(element('.doc-example-live :button').prop('disabled')).toBeFalsy();
+          input('checked').check();
+          expect(element('.doc-example-live :button').prop('disabled')).toBeTruthy();
+        });
+     </doc:scenario>
+     </doc:example>
+     *
+     * @element INPUT
+     * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
+     *     then special attribute "disabled" will be set on the element
+     */
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngChecked
+     * @restrict A
+     *
+     * @description
+     * The HTML specification does not require browsers to preserve the values of boolean attributes
+     * such as checked. (Their presence means true and their absence means false.)
+     * If we put an Angular interpolation expression into such an attribute then the
+     * binding information would be lost when the browser removes the attribute.
+     * The `ngChecked` directive solves this problem for the `checked` attribute.
+     * This complementary directive is not removed by the browser and so provides
+     * a permanent reliable place to store the binding information.
+     * @example
+     <doc:example>
+     <doc:source>
+     Check me to check both: <input type="checkbox" ng-model="master"><br/>
+     <input id="checkSlave" type="checkbox" ng-checked="master">
+     </doc:source>
+     <doc:scenario>
+     it('should check both checkBoxes', function() {
+          expect(element('.doc-example-live #checkSlave').prop('checked')).toBeFalsy();
+          input('master').check();
+          expect(element('.doc-example-live #checkSlave').prop('checked')).toBeTruthy();
+        });
+     </doc:scenario>
+     </doc:example>
+     *
+     * @element INPUT
+     * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
+     *     then special attribute "checked" will be set on the element
+     */
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngReadonly
+     * @restrict A
+     *
+     * @description
+     * The HTML specification does not require browsers to preserve the values of boolean attributes
+     * such as readonly. (Their presence means true and their absence means false.)
+     * If we put an Angular interpolation expression into such an attribute then the
+     * binding information would be lost when the browser removes the attribute.
+     * The `ngReadonly` directive solves this problem for the `readonly` attribute.
+     * This complementary directive is not removed by the browser and so provides
+     * a permanent reliable place to store the binding information.
+
+     * @example
+     <doc:example>
+     <doc:source>
+     Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
+     <input type="text" ng-readonly="checked" value="I'm Angular"/>
+     </doc:source>
+     <doc:scenario>
+     it('should toggle readonly attr', function() {
+          expect(element('.doc-example-live :text').prop('readonly')).toBeFalsy();
+          input('checked').check();
+          expect(element('.doc-example-live :text').prop('readonly')).toBeTruthy();
+        });
+     </doc:scenario>
+     </doc:example>
+     *
+     * @element INPUT
+     * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
+     *     then special attribute "readonly" will be set on the element
+     */
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngSelected
+     * @restrict A
+     *
+     * @description
+     * The HTML specification does not require browsers to preserve the values of boolean attributes
+     * such as selected. (Their presence means true and their absence means false.)
+     * If we put an Angular interpolation expression into such an attribute then the
+     * binding information would be lost when the browser removes the attribute.
+     * The `ngSelected` directive solves this problem for the `selected` atttribute.
+     * This complementary directive is not removed by the browser and so provides
+     * a permanent reliable place to store the binding information.
+     * @example
+     <doc:example>
+     <doc:source>
+     Check me to select: <input type="checkbox" ng-model="selected"><br/>
+     <select>
+     <option>Hello!</option>
+     <option id="greet" ng-selected="selected">Greetings!</option>
+     </select>
+     </doc:source>
+     <doc:scenario>
+     it('should select Greetings!', function() {
+          expect(element('.doc-example-live #greet').prop('selected')).toBeFalsy();
+          input('selected').check();
+          expect(element('.doc-example-live #greet').prop('selected')).toBeTruthy();
+        });
+     </doc:scenario>
+     </doc:example>
+     *
+     * @element OPTION
+     * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
+     *     then special attribute "selected" will be set on the element
+     */
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngOpen
+     * @restrict A
+     *
+     * @description
+     * The HTML specification does not require browsers to preserve the values of boolean attributes
+     * such as open. (Their presence means true and their absence means false.)
+     * If we put an Angular interpolation expression into such an attribute then the
+     * binding information would be lost when the browser removes the attribute.
+     * The `ngOpen` directive solves this problem for the `open` attribute.
+     * This complementary directive is not removed by the browser and so provides
+     * a permanent reliable place to store the binding information.
+
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     Check me check multiple: <input type="checkbox" ng-model="open"><br/>
+     <details id="details" ng-open="open">
+     <summary>Show/Hide me</summary>
+     </details>
+     </doc:source>
+     <doc:scenario>
+     it('should toggle open', function() {
+           expect(element('#details').prop('open')).toBeFalsy();
+           input('open').check();
+           expect(element('#details').prop('open')).toBeTruthy();
+         });
+     </doc:scenario>
+     </doc:example>
+     *
+     * @element DETAILS
+     * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
+     *     then special attribute "open" will be set on the element
+     */
+
+    var ngAttributeAliasDirectives = {};
+
+
+// boolean attrs are evaluated
+    forEach(BOOLEAN_ATTR, function(propName, attrName) {
+        // binding to multiple is not supported
+        if (propName == "multiple") return;
+
+        var normalized = directiveNormalize('ng-' + attrName);
+        ngAttributeAliasDirectives[normalized] = function() {
+            return {
+                priority: 100,
+                compile: function() {
+                    return function(scope, element, attr) {
+                        scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
+                            attr.$set(attrName, !!value);
+                        });
+                    };
+                }
+            };
+        };
+    });
+
+
+// ng-src, ng-srcset, ng-href are interpolated
+    forEach(['src', 'srcset', 'href'], function(attrName) {
+        var normalized = directiveNormalize('ng-' + attrName);
+        ngAttributeAliasDirectives[normalized] = function() {
+            return {
+                priority: 99, // it needs to run after the attributes are interpolated
+                link: function(scope, element, attr) {
+                    attr.$observe(normalized, function(value) {
+                        if (!value)
+                            return;
+
+                        attr.$set(attrName, value);
+
+                        // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
+                        // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
+                        // to set the property as well to achieve the desired effect.
+                        // we use attr[attrName] value since $set can sanitize the url.
+                        if (msie) element.prop(attrName, attr[attrName]);
+                    });
+                }
+            };
+        };
+    });
+
+    /* global -nullFormCtrl */
+    var nullFormCtrl = {
+        $addControl: noop,
+        $removeControl: noop,
+        $setValidity: noop,
+        $setDirty: noop,
+        $setPristine: noop
+    };
+
+    /**
+     * @ngdoc object
+     * @name ng.directive:form.FormController
+     *
+     * @property {boolean} $pristine True if user has not interacted with the form yet.
+     * @property {boolean} $dirty True if user has already interacted with the form.
+     * @property {boolean} $valid True if all of the containing forms and controls are valid.
+     * @property {boolean} $invalid True if at least one containing control or form is invalid.
+     *
+     * @property {Object} $error Is an object hash, containing references to all invalid controls or
+     *  forms, where:
+     *
+     *  - keys are validation tokens (error names) — such as `required`, `url` or `email`,
+     *  - values are arrays of controls or forms that are invalid with given error.
+     *
+     * @description
+     * `FormController` keeps track of all its controls and nested forms as well as state of them,
+     * such as being valid/invalid or dirty/pristine.
+     *
+     * Each {@link ng.directive:form form} directive creates an instance
+     * of `FormController`.
+     *
+     */
+//asks for $scope to fool the BC controller module
+    FormController.$inject = ['$element', '$attrs', '$scope'];
+    function FormController(element, attrs) {
+        var form = this,
+                parentForm = element.parent().controller('form') || nullFormCtrl,
+                invalidCount = 0, // used to easily determine if we are valid
+                errors = form.$error = {},
+                controls = [];
+
+        // init state
+        form.$name = attrs.name || attrs.ngForm;
+        form.$dirty = false;
+        form.$pristine = true;
+        form.$valid = true;
+        form.$invalid = false;
+
+        parentForm.$addControl(form);
+
+        // Setup initial state of the control
+        element.addClass(PRISTINE_CLASS);
+        toggleValidCss(true);
+
+        // convenience method for easy toggling of classes
+        function toggleValidCss(isValid, validationErrorKey) {
+            validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
+            element.
+                    removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey).
+                    addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
+        }
+
+        /**
+         * @ngdoc function
+         * @name ng.directive:form.FormController#$addControl
+         * @methodOf ng.directive:form.FormController
+         *
+         * @description
+         * Register a control with the form.
+         *
+         * Input elements using ngModelController do this automatically when they are linked.
+         */
+        form.$addControl = function(control) {
+            // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
+            // and not added to the scope.  Now we throw an error.
+            assertNotHasOwnProperty(control.$name, 'input');
+            controls.push(control);
+
+            if (control.$name) {
+                form[control.$name] = control;
+            }
+        };
+
+        /**
+         * @ngdoc function
+         * @name ng.directive:form.FormController#$removeControl
+         * @methodOf ng.directive:form.FormController
+         *
+         * @description
+         * Deregister a control from the form.
+         *
+         * Input elements using ngModelController do this automatically when they are destroyed.
+         */
+        form.$removeControl = function(control) {
+            if (control.$name && form[control.$name] === control) {
+                delete form[control.$name];
+            }
+            forEach(errors, function(queue, validationToken) {
+                form.$setValidity(validationToken, true, control);
+            });
+
+            arrayRemove(controls, control);
+        };
+
+        /**
+         * @ngdoc function
+         * @name ng.directive:form.FormController#$setValidity
+         * @methodOf ng.directive:form.FormController
+         *
+         * @description
+         * Sets the validity of a form control.
+         *
+         * This method will also propagate to parent forms.
+         */
+        form.$setValidity = function(validationToken, isValid, control) {
+            var queue = errors[validationToken];
+
+            if (isValid) {
+                if (queue) {
+                    arrayRemove(queue, control);
+                    if (!queue.length) {
+                        invalidCount--;
+                        if (!invalidCount) {
+                            toggleValidCss(isValid);
+                            form.$valid = true;
+                            form.$invalid = false;
+                        }
+                        errors[validationToken] = false;
+                        toggleValidCss(true, validationToken);
+                        parentForm.$setValidity(validationToken, true, form);
+                    }
+                }
+
+            } else {
+                if (!invalidCount) {
+                    toggleValidCss(isValid);
+                }
+                if (queue) {
+                    if (includes(queue, control)) return;
+                } else {
+                    errors[validationToken] = queue = [];
+                    invalidCount++;
+                    toggleValidCss(false, validationToken);
+                    parentForm.$setValidity(validationToken, false, form);
+                }
+                queue.push(control);
+
+                form.$valid = false;
+                form.$invalid = true;
+            }
+        };
+
+        /**
+         * @ngdoc function
+         * @name ng.directive:form.FormController#$setDirty
+         * @methodOf ng.directive:form.FormController
+         *
+         * @description
+         * Sets the form to a dirty state.
+         *
+         * This method can be called to add the 'ng-dirty' class and set the form to a dirty
+         * state (ng-dirty class). This method will also propagate to parent forms.
+         */
+        form.$setDirty = function() {
+            element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS);
+            form.$dirty = true;
+            form.$pristine = false;
+            parentForm.$setDirty();
+        };
+
+        /**
+         * @ngdoc function
+         * @name ng.directive:form.FormController#$setPristine
+         * @methodOf ng.directive:form.FormController
+         *
+         * @description
+         * Sets the form to its pristine state.
+         *
+         * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
+         * state (ng-pristine class). This method will also propagate to all the controls contained
+         * in this form.
+         *
+         * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
+         * saving or resetting it.
+         */
+        form.$setPristine = function () {
+            element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
+            form.$dirty = false;
+            form.$pristine = true;
+            forEach(controls, function(control) {
+                control.$setPristine();
+            });
+        };
+    }
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngForm
+     * @restrict EAC
+     *
+     * @description
+     * Nestable alias of {@link ng.directive:form `form`} directive. HTML
+     * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
+     * sub-group of controls needs to be determined.
+     *
+     * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
+     *                       related scope, under this name.
+     *
+     */
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:form
+     * @restrict E
+     *
+     * @description
+     * Directive that instantiates
+     * {@link ng.directive:form.FormController FormController}.
+     *
+     * If the `name` attribute is specified, the form controller is published onto the current scope under
+     * this name.
+     *
+     * # Alias: {@link ng.directive:ngForm `ngForm`}
+     *
+     * In Angular forms can be nested. This means that the outer form is valid when all of the child
+     * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
+     * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
+     * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
+     * using Angular validation directives in forms that are dynamically generated using the
+     * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
+     * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
+     * `ngForm` directive and nest these in an outer `form` element.
+     *
+     *
+     * # CSS classes
+     *  - `ng-valid` Is set if the form is valid.
+     *  - `ng-invalid` Is set if the form is invalid.
+     *  - `ng-pristine` Is set if the form is pristine.
+     *  - `ng-dirty` Is set if the form is dirty.
+     *
+     *
+     * # Submitting a form and preventing the default action
+     *
+     * Since the role of forms in client-side Angular applications is different than in classical
+     * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
+     * page reload that sends the data to the server. Instead some javascript logic should be triggered
+     * to handle the form submission in an application-specific way.
+     *
+     * For this reason, Angular prevents the default action (form submission to the server) unless the
+     * `<form>` element has an `action` attribute specified.
+     *
+     * You can use one of the following two ways to specify what javascript method should be called when
+     * a form is submitted:
+     *
+     * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
+     * - {@link ng.directive:ngClick ngClick} directive on the first
+     *  button or input field of type submit (input[type=submit])
+     *
+     * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
+     * or {@link ng.directive:ngClick ngClick} directives.
+     * This is because of the following form submission rules in the HTML specification:
+     *
+     * - If a form has only one input field then hitting enter in this field triggers form submit
+     * (`ngSubmit`)
+     * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
+     * doesn't trigger submit
+     * - if a form has one or more input fields and one or more buttons or input[type=submit] then
+     * hitting enter in any of the input fields will trigger the click handler on the *first* button or
+     * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
+     *
+     * @param {string=} name Name of the form. If specified, the form controller will be published into
+     *                       related scope, under this name.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+           $scope.userType = 'guest';
+         }
+     </script>
+     <form name="myForm" ng-controller="Ctrl">
+     userType: <input name="input" ng-model="userType" required>
+     <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
+     <tt>userType = {{userType}}</tt><br>
+     <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br>
+     <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br>
+     <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
+     <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
+     </form>
+     </doc:source>
+     <doc:scenario>
+     it('should initialize to model', function() {
+         expect(binding('userType')).toEqual('guest');
+         expect(binding('myForm.input.$valid')).toEqual('true');
+        });
+
+     it('should be invalid if empty', function() {
+         input('userType').enter('');
+         expect(binding('userType')).toEqual('');
+         expect(binding('myForm.input.$valid')).toEqual('false');
+        });
+     </doc:scenario>
+     </doc:example>
+     */
+    var formDirectiveFactory = function(isNgForm) {
+        return ['$timeout', function($timeout) {
+            var formDirective = {
+                name: 'form',
+                restrict: isNgForm ? 'EAC' : 'E',
+                controller: FormController,
+                compile: function() {
+                    return {
+                        pre: function(scope, formElement, attr, controller) {
+                            if (!attr.action) {
+                                // we can't use jq events because if a form is destroyed during submission the default
+                                // action is not prevented. see #1238
+                                //
+                                // IE 9 is not affected because it doesn't fire a submit event and try to do a full
+                                // page reload if the form was destroyed by submission of the form via a click handler
+                                // on a button in the form. Looks like an IE9 specific bug.
+                                var preventDefaultListener = function(event) {
+                                    event.preventDefault
+                                            ? event.preventDefault()
+                                            : event.returnValue = false; // IE
+                                };
+
+                                addEventListenerFn(formElement[0], 'submit', preventDefaultListener);
+
+                                // unregister the preventDefault listener so that we don't not leak memory but in a
+                                // way that will achieve the prevention of the default action.
+                                formElement.on('$destroy', function() {
+                                    $timeout(function() {
+                                        removeEventListenerFn(formElement[0], 'submit', preventDefaultListener);
+                                    }, 0, false);
+                                });
+                            }
+
+                            var parentFormCtrl = formElement.parent().controller('form'),
+                                    alias = attr.name || attr.ngForm;
+
+                            if (alias) {
+                                setter(scope, alias, controller, alias);
+                            }
+                            if (parentFormCtrl) {
+                                formElement.on('$destroy', function() {
+                                    parentFormCtrl.$removeControl(controller);
+                                    if (alias) {
+                                        setter(scope, alias, undefined, alias);
+                                    }
+                                    extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
+                                });
+                            }
+                        }
+                    };
+                }
+            };
+
+            return formDirective;
+        }];
+    };
+
+    var formDirective = formDirectiveFactory();
+    var ngFormDirective = formDirectiveFactory(true);
+
+    /* global
+
+     -VALID_CLASS,
+     -INVALID_CLASS,
+     -PRISTINE_CLASS,
+     -DIRTY_CLASS
+     */
+
+    var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
+    var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$/;
+    var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
+
+    var inputType = {
+
+        /**
+         * @ngdoc inputType
+         * @name ng.directive:input.text
+         *
+         * @description
+         * Standard HTML text input with angular data binding.
+         *
+         * @param {string} ngModel Assignable angular expression to data-bind to.
+         * @param {string=} name Property name of the form under which the control is published.
+         * @param {string=} required Adds `required` validation error key if the value is not entered.
+         * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
+         *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
+         *    `required` when you want to data-bind to the `required` attribute.
+         * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
+         *    minlength.
+         * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
+         *    maxlength.
+         * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
+         *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
+         *    patterns defined as scope expressions.
+         * @param {string=} ngChange Angular expression to be executed when input changes due to user
+         *    interaction with the input element.
+         * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
+         *
+         * @example
+         <doc:example>
+         <doc:source>
+         <script>
+         function Ctrl($scope) {
+             $scope.text = 'guest';
+             $scope.word = /^\s*\w*\s*$/;
+           }
+         </script>
+         <form name="myForm" ng-controller="Ctrl">
+         Single word: <input type="text" name="input" ng-model="text"
+         ng-pattern="word" required ng-trim="false">
+         <span class="error" ng-show="myForm.input.$error.required">
+         Required!</span>
+         <span class="error" ng-show="myForm.input.$error.pattern">
+         Single word only!</span>
+
+         <tt>text = {{text}}</tt><br/>
+         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
+         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
+         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
+         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
+         </form>
+         </doc:source>
+         <doc:scenario>
+         it('should initialize to model', function() {
+            expect(binding('text')).toEqual('guest');
+            expect(binding('myForm.input.$valid')).toEqual('true');
+          });
+
+         it('should be invalid if empty', function() {
+            input('text').enter('');
+            expect(binding('text')).toEqual('');
+            expect(binding('myForm.input.$valid')).toEqual('false');
+          });
+
+         it('should be invalid if multi word', function() {
+            input('text').enter('hello world');
+            expect(binding('myForm.input.$valid')).toEqual('false');
+          });
+
+         it('should not be trimmed', function() {
+            input('text').enter('untrimmed ');
+            expect(binding('text')).toEqual('untrimmed ');
+            expect(binding('myForm.input.$valid')).toEqual('true');
+          });
+         </doc:scenario>
+         </doc:example>
+         */
+        'text': textInputType,
+
+
+        /**
+         * @ngdoc inputType
+         * @name ng.directive:input.number
+         *
+         * @description
+         * Text input with number validation and transformation. Sets the `number` validation
+         * error if not a valid number.
+         *
+         * @param {string} ngModel Assignable angular expression to data-bind to.
+         * @param {string=} name Property name of the form under which the control is published.
+         * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
+         * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
+         * @param {string=} required Sets `required` validation error key if the value is not entered.
+         * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
+         *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
+         *    `required` when you want to data-bind to the `required` attribute.
+         * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
+         *    minlength.
+         * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
+         *    maxlength.
+         * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
+         *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
+         *    patterns defined as scope expressions.
+         * @param {string=} ngChange Angular expression to be executed when input changes due to user
+         *    interaction with the input element.
+         *
+         * @example
+         <doc:example>
+         <doc:source>
+         <script>
+         function Ctrl($scope) {
+             $scope.value = 12;
+           }
+         </script>
+         <form name="myForm" ng-controller="Ctrl">
+         Number: <input type="number" name="input" ng-model="value"
+         min="0" max="99" required>
+         <span class="error" ng-show="myForm.input.$error.required">
+         Required!</span>
+         <span class="error" ng-show="myForm.input.$error.number">
+         Not valid number!</span>
+         <tt>value = {{value}}</tt><br/>
+         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
+         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
+         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
+         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
+         </form>
+         </doc:source>
+         <doc:scenario>
+         it('should initialize to model', function() {
+           expect(binding('value')).toEqual('12');
+           expect(binding('myForm.input.$valid')).toEqual('true');
+          });
+
+         it('should be invalid if empty', function() {
+           input('value').enter('');
+           expect(binding('value')).toEqual('');
+           expect(binding('myForm.input.$valid')).toEqual('false');
+          });
+
+         it('should be invalid if over max', function() {
+           input('value').enter('123');
+           expect(binding('value')).toEqual('');
+           expect(binding('myForm.input.$valid')).toEqual('false');
+          });
+         </doc:scenario>
+         </doc:example>
+         */
+        'number': numberInputType,
+
+
+        /**
+         * @ngdoc inputType
+         * @name ng.directive:input.url
+         *
+         * @description
+         * Text input with URL validation. Sets the `url` validation error key if the content is not a
+         * valid URL.
+         *
+         * @param {string} ngModel Assignable angular expression to data-bind to.
+         * @param {string=} name Property name of the form under which the control is published.
+         * @param {string=} required Sets `required` validation error key if the value is not entered.
+         * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
+         *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
+         *    `required` when you want to data-bind to the `required` attribute.
+         * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
+         *    minlength.
+         * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
+         *    maxlength.
+         * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
+         *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
+         *    patterns defined as scope expressions.
+         * @param {string=} ngChange Angular expression to be executed when input changes due to user
+         *    interaction with the input element.
+         *
+         * @example
+         <doc:example>
+         <doc:source>
+         <script>
+         function Ctrl($scope) {
+             $scope.text = 'http://google.com';
+           }
+         </script>
+         <form name="myForm" ng-controller="Ctrl">
+         URL: <input type="url" name="input" ng-model="text" required>
+         <span class="error" ng-show="myForm.input.$error.required">
+         Required!</span>
+         <span class="error" ng-show="myForm.input.$error.url">
+         Not valid url!</span>
+         <tt>text = {{text}}</tt><br/>
+         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
+         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
+         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
+         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
+         <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
+         </form>
+         </doc:source>
+         <doc:scenario>
+         it('should initialize to model', function() {
+            expect(binding('text')).toEqual('http://google.com');
+            expect(binding('myForm.input.$valid')).toEqual('true');
+          });
+
+         it('should be invalid if empty', function() {
+            input('text').enter('');
+            expect(binding('text')).toEqual('');
+            expect(binding('myForm.input.$valid')).toEqual('false');
+          });
+
+         it('should be invalid if not url', function() {
+            input('text').enter('xxx');
+            expect(binding('myForm.input.$valid')).toEqual('false');
+          });
+         </doc:scenario>
+         </doc:example>
+         */
+        'url': urlInputType,
+
+
+        /**
+         * @ngdoc inputType
+         * @name ng.directive:input.email
+         *
+         * @description
+         * Text input with email validation. Sets the `email` validation error key if not a valid email
+         * address.
+         *
+         * @param {string} ngModel Assignable angular expression to data-bind to.
+         * @param {string=} name Property name of the form under which the control is published.
+         * @param {string=} required Sets `required` validation error key if the value is not entered.
+         * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
+         *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
+         *    `required` when you want to data-bind to the `required` attribute.
+         * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
+         *    minlength.
+         * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
+         *    maxlength.
+         * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
+         *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
+         *    patterns defined as scope expressions.
+         * @param {string=} ngChange Angular expression to be executed when input changes due to user
+         *    interaction with the input element.
+         *
+         * @example
+         <doc:example>
+         <doc:source>
+         <script>
+         function Ctrl($scope) {
+             $scope.text = 'me@example.com';
+         }
+         </script>
+         <form name="myForm" ng-controller="Ctrl">
+         Email: <input type="email" name="input" ng-model="text" required>
+         <span class="error" ng-show="myForm.input.$error.required">
+         Required!</span>
+         <span class="error" ng-show="myForm.input.$error.email">
+         Not valid email!</span>
+         <tt>text = {{text}}</tt><br/>
+         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
+         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
+         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
+         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
+         <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
+         </form>
+         </doc:source>
+         <doc:scenario>
+         it('should initialize to model', function() {
+            expect(binding('text')).toEqual('me@example.com');
+         expect(binding('myForm.input.$valid')).toEqual('true');
+         });
+
+         it('should be invalid if empty', function() {
+            input('text').enter('');
+            expect(binding('text')).toEqual('');
+            expect(binding('myForm.input.$valid')).toEqual('false');
+          });
+
+         it('should be invalid if not email', function() {
+            input('text').enter('xxx');
+            expect(binding('myForm.input.$valid')).toEqual('false');
+          });
+         </doc:scenario>
+         </doc:example>
+         */
+        'email': emailInputType,
+
+
+        /**
+         * @ngdoc inputType
+         * @name ng.directive:input.radio
+         *
+         * @description
+         * HTML radio button.
+         *
+         * @param {string} ngModel Assignable angular expression to data-bind to.
+         * @param {string} value The value to which the expression should be set when selected.
+         * @param {string=} name Property name of the form under which the control is published.
+         * @param {string=} ngChange Angular expression to be executed when input changes due to user
+         *    interaction with the input element.
+         *
+         * @example
+         <doc:example>
+         <doc:source>
+         <script>
+         function Ctrl($scope) {
+             $scope.color = 'blue';
+           }
+         </script>
+         <form name="myForm" ng-controller="Ctrl">
+         <input type="radio" ng-model="color" value="red">  Red <br/>
+         <input type="radio" ng-model="color" value="green"> Green <br/>
+         <input type="radio" ng-model="color" value="blue"> Blue <br/>
+         <tt>color = {{color}}</tt><br/>
+         </form>
+         </doc:source>
+         <doc:scenario>
+         it('should change state', function() {
+            expect(binding('color')).toEqual('blue');
+
+            input('color').select('red');
+            expect(binding('color')).toEqual('red');
+          });
+         </doc:scenario>
+         </doc:example>
+         */
+        'radio': radioInputType,
+
+
+        /**
+         * @ngdoc inputType
+         * @name ng.directive:input.checkbox
+         *
+         * @description
+         * HTML checkbox.
+         *
+         * @param {string} ngModel Assignable angular expression to data-bind to.
+         * @param {string=} name Property name of the form under which the control is published.
+         * @param {string=} ngTrueValue The value to which the expression should be set when selected.
+         * @param {string=} ngFalseValue The value to which the expression should be set when not selected.
+         * @param {string=} ngChange Angular expression to be executed when input changes due to user
+         *    interaction with the input element.
+         *
+         * @example
+         <doc:example>
+         <doc:source>
+         <script>
+         function Ctrl($scope) {
+             $scope.value1 = true;
+             $scope.value2 = 'YES'
+           }
+         </script>
+         <form name="myForm" ng-controller="Ctrl">
+         Value1: <input type="checkbox" ng-model="value1"> <br/>
+         Value2: <input type="checkbox" ng-model="value2"
+         ng-true-value="YES" ng-false-value="NO"> <br/>
+         <tt>value1 = {{value1}}</tt><br/>
+         <tt>value2 = {{value2}}</tt><br/>
+         </form>
+         </doc:source>
+         <doc:scenario>
+         it('should change state', function() {
+            expect(binding('value1')).toEqual('true');
+            expect(binding('value2')).toEqual('YES');
+
+            input('value1').check();
+            input('value2').check();
+            expect(binding('value1')).toEqual('false');
+            expect(binding('value2')).toEqual('NO');
+          });
+         </doc:scenario>
+         </doc:example>
+         */
+        'checkbox': checkboxInputType,
+
+        'hidden': noop,
+        'button': noop,
+        'submit': noop,
+        'reset': noop
+    };
+
+
+    function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
+        // In composition mode, users are still inputing intermediate text buffer,
+        // hold the listener until composition is done.
+        // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
+        var composing = false;
+
+        element.on('compositionstart', function() {
+            composing = true;
+        });
+
+        element.on('compositionend', function() {
+            composing = false;
+        });
+
+        var listener = function() {
+            if (composing) return;
+            var value = element.val();
+
+            // By default we will trim the value
+            // If the attribute ng-trim exists we will avoid trimming
+            // e.g. <input ng-model="foo" ng-trim="false">
+            if (toBoolean(attr.ngTrim || 'T')) {
+                value = trim(value);
+            }
+
+            if (ctrl.$viewValue !== value) {
+                scope.$apply(function() {
+                    ctrl.$setViewValue(value);
+                });
+            }
+        };
+
+        // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
+        // input event on backspace, delete or cut
+        if ($sniffer.hasEvent('input')) {
+            element.on('input', listener);
+        } else {
+            var timeout;
+
+            var deferListener = function() {
+                if (!timeout) {
+                    timeout = $browser.defer(function() {
+                        listener();
+                        timeout = null;
+                    });
+                }
+            };
+
+            element.on('keydown', function(event) {
+                var key = event.keyCode;
+
+                // ignore
+                //    command            modifiers                   arrows
+                if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
+
+                deferListener();
+            });
+
+            // if user paste into input using mouse, we need "change" event to catch it
+            element.on('change', listener);
+
+            // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
+            if ($sniffer.hasEvent('paste')) {
+                element.on('paste cut', deferListener);
+            }
+        }
+
+
+        ctrl.$render = function() {
+            element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
+        };
+
+        // pattern validator
+        var pattern = attr.ngPattern,
+                patternValidator,
+                match;
+
+        var validate = function(regexp, value) {
+            if (ctrl.$isEmpty(value) || regexp.test(value)) {
+                ctrl.$setValidity('pattern', true);
+                return value;
+            } else {
+                ctrl.$setValidity('pattern', false);
+                return undefined;
+            }
+        };
+
+        if (pattern) {
+            match = pattern.match(/^\/(.*)\/([gim]*)$/);
+            if (match) {
+                pattern = new RegExp(match[1], match[2]);
+                patternValidator = function(value) {
+                    return validate(pattern, value);
+                };
+            } else {
+                patternValidator = function(value) {
+                    var patternObj = scope.$eval(pattern);
+
+                    if (!patternObj || !patternObj.test) {
+                        throw minErr('ngPattern')('noregexp',
+                                'Expected {0} to be a RegExp but was {1}. Element: {2}', pattern,
+                                patternObj, startingTag(element));
+                    }
+                    return validate(patternObj, value);
+                };
+            }
+
+            ctrl.$formatters.push(patternValidator);
+            ctrl.$parsers.push(patternValidator);
+        }
+
+        // min length validator
+        if (attr.ngMinlength) {
+            var minlength = int(attr.ngMinlength);
+            var minLengthValidator = function(value) {
+                if (!ctrl.$isEmpty(value) && value.length < minlength) {
+                    ctrl.$setValidity('minlength', false);
+                    return undefined;
+                } else {
+                    ctrl.$setValidity('minlength', true);
+                    return value;
+                }
+            };
+
+            ctrl.$parsers.push(minLengthValidator);
+            ctrl.$formatters.push(minLengthValidator);
+        }
+
+        // max length validator
+        if (attr.ngMaxlength) {
+            var maxlength = int(attr.ngMaxlength);
+            var maxLengthValidator = function(value) {
+                if (!ctrl.$isEmpty(value) && value.length > maxlength) {
+                    ctrl.$setValidity('maxlength', false);
+                    return undefined;
+                } else {
+                    ctrl.$setValidity('maxlength', true);
+                    return value;
+                }
+            };
+
+            ctrl.$parsers.push(maxLengthValidator);
+            ctrl.$formatters.push(maxLengthValidator);
+        }
+    }
+
+    function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
+        textInputType(scope, element, attr, ctrl, $sniffer, $browser);
+
+        ctrl.$parsers.push(function(value) {
+            var empty = ctrl.$isEmpty(value);
+            if (empty || NUMBER_REGEXP.test(value)) {
+                ctrl.$setValidity('number', true);
+                return value === '' ? null : (empty ? value : parseFloat(value));
+            } else {
+                ctrl.$setValidity('number', false);
+                return undefined;
+            }
+        });
+
+        ctrl.$formatters.push(function(value) {
+            return ctrl.$isEmpty(value) ? '' : '' + value;
+        });
+
+        if (attr.min) {
+            var minValidator = function(value) {
+                var min = parseFloat(attr.min);
+                if (!ctrl.$isEmpty(value) && value < min) {
+                    ctrl.$setValidity('min', false);
+                    return undefined;
+                } else {
+                    ctrl.$setValidity('min', true);
+                    return value;
+                }
+            };
+
+            ctrl.$parsers.push(minValidator);
+            ctrl.$formatters.push(minValidator);
+        }
+
+        if (attr.max) {
+            var maxValidator = function(value) {
+                var max = parseFloat(attr.max);
+                if (!ctrl.$isEmpty(value) && value > max) {
+                    ctrl.$setValidity('max', false);
+                    return undefined;
+                } else {
+                    ctrl.$setValidity('max', true);
+                    return value;
+                }
+            };
+
+            ctrl.$parsers.push(maxValidator);
+            ctrl.$formatters.push(maxValidator);
+        }
+
+        ctrl.$formatters.push(function(value) {
+
+            if (ctrl.$isEmpty(value) || isNumber(value)) {
+                ctrl.$setValidity('number', true);
+                return value;
+            } else {
+                ctrl.$setValidity('number', false);
+                return undefined;
+            }
+        });
+    }
+
+    function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
+        textInputType(scope, element, attr, ctrl, $sniffer, $browser);
+
+        var urlValidator = function(value) {
+            if (ctrl.$isEmpty(value) || URL_REGEXP.test(value)) {
+                ctrl.$setValidity('url', true);
+                return value;
+            } else {
+                ctrl.$setValidity('url', false);
+                return undefined;
+            }
+        };
+
+        ctrl.$formatters.push(urlValidator);
+        ctrl.$parsers.push(urlValidator);
+    }
+
+    function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
+        textInputType(scope, element, attr, ctrl, $sniffer, $browser);
+
+        var emailValidator = function(value) {
+            if (ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value)) {
+                ctrl.$setValidity('email', true);
+                return value;
+            } else {
+                ctrl.$setValidity('email', false);
+                return undefined;
+            }
+        };
+
+        ctrl.$formatters.push(emailValidator);
+        ctrl.$parsers.push(emailValidator);
+    }
+
+    function radioInputType(scope, element, attr, ctrl) {
+        // make the name unique, if not defined
+        if (isUndefined(attr.name)) {
+            element.attr('name', nextUid());
+        }
+
+        element.on('click', function() {
+            if (element[0].checked) {
+                scope.$apply(function() {
+                    ctrl.$setViewValue(attr.value);
+                });
+            }
+        });
+
+        ctrl.$render = function() {
+            var value = attr.value;
+            element[0].checked = (value == ctrl.$viewValue);
+        };
+
+        attr.$observe('value', ctrl.$render);
+    }
+
+    function checkboxInputType(scope, element, attr, ctrl) {
+        var trueValue = attr.ngTrueValue,
+                falseValue = attr.ngFalseValue;
+
+        if (!isString(trueValue)) trueValue = true;
+        if (!isString(falseValue)) falseValue = false;
+
+        element.on('click', function() {
+            scope.$apply(function() {
+                ctrl.$setViewValue(element[0].checked);
+            });
+        });
+
+        ctrl.$render = function() {
+            element[0].checked = ctrl.$viewValue;
+        };
+
+        // Override the standard `$isEmpty` because a value of `false` means empty in a checkbox.
+        ctrl.$isEmpty = function(value) {
+            return value !== trueValue;
+        };
+
+        ctrl.$formatters.push(function(value) {
+            return value === trueValue;
+        });
+
+        ctrl.$parsers.push(function(value) {
+            return value ? trueValue : falseValue;
+        });
+    }
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:textarea
+     * @restrict E
+     *
+     * @description
+     * HTML textarea element control with angular data-binding. The data-binding and validation
+     * properties of this element are exactly the same as those of the
+     * {@link ng.directive:input input element}.
+     *
+     * @param {string} ngModel Assignable angular expression to data-bind to.
+     * @param {string=} name Property name of the form under which the control is published.
+     * @param {string=} required Sets `required` validation error key if the value is not entered.
+     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
+     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
+     *    `required` when you want to data-bind to the `required` attribute.
+     * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
+     *    minlength.
+     * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
+     *    maxlength.
+     * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
+     *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
+     *    patterns defined as scope expressions.
+     * @param {string=} ngChange Angular expression to be executed when input changes due to user
+     *    interaction with the input element.
+     */
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:input
+     * @restrict E
+     *
+     * @description
+     * HTML input element control with angular data-binding. Input control follows HTML5 input types
+     * and polyfills the HTML5 validation behavior for older browsers.
+     *
+     * @param {string} ngModel Assignable angular expression to data-bind to.
+     * @param {string=} name Property name of the form under which the control is published.
+     * @param {string=} required Sets `required` validation error key if the value is not entered.
+     * @param {boolean=} ngRequired Sets `required` attribute if set to true
+     * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
+     *    minlength.
+     * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
+     *    maxlength.
+     * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
+     *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
+     *    patterns defined as scope expressions.
+     * @param {string=} ngChange Angular expression to be executed when input changes due to user
+     *    interaction with the input element.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+           $scope.user = {name: 'guest', last: 'visitor'};
+         }
+     </script>
+     <div ng-controller="Ctrl">
+     <form name="myForm">
+     User name: <input type="text" name="userName" ng-model="user.name" required>
+     <span class="error" ng-show="myForm.userName.$error.required">
+     Required!</span><br>
+     Last name: <input type="text" name="lastName" ng-model="user.last"
+     ng-minlength="3" ng-maxlength="10">
+     <span class="error" ng-show="myForm.lastName.$error.minlength">
+     Too short!</span>
+     <span class="error" ng-show="myForm.lastName.$error.maxlength">
+     Too long!</span><br>
+     </form>
+     <hr>
+     <tt>user = {{user}}</tt><br/>
+     <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
+     <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
+     <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
+     <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
+     <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
+     <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
+     <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
+     <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should initialize to model', function() {
+          expect(binding('user')).toEqual('{"name":"guest","last":"visitor"}');
+          expect(binding('myForm.userName.$valid')).toEqual('true');
+          expect(binding('myForm.$valid')).toEqual('true');
+        });
+
+     it('should be invalid if empty when required', function() {
+          input('user.name').enter('');
+          expect(binding('user')).toEqual('{"last":"visitor"}');
+          expect(binding('myForm.userName.$valid')).toEqual('false');
+          expect(binding('myForm.$valid')).toEqual('false');
+        });
+
+     it('should be valid if empty when min length is set', function() {
+          input('user.last').enter('');
+          expect(binding('user')).toEqual('{"name":"guest","last":""}');
+          expect(binding('myForm.lastName.$valid')).toEqual('true');
+          expect(binding('myForm.$valid')).toEqual('true');
+        });
+
+     it('should be invalid if less than required min length', function() {
+          input('user.last').enter('xx');
+          expect(binding('user')).toEqual('{"name":"guest"}');
+          expect(binding('myForm.lastName.$valid')).toEqual('false');
+          expect(binding('myForm.lastName.$error')).toMatch(/minlength/);
+          expect(binding('myForm.$valid')).toEqual('false');
+        });
+
+     it('should be invalid if longer than max length', function() {
+          input('user.last').enter('some ridiculously long name');
+          expect(binding('user'))
+            .toEqual('{"name":"guest"}');
+          expect(binding('myForm.lastName.$valid')).toEqual('false');
+          expect(binding('myForm.lastName.$error')).toMatch(/maxlength/);
+          expect(binding('myForm.$valid')).toEqual('false');
+        });
+     </doc:scenario>
+     </doc:example>
+     */
+    var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) {
+        return {
+            restrict: 'E',
+            require: '?ngModel',
+            link: function(scope, element, attr, ctrl) {
+                if (ctrl) {
+                    (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer,
+                            $browser);
+                }
+            }
+        };
+    }];
+
+    var VALID_CLASS = 'ng-valid',
+            INVALID_CLASS = 'ng-invalid',
+            PRISTINE_CLASS = 'ng-pristine',
+            DIRTY_CLASS = 'ng-dirty';
+
+    /**
+     * @ngdoc object
+     * @name ng.directive:ngModel.NgModelController
+     *
+     * @property {string} $viewValue Actual string value in the view.
+     * @property {*} $modelValue The value in the model, that the control is bound to.
+     * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
+     the control reads value from the DOM.  Each function is called, in turn, passing the value
+     through to the next. Used to sanitize / convert the value as well as validation.
+     For validation, the parsers should update the validity state using
+     {@link ng.directive:ngModel.NgModelController#methods_$setValidity $setValidity()},
+     and return `undefined` for invalid values.
+
+     *
+     * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
+     the model value changes. Each function is called, in turn, passing the value through to the
+     next. Used to format / convert values for display in the control and validation.
+     *      <pre>
+     *      function formatter(value) {
+ *        if (value) {
+ *          return value.toUpperCase();
+ *        }
+ *      }
+     *      ngModel.$formatters.push(formatter);
+     *      </pre>
+     *
+     * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
+     *     view value has changed. It is called with no arguments, and its return value is ignored.
+     *     This can be used in place of additional $watches against the model value.
+     *
+     * @property {Object} $error An object hash with all errors as keys.
+     *
+     * @property {boolean} $pristine True if user has not interacted with the control yet.
+     * @property {boolean} $dirty True if user has already interacted with the control.
+     * @property {boolean} $valid True if there is no error.
+     * @property {boolean} $invalid True if at least one error on the control.
+     *
+     * @description
+     *
+     * `NgModelController` provides API for the `ng-model` directive. The controller contains
+     * services for data-binding, validation, CSS updates, and value formatting and parsing. It
+     * purposefully does not contain any logic which deals with DOM rendering or listening to
+     * DOM events. Such DOM related logic should be provided by other directives which make use of
+     * `NgModelController` for data-binding.
+     *
+     * ## Custom Control Example
+     * This example shows how to use `NgModelController` with a custom control to achieve
+     * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
+     * collaborate together to achieve the desired result.
+     *
+     * Note that `contenteditable` is an HTML5 attribute, which tells the browser to let the element
+     * contents be edited in place by the user.  This will not work on older browsers.
+     *
+     * <example module="customControl">
+     <file name="style.css">
+     [contenteditable] {
+        border: 1px solid black;
+        background-color: white;
+        min-height: 20px;
+      }
+
+     .ng-invalid {
+        border: 1px solid red;
+      }
+
+     </file>
+     <file name="script.js">
+     angular.module('customControl', []).
+     directive('contenteditable', function() {
+          return {
+            restrict: 'A', // only activate on element attribute
+            require: '?ngModel', // get a hold of NgModelController
+            link: function(scope, element, attrs, ngModel) {
+              if(!ngModel) return; // do nothing if no ng-model
+
+              // Specify how UI should be updated
+              ngModel.$render = function() {
+                element.html(ngModel.$viewValue || '');
+              };
+
+              // Listen for change events to enable binding
+              element.on('blur keyup change', function() {
+                scope.$apply(read);
+              });
+              read(); // initialize
+
+              // Write data to the model
+              function read() {
+                var html = element.html();
+                // When we clear the content editable the browser leaves a <br> behind
+                // If strip-br attribute is provided then we strip this out
+                if( attrs.stripBr && html == '<br>' ) {
+                  html = '';
+                }
+                ngModel.$setViewValue(html);
+              }
+            }
+          };
+        });
+     </file>
+     <file name="index.html">
+     <form name="myForm">
+     <div contenteditable
+     name="myWidget" ng-model="userContent"
+     strip-br="true"
+     required>Change me!</div>
+     <span ng-show="myForm.myWidget.$error.required">Required!</span>
+     <hr>
+     <textarea ng-model="userContent"></textarea>
+     </form>
+     </file>
+     <file name="scenario.js">
+     it('should data-bind and become invalid', function() {
+        var contentEditable = element('[contenteditable]');
+
+        expect(contentEditable.text()).toEqual('Change me!');
+        input('userContent').enter('');
+        expect(contentEditable.text()).toEqual('');
+        expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/);
+      });
+     </file>
+     * </example>
+     *
+     * ## Isolated Scope Pitfall
+     *
+     * Note that if you have a directive with an isolated scope, you cannot require `ngModel`
+     * since the model value will be looked up on the isolated scope rather than the outer scope.
+     * When the directive updates the model value, calling `ngModel.$setViewValue()` the property
+     * on the outer scope will not be updated. However you can get around this by using $parent.
+     *
+     * Here is an example of this situation.  You'll notice that the first div is not updating the input.
+     * However the second div can update the input properly.
+     *
+     * <example module="badIsolatedDirective">
+     <file name="script.js">
+     angular.module('badIsolatedDirective', []).directive('isolate', function() {
+      return {
+        require: 'ngModel',
+        scope: { },
+        template: '<input ng-model="innerModel">',
+        link: function(scope, element, attrs, ngModel) {
+          scope.$watch('innerModel', function(value) {
+            console.log(value);
+            ngModel.$setViewValue(value);
+          });
+        }
+      };
+		});
+     </file>
+     <file name="index.html">
+     <input ng-model="someModel"/>
+     <div isolate ng-model="someModel"></div>
+     <div isolate ng-model="$parent.someModel"></div>
+     </file>
+     * </example>
+     *
+     *
+     */
+    var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse',
+        function($scope, $exceptionHandler, $attr, $element, $parse) {
+            this.$viewValue = Number.NaN;
+            this.$modelValue = Number.NaN;
+            this.$parsers = [];
+            this.$formatters = [];
+            this.$viewChangeListeners = [];
+            this.$pristine = true;
+            this.$dirty = false;
+            this.$valid = true;
+            this.$invalid = false;
+            this.$name = $attr.name;
+
+            var ngModelGet = $parse($attr.ngModel),
+                    ngModelSet = ngModelGet.assign;
+
+            if (!ngModelSet) {
+                throw minErr('ngModel')('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
+                        $attr.ngModel, startingTag($element));
+            }
+
+            /**
+             * @ngdoc function
+             * @name ng.directive:ngModel.NgModelController#$render
+             * @methodOf ng.directive:ngModel.NgModelController
+             *
+             * @description
+             * Called when the view needs to be updated. It is expected that the user of the ng-model
+             * directive will implement this method.
+             */
+            this.$render = noop;
+
+            /**
+             * @ngdoc function
+             * @name { ng.directive:ngModel.NgModelController#$isEmpty
+   * @methodOf ng.directive:ngModel.NgModelController
+             *
+             * @description
+             * This is called when we need to determine if the value of the input is empty.
+             *
+             * For instance, the required directive does this to work out if the input has data or not.
+             * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
+             *
+             * You can override this for input directives whose concept of being empty is different to the
+             * default. The `checkboxInputType` directive does this because in its case a value of `false`
+             * implies empty.
+             */
+            this.$isEmpty = function(value) {
+                return isUndefined(value) || value === '' || value === null || value !== value;
+            };
+
+            var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
+                    invalidCount = 0, // used to easily determine if we are valid
+                    $error = this.$error = {}; // keep invalid keys here
+
+
+            // Setup initial state of the control
+            $element.addClass(PRISTINE_CLASS);
+            toggleValidCss(true);
+
+            // convenience method for easy toggling of classes
+            function toggleValidCss(isValid, validationErrorKey) {
+                validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
+                $element.
+                        removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey).
+                        addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
+            }
+
+            /**
+             * @ngdoc function
+             * @name ng.directive:ngModel.NgModelController#$setValidity
+             * @methodOf ng.directive:ngModel.NgModelController
+             *
+             * @description
+             * Change the validity state, and notifies the form when the control changes validity. (i.e. it
+             * does not notify form if given validator is already marked as invalid).
+             *
+             * This method should be called by validators - i.e. the parser or formatter functions.
+             *
+             * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign
+             *        to `$error[validationErrorKey]=isValid` so that it is available for data-binding.
+             *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
+             *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
+             *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
+             * @param {boolean} isValid Whether the current state is valid (true) or invalid (false).
+             */
+            this.$setValidity = function(validationErrorKey, isValid) {
+                // Purposeful use of ! here to cast isValid to boolean in case it is undefined
+                // jshint -W018
+                if ($error[validationErrorKey] === !isValid) return;
+                // jshint +W018
+
+                if (isValid) {
+                    if ($error[validationErrorKey]) invalidCount--;
+                    if (!invalidCount) {
+                        toggleValidCss(true);
+                        this.$valid = true;
+                        this.$invalid = false;
+                    }
+                } else {
+                    toggleValidCss(false);
+                    this.$invalid = true;
+                    this.$valid = false;
+                    invalidCount++;
+                }
+
+                $error[validationErrorKey] = !isValid;
+                toggleValidCss(isValid, validationErrorKey);
+
+                parentForm.$setValidity(validationErrorKey, isValid, this);
+            };
+
+            /**
+             * @ngdoc function
+             * @name ng.directive:ngModel.NgModelController#$setPristine
+             * @methodOf ng.directive:ngModel.NgModelController
+             *
+             * @description
+             * Sets the control to its pristine state.
+             *
+             * This method can be called to remove the 'ng-dirty' class and set the control to its pristine
+             * state (ng-pristine class).
+             */
+            this.$setPristine = function () {
+                this.$dirty = false;
+                this.$pristine = true;
+                $element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
+            };
+
+            /**
+             * @ngdoc function
+             * @name ng.directive:ngModel.NgModelController#$setViewValue
+             * @methodOf ng.directive:ngModel.NgModelController
+             *
+             * @description
+             * Update the view value.
+             *
+             * This method should be called when the view value changes, typically from within a DOM event handler.
+             * For example {@link ng.directive:input input} and
+             * {@link ng.directive:select select} directives call it.
+             *
+             * It will update the $viewValue, then pass this value through each of the functions in `$parsers`,
+             * which includes any validators. The value that comes out of this `$parsers` pipeline, be applied to
+             * `$modelValue` and the **expression** specified in the `ng-model` attribute.
+             *
+             * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
+             *
+             * Note that calling this function does not trigger a `$digest`.
+             *
+             * @param {string} value Value from the view.
+             */
+            this.$setViewValue = function(value) {
+                this.$viewValue = value;
+
+                // change to dirty
+                if (this.$pristine) {
+                    this.$dirty = true;
+                    this.$pristine = false;
+                    $element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS);
+                    parentForm.$setDirty();
+                }
+
+                forEach(this.$parsers, function(fn) {
+                    value = fn(value);
+                });
+
+                if (this.$modelValue !== value) {
+                    this.$modelValue = value;
+                    ngModelSet($scope, value);
+                    forEach(this.$viewChangeListeners, function(listener) {
+                        try {
+                            listener();
+                        } catch(e) {
+                            $exceptionHandler(e);
+                        }
+                    });
+                }
+            };
+
+            // model -> value
+            var ctrl = this;
+
+            $scope.$watch(function ngModelWatch() {
+                var value = ngModelGet($scope);
+
+                // if scope model value and ngModel value are out of sync
+                if (ctrl.$modelValue !== value) {
+
+                    var formatters = ctrl.$formatters,
+                            idx = formatters.length;
+
+                    ctrl.$modelValue = value;
+                    while(idx--) {
+                        value = formatters[idx](value);
+                    }
+
+                    if (ctrl.$viewValue !== value) {
+                        ctrl.$viewValue = value;
+                        ctrl.$render();
+                    }
+                }
+            });
+        }];
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngModel
+     *
+     * @element input
+     *
+     * @description
+     * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
+     * property on the scope using {@link ng.directive:ngModel.NgModelController NgModelController},
+     * which is created and exposed by this directive.
+     *
+     * `ngModel` is responsible for:
+     *
+     * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
+     *   require.
+     * - Providing validation behavior (i.e. required, number, email, url).
+     * - Keeping the state of the control (valid/invalid, dirty/pristine, validation errors).
+     * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`).
+     * - Registering the control with its parent {@link ng.directive:form form}.
+     *
+     * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
+     * current scope. If the property doesn't already exist on this scope, it will be created
+     * implicitly and added to the scope.
+     *
+     * For best practices on using `ngModel`, see:
+     *
+     *  - {@link https://github.com/angular/angular.js/wiki/Understanding-Scopes}
+     *
+     * For basic examples, how to use `ngModel`, see:
+     *
+     *  - {@link ng.directive:input input}
+     *    - {@link ng.directive:input.text text}
+     *    - {@link ng.directive:input.checkbox checkbox}
+     *    - {@link ng.directive:input.radio radio}
+     *    - {@link ng.directive:input.number number}
+     *    - {@link ng.directive:input.email email}
+     *    - {@link ng.directive:input.url url}
+     *  - {@link ng.directive:select select}
+     *  - {@link ng.directive:textarea textarea}
+     *
+     */
+    var ngModelDirective = function() {
+        return {
+            require: ['ngModel', '^?form'],
+            controller: NgModelController,
+            link: function(scope, element, attr, ctrls) {
+                // notify others, especially parent forms
+
+                var modelCtrl = ctrls[0],
+                        formCtrl = ctrls[1] || nullFormCtrl;
+
+                formCtrl.$addControl(modelCtrl);
+
+                scope.$on('$destroy', function() {
+                    formCtrl.$removeControl(modelCtrl);
+                });
+            }
+        };
+    };
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngChange
+     *
+     * @description
+     * Evaluate given expression when user changes the input.
+     * The expression is not evaluated when the value change is coming from the model.
+     *
+     * Note, this directive requires `ngModel` to be present.
+     *
+     * @element input
+     * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
+     * in input value.
+     *
+     * @example
+     * <doc:example>
+     *   <doc:source>
+     *     <script>
+     *       function Controller($scope) {
+ *         $scope.counter = 0;
+ *         $scope.change = function() {
+ *           $scope.counter++;
+ *         };
+ *       }
+     *     </script>
+     *     <div ng-controller="Controller">
+     *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
+     *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
+     *       <label for="ng-change-example2">Confirmed</label><br />
+     *       debug = {{confirmed}}<br />
+     *       counter = {{counter}}
+     *     </div>
+     *   </doc:source>
+     *   <doc:scenario>
+     *     it('should evaluate the expression if changing from view', function() {
+ *       expect(binding('counter')).toEqual('0');
+ *       element('#ng-change-example1').click();
+ *       expect(binding('counter')).toEqual('1');
+ *       expect(binding('confirmed')).toEqual('true');
+ *     });
+     *
+     *     it('should not evaluate the expression if changing from model', function() {
+ *       element('#ng-change-example2').click();
+ *       expect(binding('counter')).toEqual('0');
+ *       expect(binding('confirmed')).toEqual('true');
+ *     });
+     *   </doc:scenario>
+     * </doc:example>
+     */
+    var ngChangeDirective = valueFn({
+        require: 'ngModel',
+        link: function(scope, element, attr, ctrl) {
+            ctrl.$viewChangeListeners.push(function() {
+                scope.$eval(attr.ngChange);
+            });
+        }
+    });
+
+
+    var requiredDirective = function() {
+        return {
+            require: '?ngModel',
+            link: function(scope, elm, attr, ctrl) {
+                if (!ctrl) return;
+                attr.required = true; // force truthy in case we are on non input element
+
+                var validator = function(value) {
+                    if (attr.required && ctrl.$isEmpty(value)) {
+                        ctrl.$setValidity('required', false);
+                        return;
+                    } else {
+                        ctrl.$setValidity('required', true);
+                        return value;
+                    }
+                };
+
+                ctrl.$formatters.push(validator);
+                ctrl.$parsers.unshift(validator);
+
+                attr.$observe('required', function() {
+                    validator(ctrl.$viewValue);
+                });
+            }
+        };
+    };
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngList
+     *
+     * @description
+     * Text input that converts between a delimited string and an array of strings. The delimiter
+     * can be a fixed string (by default a comma) or a regular expression.
+     *
+     * @element input
+     * @param {string=} ngList optional delimiter that should be used to split the value. If
+     *   specified in form `/something/` then the value will be converted into a regular expression.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+           $scope.names = ['igor', 'misko', 'vojta'];
+         }
+     </script>
+     <form name="myForm" ng-controller="Ctrl">
+     List: <input name="namesInput" ng-model="names" ng-list required>
+     <span class="error" ng-show="myForm.namesInput.$error.required">
+     Required!</span>
+     <br>
+     <tt>names = {{names}}</tt><br/>
+     <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
+     <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
+     <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
+     <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
+     </form>
+     </doc:source>
+     <doc:scenario>
+     it('should initialize to model', function() {
+          expect(binding('names')).toEqual('["igor","misko","vojta"]');
+          expect(binding('myForm.namesInput.$valid')).toEqual('true');
+          expect(element('span.error').css('display')).toBe('none');
+        });
+
+     it('should be invalid if empty', function() {
+          input('names').enter('');
+          expect(binding('names')).toEqual('');
+          expect(binding('myForm.namesInput.$valid')).toEqual('false');
+          expect(element('span.error').css('display')).not().toBe('none');
+        });
+     </doc:scenario>
+     </doc:example>
+     */
+    var ngListDirective = function() {
+        return {
+            require: 'ngModel',
+            link: function(scope, element, attr, ctrl) {
+                var match = /\/(.*)\//.exec(attr.ngList),
+                        separator = match && new RegExp(match[1]) || attr.ngList || ',';
+
+                var parse = function(viewValue) {
+                    // If the viewValue is invalid (say required but empty) it will be `undefined`
+                    if (isUndefined(viewValue)) return;
+
+                    var list = [];
+
+                    if (viewValue) {
+                        forEach(viewValue.split(separator), function(value) {
+                            if (value) list.push(trim(value));
+                        });
+                    }
+
+                    return list;
+                };
+
+                ctrl.$parsers.push(parse);
+                ctrl.$formatters.push(function(value) {
+                    if (isArray(value)) {
+                        return value.join(', ');
+                    }
+
+                    return undefined;
+                });
+
+                // Override the standard $isEmpty because an empty array means the input is empty.
+                ctrl.$isEmpty = function(value) {
+                    return !value || !value.length;
+                };
+            }
+        };
+    };
+
+
+    var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngValue
+     *
+     * @description
+     * Binds the given expression to the value of `input[select]` or `input[radio]`, so
+     * that when the element is selected, the `ngModel` of that element is set to the
+     * bound value.
+     *
+     * `ngValue` is useful when dynamically generating lists of radio buttons using `ng-repeat`, as
+     * shown below.
+     *
+     * @element input
+     * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
+     *   of the `input` element
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+            $scope.names = ['pizza', 'unicorns', 'robots'];
+            $scope.my = { favorite: 'unicorns' };
+          }
+     </script>
+     <form ng-controller="Ctrl">
+     <h2>Which is your favorite?</h2>
+     <label ng-repeat="name in names" for="{{name}}">
+     {{name}}
+     <input type="radio"
+     ng-model="my.favorite"
+     ng-value="name"
+     id="{{name}}"
+     name="favorite">
+     </label>
+     </span>
+     <div>You chose {{my.favorite}}</div>
+     </form>
+     </doc:source>
+     <doc:scenario>
+     it('should initialize to model', function() {
+          expect(binding('my.favorite')).toEqual('unicorns');
+        });
+     it('should bind the values to the inputs', function() {
+          input('my.favorite').select('pizza');
+          expect(binding('my.favorite')).toEqual('pizza');
+        });
+     </doc:scenario>
+     </doc:example>
+     */
+    var ngValueDirective = function() {
+        return {
+            priority: 100,
+            compile: function(tpl, tplAttr) {
+                if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
+                    return function ngValueConstantLink(scope, elm, attr) {
+                        attr.$set('value', scope.$eval(attr.ngValue));
+                    };
+                } else {
+                    return function ngValueLink(scope, elm, attr) {
+                        scope.$watch(attr.ngValue, function valueWatchAction(value) {
+                            attr.$set('value', value);
+                        });
+                    };
+                }
+            }
+        };
+    };
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngBind
+     * @restrict AC
+     *
+     * @description
+     * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
+     * with the value of a given expression, and to update the text content when the value of that
+     * expression changes.
+     *
+     * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
+     * `{{ expression }}` which is similar but less verbose.
+     *
+     * It is preferrable to use `ngBind` instead of `{{ expression }}` when a template is momentarily
+     * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
+     * element attribute, it makes the bindings invisible to the user while the page is loading.
+     *
+     * An alternative solution to this problem would be using the
+     * {@link ng.directive:ngCloak ngCloak} directive.
+     *
+     *
+     * @element ANY
+     * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
+     *
+     * @example
+     * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+           $scope.name = 'Whirled';
+         }
+     </script>
+     <div ng-controller="Ctrl">
+     Enter name: <input type="text" ng-model="name"><br>
+     Hello <span ng-bind="name"></span>!
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should check ng-bind', function() {
+         expect(using('.doc-example-live').binding('name')).toBe('Whirled');
+         using('.doc-example-live').input('name').enter('world');
+         expect(using('.doc-example-live').binding('name')).toBe('world');
+       });
+     </doc:scenario>
+     </doc:example>
+     */
+    var ngBindDirective = ngDirective(function(scope, element, attr) {
+        element.addClass('ng-binding').data('$binding', attr.ngBind);
+        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
+            // We are purposefully using == here rather than === because we want to
+            // catch when value is "null or undefined"
+            // jshint -W041
+            element.text(value == undefined ? '' : value);
+        });
+    });
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngBindTemplate
+     *
+     * @description
+     * The `ngBindTemplate` directive specifies that the element
+     * text content should be replaced with the interpolation of the template
+     * in the `ngBindTemplate` attribute.
+     * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
+     * expressions. This directive is needed since some HTML elements
+     * (such as TITLE and OPTION) cannot contain SPAN elements.
+     *
+     * @element ANY
+     * @param {string} ngBindTemplate template of form
+     *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
+     *
+     * @example
+     * Try it here: enter text in text box and watch the greeting change.
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+           $scope.salutation = 'Hello';
+           $scope.name = 'World';
+         }
+     </script>
+     <div ng-controller="Ctrl">
+     Salutation: <input type="text" ng-model="salutation"><br>
+     Name: <input type="text" ng-model="name"><br>
+     <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should check ng-bind', function() {
+         expect(using('.doc-example-live').binding('salutation')).
+           toBe('Hello');
+         expect(using('.doc-example-live').binding('name')).
+           toBe('World');
+         using('.doc-example-live').input('salutation').enter('Greetings');
+         using('.doc-example-live').input('name').enter('user');
+         expect(using('.doc-example-live').binding('salutation')).
+           toBe('Greetings');
+         expect(using('.doc-example-live').binding('name')).
+           toBe('user');
+       });
+     </doc:scenario>
+     </doc:example>
+     */
+    var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
+        return function(scope, element, attr) {
+            // TODO: move this to scenario runner
+            var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
+            element.addClass('ng-binding').data('$binding', interpolateFn);
+            attr.$observe('ngBindTemplate', function(value) {
+                element.text(value);
+            });
+        };
+    }];
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngBindHtml
+     *
+     * @description
+     * Creates a binding that will innerHTML the result of evaluating the `expression` into the current
+     * element in a secure way.  By default, the innerHTML-ed content will be sanitized using the {@link
+     * ngSanitize.$sanitize $sanitize} service.  To utilize this functionality, ensure that `$sanitize`
+     * is available, for example, by including {@link ngSanitize} in your module's dependencies (not in
+     * core Angular.)  You may also bypass sanitization for values you know are safe. To do so, bind to
+     * an explicitly trusted value via {@link ng.$sce#methods_trustAsHtml $sce.trustAsHtml}.  See the example
+     * under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}.
+     *
+     * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
+     * will have an exception (instead of an exploit.)
+     *
+     * @element ANY
+     * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
+     *
+     * @example
+     Try it here: enter text in text box and watch the greeting change.
+
+     <example module="ngBindHtmlExample" deps="angular-sanitize.js">
+     <file name="index.html">
+     <div ng-controller="ngBindHtmlCtrl">
+     <p ng-bind-html="myHTML"></p>
+     </div>
+     </file>
+
+     <file name="script.js">
+     angular.module('ngBindHtmlExample', ['ngSanitize'])
+
+     .controller('ngBindHtmlCtrl', ['$scope', function ngBindHtmlCtrl($scope) {
+         $scope.myHTML =
+            'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>';
+       }]);
+     </file>
+
+     <file name="scenario.js">
+     it('should check ng-bind-html', function() {
+         expect(using('.doc-example-live').binding('myHTML')).
+           toBe(
+           'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>'
+           );
+       });
+     </file>
+     </example>
+     */
+    var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) {
+        return function(scope, element, attr) {
+            element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
+
+            var parsed = $parse(attr.ngBindHtml);
+            function getStringValue() { return (parsed(scope) || '').toString(); }
+
+            scope.$watch(getStringValue, function ngBindHtmlWatchAction(value) {
+                element.html($sce.getTrustedHtml(parsed(scope)) || '');
+            });
+        };
+    }];
+
+    function classDirective(name, selector) {
+        name = 'ngClass' + name;
+        return function() {
+            return {
+                restrict: 'AC',
+                link: function(scope, element, attr) {
+                    var oldVal;
+
+                    scope.$watch(attr[name], ngClassWatchAction, true);
+
+                    attr.$observe('class', function(value) {
+                        ngClassWatchAction(scope.$eval(attr[name]));
+                    });
+
+
+                    if (name !== 'ngClass') {
+                        scope.$watch('$index', function($index, old$index) {
+                            // jshint bitwise: false
+                            var mod = $index & 1;
+                            if (mod !== old$index & 1) {
+                                var classes = flattenClasses(scope.$eval(attr[name]));
+                                mod === selector ?
+                                        attr.$addClass(classes) :
+                                        attr.$removeClass(classes);
+                            }
+                        });
+                    }
+
+
+                    function ngClassWatchAction(newVal) {
+                        if (selector === true || scope.$index % 2 === selector) {
+                            var newClasses = flattenClasses(newVal || '');
+                            if(!oldVal) {
+                                attr.$addClass(newClasses);
+                            } else if(!equals(newVal,oldVal)) {
+                                attr.$updateClass(newClasses, flattenClasses(oldVal));
+                            }
+                        }
+                        oldVal = copy(newVal);
+                    }
+
+
+                    function flattenClasses(classVal) {
+                        if(isArray(classVal)) {
+                            return classVal.join(' ');
+                        } else if (isObject(classVal)) {
+                            var classes = [], i = 0;
+                            forEach(classVal, function(v, k) {
+                                if (v) {
+                                    classes.push(k);
+                                }
+                            });
+                            return classes.join(' ');
+                        }
+
+                        return classVal;
+                    }
+                }
+            };
+        };
+    }
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngClass
+     * @restrict AC
+     *
+     * @description
+     * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
+     * an expression that represents all classes to be added.
+     *
+     * The directive won't add duplicate classes if a particular class was already set.
+     *
+     * When the expression changes, the previously added classes are removed and only then the
+     * new classes are added.
+     *
+     * @animations
+     * add - happens just before the class is applied to the element
+     * remove - happens just before the class is removed from the element
+     *
+     * @element ANY
+     * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
+     *   of the evaluation can be a string representing space delimited class
+     *   names, an array, or a map of class names to boolean values. In the case of a map, the
+     *   names of the properties whose values are truthy will be added as css classes to the
+     *   element.
+     *
+     * @example Example that demonstrates basic bindings via ngClass directive.
+     <example>
+     <file name="index.html">
+     <p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p>
+     <input type="checkbox" ng-model="deleted"> deleted (apply "strike" class)<br>
+     <input type="checkbox" ng-model="important"> important (apply "bold" class)<br>
+     <input type="checkbox" ng-model="error"> error (apply "red" class)
+     <hr>
+     <p ng-class="style">Using String Syntax</p>
+     <input type="text" ng-model="style" placeholder="Type: bold strike red">
+     <hr>
+     <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
+     <input ng-model="style1" placeholder="Type: bold, strike or red"><br>
+     <input ng-model="style2" placeholder="Type: bold, strike or red"><br>
+     <input ng-model="style3" placeholder="Type: bold, strike or red"><br>
+     </file>
+     <file name="style.css">
+     .strike {
+         text-decoration: line-through;
+       }
+     .bold {
+           font-weight: bold;
+       }
+     .red {
+           color: red;
+       }
+     </file>
+     <file name="scenario.js">
+     it('should let you toggle the class', function() {
+
+         expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/bold/);
+         expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/red/);
+
+         input('important').check();
+         expect(element('.doc-example-live p:first').prop('className')).toMatch(/bold/);
+
+         input('error').check();
+         expect(element('.doc-example-live p:first').prop('className')).toMatch(/red/);
+       });
+
+     it('should let you toggle string example', function() {
+         expect(element('.doc-example-live p:nth-of-type(2)').prop('className')).toBe('');
+         input('style').enter('red');
+         expect(element('.doc-example-live p:nth-of-type(2)').prop('className')).toBe('red');
+       });
+
+     it('array example should have 3 classes', function() {
+         expect(element('.doc-example-live p:last').prop('className')).toBe('');
+         input('style1').enter('bold');
+         input('style2').enter('strike');
+         input('style3').enter('red');
+         expect(element('.doc-example-live p:last').prop('className')).toBe('bold strike red');
+       });
+     </file>
+     </example>
+
+     ## Animations
+
+     The example below demonstrates how to perform animations using ngClass.
+
+     <example animations="true">
+     <file name="index.html">
+     <input type="button" value="set" ng-click="myVar='my-class'">
+     <input type="button" value="clear" ng-click="myVar=''">
+     <br>
+     <span class="base-class" ng-class="myVar">Sample Text</span>
+     </file>
+     <file name="style.css">
+     .base-class {
+         -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
+         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
+       }
+
+     .base-class.my-class {
+         color: red;
+         font-size:3em;
+       }
+     </file>
+     <file name="scenario.js">
+     it('should check ng-class', function() {
+         expect(element('.doc-example-live span').prop('className')).not().
+           toMatch(/my-class/);
+
+         using('.doc-example-live').element(':button:first').click();
+
+         expect(element('.doc-example-live span').prop('className')).
+           toMatch(/my-class/);
+
+         using('.doc-example-live').element(':button:last').click();
+
+         expect(element('.doc-example-live span').prop('className')).not().
+           toMatch(/my-class/);
+       });
+     </file>
+     </example>
+
+
+     ## ngClass and pre-existing CSS3 Transitions/Animations
+     The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
+     Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
+     any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
+     to view the step by step details of {@link ngAnimate.$animate#methods_addclass $animate.addClass} and
+     {@link ngAnimate.$animate#methods_removeclass $animate.removeClass}.
+     */
+    var ngClassDirective = classDirective('', true);
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngClassOdd
+     * @restrict AC
+     *
+     * @description
+     * The `ngClassOdd` and `ngClassEven` directives work exactly as
+     * {@link ng.directive:ngClass ngClass}, except they work in
+     * conjunction with `ngRepeat` and take effect only on odd (even) rows.
+     *
+     * This directive can be applied only within the scope of an
+     * {@link ng.directive:ngRepeat ngRepeat}.
+     *
+     * @element ANY
+     * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
+     *   of the evaluation can be a string representing space delimited class names or an array.
+     *
+     * @example
+     <example>
+     <file name="index.html">
+     <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
+     <li ng-repeat="name in names">
+     <span ng-class-odd="'odd'" ng-class-even="'even'">
+     {{name}}
+     </span>
+     </li>
+     </ol>
+     </file>
+     <file name="style.css">
+     .odd {
+         color: red;
+       }
+     .even {
+         color: blue;
+       }
+     </file>
+     <file name="scenario.js">
+     it('should check ng-class-odd and ng-class-even', function() {
+         expect(element('.doc-example-live li:first span').prop('className')).
+           toMatch(/odd/);
+         expect(element('.doc-example-live li:last span').prop('className')).
+           toMatch(/even/);
+       });
+     </file>
+     </example>
+     */
+    var ngClassOddDirective = classDirective('Odd', 0);
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngClassEven
+     * @restrict AC
+     *
+     * @description
+     * The `ngClassOdd` and `ngClassEven` directives work exactly as
+     * {@link ng.directive:ngClass ngClass}, except they work in
+     * conjunction with `ngRepeat` and take effect only on odd (even) rows.
+     *
+     * This directive can be applied only within the scope of an
+     * {@link ng.directive:ngRepeat ngRepeat}.
+     *
+     * @element ANY
+     * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
+     *   result of the evaluation can be a string representing space delimited class names or an array.
+     *
+     * @example
+     <example>
+     <file name="index.html">
+     <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
+     <li ng-repeat="name in names">
+     <span ng-class-odd="'odd'" ng-class-even="'even'">
+     {{name}} &nbsp; &nbsp; &nbsp;
+     </span>
+     </li>
+     </ol>
+     </file>
+     <file name="style.css">
+     .odd {
+         color: red;
+       }
+     .even {
+         color: blue;
+       }
+     </file>
+     <file name="scenario.js">
+     it('should check ng-class-odd and ng-class-even', function() {
+         expect(element('.doc-example-live li:first span').prop('className')).
+           toMatch(/odd/);
+         expect(element('.doc-example-live li:last span').prop('className')).
+           toMatch(/even/);
+       });
+     </file>
+     </example>
+     */
+    var ngClassEvenDirective = classDirective('Even', 1);
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngCloak
+     * @restrict AC
+     *
+     * @description
+     * The `ngCloak` directive is used to prevent the Angular html template from being briefly
+     * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
+     * directive to avoid the undesirable flicker effect caused by the html template display.
+     *
+     * The directive can be applied to the `<body>` element, but the preferred usage is to apply
+     * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
+     * of the browser view.
+     *
+     * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
+     * `angular.min.js`.
+     * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
+     *
+     * <pre>
+     * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
+ *   display: none !important;
+ * }
+     * </pre>
+     *
+     * When this css rule is loaded by the browser, all html elements (including their children) that
+     * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
+     * during the compilation of the template it deletes the `ngCloak` element attribute, making
+     * the compiled element visible.
+     *
+     * For the best result, the `angular.js` script must be loaded in the head section of the html
+     * document; alternatively, the css rule above must be included in the external stylesheet of the
+     * application.
+     *
+     * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
+     * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
+     * class `ngCloak` in addition to the `ngCloak` directive as shown in the example below.
+     *
+     * @element ANY
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <div id="template1" ng-cloak>{{ 'hello' }}</div>
+     <div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div>
+     </doc:source>
+     <doc:scenario>
+     it('should remove the template directive and css class', function() {
+         expect(element('.doc-example-live #template1').attr('ng-cloak')).
+           not().toBeDefined();
+         expect(element('.doc-example-live #template2').attr('ng-cloak')).
+           not().toBeDefined();
+       });
+     </doc:scenario>
+     </doc:example>
+     *
+     */
+    var ngCloakDirective = ngDirective({
+        compile: function(element, attr) {
+            attr.$set('ngCloak', undefined);
+            element.removeClass('ng-cloak');
+        }
+    });
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngController
+     *
+     * @description
+     * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
+     * supports the principles behind the Model-View-Controller design pattern.
+     *
+     * MVC components in angular:
+     *
+     * * Model — The Model is scope properties; scopes are attached to the DOM where scope properties
+     *   are accessed through bindings.
+     * * View — The template (HTML with data bindings) that is rendered into the View.
+     * * Controller — The `ngController` directive specifies a Controller class; the class contains business
+     *   logic behind the application to decorate the scope with functions and values
+     *
+     * Note that you can also attach controllers to the DOM by declaring it in a route definition
+     * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
+     * again using `ng-controller` in the template itself.  This will cause the controller to be attached
+     * and executed twice.
+     *
+     * @element ANY
+     * @scope
+     * @param {expression} ngController Name of a globally accessible constructor function or an
+     *     {@link guide/expression expression} that on the current scope evaluates to a
+     *     constructor function. The controller instance can be published into a scope property
+     *     by specifying `as propertyName`.
+     *
+     * @example
+     * Here is a simple form for editing user contact information. Adding, removing, clearing, and
+     * greeting are methods declared on the controller (see source tab). These methods can
+     * easily be called from the angular markup. Notice that the scope becomes the `this` for the
+     * controller's instance. This allows for easy access to the view data from the controller. Also
+     * notice that any changes to the data are automatically reflected in the View without the need
+     * for a manual update. The example is shown in two different declaration styles you may use
+     * according to preference.
+     <doc:example>
+     <doc:source>
+     <script>
+     function SettingsController1() {
+          this.name = "John Smith";
+          this.contacts = [
+            {type: 'phone', value: '408 555 1212'},
+            {type: 'email', value: 'john.smith@example.org'} ];
+     };
+
+     SettingsController1.prototype.greet = function() {
+          alert(this.name);
+        };
+
+     SettingsController1.prototype.addContact = function() {
+          this.contacts.push({type: 'email', value: 'yourname@example.org'});
+     };
+
+     SettingsController1.prototype.removeContact = function(contactToRemove) {
+         var index = this.contacts.indexOf(contactToRemove);
+          this.contacts.splice(index, 1);
+        };
+
+     SettingsController1.prototype.clearContact = function(contact) {
+          contact.type = 'phone';
+          contact.value = '';
+        };
+     </script>
+     <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
+     Name: <input type="text" ng-model="settings.name"/>
+     [ <a href="" ng-click="settings.greet()">greet</a> ]<br/>
+     Contact:
+     <ul>
+     <li ng-repeat="contact in settings.contacts">
+     <select ng-model="contact.type">
+     <option>phone</option>
+     <option>email</option>
+     </select>
+     <input type="text" ng-model="contact.value"/>
+     [ <a href="" ng-click="settings.clearContact(contact)">clear</a>
+     | <a href="" ng-click="settings.removeContact(contact)">X</a> ]
+     </li>
+     <li>[ <a href="" ng-click="settings.addContact()">add</a> ]</li>
+     </ul>
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should check controller as', function() {
+         expect(element('#ctrl-as-exmpl>:input').val()).toBe('John Smith');
+         expect(element('#ctrl-as-exmpl li:nth-child(1) input').val())
+           .toBe('408 555 1212');
+         expect(element('#ctrl-as-exmpl li:nth-child(2) input').val())
+           .toBe('john.smith@example.org');
+
+     element('#ctrl-as-exmpl li:first a:contains("clear")').click();
+     expect(element('#ctrl-as-exmpl li:first input').val()).toBe('');
+
+     element('#ctrl-as-exmpl li:last a:contains("add")').click();
+     expect(element('#ctrl-as-exmpl li:nth-child(3) input').val())
+     .toBe('yourname@example.org');
+     });
+     </doc:scenario>
+     </doc:example>
+     <doc:example>
+     <doc:source>
+     <script>
+     function SettingsController2($scope) {
+          $scope.name = "John Smith";
+          $scope.contacts = [
+            {type:'phone', value:'408 555 1212'},
+            {type:'email', value:'john.smith@example.org'} ];
+
+     $scope.greet = function() {
+           alert(this.name);
+          };
+
+     $scope.addContact = function() {
+           this.contacts.push({type:'email', value:'yourname@example.org'});
+     };
+
+     $scope.removeContact = function(contactToRemove) {
+           var index = this.contacts.indexOf(contactToRemove);
+           this.contacts.splice(index, 1);
+          };
+
+     $scope.clearContact = function(contact) {
+           contact.type = 'phone';
+           contact.value = '';
+          };
+     }
+     </script>
+     <div id="ctrl-exmpl" ng-controller="SettingsController2">
+     Name: <input type="text" ng-model="name"/>
+     [ <a href="" ng-click="greet()">greet</a> ]<br/>
+     Contact:
+     <ul>
+     <li ng-repeat="contact in contacts">
+     <select ng-model="contact.type">
+     <option>phone</option>
+     <option>email</option>
+     </select>
+     <input type="text" ng-model="contact.value"/>
+     [ <a href="" ng-click="clearContact(contact)">clear</a>
+     | <a href="" ng-click="removeContact(contact)">X</a> ]
+     </li>
+     <li>[ <a href="" ng-click="addContact()">add</a> ]</li>
+     </ul>
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should check controller', function() {
+         expect(element('#ctrl-exmpl>:input').val()).toBe('John Smith');
+         expect(element('#ctrl-exmpl li:nth-child(1) input').val())
+           .toBe('408 555 1212');
+         expect(element('#ctrl-exmpl li:nth-child(2) input').val())
+           .toBe('john.smith@example.org');
+
+     element('#ctrl-exmpl li:first a:contains("clear")').click();
+     expect(element('#ctrl-exmpl li:first input').val()).toBe('');
+
+     element('#ctrl-exmpl li:last a:contains("add")').click();
+     expect(element('#ctrl-exmpl li:nth-child(3) input').val())
+     .toBe('yourname@example.org');
+     });
+     </doc:scenario>
+     </doc:example>
+
+     */
+    var ngControllerDirective = [function() {
+        return {
+            scope: true,
+            controller: '@',
+            priority: 500
+        };
+    }];
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngCsp
+     *
+     * @element html
+     * @description
+     * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
+     *
+     * This is necessary when developing things like Google Chrome Extensions.
+     *
+     * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
+     * For us to be compatible, we just need to implement the "getterFn" in $parse without violating
+     * any of these restrictions.
+     *
+     * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
+     * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
+     * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
+     * be raised.
+     *
+     * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically
+     * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
+     * To make those directives work in CSP mode, include the `angular-csp.css` manually.
+     *
+     * In order to use this feature put the `ngCsp` directive on the root element of the application.
+     *
+     * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
+     *
+     * @example
+     * This example shows how to apply the `ngCsp` directive to the `html` tag.
+     <pre>
+     <!doctype html>
+     <html ng-app ng-csp>
+     ...
+     ...
+     </html>
+     </pre>
+     */
+
+// ngCsp is not implemented as a proper directive any more, because we need it be processed while we bootstrap
+// the system (before $parse is instantiated), for this reason we just have a csp() fn that looks for ng-csp attribute
+// anywhere in the current doc
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngClick
+     *
+     * @description
+     * The ngClick directive allows you to specify custom behavior when
+     * an element is clicked.
+     *
+     * @element ANY
+     * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
+     * click. (Event object is available as `$event`)
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <button ng-click="count = count + 1" ng-init="count=0">
+     Increment
+     </button>
+     count: {{count}}
+     </doc:source>
+     <doc:scenario>
+     it('should check ng-click', function() {
+         expect(binding('count')).toBe('0');
+         element('.doc-example-live :button').click();
+         expect(binding('count')).toBe('1');
+       });
+     </doc:scenario>
+     </doc:example>
+     */
+    /*
+     * A directive that allows creation of custom onclick handlers that are defined as angular
+     * expressions and are compiled and executed within the current scope.
+     *
+     * Events that are handled via these handler are always configured not to propagate further.
+     */
+    var ngEventDirectives = {};
+    forEach(
+            'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
+            function(name) {
+                var directiveName = directiveNormalize('ng-' + name);
+                ngEventDirectives[directiveName] = ['$parse', function($parse) {
+                    return {
+                        compile: function($element, attr) {
+                            var fn = $parse(attr[directiveName]);
+                            return function(scope, element, attr) {
+                                element.on(lowercase(name), function(event) {
+                                    scope.$apply(function() {
+                                        fn(scope, {$event:event});
+                                    });
+                                });
+                            };
+                        }
+                    };
+                }];
+            }
+    );
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngDblclick
+     *
+     * @description
+     * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
+     *
+     * @element ANY
+     * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
+     * a dblclick. (The Event object is available as `$event`)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngMousedown
+     *
+     * @description
+     * The ngMousedown directive allows you to specify custom behavior on mousedown event.
+     *
+     * @element ANY
+     * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
+     * mousedown. (Event object is available as `$event`)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngMouseup
+     *
+     * @description
+     * Specify custom behavior on mouseup event.
+     *
+     * @element ANY
+     * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
+     * mouseup. (Event object is available as `$event`)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngMouseover
+     *
+     * @description
+     * Specify custom behavior on mouseover event.
+     *
+     * @element ANY
+     * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
+     * mouseover. (Event object is available as `$event`)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngMouseenter
+     *
+     * @description
+     * Specify custom behavior on mouseenter event.
+     *
+     * @element ANY
+     * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
+     * mouseenter. (Event object is available as `$event`)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngMouseleave
+     *
+     * @description
+     * Specify custom behavior on mouseleave event.
+     *
+     * @element ANY
+     * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
+     * mouseleave. (Event object is available as `$event`)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngMousemove
+     *
+     * @description
+     * Specify custom behavior on mousemove event.
+     *
+     * @element ANY
+     * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
+     * mousemove. (Event object is available as `$event`)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngKeydown
+     *
+     * @description
+     * Specify custom behavior on keydown event.
+     *
+     * @element ANY
+     * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
+     * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngKeyup
+     *
+     * @description
+     * Specify custom behavior on keyup event.
+     *
+     * @element ANY
+     * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
+     * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngKeypress
+     *
+     * @description
+     * Specify custom behavior on keypress event.
+     *
+     * @element ANY
+     * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
+     * keypress. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngSubmit
+     *
+     * @description
+     * Enables binding angular expressions to onsubmit events.
+     *
+     * Additionally it prevents the default action (which for form means sending the request to the
+     * server and reloading the current page) **but only if the form does not contain an `action`
+     * attribute**.
+     *
+     * @element form
+     * @param {expression} ngSubmit {@link guide/expression Expression} to eval. (Event object is available as `$event`)
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+          $scope.list = [];
+          $scope.text = 'hello';
+          $scope.submit = function() {
+            if (this.text) {
+              this.list.push(this.text);
+              this.text = '';
+            }
+          };
+        }
+     </script>
+     <form ng-submit="submit()" ng-controller="Ctrl">
+     Enter text and hit enter:
+     <input type="text" ng-model="text" name="text" />
+     <input type="submit" id="submit" value="Submit" />
+     <pre>list={{list}}</pre>
+     </form>
+     </doc:source>
+     <doc:scenario>
+     it('should check ng-submit', function() {
+         expect(binding('list')).toBe('[]');
+         element('.doc-example-live #submit').click();
+         expect(binding('list')).toBe('["hello"]');
+         expect(input('text').val()).toBe('');
+       });
+     it('should ignore empty strings', function() {
+         expect(binding('list')).toBe('[]');
+         element('.doc-example-live #submit').click();
+         element('.doc-example-live #submit').click();
+         expect(binding('list')).toBe('["hello"]');
+       });
+     </doc:scenario>
+     </doc:example>
+     */
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngFocus
+     *
+     * @description
+     * Specify custom behavior on focus event.
+     *
+     * @element window, input, select, textarea, a
+     * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
+     * focus. (Event object is available as `$event`)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngBlur
+     *
+     * @description
+     * Specify custom behavior on blur event.
+     *
+     * @element window, input, select, textarea, a
+     * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
+     * blur. (Event object is available as `$event`)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngCopy
+     *
+     * @description
+     * Specify custom behavior on copy event.
+     *
+     * @element window, input, select, textarea, a
+     * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
+     * copy. (Event object is available as `$event`)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngCut
+     *
+     * @description
+     * Specify custom behavior on cut event.
+     *
+     * @element window, input, select, textarea, a
+     * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
+     * cut. (Event object is available as `$event`)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngPaste
+     *
+     * @description
+     * Specify custom behavior on paste event.
+     *
+     * @element window, input, select, textarea, a
+     * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
+     * paste. (Event object is available as `$event`)
+     *
+     * @example
+     * See {@link ng.directive:ngClick ngClick}
+     */
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngIf
+     * @restrict A
+     *
+     * @description
+     * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
+     * {expression}. If the expression assigned to `ngIf` evaluates to a false
+     * value then the element is removed from the DOM, otherwise a clone of the
+     * element is reinserted into the DOM.
+     *
+     * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
+     * element in the DOM rather than changing its visibility via the `display` css property.  A common
+     * case when this difference is significant is when using css selectors that rely on an element's
+     * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
+     *
+     * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
+     * is created when the element is restored.  The scope created within `ngIf` inherits from
+     * its parent scope using
+     * {@link https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance prototypal inheritance}.
+     * An important implication of this is if `ngModel` is used within `ngIf` to bind to
+     * a javascript primitive defined in the parent scope. In this case any modifications made to the
+     * variable within the child scope will override (hide) the value in the parent scope.
+     *
+     * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
+     * is if an element's class attribute is directly modified after it's compiled, using something like
+     * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
+     * the added class will be lost because the original compiled state is used to regenerate the element.
+     *
+     * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
+     * and `leave` effects.
+     *
+     * @animations
+     * enter - happens just after the ngIf contents change and a new DOM element is created and injected into the ngIf container
+     * leave - happens just before the ngIf contents are removed from the DOM
+     *
+     * @element ANY
+     * @scope
+     * @priority 600
+     * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
+     *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
+     *     element is added to the DOM tree.
+     *
+     * @example
+     <example animations="true">
+     <file name="index.html">
+     Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/>
+     Show when checked:
+     <span ng-if="checked" class="animate-if">
+     I'm removed when the checkbox is unchecked.
+     </span>
+     </file>
+     <file name="animations.css">
+     .animate-if {
+        background:white;
+        border:1px solid black;
+        padding:10px;
+      }
+
+     /&#42;
+     The transition styles can also be placed on the CSS base class above
+     &#42;/
+     .animate-if.ng-enter, .animate-if.ng-leave {
+        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
+        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
+      }
+
+     .animate-if.ng-enter,
+     .animate-if.ng-leave.ng-leave-active {
+        opacity:0;
+      }
+
+     .animate-if.ng-leave,
+     .animate-if.ng-enter.ng-enter-active {
+        opacity:1;
+      }
+     </file>
+     </example>
+     */
+    var ngIfDirective = ['$animate', function($animate) {
+        return {
+            transclude: 'element',
+            priority: 600,
+            terminal: true,
+            restrict: 'A',
+            $$tlb: true,
+            link: function ($scope, $element, $attr, ctrl, $transclude) {
+                var block, childScope;
+                $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
+
+                    if (toBoolean(value)) {
+                        if (!childScope) {
+                            childScope = $scope.$new();
+                            $transclude(childScope, function (clone) {
+                                block = {
+                                    startNode: clone[0],
+                                    endNode: clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ')
+                                };
+                                $animate.enter(clone, $element.parent(), $element);
+                            });
+                        }
+                    } else {
+
+                        if (childScope) {
+                            childScope.$destroy();
+                            childScope = null;
+                        }
+
+                        if (block) {
+                            $animate.leave(getBlockElements(block));
+                            block = null;
+                        }
+                    }
+                });
+            }
+        };
+    }];
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngInclude
+     * @restrict ECA
+     *
+     * @description
+     * Fetches, compiles and includes an external HTML fragment.
+     *
+     * By default, the template URL is restricted to the same domain and protocol as the
+     * application document. This is done by calling {@link ng.$sce#methods_getTrustedResourceUrl
+     * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
+     * you may either {@link ng.$sceDelegateProvider#methods_resourceUrlWhitelist whitelist them} or
+     * {@link ng.$sce#methods_trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
+     * ng.$sce Strict Contextual Escaping}.
+     *
+     * In addition, the browser's
+     * {@link https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest
+     * Same Origin Policy} and {@link http://www.w3.org/TR/cors/ Cross-Origin Resource Sharing
+     * (CORS)} policy may further restrict whether the template is successfully loaded.
+     * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
+     * access on some browsers.
+     *
+     * @animations
+     * enter - animation is used to bring new content into the browser.
+     * leave - animation is used to animate existing content away.
+     *
+     * The enter and leave animation occur concurrently.
+     *
+     * @scope
+     * @priority 400
+     *
+     * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
+     *                 make sure you wrap it in quotes, e.g. `src="'myPartialTemplate.html'"`.
+     * @param {string=} onload Expression to evaluate when a new partial is loaded.
+     *
+     * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
+     *                  $anchorScroll} to scroll the viewport after the content is loaded.
+     *
+     *                  - If the attribute is not set, disable scrolling.
+     *                  - If the attribute is set without value, enable scrolling.
+     *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
+     *
+     * @example
+     <example animations="true">
+     <file name="index.html">
+     <div ng-controller="Ctrl">
+     <select ng-model="template" ng-options="t.name for t in templates">
+     <option value="">(blank)</option>
+     </select>
+     url of the template: <tt>{{template.url}}</tt>
+     <hr/>
+     <div class="slide-animate-container">
+     <div class="slide-animate" ng-include="template.url"></div>
+     </div>
+     </div>
+     </file>
+     <file name="script.js">
+     function Ctrl($scope) {
+        $scope.templates =
+          [ { name: 'template1.html', url: 'template1.html'}
+          , { name: 'template2.html', url: 'template2.html'} ];
+        $scope.template = $scope.templates[0];
+      }
+     </file>
+     <file name="template1.html">
+     Content of template1.html
+     </file>
+     <file name="template2.html">
+     Content of template2.html
+     </file>
+     <file name="animations.css">
+     .slide-animate-container {
+        position:relative;
+        background:white;
+        border:1px solid black;
+        height:40px;
+        overflow:hidden;
+      }
+
+     .slide-animate {
+        padding:10px;
+      }
+
+     .slide-animate.ng-enter, .slide-animate.ng-leave {
+        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
+        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
+
+        position:absolute;
+        top:0;
+        left:0;
+        right:0;
+        bottom:0;
+        display:block;
+        padding:10px;
+      }
+
+     .slide-animate.ng-enter {
+        top:-50px;
+      }
+     .slide-animate.ng-enter.ng-enter-active {
+        top:0;
+      }
+
+     .slide-animate.ng-leave {
+        top:0;
+      }
+     .slide-animate.ng-leave.ng-leave-active {
+        top:50px;
+      }
+     </file>
+     <file name="scenario.js">
+     it('should load template1.html', function() {
+       expect(element('.doc-example-live [ng-include]').text()).
+         toMatch(/Content of template1.html/);
+      });
+     it('should load template2.html', function() {
+       select('template').option('1');
+       expect(element('.doc-example-live [ng-include]').text()).
+         toMatch(/Content of template2.html/);
+      });
+     it('should change to blank', function() {
+       select('template').option('');
+       expect(element('.doc-example-live [ng-include]')).toBe(undefined);
+      });
+     </file>
+     </example>
+     */
+
+
+    /**
+     * @ngdoc event
+     * @name ng.directive:ngInclude#$includeContentRequested
+     * @eventOf ng.directive:ngInclude
+     * @eventType emit on the scope ngInclude was declared in
+     * @description
+     * Emitted every time the ngInclude content is requested.
+     */
+
+
+    /**
+     * @ngdoc event
+     * @name ng.directive:ngInclude#$includeContentLoaded
+     * @eventOf ng.directive:ngInclude
+     * @eventType emit on the current ngInclude scope
+     * @description
+     * Emitted every time the ngInclude content is reloaded.
+     */
+    var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', '$animate', '$sce',
+        function($http,   $templateCache,   $anchorScroll,   $compile,   $animate,   $sce) {
+            return {
+                restrict: 'ECA',
+                priority: 400,
+                terminal: true,
+                transclude: 'element',
+                compile: function(element, attr) {
+                    var srcExp = attr.ngInclude || attr.src,
+                            onloadExp = attr.onload || '',
+                            autoScrollExp = attr.autoscroll;
+
+                    return function(scope, $element, $attr, ctrl, $transclude) {
+                        var changeCounter = 0,
+                                currentScope,
+                                currentElement;
+
+                        var cleanupLastIncludeContent = function() {
+                            if (currentScope) {
+                                currentScope.$destroy();
+                                currentScope = null;
+                            }
+                            if(currentElement) {
+                                $animate.leave(currentElement);
+                                currentElement = null;
+                            }
+                        };
+
+                        scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
+                            var afterAnimation = function() {
+                                if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
+                                    $anchorScroll();
+                                }
+                            };
+                            var thisChangeId = ++changeCounter;
+
+                            if (src) {
+                                $http.get(src, {cache: $templateCache}).success(function(response) {
+                                    if (thisChangeId !== changeCounter) return;
+                                    var newScope = scope.$new();
+
+                                    // Note: This will also link all children of ng-include that were contained in the original
+                                    // html. If that content contains controllers, ... they could pollute/change the scope.
+                                    // However, using ng-include on an element with additional content does not make sense...
+                                    // Note: We can't remove them in the cloneAttchFn of $transclude as that
+                                    // function is called before linking the content, which would apply child
+                                    // directives to non existing elements.
+                                    var clone = $transclude(newScope, noop);
+                                    cleanupLastIncludeContent();
+
+                                    currentScope = newScope;
+                                    currentElement = clone;
+
+                                    currentElement.html(response);
+                                    $animate.enter(currentElement, null, $element, afterAnimation);
+                                    $compile(currentElement.contents())(currentScope);
+                                    currentScope.$emit('$includeContentLoaded');
+                                    scope.$eval(onloadExp);
+                                }).error(function() {
+                                            if (thisChangeId === changeCounter) cleanupLastIncludeContent();
+                                        });
+                                scope.$emit('$includeContentRequested');
+                            } else {
+                                cleanupLastIncludeContent();
+                            }
+                        });
+                    };
+                }
+            };
+        }];
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngInit
+     * @restrict AC
+     *
+     * @description
+     * The `ngInit` directive allows you to evaluate an expression in the
+     * current scope.
+     *
+     * <div class="alert alert-error">
+     * The only appropriate use of `ngInit` for aliasing special properties of
+     * {@link api/ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
+     * should use {@link guide/controller controllers} rather than `ngInit`
+     * to initialize values on a scope.
+     * </div>
+     *
+     * @element ANY
+     * @param {expression} ngInit {@link guide/expression Expression} to eval.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+       $scope.list = [['a', 'b'], ['c', 'd']];
+     }
+     </script>
+     <div ng-controller="Ctrl">
+     <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
+     <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
+     <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
+     </div>
+     </div>
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should alias index positions', function() {
+         expect(element('.example-init').text())
+           .toBe('list[ 0 ][ 0 ] = a;' +
+                 'list[ 0 ][ 1 ] = b;' +
+                 'list[ 1 ][ 0 ] = c;' +
+                 'list[ 1 ][ 1 ] = d;');
+       });
+     </doc:scenario>
+     </doc:example>
+     */
+    var ngInitDirective = ngDirective({
+        compile: function() {
+            return {
+                pre: function(scope, element, attrs) {
+                    scope.$eval(attrs.ngInit);
+                }
+            };
+        }
+    });
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngNonBindable
+     * @restrict AC
+     * @priority 1000
+     *
+     * @description
+     * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
+     * DOM element. This is useful if the element contains what appears to be Angular directives and
+     * bindings but which should be ignored by Angular. This could be the case if you have a site that
+     * displays snippets of code, for instance.
+     *
+     * @element ANY
+     *
+     * @example
+     * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
+     * but the one wrapped in `ngNonBindable` is left alone.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <div>Normal: {{1 + 2}}</div>
+     <div ng-non-bindable>Ignored: {{1 + 2}}</div>
+     </doc:source>
+     <doc:scenario>
+     it('should check ng-non-bindable', function() {
+         expect(using('.doc-example-live').binding('1 + 2')).toBe('3');
+         expect(using('.doc-example-live').element('div:last').text()).
+           toMatch(/1 \+ 2/);
+       });
+     </doc:scenario>
+     </doc:example>
+     */
+    var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngPluralize
+     * @restrict EA
+     *
+     * @description
+     * # Overview
+     * `ngPluralize` is a directive that displays messages according to en-US localization rules.
+     * These rules are bundled with angular.js, but can be overridden
+     * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
+     * by specifying the mappings between
+     * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
+     * plural categories} and the strings to be displayed.
+     *
+     * # Plural categories and explicit number rules
+     * There are two
+     * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
+     * plural categories} in Angular's default en-US locale: "one" and "other".
+     *
+     * While a plural category may match many numbers (for example, in en-US locale, "other" can match
+     * any number that is not 1), an explicit number rule can only match one number. For example, the
+     * explicit number rule for "3" matches the number 3. There are examples of plural categories
+     * and explicit number rules throughout the rest of this documentation.
+     *
+     * # Configuring ngPluralize
+     * You configure ngPluralize by providing 2 attributes: `count` and `when`.
+     * You can also provide an optional attribute, `offset`.
+     *
+     * The value of the `count` attribute can be either a string or an {@link guide/expression
+     * Angular expression}; these are evaluated on the current scope for its bound value.
+     *
+     * The `when` attribute specifies the mappings between plural categories and the actual
+     * string to be displayed. The value of the attribute should be a JSON object.
+     *
+     * The following example shows how to configure ngPluralize:
+     *
+     * <pre>
+     * <ng-pluralize count="personCount"
+     when="{'0': 'Nobody is viewing.',
+ *                      'one': '1 person is viewing.',
+ *                      'other': '{} people are viewing.'}">
+     * </ng-pluralize>
+     *</pre>
+     *
+     * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
+     * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
+     * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
+     * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
+     * show "a dozen people are viewing".
+     *
+     * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
+     * into pluralized strings. In the previous example, Angular will replace `{}` with
+     * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
+     * for <span ng-non-bindable>{{numberExpression}}</span>.
+     *
+     * # Configuring ngPluralize with offset
+     * The `offset` attribute allows further customization of pluralized text, which can result in
+     * a better user experience. For example, instead of the message "4 people are viewing this document",
+     * you might display "John, Kate and 2 others are viewing this document".
+     * The offset attribute allows you to offset a number by any desired value.
+     * Let's take a look at an example:
+     *
+     * <pre>
+     * <ng-pluralize count="personCount" offset=2
+     *               when="{'0': 'Nobody is viewing.',
+ *                      '1': '{{person1}} is viewing.',
+ *                      '2': '{{person1}} and {{person2}} are viewing.',
+ *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
+ *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
+     * </ng-pluralize>
+     * </pre>
+     *
+     * Notice that we are still using two plural categories(one, other), but we added
+     * three explicit number rules 0, 1 and 2.
+     * When one person, perhaps John, views the document, "John is viewing" will be shown.
+     * When three people view the document, no explicit number rule is found, so
+     * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
+     * In this case, plural category 'one' is matched and "John, Marry and one other person are viewing"
+     * is shown.
+     *
+     * Note that when you specify offsets, you must provide explicit number rules for
+     * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
+     * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
+     * plural categories "one" and "other".
+     *
+     * @param {string|expression} count The variable to be bounded to.
+     * @param {string} when The mapping between plural category to its corresponding strings.
+     * @param {number=} offset Offset to deduct from the total number.
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+            $scope.person1 = 'Igor';
+            $scope.person2 = 'Misko';
+            $scope.personCount = 1;
+          }
+     </script>
+     <div ng-controller="Ctrl">
+     Person 1:<input type="text" ng-model="person1" value="Igor" /><br/>
+     Person 2:<input type="text" ng-model="person2" value="Misko" /><br/>
+     Number of People:<input type="text" ng-model="personCount" value="1" /><br/>
+
+     <!--- Example with simple pluralization rules for en locale --->
+     Without Offset:
+     <ng-pluralize count="personCount"
+     when="{'0': 'Nobody is viewing.',
+                               'one': '1 person is viewing.',
+                               'other': '{} people are viewing.'}">
+     </ng-pluralize><br>
+
+     <!--- Example with offset --->
+     With Offset(2):
+     <ng-pluralize count="personCount" offset=2
+     when="{'0': 'Nobody is viewing.',
+                               '1': '{{person1}} is viewing.',
+                               '2': '{{person1}} and {{person2}} are viewing.',
+                               'one': '{{person1}}, {{person2}} and one other person are viewing.',
+                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
+     </ng-pluralize>
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should show correct pluralized string', function() {
+          expect(element('.doc-example-live ng-pluralize:first').text()).
+                                             toBe('1 person is viewing.');
+          expect(element('.doc-example-live ng-pluralize:last').text()).
+                                                toBe('Igor is viewing.');
+
+          using('.doc-example-live').input('personCount').enter('0');
+          expect(element('.doc-example-live ng-pluralize:first').text()).
+                                               toBe('Nobody is viewing.');
+          expect(element('.doc-example-live ng-pluralize:last').text()).
+                                              toBe('Nobody is viewing.');
+
+          using('.doc-example-live').input('personCount').enter('2');
+          expect(element('.doc-example-live ng-pluralize:first').text()).
+                                            toBe('2 people are viewing.');
+          expect(element('.doc-example-live ng-pluralize:last').text()).
+                              toBe('Igor and Misko are viewing.');
+
+          using('.doc-example-live').input('personCount').enter('3');
+          expect(element('.doc-example-live ng-pluralize:first').text()).
+                                            toBe('3 people are viewing.');
+          expect(element('.doc-example-live ng-pluralize:last').text()).
+                              toBe('Igor, Misko and one other person are viewing.');
+
+          using('.doc-example-live').input('personCount').enter('4');
+          expect(element('.doc-example-live ng-pluralize:first').text()).
+                                            toBe('4 people are viewing.');
+          expect(element('.doc-example-live ng-pluralize:last').text()).
+                              toBe('Igor, Misko and 2 other people are viewing.');
+        });
+
+     it('should show data-binded names', function() {
+          using('.doc-example-live').input('personCount').enter('4');
+          expect(element('.doc-example-live ng-pluralize:last').text()).
+              toBe('Igor, Misko and 2 other people are viewing.');
+
+          using('.doc-example-live').input('person1').enter('Di');
+          using('.doc-example-live').input('person2').enter('Vojta');
+          expect(element('.doc-example-live ng-pluralize:last').text()).
+              toBe('Di, Vojta and 2 other people are viewing.');
+        });
+     </doc:scenario>
+     </doc:example>
+     */
+    var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
+        var BRACE = /{}/g;
+        return {
+            restrict: 'EA',
+            link: function(scope, element, attr) {
+                var numberExp = attr.count,
+                        whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
+                        offset = attr.offset || 0,
+                        whens = scope.$eval(whenExp) || {},
+                        whensExpFns = {},
+                        startSymbol = $interpolate.startSymbol(),
+                        endSymbol = $interpolate.endSymbol(),
+                        isWhen = /^when(Minus)?(.+)$/;
+
+                forEach(attr, function(expression, attributeName) {
+                    if (isWhen.test(attributeName)) {
+                        whens[lowercase(attributeName.replace('when', '').replace('Minus', '-'))] =
+                                element.attr(attr.$attr[attributeName]);
+                    }
+                });
+                forEach(whens, function(expression, key) {
+                    whensExpFns[key] =
+                            $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' +
+                                    offset + endSymbol));
+                });
+
+                scope.$watch(function ngPluralizeWatch() {
+                    var value = parseFloat(scope.$eval(numberExp));
+
+                    if (!isNaN(value)) {
+                        //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
+                        //check it against pluralization rules in $locale service
+                        if (!(value in whens)) value = $locale.pluralCat(value - offset);
+                        return whensExpFns[value](scope, element, true);
+                    } else {
+                        return '';
+                    }
+                }, function ngPluralizeWatchAction(newVal) {
+                    element.text(newVal);
+                });
+            }
+        };
+    }];
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngRepeat
+     *
+     * @description
+     * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
+     * instance gets its own scope, where the given loop variable is set to the current collection item,
+     * and `$index` is set to the item index or key.
+     *
+     * Special properties are exposed on the local scope of each template instance, including:
+     *
+     * | Variable  | Type            | Details                                                                     |
+     * |-----------|-----------------|-----------------------------------------------------------------------------|
+     * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
+     * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
+     * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
+     * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
+     * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
+     * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
+     *
+     *
+     * # Special repeat start and end points
+     * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
+     * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
+     * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on)
+     * up to and including the ending HTML tag where **ng-repeat-end** is placed.
+     *
+     * The example below makes use of this feature:
+     * <pre>
+     *   <header ng-repeat-start="item in items">
+     *     Header {{ item }}
+     *   </header>
+     *   <div class="body">
+     *     Body {{ item }}
+     *   </div>
+     *   <footer ng-repeat-end>
+     *     Footer {{ item }}
+     *   </footer>
+     * </pre>
+     *
+     * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
+     * <pre>
+     *   <header>
+     *     Header A
+     *   </header>
+     *   <div class="body">
+     *     Body A
+     *   </div>
+     *   <footer>
+     *     Footer A
+     *   </footer>
+     *   <header>
+     *     Header B
+     *   </header>
+     *   <div class="body">
+     *     Body B
+     *   </div>
+     *   <footer>
+     *     Footer B
+     *   </footer>
+     * </pre>
+     *
+     * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
+     * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
+     *
+     * @animations
+     * enter - when a new item is added to the list or when an item is revealed after a filter
+     * leave - when an item is removed from the list or when an item is filtered out
+     * move - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
+     *
+     * @element ANY
+     * @scope
+     * @priority 1000
+     * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
+     *   formats are currently supported:
+     *
+     *   * `variable in expression` – where variable is the user defined loop variable and `expression`
+     *     is a scope expression giving the collection to enumerate.
+     *
+     *     For example: `album in artist.albums`.
+     *
+     *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
+     *     and `expression` is the scope expression giving the collection to enumerate.
+     *
+     *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
+     *
+     *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking function
+     *     which can be used to associate the objects in the collection with the DOM elements. If no tracking function
+     *     is specified the ng-repeat associates elements by identity in the collection. It is an error to have
+     *     more than one tracking function to resolve to the same key. (This would mean that two distinct objects are
+     *     mapped to the same DOM element, which is not possible.)  Filters should be applied to the expression,
+     *     before specifying a tracking expression.
+     *
+     *     For example: `item in items` is equivalent to `item in items track by $id(item)'. This implies that the DOM elements
+     *     will be associated by item identity in the array.
+     *
+     *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
+     *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
+     *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
+     *     element in the same way in the DOM.
+     *
+     *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
+     *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
+     *     property is same.
+     *
+     *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
+     *     to items in conjunction with a tracking expression.
+     *
+     * @example
+     * This example initializes the scope to a list of names and
+     * then uses `ngRepeat` to display every person:
+     <example animations="true">
+     <file name="index.html">
+     <div ng-init="friends = [
+     {name:'John', age:25, gender:'boy'},
+     {name:'Jessie', age:30, gender:'girl'},
+     {name:'Johanna', age:28, gender:'girl'},
+     {name:'Joy', age:15, gender:'girl'},
+     {name:'Mary', age:28, gender:'girl'},
+     {name:'Peter', age:95, gender:'boy'},
+     {name:'Sebastian', age:50, gender:'boy'},
+     {name:'Erika', age:27, gender:'girl'},
+     {name:'Patrick', age:40, gender:'boy'},
+     {name:'Samantha', age:60, gender:'girl'}
+     ]">
+     I have {{friends.length}} friends. They are:
+     <input type="search" ng-model="q" placeholder="filter friends..." />
+     <ul class="example-animate-container">
+     <li class="animate-repeat" ng-repeat="friend in friends | filter:q">
+     [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
+     </li>
+     </ul>
+     </div>
+     </file>
+     <file name="animations.css">
+     .example-animate-container {
+        background:white;
+        border:1px solid black;
+        list-style:none;
+        margin:0;
+        padding:0 10px;
+      }
+
+     .animate-repeat {
+        line-height:40px;
+        list-style:none;
+        box-sizing:border-box;
+      }
+
+     .animate-repeat.ng-move,
+     .animate-repeat.ng-enter,
+     .animate-repeat.ng-leave {
+        -webkit-transition:all linear 0.5s;
+        transition:all linear 0.5s;
+      }
+
+     .animate-repeat.ng-leave.ng-leave-active,
+     .animate-repeat.ng-move,
+     .animate-repeat.ng-enter {
+        opacity:0;
+        max-height:0;
+      }
+
+     .animate-repeat.ng-leave,
+     .animate-repeat.ng-move.ng-move-active,
+     .animate-repeat.ng-enter.ng-enter-active {
+        opacity:1;
+        max-height:40px;
+      }
+     </file>
+     <file name="scenario.js">
+     it('should render initial data set', function() {
+         var r = using('.doc-example-live').repeater('ul li');
+         expect(r.count()).toBe(10);
+         expect(r.row(0)).toEqual(["1","John","25"]);
+         expect(r.row(1)).toEqual(["2","Jessie","30"]);
+         expect(r.row(9)).toEqual(["10","Samantha","60"]);
+         expect(binding('friends.length')).toBe("10");
+       });
+
+     it('should update repeater when filter predicate changes', function() {
+         var r = using('.doc-example-live').repeater('ul li');
+         expect(r.count()).toBe(10);
+
+         input('q').enter('ma');
+
+         expect(r.count()).toBe(2);
+         expect(r.row(0)).toEqual(["1","Mary","28"]);
+         expect(r.row(1)).toEqual(["2","Samantha","60"]);
+       });
+     </file>
+     </example>
+     */
+    var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
+        var NG_REMOVED = '$$NG_REMOVED';
+        var ngRepeatMinErr = minErr('ngRepeat');
+        return {
+            transclude: 'element',
+            priority: 1000,
+            terminal: true,
+            $$tlb: true,
+            link: function($scope, $element, $attr, ctrl, $transclude){
+                var expression = $attr.ngRepeat;
+                var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),
+                        trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
+                        lhs, rhs, valueIdentifier, keyIdentifier,
+                        hashFnLocals = {$id: hashKey};
+
+                if (!match) {
+                    throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
+                            expression);
+                }
+
+                lhs = match[1];
+                rhs = match[2];
+                trackByExp = match[4];
+
+                if (trackByExp) {
+                    trackByExpGetter = $parse(trackByExp);
+                    trackByIdExpFn = function(key, value, index) {
+                        // assign key, value, and $index to the locals so that they can be used in hash functions
+                        if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
+                        hashFnLocals[valueIdentifier] = value;
+                        hashFnLocals.$index = index;
+                        return trackByExpGetter($scope, hashFnLocals);
+                    };
+                } else {
+                    trackByIdArrayFn = function(key, value) {
+                        return hashKey(value);
+                    };
+                    trackByIdObjFn = function(key) {
+                        return key;
+                    };
+                }
+
+                match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);
+                if (!match) {
+                    throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
+                            lhs);
+                }
+                valueIdentifier = match[3] || match[1];
+                keyIdentifier = match[2];
+
+                // Store a list of elements from previous run. This is a hash where key is the item from the
+                // iterator, and the value is objects with following properties.
+                //   - scope: bound scope
+                //   - element: previous element.
+                //   - index: position
+                var lastBlockMap = {};
+
+                //watch props
+                $scope.$watchCollection(rhs, function ngRepeatAction(collection){
+                    var index, length,
+                            previousNode = $element[0],     // current position of the node
+                            nextNode,
+                    // Same as lastBlockMap but it has the current state. It will become the
+                    // lastBlockMap on the next iteration.
+                            nextBlockMap = {},
+                            arrayLength,
+                            childScope,
+                            key, value, // key/value of iteration
+                            trackById,
+                            trackByIdFn,
+                            collectionKeys,
+                            block,       // last object information {scope, element, id}
+                            nextBlockOrder = [],
+                            elementsToRemove;
+
+
+                    if (isArrayLike(collection)) {
+                        collectionKeys = collection;
+                        trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
+                    } else {
+                        trackByIdFn = trackByIdExpFn || trackByIdObjFn;
+                        // if object, extract keys, sort them and use to determine order of iteration over obj props
+                        collectionKeys = [];
+                        for (key in collection) {
+                            if (collection.hasOwnProperty(key) && key.charAt(0) != '$') {
+                                collectionKeys.push(key);
+                            }
+                        }
+                        collectionKeys.sort();
+                    }
+
+                    arrayLength = collectionKeys.length;
+
+                    // locate existing items
+                    length = nextBlockOrder.length = collectionKeys.length;
+                    for(index = 0; index < length; index++) {
+                        key = (collection === collectionKeys) ? index : collectionKeys[index];
+                        value = collection[key];
+                        trackById = trackByIdFn(key, value, index);
+                        assertNotHasOwnProperty(trackById, '`track by` id');
+                        if(lastBlockMap.hasOwnProperty(trackById)) {
+                            block = lastBlockMap[trackById];
+                            delete lastBlockMap[trackById];
+                            nextBlockMap[trackById] = block;
+                            nextBlockOrder[index] = block;
+                        } else if (nextBlockMap.hasOwnProperty(trackById)) {
+                            // restore lastBlockMap
+                            forEach(nextBlockOrder, function(block) {
+                                if (block && block.startNode) lastBlockMap[block.id] = block;
+                            });
+                            // This is a duplicate and we need to throw an error
+                            throw ngRepeatMinErr('dupes', "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}",
+                                    expression,       trackById);
+                        } else {
+                            // new never before seen block
+                            nextBlockOrder[index] = { id: trackById };
+                            nextBlockMap[trackById] = false;
+                        }
+                    }
+
+                    // remove existing items
+                    for (key in lastBlockMap) {
+                        // lastBlockMap is our own object so we don't need to use special hasOwnPropertyFn
+                        if (lastBlockMap.hasOwnProperty(key)) {
+                            block = lastBlockMap[key];
+                            elementsToRemove = getBlockElements(block);
+                            $animate.leave(elementsToRemove);
+                            forEach(elementsToRemove, function(element) { element[NG_REMOVED] = true; });
+                            block.scope.$destroy();
+                        }
+                    }
+
+                    // we are not using forEach for perf reasons (trying to avoid #call)
+                    for (index = 0, length = collectionKeys.length; index < length; index++) {
+                        key = (collection === collectionKeys) ? index : collectionKeys[index];
+                        value = collection[key];
+                        block = nextBlockOrder[index];
+                        if (nextBlockOrder[index - 1]) previousNode = nextBlockOrder[index - 1].endNode;
+
+                        if (block.startNode) {
+                            // if we have already seen this object, then we need to reuse the
+                            // associated scope/element
+                            childScope = block.scope;
+
+                            nextNode = previousNode;
+                            do {
+                                nextNode = nextNode.nextSibling;
+                            } while(nextNode && nextNode[NG_REMOVED]);
+
+                            if (block.startNode != nextNode) {
+                                // existing item which got moved
+                                $animate.move(getBlockElements(block), null, jqLite(previousNode));
+                            }
+                            previousNode = block.endNode;
+                        } else {
+                            // new item which we don't know about
+                            childScope = $scope.$new();
+                        }
+
+                        childScope[valueIdentifier] = value;
+                        if (keyIdentifier) childScope[keyIdentifier] = key;
+                        childScope.$index = index;
+                        childScope.$first = (index === 0);
+                        childScope.$last = (index === (arrayLength - 1));
+                        childScope.$middle = !(childScope.$first || childScope.$last);
+                        // jshint bitwise: false
+                        childScope.$odd = !(childScope.$even = (index&1) === 0);
+                        // jshint bitwise: true
+
+                        if (!block.startNode) {
+                            $transclude(childScope, function(clone) {
+                                clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' ');
+                                $animate.enter(clone, null, jqLite(previousNode));
+                                previousNode = clone;
+                                block.scope = childScope;
+                                block.startNode = previousNode && previousNode.endNode ? previousNode.endNode : clone[0];
+                                block.endNode = clone[clone.length - 1];
+                                nextBlockMap[block.id] = block;
+                            });
+                        }
+                    }
+                    lastBlockMap = nextBlockMap;
+                });
+            }
+        };
+    }];
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngShow
+     *
+     * @description
+     * The `ngShow` directive shows or hides the given HTML element based on the expression
+     * provided to the ngShow attribute. The element is shown or hidden by removing or adding
+     * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
+     * in AngularJS and sets the display style to none (using an !important flag).
+     * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
+     *
+     * <pre>
+     * <!-- when $scope.myValue is truthy (element is visible) -->
+     * <div ng-show="myValue"></div>
+     *
+     * <!-- when $scope.myValue is falsy (element is hidden) -->
+     * <div ng-show="myValue" class="ng-hide"></div>
+     * </pre>
+     *
+     * When the ngShow expression evaluates to false then the ng-hide CSS class is added to the class attribute
+     * on the element causing it to become hidden. When true, the ng-hide CSS class is removed
+     * from the element causing the element not to appear hidden.
+     *
+     * ## Why is !important used?
+     *
+     * You may be wondering why !important is used for the .ng-hide CSS class. This is because the `.ng-hide` selector
+     * can be easily overridden by heavier selectors. For example, something as simple
+     * as changing the display style on a HTML list item would make hidden elements appear visible.
+     * This also becomes a bigger issue when dealing with CSS frameworks.
+     *
+     * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
+     * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
+     * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
+     *
+     * ### Overriding .ng-hide
+     *
+     * If you wish to change the hide behavior with ngShow/ngHide then this can be achieved by
+     * restating the styles for the .ng-hide class in CSS:
+     * <pre>
+     * .ng-hide {
+ *   //!annotate CSS Specificity|Not to worry, this will override the AngularJS default...
+ *   display:block!important;
+ *
+ *   //this is just another form of hiding an element
+ *   position:absolute;
+ *   top:-9999px;
+ *   left:-9999px;
+ * }
+     * </pre>
+     *
+     * Just remember to include the important flag so the CSS override will function.
+     *
+     * ## A note about animations with ngShow
+     *
+     * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
+     * is true and false. This system works like the animation system present with ngClass except that
+     * you must also include the !important flag to override the display property
+     * so that you can perform an animation when the element is hidden during the time of the animation.
+     *
+     * <pre>
+     * //
+     * //a working example can be found at the bottom of this page
+     * //
+     * .my-element.ng-hide-add, .my-element.ng-hide-remove {
+ *   transition:0.5s linear all;
+ *   display:block!important;
+ * }
+     *
+     * .my-element.ng-hide-add { ... }
+     * .my-element.ng-hide-add.ng-hide-add-active { ... }
+     * .my-element.ng-hide-remove { ... }
+     * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
+     * </pre>
+     *
+     * @animations
+     * addClass: .ng-hide - happens after the ngShow expression evaluates to a truthy value and the just before contents are set to visible
+     * removeClass: .ng-hide - happens after the ngShow expression evaluates to a non truthy value and just before the contents are set to hidden
+     *
+     * @element ANY
+     * @param {expression} ngShow If the {@link guide/expression expression} is truthy
+     *     then the element is shown or hidden respectively.
+     *
+     * @example
+     <example animations="true">
+     <file name="index.html">
+     Click me: <input type="checkbox" ng-model="checked"><br/>
+     <div>
+     Show:
+     <div class="check-element animate-show" ng-show="checked">
+     <span class="icon-thumbs-up"></span> I show up when your checkbox is checked.
+     </div>
+     </div>
+     <div>
+     Hide:
+     <div class="check-element animate-show" ng-hide="checked">
+     <span class="icon-thumbs-down"></span> I hide when your checkbox is checked.
+     </div>
+     </div>
+     </file>
+     <file name="animations.css">
+     .animate-show {
+        -webkit-transition:all linear 0.5s;
+        transition:all linear 0.5s;
+        line-height:20px;
+        opacity:1;
+        padding:10px;
+        border:1px solid black;
+        background:white;
+      }
+
+     .animate-show.ng-hide-add,
+     .animate-show.ng-hide-remove {
+        display:block!important;
+      }
+
+     .animate-show.ng-hide {
+        line-height:0;
+        opacity:0;
+        padding:0 10px;
+      }
+
+     .check-element {
+        padding:10px;
+        border:1px solid black;
+        background:white;
+      }
+     </file>
+     <file name="scenario.js">
+     it('should check ng-show / ng-hide', function() {
+         expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
+         expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
+
+         input('checked').check();
+
+         expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
+         expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
+       });
+     </file>
+     </example>
+     */
+    var ngShowDirective = ['$animate', function($animate) {
+        return function(scope, element, attr) {
+            scope.$watch(attr.ngShow, function ngShowWatchAction(value){
+                $animate[toBoolean(value) ? 'removeClass' : 'addClass'](element, 'ng-hide');
+            });
+        };
+    }];
+
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngHide
+     *
+     * @description
+     * The `ngHide` directive shows or hides the given HTML element based on the expression
+     * provided to the ngHide attribute. The element is shown or hidden by removing or adding
+     * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
+     * in AngularJS and sets the display style to none (using an !important flag).
+     * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
+     *
+     * <pre>
+     * <!-- when $scope.myValue is truthy (element is hidden) -->
+     * <div ng-hide="myValue"></div>
+     *
+     * <!-- when $scope.myValue is falsy (element is visible) -->
+     * <div ng-hide="myValue" class="ng-hide"></div>
+     * </pre>
+     *
+     * When the ngHide expression evaluates to true then the .ng-hide CSS class is added to the class attribute
+     * on the element causing it to become hidden. When false, the ng-hide CSS class is removed
+     * from the element causing the element not to appear hidden.
+     *
+     * ## Why is !important used?
+     *
+     * You may be wondering why !important is used for the .ng-hide CSS class. This is because the `.ng-hide` selector
+     * can be easily overridden by heavier selectors. For example, something as simple
+     * as changing the display style on a HTML list item would make hidden elements appear visible.
+     * This also becomes a bigger issue when dealing with CSS frameworks.
+     *
+     * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
+     * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
+     * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
+     *
+     * ### Overriding .ng-hide
+     *
+     * If you wish to change the hide behavior with ngShow/ngHide then this can be achieved by
+     * restating the styles for the .ng-hide class in CSS:
+     * <pre>
+     * .ng-hide {
+ *   //!annotate CSS Specificity|Not to worry, this will override the AngularJS default...
+ *   display:block!important;
+ *
+ *   //this is just another form of hiding an element
+ *   position:absolute;
+ *   top:-9999px;
+ *   left:-9999px;
+ * }
+     * </pre>
+     *
+     * Just remember to include the important flag so the CSS override will function.
+     *
+     * ## A note about animations with ngHide
+     *
+     * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
+     * is true and false. This system works like the animation system present with ngClass, except that
+     * you must also include the !important flag to override the display property so
+     * that you can perform an animation when the element is hidden during the time of the animation.
+     *
+     * <pre>
+     * //
+     * //a working example can be found at the bottom of this page
+     * //
+     * .my-element.ng-hide-add, .my-element.ng-hide-remove {
+ *   transition:0.5s linear all;
+ *   display:block!important;
+ * }
+     *
+     * .my-element.ng-hide-add { ... }
+     * .my-element.ng-hide-add.ng-hide-add-active { ... }
+     * .my-element.ng-hide-remove { ... }
+     * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
+     * </pre>
+     *
+     * @animations
+     * removeClass: .ng-hide - happens after the ngHide expression evaluates to a truthy value and just before the contents are set to hidden
+     * addClass: .ng-hide - happens after the ngHide expression evaluates to a non truthy value and just before the contents are set to visible
+     *
+     * @element ANY
+     * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
+     *     the element is shown or hidden respectively.
+     *
+     * @example
+     <example animations="true">
+     <file name="index.html">
+     Click me: <input type="checkbox" ng-model="checked"><br/>
+     <div>
+     Show:
+     <div class="check-element animate-hide" ng-show="checked">
+     <span class="icon-thumbs-up"></span> I show up when your checkbox is checked.
+     </div>
+     </div>
+     <div>
+     Hide:
+     <div class="check-element animate-hide" ng-hide="checked">
+     <span class="icon-thumbs-down"></span> I hide when your checkbox is checked.
+     </div>
+     </div>
+     </file>
+     <file name="animations.css">
+     .animate-hide {
+        -webkit-transition:all linear 0.5s;
+        transition:all linear 0.5s;
+        line-height:20px;
+        opacity:1;
+        padding:10px;
+        border:1px solid black;
+        background:white;
+      }
+
+     .animate-hide.ng-hide-add,
+     .animate-hide.ng-hide-remove {
+        display:block!important;
+      }
+
+     .animate-hide.ng-hide {
+        line-height:0;
+        opacity:0;
+        padding:0 10px;
+      }
+
+     .check-element {
+        padding:10px;
+        border:1px solid black;
+        background:white;
+      }
+     </file>
+     <file name="scenario.js">
+     it('should check ng-show / ng-hide', function() {
+         expect(element('.doc-example-live .check-element:first:hidden').count()).toEqual(1);
+         expect(element('.doc-example-live .check-element:last:visible').count()).toEqual(1);
+
+         input('checked').check();
+
+         expect(element('.doc-example-live .check-element:first:visible').count()).toEqual(1);
+         expect(element('.doc-example-live .check-element:last:hidden').count()).toEqual(1);
+       });
+     </file>
+     </example>
+     */
+    var ngHideDirective = ['$animate', function($animate) {
+        return function(scope, element, attr) {
+            scope.$watch(attr.ngHide, function ngHideWatchAction(value){
+                $animate[toBoolean(value) ? 'addClass' : 'removeClass'](element, 'ng-hide');
+            });
+        };
+    }];
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngStyle
+     * @restrict AC
+     *
+     * @description
+     * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
+     *
+     * @element ANY
+     * @param {expression} ngStyle {@link guide/expression Expression} which evals to an
+     *      object whose keys are CSS style names and values are corresponding values for those CSS
+     *      keys.
+     *
+     * @example
+     <example>
+     <file name="index.html">
+     <input type="button" value="set" ng-click="myStyle={color:'red'}">
+     <input type="button" value="clear" ng-click="myStyle={}">
+     <br/>
+     <span ng-style="myStyle">Sample Text</span>
+     <pre>myStyle={{myStyle}}</pre>
+     </file>
+     <file name="style.css">
+     span {
+         color: black;
+       }
+     </file>
+     <file name="scenario.js">
+     it('should check ng-style', function() {
+         expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
+         element('.doc-example-live :button[value=set]').click();
+         expect(element('.doc-example-live span').css('color')).toBe('rgb(255, 0, 0)');
+         element('.doc-example-live :button[value=clear]').click();
+         expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
+       });
+     </file>
+     </example>
+     */
+    var ngStyleDirective = ngDirective(function(scope, element, attr) {
+        scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
+            if (oldStyles && (newStyles !== oldStyles)) {
+                forEach(oldStyles, function(val, style) { element.css(style, '');});
+            }
+            if (newStyles) element.css(newStyles);
+        }, true);
+    });
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngSwitch
+     * @restrict EA
+     *
+     * @description
+     * The ngSwitch directive is used to conditionally swap DOM structure on your template based on a scope expression.
+     * Elements within ngSwitch but without ngSwitchWhen or ngSwitchDefault directives will be preserved at the location
+     * as specified in the template.
+     *
+     * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
+     * from the template cache), ngSwitch simply choses one of the nested elements and makes it visible based on which element
+     * matches the value obtained from the evaluated expression. In other words, you define a container element
+     * (where you place the directive), place an expression on the **on="..." attribute**
+     * (or the **ng-switch="..." attribute**), define any inner elements inside of the directive and place
+     * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
+     * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
+     * attribute is displayed.
+     *
+     * @animations
+     * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
+     * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
+     *
+     * @usage
+     * <ANY ng-switch="expression">
+     *   <ANY ng-switch-when="matchValue1">...</ANY>
+     *   <ANY ng-switch-when="matchValue2">...</ANY>
+     *   <ANY ng-switch-default>...</ANY>
+     * </ANY>
+     *
+     * @scope
+     * @priority 800
+     * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
+     * @paramDescription
+     * On child elements add:
+     *
+     * * `ngSwitchWhen`: the case statement to match against. If match then this
+     *   case will be displayed. If the same match appears multiple times, all the
+     *   elements will be displayed.
+     * * `ngSwitchDefault`: the default case when no other case match. If there
+     *   are multiple default cases, all of them will be displayed when no other
+     *   case match.
+     *
+     *
+     * @example
+     <example animations="true">
+     <file name="index.html">
+     <div ng-controller="Ctrl">
+     <select ng-model="selection" ng-options="item for item in items">
+     </select>
+     <tt>selection={{selection}}</tt>
+     <hr/>
+     <div class="animate-switch-container"
+     ng-switch on="selection">
+     <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
+     <div class="animate-switch" ng-switch-when="home">Home Span</div>
+     <div class="animate-switch" ng-switch-default>default</div>
+     </div>
+     </div>
+     </file>
+     <file name="script.js">
+     function Ctrl($scope) {
+        $scope.items = ['settings', 'home', 'other'];
+        $scope.selection = $scope.items[0];
+      }
+     </file>
+     <file name="animations.css">
+     .animate-switch-container {
+        position:relative;
+        background:white;
+        border:1px solid black;
+        height:40px;
+        overflow:hidden;
+      }
+
+     .animate-switch {
+        padding:10px;
+      }
+
+     .animate-switch.ng-animate {
+        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
+        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
+
+        position:absolute;
+        top:0;
+        left:0;
+        right:0;
+        bottom:0;
+      }
+
+     .animate-switch.ng-leave.ng-leave-active,
+     .animate-switch.ng-enter {
+        top:-50px;
+      }
+     .animate-switch.ng-leave,
+     .animate-switch.ng-enter.ng-enter-active {
+        top:0;
+      }
+     </file>
+     <file name="scenario.js">
+     it('should start in settings', function() {
+        expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Settings Div/);
+      });
+     it('should change to home', function() {
+        select('selection').option('home');
+        expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Home Span/);
+      });
+     it('should select default', function() {
+        select('selection').option('other');
+        expect(element('.doc-example-live [ng-switch]').text()).toMatch(/default/);
+      });
+     </file>
+     </example>
+     */
+    var ngSwitchDirective = ['$animate', function($animate) {
+        return {
+            restrict: 'EA',
+            require: 'ngSwitch',
+
+            // asks for $scope to fool the BC controller module
+            controller: ['$scope', function ngSwitchController() {
+                this.cases = {};
+            }],
+            link: function(scope, element, attr, ngSwitchController) {
+                var watchExpr = attr.ngSwitch || attr.on,
+                        selectedTranscludes,
+                        selectedElements,
+                        selectedScopes = [];
+
+                scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
+                    for (var i= 0, ii=selectedScopes.length; i<ii; i++) {
+                        selectedScopes[i].$destroy();
+                        $animate.leave(selectedElements[i]);
+                    }
+
+                    selectedElements = [];
+                    selectedScopes = [];
+
+                    if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
+                        scope.$eval(attr.change);
+                        forEach(selectedTranscludes, function(selectedTransclude) {
+                            var selectedScope = scope.$new();
+                            selectedScopes.push(selectedScope);
+                            selectedTransclude.transclude(selectedScope, function(caseElement) {
+                                var anchor = selectedTransclude.element;
+
+                                selectedElements.push(caseElement);
+                                $animate.enter(caseElement, anchor.parent(), anchor);
+                            });
+                        });
+                    }
+                });
+            }
+        };
+    }];
+
+    var ngSwitchWhenDirective = ngDirective({
+        transclude: 'element',
+        priority: 800,
+        require: '^ngSwitch',
+        compile: function(element, attrs) {
+            return function(scope, element, attr, ctrl, $transclude) {
+                ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
+                ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
+            };
+        }
+    });
+
+    var ngSwitchDefaultDirective = ngDirective({
+        transclude: 'element',
+        priority: 800,
+        require: '^ngSwitch',
+        link: function(scope, element, attr, ctrl, $transclude) {
+            ctrl.cases['?'] = (ctrl.cases['?'] || []);
+            ctrl.cases['?'].push({ transclude: $transclude, element: element });
+        }
+    });
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:ngTransclude
+     * @restrict AC
+     *
+     * @description
+     * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
+     *
+     * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
+     *
+     * @element ANY
+     *
+     * @example
+     <doc:example module="transclude">
+     <doc:source>
+     <script>
+     function Ctrl($scope) {
+           $scope.title = 'Lorem Ipsum';
+           $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
+         }
+
+     angular.module('transclude', [])
+     .directive('pane', function(){
+             return {
+               restrict: 'E',
+               transclude: true,
+               scope: { title:'@' },
+     template: '<div style="border: 1px solid black;">' +
+     '<div style="background-color: gray">{{title}}</div>' +
+     '<div ng-transclude></div>' +
+     '</div>'
+     };
+     });
+     </script>
+     <div ng-controller="Ctrl">
+     <input ng-model="title"><br>
+     <textarea ng-model="text"></textarea> <br/>
+     <pane title="{{title}}">{{text}}</pane>
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should have transcluded', function() {
+          input('title').enter('TITLE');
+          input('text').enter('TEXT');
+          expect(binding('title')).toEqual('TITLE');
+          expect(binding('text')).toEqual('TEXT');
+        });
+     </doc:scenario>
+     </doc:example>
+     *
+     */
+    var ngTranscludeDirective = ngDirective({
+        controller: ['$element', '$transclude', function($element, $transclude) {
+            if (!$transclude) {
+                throw minErr('ngTransclude')('orphan',
+                        'Illegal use of ngTransclude directive in the template! ' +
+                                'No parent directive that requires a transclusion found. ' +
+                                'Element: {0}',
+                        startingTag($element));
+            }
+
+            // remember the transclusion fn but call it during linking so that we don't process transclusion before directives on
+            // the parent element even when the transclusion replaces the current element. (we can't use priority here because
+            // that applies only to compile fns and not controllers
+            this.$transclude = $transclude;
+        }],
+
+        link: function($scope, $element, $attrs, controller) {
+            controller.$transclude(function(clone) {
+                $element.html('');
+                $element.append(clone);
+            });
+        }
+    });
+
+    /**
+     * @ngdoc directive
+     * @name ng.directive:script
+     * @restrict E
+     *
+     * @description
+     * Load content of a script tag, with type `text/ng-template`, into `$templateCache`, so that the
+     * template can be used by `ngInclude`, `ngView` or directive templates.
+     *
+     * @param {'text/ng-template'} type must be set to `'text/ng-template'`
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script type="text/ng-template" id="/tpl.html">
+     Content of the template.
+     </script>
+
+     <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
+     <div id="tpl-content" ng-include src="currentTpl"></div>
+     </doc:source>
+     <doc:scenario>
+     it('should load template defined inside script tag', function() {
+        element('#tpl-link').click();
+        expect(element('#tpl-content').text()).toMatch(/Content of the template/);
+      });
+     </doc:scenario>
+     </doc:example>
+     */
+    var scriptDirective = ['$templateCache', function($templateCache) {
+        return {
+            restrict: 'E',
+            terminal: true,
+            compile: function(element, attr) {
+                if (attr.type == 'text/ng-template') {
+                    var templateUrl = attr.id,
+                    // IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent
+                            text = element[0].text;
+
+                    $templateCache.put(templateUrl, text);
+                }
+            }
+        };
+    }];
+
+    var ngOptionsMinErr = minErr('ngOptions');
+    /**
+     * @ngdoc directive
+     * @name ng.directive:select
+     * @restrict E
+     *
+     * @description
+     * HTML `SELECT` element with angular data-binding.
+     *
+     * # `ngOptions`
+     *
+     * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
+     * elements for the `<select>` element using the array or object obtained by evaluating the
+     * `ngOptions` comprehension_expression.
+     *
+     * When an item in the `<select>` menu is selected, the array element or object property
+     * represented by the selected option will be bound to the model identified by the `ngModel`
+     * directive.
+     *
+     * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
+     * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
+     * option. See example below for demonstration.
+     *
+     * Note: `ngOptions` provides iterator facility for `<option>` element which should be used instead
+     * of {@link ng.directive:ngRepeat ngRepeat} when you want the
+     * `select` model to be bound to a non-string value. This is because an option element can only
+     * be bound to string values at present.
+     *
+     * @param {string} ngModel Assignable angular expression to data-bind to.
+     * @param {string=} name Property name of the form under which the control is published.
+     * @param {string=} required The control is considered valid only if value is entered.
+     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
+     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
+     *    `required` when you want to data-bind to the `required` attribute.
+     * @param {comprehension_expression=} ngOptions in one of the following forms:
+     *
+     *   * for array data sources:
+     *     * `label` **`for`** `value` **`in`** `array`
+     *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
+     *     * `label`  **`group by`** `group` **`for`** `value` **`in`** `array`
+     *     * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
+     *   * for object data sources:
+     *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
+     *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
+     *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
+     *     * `select` **`as`** `label` **`group by`** `group`
+     *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
+     *
+     * Where:
+     *
+     *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
+     *   * `value`: local variable which will refer to each item in the `array` or each property value
+     *      of `object` during iteration.
+     *   * `key`: local variable which will refer to a property name in `object` during iteration.
+     *   * `label`: The result of this expression will be the label for `<option>` element. The
+     *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
+     *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
+     *      element. If not specified, `select` expression will default to `value`.
+     *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
+     *      DOM element.
+     *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
+     *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
+     *     `value` variable (e.g. `value.propertyName`).
+     *
+     * @example
+     <doc:example>
+     <doc:source>
+     <script>
+     function MyCntrl($scope) {
+          $scope.colors = [
+            {name:'black', shade:'dark'},
+            {name:'white', shade:'light'},
+            {name:'red', shade:'dark'},
+            {name:'blue', shade:'dark'},
+            {name:'yellow', shade:'light'}
+          ];
+          $scope.color = $scope.colors[2]; // red
+        }
+     </script>
+     <div ng-controller="MyCntrl">
+     <ul>
+     <li ng-repeat="color in colors">
+     Name: <input ng-model="color.name">
+     [<a href ng-click="colors.splice($index, 1)">X</a>]
+     </li>
+     <li>
+     [<a href ng-click="colors.push({})">add</a>]
+     </li>
+     </ul>
+     <hr/>
+     Color (null not allowed):
+     <select ng-model="color" ng-options="c.name for c in colors"></select><br>
+
+     Color (null allowed):
+     <span  class="nullable">
+     <select ng-model="color" ng-options="c.name for c in colors">
+     <option value="">-- choose color --</option>
+     </select>
+     </span><br/>
+
+     Color grouped by shade:
+     <select ng-model="color" ng-options="c.name group by c.shade for c in colors">
+     </select><br/>
+
+
+     Select <a href ng-click="color={name:'not in list'}">bogus</a>.<br>
+     <hr/>
+     Currently selected: {{ {selected_color:color}  }}
+     <div style="border:solid 1px black; height:20px"
+     ng-style="{'background-color':color.name}">
+     </div>
+     </div>
+     </doc:source>
+     <doc:scenario>
+     it('should check ng-options', function() {
+           expect(binding('{selected_color:color}')).toMatch('red');
+           select('color').option('0');
+           expect(binding('{selected_color:color}')).toMatch('black');
+           using('.nullable').select('color').option('');
+           expect(binding('{selected_color:color}')).toMatch('null');
+         });
+     </doc:scenario>
+     </doc:example>
+     */
+
+    var ngOptionsDirective = valueFn({ terminal: true });
+// jshint maxlen: false
+    var selectDirective = ['$compile', '$parse', function($compile,   $parse) {
+        //0000111110000000000022220000000000000000000000333300000000000000444444444444444000000000555555555555555000000066666666666666600000000000000007777000000000000000000088888
+        var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/,
+                nullModelCtrl = {$setViewValue: noop};
+// jshint maxlen: 100
+
+        return {
+            restrict: 'E',
+            require: ['select', '?ngModel'],
+            controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
+                var self = this,
+                        optionsMap = {},
+                        ngModelCtrl = nullModelCtrl,
+                        nullOption,
+                        unknownOption;
+
+
+                self.databound = $attrs.ngModel;
+
+
+                self.init = function(ngModelCtrl_, nullOption_, unknownOption_) {
+                    ngModelCtrl = ngModelCtrl_;
+                    nullOption = nullOption_;
+                    unknownOption = unknownOption_;
+                };
+
+
+                self.addOption = function(value) {
+                    assertNotHasOwnProperty(value, '"option value"');
+                    optionsMap[value] = true;
+
+                    if (ngModelCtrl.$viewValue == value) {
+                        $element.val(value);
+                        if (unknownOption.parent()) unknownOption.remove();
+                    }
+                };
+
+
+                self.removeOption = function(value) {
+                    if (this.hasOption(value)) {
+                        delete optionsMap[value];
+                        if (ngModelCtrl.$viewValue == value) {
+                            this.renderUnknownOption(value);
+                        }
+                    }
+                };
+
+
+                self.renderUnknownOption = function(val) {
+                    var unknownVal = '? ' + hashKey(val) + ' ?';
+                    unknownOption.val(unknownVal);
+                    $element.prepend(unknownOption);
+                    $element.val(unknownVal);
+                    unknownOption.prop('selected', true); // needed for IE
+                };
+
+
+                self.hasOption = function(value) {
+                    return optionsMap.hasOwnProperty(value);
+                };
+
+                $scope.$on('$destroy', function() {
+                    // disable unknown option so that we don't do work when the whole select is being destroyed
+                    self.renderUnknownOption = noop;
+                });
+            }],
+
+            link: function(scope, element, attr, ctrls) {
+                // if ngModel is not defined, we don't need to do anything
+                if (!ctrls[1]) return;
+
+                var selectCtrl = ctrls[0],
+                        ngModelCtrl = ctrls[1],
+                        multiple = attr.multiple,
+                        optionsExp = attr.ngOptions,
+                        nullOption = false, // if false, user will not be able to select it (used by ngOptions)
+                        emptyOption,
+                // we can't just jqLite('<option>') since jqLite is not smart enough
+                // to create it in <select> and IE barfs otherwise.
+                        optionTemplate = jqLite(document.createElement('option')),
+                        optGroupTemplate =jqLite(document.createElement('optgroup')),
+                        unknownOption = optionTemplate.clone();
+
+                // find "null" option
+                for(var i = 0, children = element.children(), ii = children.length; i < ii; i++) {
+                    if (children[i].value === '') {
+                        emptyOption = nullOption = children.eq(i);
+                        break;
+                    }
+                }
+
+                selectCtrl.init(ngModelCtrl, nullOption, unknownOption);
+
+                // required validator
+                if (multiple && (attr.required || attr.ngRequired)) {
+                    var requiredValidator = function(value) {
+                        ngModelCtrl.$setValidity('required', !attr.required || (value && value.length));
+                        return value;
+                    };
+
+                    ngModelCtrl.$parsers.push(requiredValidator);
+                    ngModelCtrl.$formatters.unshift(requiredValidator);
+
+                    attr.$observe('required', function() {
+                        requiredValidator(ngModelCtrl.$viewValue);
+                    });
+                }
+
+                if (optionsExp) setupAsOptions(scope, element, ngModelCtrl);
+                else if (multiple) setupAsMultiple(scope, element, ngModelCtrl);
+                else setupAsSingle(scope, element, ngModelCtrl, selectCtrl);
+
+
+                ////////////////////////////
+
+
+
+                function setupAsSingle(scope, selectElement, ngModelCtrl, selectCtrl) {
+                    ngModelCtrl.$render = function() {
+                        var viewValue = ngModelCtrl.$viewValue;
+
+                        if (selectCtrl.hasOption(viewValue)) {
+                            if (unknownOption.parent()) unknownOption.remove();
+                            selectElement.val(viewValue);
+                            if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy
+                        } else {
+                            if (isUndefined(viewValue) && emptyOption) {
+                                selectElement.val('');
+                            } else {
+                                selectCtrl.renderUnknownOption(viewValue);
+                            }
+                        }
+                    };
+
+                    selectElement.on('change', function() {
+                        scope.$apply(function() {
+                            if (unknownOption.parent()) unknownOption.remove();
+                            ngModelCtrl.$setViewValue(selectElement.val());
+                        });
+                    });
+                }
+
+                function setupAsMultiple(scope, selectElement, ctrl) {
+                    var lastView;
+                    ctrl.$render = function() {
+                        var items = new HashMap(ctrl.$viewValue);
+                        forEach(selectElement.find('option'), function(option) {
+                            option.selected = isDefined(items.get(option.value));
+                        });
+                    };
+
+                    // we have to do it on each watch since ngModel watches reference, but
+                    // we need to work of an array, so we need to see if anything was inserted/removed
+                    scope.$watch(function selectMultipleWatch() {
+                        if (!equals(lastView, ctrl.$viewValue)) {
+                            lastView = copy(ctrl.$viewValue);
+                            ctrl.$render();
+                        }
+                    });
+
+                    selectElement.on('change', function() {
+                        scope.$apply(function() {
+                            var array = [];
+                            forEach(selectElement.find('option'), function(option) {
+                                if (option.selected) {
+                                    array.push(option.value);
+                                }
+                            });
+                            ctrl.$setViewValue(array);
+                        });
+                    });
+                }
+
+                function setupAsOptions(scope, selectElement, ctrl) {
+                    var match;
+
+                    if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) {
+                        throw ngOptionsMinErr('iexp',
+                                "Expected expression in form of " +
+                                        "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
+                                        " but got '{0}'. Element: {1}",
+                                optionsExp, startingTag(selectElement));
+                    }
+
+                    var displayFn = $parse(match[2] || match[1]),
+                            valueName = match[4] || match[6],
+                            keyName = match[5],
+                            groupByFn = $parse(match[3] || ''),
+                            valueFn = $parse(match[2] ? match[1] : valueName),
+                            valuesFn = $parse(match[7]),
+                            track = match[8],
+                            trackFn = track ? $parse(match[8]) : null,
+                    // This is an array of array of existing option groups in DOM.
+                    // We try to reuse these if possible
+                    // - optionGroupsCache[0] is the options with no option group
+                    // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
+                            optionGroupsCache = [[{element: selectElement, label:''}]];
+
+                    if (nullOption) {
+                        // compile the element since there might be bindings in it
+                        $compile(nullOption)(scope);
+
+                        // remove the class, which is added automatically because we recompile the element and it
+                        // becomes the compilation root
+                        nullOption.removeClass('ng-scope');
+
+                        // we need to remove it before calling selectElement.html('') because otherwise IE will
+                        // remove the label from the element. wtf?
+                        nullOption.remove();
+                    }
+
+                    // clear contents, we'll add what's needed based on the model
+                    selectElement.html('');
+
+                    selectElement.on('change', function() {
+                        scope.$apply(function() {
+                            var optionGroup,
+                                    collection = valuesFn(scope) || [],
+                                    locals = {},
+                                    key, value, optionElement, index, groupIndex, length, groupLength, trackIndex;
+
+                            if (multiple) {
+                                value = [];
+                                for (groupIndex = 0, groupLength = optionGroupsCache.length;
+                                     groupIndex < groupLength;
+                                     groupIndex++) {
+                                    // list of options for that group. (first item has the parent)
+                                    optionGroup = optionGroupsCache[groupIndex];
+
+                                    for(index = 1, length = optionGroup.length; index < length; index++) {
+                                        if ((optionElement = optionGroup[index].element)[0].selected) {
+                                            key = optionElement.val();
+                                            if (keyName) locals[keyName] = key;
+                                            if (trackFn) {
+                                                for (trackIndex = 0; trackIndex < collection.length; trackIndex++) {
+                                                    locals[valueName] = collection[trackIndex];
+                                                    if (trackFn(scope, locals) == key) break;
+                                                }
+                                            } else {
+                                                locals[valueName] = collection[key];
+                                            }
+                                            value.push(valueFn(scope, locals));
+                                        }
+                                    }
+                                }
+                            } else {
+                                key = selectElement.val();
+                                if (key == '?') {
+                                    value = undefined;
+                                } else if (key === ''){
+                                    value = null;
+                                } else {
+                                    if (trackFn) {
+                                        for (trackIndex = 0; trackIndex < collection.length; trackIndex++) {
+                                            locals[valueName] = collection[trackIndex];
+                                            if (trackFn(scope, locals) == key) {
+                                                value = valueFn(scope, locals);
+                                                break;
+                                            }
+                                        }
+                                    } else {
+                                        locals[valueName] = collection[key];
+                                        if (keyName) locals[keyName] = key;
+                                        value = valueFn(scope, locals);
+                                    }
+                                }
+                            }
+                            ctrl.$setViewValue(value);
+                        });
+                    });
+
+                    ctrl.$render = render;
+
+                    // TODO(vojta): can't we optimize this ?
+                    scope.$watch(render);
+
+                    function render() {
+                        // Temporary location for the option groups before we render them
+                        var optionGroups = {'':[]},
+                                optionGroupNames = [''],
+                                optionGroupName,
+                                optionGroup,
+                                option,
+                                existingParent, existingOptions, existingOption,
+                                modelValue = ctrl.$modelValue,
+                                values = valuesFn(scope) || [],
+                                keys = keyName ? sortedKeys(values) : values,
+                                key,
+                                groupLength, length,
+                                groupIndex, index,
+                                locals = {},
+                                selected,
+                                selectedSet = false, // nothing is selected yet
+                                lastElement,
+                                element,
+                                label;
+
+                        if (multiple) {
+                            if (trackFn && isArray(modelValue)) {
+                                selectedSet = new HashMap([]);
+                                for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
+                                    locals[valueName] = modelValue[trackIndex];
+                                    selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
+                                }
+                            } else {
+                                selectedSet = new HashMap(modelValue);
+                            }
+                        }
+
+                        // We now build up the list of options we need (we merge later)
+                        for (index = 0; length = keys.length, index < length; index++) {
+
+                            key = index;
+                            if (keyName) {
+                                key = keys[index];
+                                if ( key.charAt(0) === '$' ) continue;
+                                locals[keyName] = key;
+                            }
+
+                            locals[valueName] = values[key];
+
+                            optionGroupName = groupByFn(scope, locals) || '';
+                            if (!(optionGroup = optionGroups[optionGroupName])) {
+                                optionGroup = optionGroups[optionGroupName] = [];
+                                optionGroupNames.push(optionGroupName);
+                            }
+                            if (multiple) {
+                                selected = isDefined(
+                                        selectedSet.remove(trackFn ? trackFn(scope, locals) : valueFn(scope, locals))
+                                );
+                            } else {
+                                if (trackFn) {
+                                    var modelCast = {};
+                                    modelCast[valueName] = modelValue;
+                                    selected = trackFn(scope, modelCast) === trackFn(scope, locals);
+                                } else {
+                                    selected = modelValue === valueFn(scope, locals);
+                                }
+                                selectedSet = selectedSet || selected; // see if at least one item is selected
+                            }
+                            label = displayFn(scope, locals); // what will be seen by the user
+
+                            // doing displayFn(scope, locals) || '' overwrites zero values
+                            label = isDefined(label) ? label : '';
+                            optionGroup.push({
+                                // either the index into array or key from object
+                                id: trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index),
+                                label: label,
+                                selected: selected                   // determine if we should be selected
+                            });
+                        }
+                        if (!multiple) {
+                            if (nullOption || modelValue === null) {
+                                // insert null option if we have a placeholder, or the model is null
+                                optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
+                            } else if (!selectedSet) {
+                                // option could not be found, we have to insert the undefined item
+                                optionGroups[''].unshift({id:'?', label:'', selected:true});
+                            }
+                        }
+
+                        // Now we need to update the list of DOM nodes to match the optionGroups we computed above
+                        for (groupIndex = 0, groupLength = optionGroupNames.length;
+                             groupIndex < groupLength;
+                             groupIndex++) {
+                            // current option group name or '' if no group
+                            optionGroupName = optionGroupNames[groupIndex];
+
+                            // list of options for that group. (first item has the parent)
+                            optionGroup = optionGroups[optionGroupName];
+
+                            if (optionGroupsCache.length <= groupIndex) {
+                                // we need to grow the optionGroups
+                                existingParent = {
+                                    element: optGroupTemplate.clone().attr('label', optionGroupName),
+                                    label: optionGroup.label
+                                };
+                                existingOptions = [existingParent];
+                                optionGroupsCache.push(existingOptions);
+                                selectElement.append(existingParent.element);
+                            } else {
+                                existingOptions = optionGroupsCache[groupIndex];
+                                existingParent = existingOptions[0];  // either SELECT (no group) or OPTGROUP element
+
+                                // update the OPTGROUP label if not the same.
+                                if (existingParent.label != optionGroupName) {
+                                    existingParent.element.attr('label', existingParent.label = optionGroupName);
+                                }
+                            }
+
+                            lastElement = null;  // start at the beginning
+                            for(index = 0, length = optionGroup.length; index < length; index++) {
+                                option = optionGroup[index];
+                                if ((existingOption = existingOptions[index+1])) {
+                                    // reuse elements
+                                    lastElement = existingOption.element;
+                                    if (existingOption.label !== option.label) {
+                                        lastElement.text(existingOption.label = option.label);
+                                    }
+                                    if (existingOption.id !== option.id) {
+                                        lastElement.val(existingOption.id = option.id);
+                                    }
+                                    // lastElement.prop('selected') provided by jQuery has side-effects
+                                    if (lastElement[0].selected !== option.selected) {
+                                        lastElement.prop('selected', (existingOption.selected = option.selected));
+                                    }
+                                } else {
+                                    // grow elements
+
+                                    // if it's a null option
+                                    if (option.id === '' && nullOption) {
+                                        // put back the pre-compiled element
+                                        element = nullOption;
+                                    } else {
+                                        // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
+                                        // in this version of jQuery on some browser the .text() returns a string
+                                        // rather then the element.
+                                        (element = optionTemplate.clone())
+                                                .val(option.id)
+                                                .attr('selected', option.selected)
+                                                .text(option.label);
+                                    }
+
+                                    existingOptions.push(existingOption = {
+                                        element: element,
+                                        label: option.label,
+                                        id: option.id,
+                                        selected: option.selected
+                                    });
+                                    if (lastElement) {
+                                        lastElement.after(element);
+                                    } else {
+                                        existingParent.element.append(element);
+                                    }
+                                    lastElement = element;
+                                }
+                            }
+                            // remove any excessive OPTIONs in a group
+                            index++; // increment since the existingOptions[0] is parent element not OPTION
+                            while(existingOptions.length > index) {
+                                existingOptions.pop().element.remove();
+                            }
+                        }
+                        // remove any excessive OPTGROUPs from select
+                        while(optionGroupsCache.length > groupIndex) {
+                            optionGroupsCache.pop()[0].element.remove();
+                        }
+                    }
+                }
+            }
+        };
+    }];
+
+    var optionDirective = ['$interpolate', function($interpolate) {
+        var nullSelectCtrl = {
+            addOption: noop,
+            removeOption: noop
+        };
+
+        return {
+            restrict: 'E',
+            priority: 100,
+            compile: function(element, attr) {
+                if (isUndefined(attr.value)) {
+                    var interpolateFn = $interpolate(element.text(), true);
+                    if (!interpolateFn) {
+                        attr.$set('value', element.text());
+                    }
+                }
+
+                return function (scope, element, attr) {
+                    var selectCtrlName = '$selectController',
+                            parent = element.parent(),
+                            selectCtrl = parent.data(selectCtrlName) ||
+                                    parent.parent().data(selectCtrlName); // in case we are in optgroup
+
+                    if (selectCtrl && selectCtrl.databound) {
+                        // For some reason Opera defaults to true and if not overridden this messes up the repeater.
+                        // We don't want the view to drive the initialization of the model anyway.
+                        element.prop('selected', false);
+                    } else {
+                        selectCtrl = nullSelectCtrl;
+                    }
+
+                    if (interpolateFn) {
+                        scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
+                            attr.$set('value', newVal);
+                            if (newVal !== oldVal) selectCtrl.removeOption(oldVal);
+                            selectCtrl.addOption(newVal);
+                        });
+                    } else {
+                        selectCtrl.addOption(attr.value);
+                    }
+
+                    element.on('$destroy', function() {
+                        selectCtrl.removeOption(attr.value);
+                    });
+                };
+            }
+        };
+    }];
+
+    var styleDirective = valueFn({
+        restrict: 'E',
+        terminal: true
+    });
+
+    //try to bind to jquery now so that one can write angular.element().read()
+    //but we will rebind on bootstrap again.
+    bindJQuery();
+
+    publishExternalAPI(angular);
+
+    jqLite(document).ready(function() {
+        angularInit(document, bootstrap);
+    });
+
+})(window, document);
+
+!angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}.ng-animate-start{border-spacing:1px 1px;-ms-zoom:1.0001;}.ng-animate-active{border-spacing:0px 0px;-ms-zoom:1;}</style>');
\ No newline at end of file
diff --git a/lib/jquery-2.0.3.js b/lib/jquery.js
similarity index 100%
rename from lib/jquery-2.0.3.js
rename to lib/jquery.js
diff --git a/lib/lodash.js b/lib/lodash.js
new file mode 100755
index 000000000..d29ec8d90
--- /dev/null
+++ b/lib/lodash.js
@@ -0,0 +1,6767 @@
+
+/**
+ * @license
+ * Lo-Dash 2.4.0 (Custom Build) <http://lodash.com/>
+ * Build: `lodash modern -o ./dist/lodash.js`
+ * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
+ * Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
+ * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ * Available under MIT license <http://lodash.com/license>
+ */
+;(function() {
+
+    /** Used as a safe reference for `undefined` in pre ES5 environments */
+    var undefined;
+
+    /** Used to pool arrays and objects used internally */
+    var arrayPool = [],
+            objectPool = [];
+
+    /** Used to generate unique IDs */
+    var idCounter = 0;
+
+    /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */
+    var keyPrefix = +new Date + '';
+
+    /** Used as the size when optimizations are enabled for large arrays */
+    var largeArraySize = 75;
+
+    /** Used as the max size of the `arrayPool` and `objectPool` */
+    var maxPoolSize = 40;
+
+    /** Used to detect and test whitespace */
+    var whitespace = (
+        // whitespace
+            ' \t\x0B\f\xA0\ufeff' +
+
+                // line terminators
+                    '\n\r\u2028\u2029' +
+
+                // unicode category "Zs" space separators
+                    '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
+            );
+
+    /** Used to match empty string literals in compiled template source */
+    var reEmptyStringLeading = /\b__p \+= '';/g,
+            reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
+            reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
+
+    /**
+     * Used to match ES6 template delimiters
+     * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
+     */
+    var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
+
+    /** Used to match regexp flags from their coerced string values */
+    var reFlags = /\w*$/;
+
+    /** Used to detected named functions */
+    var reFuncName = /^\s*function[ \n\r\t]+\w/;
+
+    /** Used to match "interpolate" template delimiters */
+    var reInterpolate = /<%=([\s\S]+?)%>/g;
+
+    /** Used to match leading whitespace and zeros to be removed */
+    var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)');
+
+    /** Used to ensure capturing order of template delimiters */
+    var reNoMatch = /($^)/;
+
+    /** Used to detect functions containing a `this` reference */
+    var reThis = /\bthis\b/;
+
+    /** Used to match unescaped characters in compiled string literals */
+    var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
+
+    /** Used to assign default `context` object properties */
+    var contextProps = [
+        'Array', 'Boolean', 'Date', 'Function', 'Math', 'Number', 'Object',
+        'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN',
+        'parseInt', 'setTimeout'
+    ];
+
+    /** Used to make template sourceURLs easier to identify */
+    var templateCounter = 0;
+
+    /** `Object#toString` result shortcuts */
+    var argsClass = '[object Arguments]',
+            arrayClass = '[object Array]',
+            boolClass = '[object Boolean]',
+            dateClass = '[object Date]',
+            funcClass = '[object Function]',
+            numberClass = '[object Number]',
+            objectClass = '[object Object]',
+            regexpClass = '[object RegExp]',
+            stringClass = '[object String]';
+
+    /** Used to identify object classifications that `_.clone` supports */
+    var cloneableClasses = {};
+    cloneableClasses[funcClass] = false;
+    cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
+            cloneableClasses[boolClass] = cloneableClasses[dateClass] =
+                    cloneableClasses[numberClass] = cloneableClasses[objectClass] =
+                            cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;
+
+    /** Used as an internal `_.debounce` options object */
+    var debounceOptions = {
+        'leading': false,
+        'maxWait': 0,
+        'trailing': false
+    };
+
+    /** Used as the property descriptor for `__bindData__` */
+    var descriptor = {
+        'configurable': false,
+        'enumerable': false,
+        'value': null,
+        'writable': false
+    };
+
+    /** Used to determine if values are of the language type Object */
+    var objectTypes = {
+        'boolean': false,
+        'function': true,
+        'object': true,
+        'number': false,
+        'string': false,
+        'undefined': false
+    };
+
+    /** Used to escape characters for inclusion in compiled string literals */
+    var stringEscapes = {
+        '\\': '\\',
+        "'": "'",
+        '\n': 'n',
+        '\r': 'r',
+        '\t': 't',
+        '\u2028': 'u2028',
+        '\u2029': 'u2029'
+    };
+
+    /** Used as a reference to the global object */
+    var root = (objectTypes[typeof window] && window) || this;
+
+    /** Detect free variable `exports` */
+    var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
+
+    /** Detect free variable `module` */
+    var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
+
+    /** Detect the popular CommonJS extension `module.exports` */
+    var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
+
+    /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
+    var freeGlobal = objectTypes[typeof global] && global;
+    if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
+        root = freeGlobal;
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * The base implementation of `_.indexOf` without support for binary searches
+     * or `fromIndex` constraints.
+     *
+     * @private
+     * @param {Array} array The array to search.
+     * @param {*} value The value to search for.
+     * @param {number} [fromIndex=0] The index to search from.
+     * @returns {number} Returns the index of the matched value or `-1`.
+     */
+    function baseIndexOf(array, value, fromIndex) {
+        var index = (fromIndex || 0) - 1,
+                length = array ? array.length : 0;
+
+        while (++index < length) {
+            if (array[index] === value) {
+                return index;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * An implementation of `_.contains` for cache objects that mimics the return
+     * signature of `_.indexOf` by returning `0` if the value is found, else `-1`.
+     *
+     * @private
+     * @param {Object} cache The cache object to inspect.
+     * @param {*} value The value to search for.
+     * @returns {number} Returns `0` if `value` is found, else `-1`.
+     */
+    function cacheIndexOf(cache, value) {
+        var type = typeof value;
+        cache = cache.cache;
+
+        if (type == 'boolean' || value == null) {
+            return cache[value] ? 0 : -1;
+        }
+        if (type != 'number' && type != 'string') {
+            type = 'object';
+        }
+        var key = type == 'number' ? value : keyPrefix + value;
+        cache = (cache = cache[type]) && cache[key];
+
+        return type == 'object'
+                ? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1)
+                : (cache ? 0 : -1);
+    }
+
+    /**
+     * Adds a given value to the corresponding cache object.
+     *
+     * @private
+     * @param {*} value The value to add to the cache.
+     */
+    function cachePush(value) {
+        var cache = this.cache,
+                type = typeof value;
+
+        if (type == 'boolean' || value == null) {
+            cache[value] = true;
+        } else {
+            if (type != 'number' && type != 'string') {
+                type = 'object';
+            }
+            var key = type == 'number' ? value : keyPrefix + value,
+                    typeCache = cache[type] || (cache[type] = {});
+
+            if (type == 'object') {
+                (typeCache[key] || (typeCache[key] = [])).push(value);
+            } else {
+                typeCache[key] = true;
+            }
+        }
+    }
+
+    /**
+     * Used by `_.max` and `_.min` as the default callback when a given
+     * collection is a string value.
+     *
+     * @private
+     * @param {string} value The character to inspect.
+     * @returns {number} Returns the code unit of given character.
+     */
+    function charAtCallback(value) {
+        return value.charCodeAt(0);
+    }
+
+    /**
+     * Used by `sortBy` to compare transformed `collection` elements, stable sorting
+     * them in ascending order.
+     *
+     * @private
+     * @param {Object} a The object to compare to `b`.
+     * @param {Object} b The object to compare to `a`.
+     * @returns {number} Returns the sort order indicator of `1` or `-1`.
+     */
+    function compareAscending(a, b) {
+        var ac = a.criteria,
+                bc = b.criteria,
+                index = -1,
+                length = ac.length;
+
+        while (++index < length) {
+            var value = ac[index],
+                    other = bc[index];
+
+            if (value !== other) {
+                if (value > other || typeof value == 'undefined') {
+                    return 1;
+                }
+                if (value < other || typeof other == 'undefined') {
+                    return -1;
+                }
+            }
+        }
+        // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+        // that causes it, under certain circumstances, to return the same value for
+        // `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247
+        //
+        // This also ensures a stable sort in V8 and other engines.
+        // See http://code.google.com/p/v8/issues/detail?id=90
+        return a.index - b.index;
+    }
+
+    /**
+     * Creates a cache object to optimize linear searches of large arrays.
+     *
+     * @private
+     * @param {Array} [array=[]] The array to search.
+     * @returns {null|Object} Returns the cache object or `null` if caching should not be used.
+     */
+    function createCache(array) {
+        var index = -1,
+                length = array.length,
+                first = array[0],
+                mid = array[(length / 2) | 0],
+                last = array[length - 1];
+
+        if (first && typeof first == 'object' &&
+                mid && typeof mid == 'object' && last && typeof last == 'object') {
+            return false;
+        }
+        var cache = getObject();
+        cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false;
+
+        var result = getObject();
+        result.array = array;
+        result.cache = cache;
+        result.push = cachePush;
+
+        while (++index < length) {
+            result.push(array[index]);
+        }
+        return result;
+    }
+
+    /**
+     * Used by `template` to escape characters for inclusion in compiled
+     * string literals.
+     *
+     * @private
+     * @param {string} match The matched character to escape.
+     * @returns {string} Returns the escaped character.
+     */
+    function escapeStringChar(match) {
+        return '\\' + stringEscapes[match];
+    }
+
+    /**
+     * Gets an array from the array pool or creates a new one if the pool is empty.
+     *
+     * @private
+     * @returns {Array} The array from the pool.
+     */
+    function getArray() {
+        return arrayPool.pop() || [];
+    }
+
+    /**
+     * Gets an object from the object pool or creates a new one if the pool is empty.
+     *
+     * @private
+     * @returns {Object} The object from the pool.
+     */
+    function getObject() {
+        return objectPool.pop() || {
+            'array': null,
+            'cache': null,
+            'criteria': null,
+            'false': false,
+            'index': 0,
+            'null': false,
+            'number': null,
+            'object': null,
+            'push': null,
+            'string': null,
+            'true': false,
+            'undefined': false,
+            'value': null
+        };
+    }
+
+    /**
+     * Releases the given array back to the array pool.
+     *
+     * @private
+     * @param {Array} [array] The array to release.
+     */
+    function releaseArray(array) {
+        array.length = 0;
+        if (arrayPool.length < maxPoolSize) {
+            arrayPool.push(array);
+        }
+    }
+
+    /**
+     * Releases the given object back to the object pool.
+     *
+     * @private
+     * @param {Object} [object] The object to release.
+     */
+    function releaseObject(object) {
+        var cache = object.cache;
+        if (cache) {
+            releaseObject(cache);
+        }
+        object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
+        if (objectPool.length < maxPoolSize) {
+            objectPool.push(object);
+        }
+    }
+
+    /**
+     * Slices the `collection` from the `start` index up to, but not including,
+     * the `end` index.
+     *
+     * Note: This function is used instead of `Array#slice` to support node lists
+     * in IE < 9 and to ensure dense arrays are returned.
+     *
+     * @private
+     * @param {Array|Object|string} collection The collection to slice.
+     * @param {number} start The start index.
+     * @param {number} end The end index.
+     * @returns {Array} Returns the new array.
+     */
+    function slice(array, start, end) {
+        start || (start = 0);
+        if (typeof end == 'undefined') {
+            end = array ? array.length : 0;
+        }
+        var index = -1,
+                length = end - start || 0,
+                result = Array(length < 0 ? 0 : length);
+
+        while (++index < length) {
+            result[index] = array[start + index];
+        }
+        return result;
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Create a new `lodash` function using the given context object.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {Object} [context=root] The context object.
+     * @returns {Function} Returns the `lodash` function.
+     */
+    function runInContext(context) {
+        // Avoid issues with some ES3 environments that attempt to use values, named
+        // after built-in constructors like `Object`, for the creation of literals.
+        // ES5 clears this up by stating that literals must use built-in constructors.
+        // See http://es5.github.io/#x11.1.5.
+        context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
+
+        /** Native constructor references */
+        var Array = context.Array,
+                Boolean = context.Boolean,
+                Date = context.Date,
+                Function = context.Function,
+                Math = context.Math,
+                Number = context.Number,
+                Object = context.Object,
+                RegExp = context.RegExp,
+                String = context.String,
+                TypeError = context.TypeError;
+
+        /**
+         * Used for `Array` method references.
+         *
+         * Normally `Array.prototype` would suffice, however, using an array literal
+         * avoids issues in Narwhal.
+         */
+        var arrayRef = [];
+
+        /** Used for native method references */
+        var objectProto = Object.prototype;
+
+        /** Used to restore the original `_` reference in `noConflict` */
+        var oldDash = context._;
+
+        /** Used to resolve the internal [[Class]] of values */
+        var toString = objectProto.toString;
+
+        /** Used to detect if a method is native */
+        var reNative = RegExp('^' +
+                String(toString)
+                        .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+                        .replace(/toString| for [^\]]+/g, '.*?') + '$'
+        );
+
+        /** Native method shortcuts */
+        var ceil = Math.ceil,
+                clearTimeout = context.clearTimeout,
+                floor = Math.floor,
+                fnToString = Function.prototype.toString,
+                getPrototypeOf = reNative.test(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
+                hasOwnProperty = objectProto.hasOwnProperty,
+                push = arrayRef.push,
+                setTimeout = context.setTimeout,
+                splice = arrayRef.splice;
+
+        /** Used to set meta data on functions */
+        var defineProperty = (function() {
+            // IE 8 only accepts DOM elements
+            try {
+                var o = {},
+                        func = reNative.test(func = Object.defineProperty) && func,
+                        result = func(o, o, o) && func;
+            } catch(e) { }
+            return result;
+        }());
+
+        /* Native method shortcuts for methods with the same name as other `lodash` methods */
+        var nativeCreate = reNative.test(nativeCreate = Object.create) && nativeCreate,
+                nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,
+                nativeIsFinite = context.isFinite,
+                nativeIsNaN = context.isNaN,
+                nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys,
+                nativeMax = Math.max,
+                nativeMin = Math.min,
+                nativeParseInt = context.parseInt,
+                nativeRandom = Math.random;
+
+        /** Used to lookup a built-in constructor by [[Class]] */
+        var ctorByClass = {};
+        ctorByClass[arrayClass] = Array;
+        ctorByClass[boolClass] = Boolean;
+        ctorByClass[dateClass] = Date;
+        ctorByClass[funcClass] = Function;
+        ctorByClass[objectClass] = Object;
+        ctorByClass[numberClass] = Number;
+        ctorByClass[regexpClass] = RegExp;
+        ctorByClass[stringClass] = String;
+
+        /*--------------------------------------------------------------------------*/
+
+        /**
+         * Creates a `lodash` object which wraps the given value to enable intuitive
+         * method chaining.
+         *
+         * In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
+         * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
+         * and `unshift`
+         *
+         * Chaining is supported in custom builds as long as the `value` method is
+         * implicitly or explicitly included in the build.
+         *
+         * The chainable wrapper functions are:
+         * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
+         * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`,
+         * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`,
+         * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
+         * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
+         * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
+         * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`,
+         * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
+         * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
+         * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
+         * and `zip`
+         *
+         * The non-chainable wrapper functions are:
+         * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`,
+         * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`,
+         * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
+         * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`,
+         * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`,
+         * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`,
+         * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`,
+         * `template`, `unescape`, `uniqueId`, and `value`
+         *
+         * The wrapper functions `first` and `last` return wrapped values when `n` is
+         * provided, otherwise they return unwrapped values.
+         *
+         * Explicit chaining can be enabled by using the `_.chain` method.
+         *
+         * @name _
+         * @constructor
+         * @category Chaining
+         * @param {*} value The value to wrap in a `lodash` instance.
+         * @returns {Object} Returns a `lodash` instance.
+         * @example
+         *
+         * var wrapped = _([1, 2, 3]);
+         *
+         * // returns an unwrapped value
+         * wrapped.reduce(function(sum, num) {
+     *   return sum + num;
+     * });
+         * // => 6
+         *
+         * // returns a wrapped value
+         * var squares = wrapped.map(function(num) {
+     *   return num * num;
+     * });
+         *
+         * _.isArray(squares);
+         * // => false
+         *
+         * _.isArray(squares.value());
+         * // => true
+         */
+        function lodash(value) {
+            // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor
+            return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__'))
+                    ? value
+                    : new lodashWrapper(value);
+        }
+
+        /**
+         * A fast path for creating `lodash` wrapper objects.
+         *
+         * @private
+         * @param {*} value The value to wrap in a `lodash` instance.
+         * @param {boolean} chainAll A flag to enable chaining for all methods
+         * @returns {Object} Returns a `lodash` instance.
+         */
+        function lodashWrapper(value, chainAll) {
+            this.__chain__ = !!chainAll;
+            this.__wrapped__ = value;
+        }
+        // ensure `new lodashWrapper` is an instance of `lodash`
+        lodashWrapper.prototype = lodash.prototype;
+
+        /**
+         * An object used to flag environments features.
+         *
+         * @static
+         * @memberOf _
+         * @type Object
+         */
+        var support = lodash.support = {};
+
+        /**
+         * Detect if functions can be decompiled by `Function#toString`
+         * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps).
+         *
+         * @memberOf _.support
+         * @type boolean
+         */
+        support.funcDecomp = !reNative.test(context.WinRTError) && reThis.test(runInContext);
+
+        /**
+         * Detect if `Function#name` is supported (all but IE).
+         *
+         * @memberOf _.support
+         * @type boolean
+         */
+        support.funcNames = typeof Function.name == 'string';
+
+        /**
+         * By default, the template delimiters used by Lo-Dash are similar to those in
+         * embedded Ruby (ERB). Change the following template settings to use alternative
+         * delimiters.
+         *
+         * @static
+         * @memberOf _
+         * @type Object
+         */
+        lodash.templateSettings = {
+
+            /**
+             * Used to detect `data` property values to be HTML-escaped.
+             *
+             * @memberOf _.templateSettings
+             * @type RegExp
+             */
+            'escape': /<%-([\s\S]+?)%>/g,
+
+            /**
+             * Used to detect code to be evaluated.
+             *
+             * @memberOf _.templateSettings
+             * @type RegExp
+             */
+            'evaluate': /<%([\s\S]+?)%>/g,
+
+            /**
+             * Used to detect `data` property values to inject.
+             *
+             * @memberOf _.templateSettings
+             * @type RegExp
+             */
+            'interpolate': reInterpolate,
+
+            /**
+             * Used to reference the data object in the template text.
+             *
+             * @memberOf _.templateSettings
+             * @type string
+             */
+            'variable': '',
+
+            /**
+             * Used to import variables into the compiled template.
+             *
+             * @memberOf _.templateSettings
+             * @type Object
+             */
+            'imports': {
+
+                /**
+                 * A reference to the `lodash` function.
+                 *
+                 * @memberOf _.templateSettings.imports
+                 * @type Function
+                 */
+                '_': lodash
+            }
+        };
+
+        /*--------------------------------------------------------------------------*/
+
+        /**
+         * The base implementation of `_.bind` that creates the bound function and
+         * sets its meta data.
+         *
+         * @private
+         * @param {Array} bindData The bind data array.
+         * @returns {Function} Returns the new bound function.
+         */
+        function baseBind(bindData) {
+            var func = bindData[0],
+                    partialArgs = bindData[2],
+                    thisArg = bindData[4];
+
+            function bound() {
+                // `Function#bind` spec
+                // http://es5.github.io/#x15.3.4.5
+                if (partialArgs) {
+                    var args = partialArgs.slice();
+                    push.apply(args, arguments);
+                }
+                // mimic the constructor's `return` behavior
+                // http://es5.github.io/#x13.2.2
+                if (this instanceof bound) {
+                    // ensure `new bound` is an instance of `func`
+                    var thisBinding = baseCreate(func.prototype),
+                            result = func.apply(thisBinding, args || arguments);
+                    return isObject(result) ? result : thisBinding;
+                }
+                return func.apply(thisArg, args || arguments);
+            }
+            setBindData(bound, bindData);
+            return bound;
+        }
+
+        /**
+         * The base implementation of `_.clone` without argument juggling or support
+         * for `thisArg` binding.
+         *
+         * @private
+         * @param {*} value The value to clone.
+         * @param {boolean} [isDeep=false] Specify a deep clone.
+         * @param {Function} [callback] The function to customize cloning values.
+         * @param {Array} [stackA=[]] Tracks traversed source objects.
+         * @param {Array} [stackB=[]] Associates clones with source counterparts.
+         * @returns {*} Returns the cloned value.
+         */
+        function baseClone(value, isDeep, callback, stackA, stackB) {
+            if (callback) {
+                var result = callback(value);
+                if (typeof result != 'undefined') {
+                    return result;
+                }
+            }
+            // inspect [[Class]]
+            var isObj = isObject(value);
+            if (isObj) {
+                var className = toString.call(value);
+                if (!cloneableClasses[className]) {
+                    return value;
+                }
+                var ctor = ctorByClass[className];
+                switch (className) {
+                    case boolClass:
+                    case dateClass:
+                        return new ctor(+value);
+
+                    case numberClass:
+                    case stringClass:
+                        return new ctor(value);
+
+                    case regexpClass:
+                        result = ctor(value.source, reFlags.exec(value));
+                        result.lastIndex = value.lastIndex;
+                        return result;
+                }
+            } else {
+                return value;
+            }
+            var isArr = isArray(value);
+            if (isDeep) {
+                // check for circular references and return corresponding clone
+                var initedStack = !stackA;
+                stackA || (stackA = getArray());
+                stackB || (stackB = getArray());
+
+                var length = stackA.length;
+                while (length--) {
+                    if (stackA[length] == value) {
+                        return stackB[length];
+                    }
+                }
+                result = isArr ? ctor(value.length) : {};
+            }
+            else {
+                result = isArr ? slice(value) : assign({}, value);
+            }
+            // add array properties assigned by `RegExp#exec`
+            if (isArr) {
+                if (hasOwnProperty.call(value, 'index')) {
+                    result.index = value.index;
+                }
+                if (hasOwnProperty.call(value, 'input')) {
+                    result.input = value.input;
+                }
+            }
+            // exit for shallow clone
+            if (!isDeep) {
+                return result;
+            }
+            // add the source value to the stack of traversed objects
+            // and associate it with its clone
+            stackA.push(value);
+            stackB.push(result);
+
+            // recursively populate clone (susceptible to call stack limits)
+            (isArr ? forEach : forOwn)(value, function(objValue, key) {
+                result[key] = baseClone(objValue, isDeep, callback, stackA, stackB);
+            });
+
+            if (initedStack) {
+                releaseArray(stackA);
+                releaseArray(stackB);
+            }
+            return result;
+        }
+
+        /**
+         * The base implementation of `_.create` without support for assigning
+         * properties to the created object.
+         *
+         * @private
+         * @param {Object} prototype The object to inherit from.
+         * @returns {Object} Returns the new object.
+         */
+        function baseCreate(prototype, properties) {
+            return isObject(prototype) ? nativeCreate(prototype) : {};
+        }
+        // fallback for browsers without `Object.create`
+        if (!nativeCreate) {
+            baseCreate = (function() {
+                function Object() {}
+                return function(prototype) {
+                    if (isObject(prototype)) {
+                        Object.prototype = prototype;
+                        var result = new Object;
+                        Object.prototype = null;
+                    }
+                    return result || context.Object();
+                };
+            }());
+        }
+
+        /**
+         * The base implementation of `_.createCallback` without support for creating
+         * "_.pluck" or "_.where" style callbacks.
+         *
+         * @private
+         * @param {*} [func=identity] The value to convert to a callback.
+         * @param {*} [thisArg] The `this` binding of the created callback.
+         * @param {number} [argCount] The number of arguments the callback accepts.
+         * @returns {Function} Returns a callback function.
+         */
+        function baseCreateCallback(func, thisArg, argCount) {
+            if (typeof func != 'function') {
+                return identity;
+            }
+            // exit early for no `thisArg` or already bound by `Function#bind`
+            if (typeof thisArg == 'undefined' || !('prototype' in func)) {
+                return func;
+            }
+            var bindData = func.__bindData__;
+            if (typeof bindData == 'undefined') {
+                if (support.funcNames) {
+                    bindData = !func.name;
+                }
+                bindData = bindData || !support.funcDecomp;
+                if (!bindData) {
+                    var source = fnToString.call(func);
+                    if (!support.funcNames) {
+                        bindData = !reFuncName.test(source);
+                    }
+                    if (!bindData) {
+                        // checks if `func` references the `this` keyword and stores the result
+                        bindData = reThis.test(source);
+                        setBindData(func, bindData);
+                    }
+                }
+            }
+            // exit early if there are no `this` references or `func` is bound
+            if (bindData === false || (bindData !== true && bindData[1] & 1)) {
+                return func;
+            }
+            switch (argCount) {
+                case 1: return function(value) {
+                    return func.call(thisArg, value);
+                };
+                case 2: return function(a, b) {
+                    return func.call(thisArg, a, b);
+                };
+                case 3: return function(value, index, collection) {
+                    return func.call(thisArg, value, index, collection);
+                };
+                case 4: return function(accumulator, value, index, collection) {
+                    return func.call(thisArg, accumulator, value, index, collection);
+                };
+            }
+            return bind(func, thisArg);
+        }
+
+        /**
+         * The base implementation of `createWrapper` that creates the wrapper and
+         * sets its meta data.
+         *
+         * @private
+         * @param {Array} bindData The bind data array.
+         * @returns {Function} Returns the new function.
+         */
+        function baseCreateWrapper(bindData) {
+            var func = bindData[0],
+                    bitmask = bindData[1],
+                    partialArgs = bindData[2],
+                    partialRightArgs = bindData[3],
+                    thisArg = bindData[4],
+                    arity = bindData[5];
+
+            var isBind = bitmask & 1,
+                    isBindKey = bitmask & 2,
+                    isCurry = bitmask & 4,
+                    isCurryBound = bitmask & 8,
+                    key = func;
+
+            function bound() {
+                var thisBinding = isBind ? thisArg : this;
+                if (partialArgs) {
+                    var args = partialArgs.slice();
+                    push.apply(args, arguments);
+                }
+                if (partialRightArgs || isCurry) {
+                    args || (args = slice(arguments));
+                    if (partialRightArgs) {
+                        push.apply(args, partialRightArgs);
+                    }
+                    if (isCurry && args.length < arity) {
+                        bitmask |= 16 & ~32;
+                        return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]);
+                    }
+                }
+                args || (args = arguments);
+                if (isBindKey) {
+                    func = thisBinding[key];
+                }
+                if (this instanceof bound) {
+                    thisBinding = baseCreate(func.prototype);
+                    var result = func.apply(thisBinding, args);
+                    return isObject(result) ? result : thisBinding;
+                }
+                return func.apply(thisBinding, args);
+            }
+            setBindData(bound, bindData);
+            return bound;
+        }
+
+        /**
+         * The base implementation of `_.difference` that accepts a single array
+         * of values to exclude.
+         *
+         * @private
+         * @param {Array} array The array to process.
+         * @param {Array} [values] The array of values to exclude.
+         * @returns {Array} Returns a new array of filtered values.
+         */
+        function baseDifference(array, values) {
+            var index = -1,
+                    indexOf = getIndexOf(),
+                    length = array ? array.length : 0,
+                    isLarge = length >= largeArraySize && indexOf === baseIndexOf,
+                    result = [];
+
+            if (isLarge) {
+                var cache = createCache(values);
+                if (cache) {
+                    indexOf = cacheIndexOf;
+                    values = cache;
+                } else {
+                    isLarge = false;
+                }
+            }
+            while (++index < length) {
+                var value = array[index];
+                if (indexOf(values, value) < 0) {
+                    result.push(value);
+                }
+            }
+            if (isLarge) {
+                releaseObject(values);
+            }
+            return result;
+        }
+
+        /**
+         * The base implementation of `_.flatten` without support for callback
+         * shorthands or `thisArg` binding.
+         *
+         * @private
+         * @param {Array} array The array to flatten.
+         * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+         * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
+         * @param {number} [fromIndex=0] The index to start from.
+         * @returns {Array} Returns a new flattened array.
+         */
+        function baseFlatten(array, isShallow, isStrict, fromIndex) {
+            var index = (fromIndex || 0) - 1,
+                    length = array ? array.length : 0,
+                    result = [];
+
+            while (++index < length) {
+                var value = array[index];
+
+                if (value && typeof value == 'object' && typeof value.length == 'number'
+                        && (isArray(value) || isArguments(value))) {
+                    // recursively flatten arrays (susceptible to call stack limits)
+                    if (!isShallow) {
+                        value = baseFlatten(value, isShallow, isStrict);
+                    }
+                    var valIndex = -1,
+                            valLength = value.length,
+                            resIndex = result.length;
+
+                    result.length += valLength;
+                    while (++valIndex < valLength) {
+                        result[resIndex++] = value[valIndex];
+                    }
+                } else if (!isStrict) {
+                    result.push(value);
+                }
+            }
+            return result;
+        }
+
+        /**
+         * The base implementation of `_.isEqual`, without support for `thisArg` binding,
+         * that allows partial "_.where" style comparisons.
+         *
+         * @private
+         * @param {*} a The value to compare.
+         * @param {*} b The other value to compare.
+         * @param {Function} [callback] The function to customize comparing values.
+         * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons.
+         * @param {Array} [stackA=[]] Tracks traversed `a` objects.
+         * @param {Array} [stackB=[]] Tracks traversed `b` objects.
+         * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+         */
+        function baseIsEqual(a, b, callback, isWhere, stackA, stackB) {
+            // used to indicate that when comparing objects, `a` has at least the properties of `b`
+            if (callback) {
+                var result = callback(a, b);
+                if (typeof result != 'undefined') {
+                    return !!result;
+                }
+            }
+            // exit early for identical values
+            if (a === b) {
+                // treat `+0` vs. `-0` as not equal
+                return a !== 0 || (1 / a == 1 / b);
+            }
+            var type = typeof a,
+                    otherType = typeof b;
+
+            // exit early for unlike primitive values
+            if (a === a &&
+                    !(a && objectTypes[type]) &&
+                    !(b && objectTypes[otherType])) {
+                return false;
+            }
+            // exit early for `null` and `undefined` avoiding ES3's Function#call behavior
+            // http://es5.github.io/#x15.3.4.4
+            if (a == null || b == null) {
+                return a === b;
+            }
+            // compare [[Class]] names
+            var className = toString.call(a),
+                    otherClass = toString.call(b);
+
+            if (className == argsClass) {
+                className = objectClass;
+            }
+            if (otherClass == argsClass) {
+                otherClass = objectClass;
+            }
+            if (className != otherClass) {
+                return false;
+            }
+            switch (className) {
+                case boolClass:
+                case dateClass:
+                    // coerce dates and booleans to numbers, dates to milliseconds and booleans
+                    // to `1` or `0` treating invalid dates coerced to `NaN` as not equal
+                    return +a == +b;
+
+                case numberClass:
+                    // treat `NaN` vs. `NaN` as equal
+                    return (a != +a)
+                            ? b != +b
+                        // but treat `+0` vs. `-0` as not equal
+                            : (a == 0 ? (1 / a == 1 / b) : a == +b);
+
+                case regexpClass:
+                case stringClass:
+                    // coerce regexes to strings (http://es5.github.io/#x15.10.6.4)
+                    // treat string primitives and their corresponding object instances as equal
+                    return a == String(b);
+            }
+            var isArr = className == arrayClass;
+            if (!isArr) {
+                // unwrap any `lodash` wrapped values
+                var aWrapped = hasOwnProperty.call(a, '__wrapped__'),
+                        bWrapped = hasOwnProperty.call(b, '__wrapped__');
+
+                if (aWrapped || bWrapped) {
+                    return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB);
+                }
+                // exit for functions and DOM nodes
+                if (className != objectClass) {
+                    return false;
+                }
+                // in older versions of Opera, `arguments` objects have `Array` constructors
+                var ctorA = a.constructor,
+                        ctorB = b.constructor;
+
+                // non `Object` object instances with different constructors are not equal
+                if (ctorA != ctorB &&
+                        !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
+                        ('constructor' in a && 'constructor' in b)
+                        ) {
+                    return false;
+                }
+            }
+            // assume cyclic structures are equal
+            // the algorithm for detecting cyclic structures is adapted from ES 5.1
+            // section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3)
+            var initedStack = !stackA;
+            stackA || (stackA = getArray());
+            stackB || (stackB = getArray());
+
+            var length = stackA.length;
+            while (length--) {
+                if (stackA[length] == a) {
+                    return stackB[length] == b;
+                }
+            }
+            var size = 0;
+            result = true;
+
+            // add `a` and `b` to the stack of traversed objects
+            stackA.push(a);
+            stackB.push(b);
+
+            // recursively compare objects and arrays (susceptible to call stack limits)
+            if (isArr) {
+                length = a.length;
+                size = b.length;
+
+                // compare lengths to determine if a deep comparison is necessary
+                result = size == a.length;
+                if (!result && !isWhere) {
+                    return result;
+                }
+                // deep compare the contents, ignoring non-numeric properties
+                while (size--) {
+                    var index = length,
+                            value = b[size];
+
+                    if (isWhere) {
+                        while (index--) {
+                            if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) {
+                                break;
+                            }
+                        }
+                    } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) {
+                        break;
+                    }
+                }
+                return result;
+            }
+            // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
+            // which, in this case, is more costly
+            forIn(b, function(value, key, b) {
+                if (hasOwnProperty.call(b, key)) {
+                    // count the number of properties.
+                    size++;
+                    // deep compare each property value.
+                    return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB));
+                }
+            });
+
+            if (result && !isWhere) {
+                // ensure both objects have the same number of properties
+                forIn(a, function(value, key, a) {
+                    if (hasOwnProperty.call(a, key)) {
+                        // `size` will be `-1` if `a` has more properties than `b`
+                        return (result = --size > -1);
+                    }
+                });
+            }
+            if (initedStack) {
+                releaseArray(stackA);
+                releaseArray(stackB);
+            }
+            return result;
+        }
+
+        /**
+         * The base implementation of `_.merge` without argument juggling or support
+         * for `thisArg` binding.
+         *
+         * @private
+         * @param {Object} object The destination object.
+         * @param {Object} source The source object.
+         * @param {Function} [callback] The function to customize merging properties.
+         * @param {Array} [stackA=[]] Tracks traversed source objects.
+         * @param {Array} [stackB=[]] Associates values with source counterparts.
+         */
+        function baseMerge(object, source, callback, stackA, stackB) {
+            (isArray(source) ? forEach : forOwn)(source, function(source, key) {
+                var found,
+                        isArr,
+                        result = source,
+                        value = object[key];
+
+                if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
+                    // avoid merging previously merged cyclic sources
+                    var stackLength = stackA.length;
+                    while (stackLength--) {
+                        if ((found = stackA[stackLength] == source)) {
+                            value = stackB[stackLength];
+                            break;
+                        }
+                    }
+                    if (!found) {
+                        var isShallow;
+                        if (callback) {
+                            result = callback(value, source);
+                            if ((isShallow = typeof result != 'undefined')) {
+                                value = result;
+                            }
+                        }
+                        if (!isShallow) {
+                            value = isArr
+                                    ? (isArray(value) ? value : [])
+                                    : (isPlainObject(value) ? value : {});
+                        }
+                        // add `source` and associated `value` to the stack of traversed objects
+                        stackA.push(source);
+                        stackB.push(value);
+
+                        // recursively merge objects and arrays (susceptible to call stack limits)
+                        if (!isShallow) {
+                            baseMerge(value, source, callback, stackA, stackB);
+                        }
+                    }
+                }
+                else {
+                    if (callback) {
+                        result = callback(value, source);
+                        if (typeof result == 'undefined') {
+                            result = source;
+                        }
+                    }
+                    if (typeof result != 'undefined') {
+                        value = result;
+                    }
+                }
+                object[key] = value;
+            });
+        }
+
+        /**
+         * The base implementation of `_.random` without argument juggling or support
+         * for returning floating-point numbers.
+         *
+         * @private
+         * @param {number} min The minimum possible value.
+         * @param {number} max The maximum possible value.
+         * @returns {number} Returns a random number.
+         */
+        function baseRandom(min, max) {
+            return min + floor(nativeRandom() * (max - min + 1));
+        }
+
+        /**
+         * The base implementation of `_.uniq` without support for callback shorthands
+         * or `thisArg` binding.
+         *
+         * @private
+         * @param {Array} array The array to process.
+         * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+         * @param {Function} [callback] The function called per iteration.
+         * @returns {Array} Returns a duplicate-value-free array.
+         */
+        function baseUniq(array, isSorted, callback) {
+            var index = -1,
+                    indexOf = getIndexOf(),
+                    length = array ? array.length : 0,
+                    result = [];
+
+            var isLarge = !isSorted && length >= largeArraySize && indexOf === baseIndexOf,
+                    seen = (callback || isLarge) ? getArray() : result;
+
+            if (isLarge) {
+                var cache = createCache(seen);
+                if (cache) {
+                    indexOf = cacheIndexOf;
+                    seen = cache;
+                } else {
+                    isLarge = false;
+                    seen = callback ? seen : (releaseArray(seen), result);
+                }
+            }
+            while (++index < length) {
+                var value = array[index],
+                        computed = callback ? callback(value, index, array) : value;
+
+                if (isSorted
+                        ? !index || seen[seen.length - 1] !== computed
+                        : indexOf(seen, computed) < 0
+                        ) {
+                    if (callback || isLarge) {
+                        seen.push(computed);
+                    }
+                    result.push(value);
+                }
+            }
+            if (isLarge) {
+                releaseArray(seen.array);
+                releaseObject(seen);
+            } else if (callback) {
+                releaseArray(seen);
+            }
+            return result;
+        }
+
+        /**
+         * Creates a function that aggregates a collection, creating an object composed
+         * of keys generated from the results of running each element of the collection
+         * through a callback. The given `setter` function sets the keys and values
+         * of the composed object.
+         *
+         * @private
+         * @param {Function} setter The setter function.
+         * @returns {Function} Returns the new aggregator function.
+         */
+        function createAggregator(setter) {
+            return function(collection, callback, thisArg) {
+                var result = {};
+                callback = lodash.createCallback(callback, thisArg, 3);
+
+                var index = -1,
+                        length = collection ? collection.length : 0;
+
+                if (typeof length == 'number') {
+                    while (++index < length) {
+                        var value = collection[index];
+                        setter(result, value, callback(value, index, collection), collection);
+                    }
+                } else {
+                    forOwn(collection, function(value, key, collection) {
+                        setter(result, value, callback(value, key, collection), collection);
+                    });
+                }
+                return result;
+            };
+        }
+
+        /**
+         * Creates a function that, when called, either curries or invokes `func`
+         * with an optional `this` binding and partially applied arguments.
+         *
+         * @private
+         * @param {Function|string} func The function or method name to reference.
+         * @param {number} bitmask The bitmask of method flags to compose.
+         *  The bitmask may be composed of the following flags:
+         *  1 - `_.bind`
+         *  2 - `_.bindKey`
+         *  4 - `_.curry`
+         *  8 - `_.curry` (bound)
+         *  16 - `_.partial`
+         *  32 - `_.partialRight`
+         * @param {Array} [partialArgs] An array of arguments to prepend to those
+         *  provided to the new function.
+         * @param {Array} [partialRightArgs] An array of arguments to append to those
+         *  provided to the new function.
+         * @param {*} [thisArg] The `this` binding of `func`.
+         * @param {number} [arity] The arity of `func`.
+         * @returns {Function} Returns the new function.
+         */
+        function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
+            var isBind = bitmask & 1,
+                    isBindKey = bitmask & 2,
+                    isCurry = bitmask & 4,
+                    isCurryBound = bitmask & 8,
+                    isPartial = bitmask & 16,
+                    isPartialRight = bitmask & 32;
+
+            if (!isBindKey && !isFunction(func)) {
+                throw new TypeError;
+            }
+            if (isPartial && !partialArgs.length) {
+                bitmask &= ~16;
+                isPartial = partialArgs = false;
+            }
+            if (isPartialRight && !partialRightArgs.length) {
+                bitmask &= ~32;
+                isPartialRight = partialRightArgs = false;
+            }
+            var bindData = func && func.__bindData__;
+            if (bindData && bindData !== true) {
+                bindData = bindData.slice();
+
+                // set `thisBinding` is not previously bound
+                if (isBind && !(bindData[1] & 1)) {
+                    bindData[4] = thisArg;
+                }
+                // set if previously bound but not currently (subsequent curried functions)
+                if (!isBind && bindData[1] & 1) {
+                    bitmask |= 8;
+                }
+                // set curried arity if not yet set
+                if (isCurry && !(bindData[1] & 4)) {
+                    bindData[5] = arity;
+                }
+                // append partial left arguments
+                if (isPartial) {
+                    push.apply(bindData[2] || (bindData[2] = []), partialArgs);
+                }
+                // append partial right arguments
+                if (isPartialRight) {
+                    push.apply(bindData[3] || (bindData[3] = []), partialRightArgs);
+                }
+                // merge flags
+                bindData[1] |= bitmask;
+                return createWrapper.apply(null, bindData);
+            }
+            // fast path for `_.bind`
+            var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper;
+            return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]);
+        }
+
+        /**
+         * Used by `escape` to convert characters to HTML entities.
+         *
+         * @private
+         * @param {string} match The matched character to escape.
+         * @returns {string} Returns the escaped character.
+         */
+        function escapeHtmlChar(match) {
+            return htmlEscapes[match];
+        }
+
+        /**
+         * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
+         * customized, this method returns the custom method, otherwise it returns
+         * the `baseIndexOf` function.
+         *
+         * @private
+         * @returns {Function} Returns the "indexOf" function.
+         */
+        function getIndexOf() {
+            var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result;
+            return result;
+        }
+
+        /**
+         * Sets `this` binding data on a given function.
+         *
+         * @private
+         * @param {Function} func The function to set data on.
+         * @param {Array} value The data array to set.
+         */
+        var setBindData = !defineProperty ? noop : function(func, value) {
+            descriptor.value = value;
+            defineProperty(func, '__bindData__', descriptor);
+        };
+
+        /**
+         * A fallback implementation of `isPlainObject` which checks if a given value
+         * is an object created by the `Object` constructor, assuming objects created
+         * by the `Object` constructor have no inherited enumerable properties and that
+         * there are no `Object.prototype` extensions.
+         *
+         * @private
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+         */
+        function shimIsPlainObject(value) {
+            var ctor,
+                    result;
+
+            // avoid non Object objects, `arguments` objects, and DOM elements
+            if (!(value && toString.call(value) == objectClass) ||
+                    (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor))) {
+                return false;
+            }
+            // In most environments an object's own properties are iterated before
+            // its inherited properties. If the last iterated property is an object's
+            // own property then there are no inherited enumerable properties.
+            forIn(value, function(value, key) {
+                result = key;
+            });
+            return typeof result == 'undefined' || hasOwnProperty.call(value, result);
+        }
+
+        /**
+         * Used by `unescape` to convert HTML entities to characters.
+         *
+         * @private
+         * @param {string} match The matched character to unescape.
+         * @returns {string} Returns the unescaped character.
+         */
+        function unescapeHtmlChar(match) {
+            return htmlUnescapes[match];
+        }
+
+        /*--------------------------------------------------------------------------*/
+
+        /**
+         * Checks if `value` is an `arguments` object.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
+         * @example
+         *
+         * (function() { return _.isArguments(arguments); })(1, 2, 3);
+         * // => true
+         *
+         * _.isArguments([1, 2, 3]);
+         * // => false
+         */
+        function isArguments(value) {
+            return value && typeof value == 'object' && typeof value.length == 'number' &&
+                    toString.call(value) == argsClass || false;
+        }
+
+        /**
+         * Checks if `value` is an array.
+         *
+         * @static
+         * @memberOf _
+         * @type Function
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is an array, else `false`.
+         * @example
+         *
+         * (function() { return _.isArray(arguments); })();
+         * // => false
+         *
+         * _.isArray([1, 2, 3]);
+         * // => true
+         */
+        var isArray = nativeIsArray || function(value) {
+            return value && typeof value == 'object' && typeof value.length == 'number' &&
+                    toString.call(value) == arrayClass || false;
+        };
+
+        /**
+         * A fallback implementation of `Object.keys` which produces an array of the
+         * given object's own enumerable property names.
+         *
+         * @private
+         * @type Function
+         * @param {Object} object The object to inspect.
+         * @returns {Array} Returns an array of property names.
+         */
+        var shimKeys = function(object) {
+            var index, iterable = object, result = [];
+            if (!iterable) return result;
+            if (!(objectTypes[typeof object])) return result;
+            for (index in iterable) {
+                if (hasOwnProperty.call(iterable, index)) {
+                    result.push(index);
+                }
+            }
+            return result
+        };
+
+        /**
+         * Creates an array composed of the own enumerable property names of an object.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} object The object to inspect.
+         * @returns {Array} Returns an array of property names.
+         * @example
+         *
+         * _.keys({ 'one': 1, 'two': 2, 'three': 3 });
+         * // => ['one', 'two', 'three'] (property order is not guaranteed across environments)
+         */
+        var keys = !nativeKeys ? shimKeys : function(object) {
+            if (!isObject(object)) {
+                return [];
+            }
+            return nativeKeys(object);
+        };
+
+        /**
+         * Used to convert characters to HTML entities:
+         *
+         * Though the `>` character is escaped for symmetry, characters like `>` and `/`
+         * don't require escaping in HTML and have no special meaning unless they're part
+         * of a tag or an unquoted attribute value.
+         * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
+         */
+        var htmlEscapes = {
+            '&': '&amp;',
+            '<': '&lt;',
+            '>': '&gt;',
+            '"': '&quot;',
+            "'": '&#39;'
+        };
+
+        /** Used to convert HTML entities to characters */
+        var htmlUnescapes = invert(htmlEscapes);
+
+        /** Used to match HTML entities and HTML characters */
+        var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'),
+                reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g');
+
+        /*--------------------------------------------------------------------------*/
+
+        /**
+         * Assigns own enumerable properties of source object(s) to the destination
+         * object. Subsequent sources will overwrite property assignments of previous
+         * sources. If a callback is provided it will be executed to produce the
+         * assigned values. The callback is bound to `thisArg` and invoked with two
+         * arguments; (objectValue, sourceValue).
+         *
+         * @static
+         * @memberOf _
+         * @type Function
+         * @alias extend
+         * @category Objects
+         * @param {Object} object The destination object.
+         * @param {...Object} [source] The source objects.
+         * @param {Function} [callback] The function to customize assigning values.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Object} Returns the destination object.
+         * @example
+         *
+         * _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
+         * // => { 'name': 'fred', 'employer': 'slate' }
+         *
+         * var defaults = _.partialRight(_.assign, function(a, b) {
+     *   return typeof a == 'undefined' ? b : a;
+     * });
+         *
+         * var object = { 'name': 'barney' };
+         * defaults(object, { 'name': 'fred', 'employer': 'slate' });
+         * // => { 'name': 'barney', 'employer': 'slate' }
+         */
+        var assign = function(object, source, guard) {
+            var index, iterable = object, result = iterable;
+            if (!iterable) return result;
+            var args = arguments,
+                    argsIndex = 0,
+                    argsLength = typeof guard == 'number' ? 2 : args.length;
+            if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {
+                var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);
+            } else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {
+                callback = args[--argsLength];
+            }
+            while (++argsIndex < argsLength) {
+                iterable = args[argsIndex];
+                if (iterable && objectTypes[typeof iterable]) {
+                    var ownIndex = -1,
+                            ownProps = objectTypes[typeof iterable] && keys(iterable),
+                            length = ownProps ? ownProps.length : 0;
+
+                    while (++ownIndex < length) {
+                        index = ownProps[ownIndex];
+                        result[index] = callback ? callback(result[index], iterable[index]) : iterable[index];
+                    }
+                }
+            }
+            return result
+        };
+
+        /**
+         * Creates a clone of `value`. If `isDeep` is `true` nested objects will also
+         * be cloned, otherwise they will be assigned by reference. If a callback
+         * is provided it will be executed to produce the cloned values. If the
+         * callback returns `undefined` cloning will be handled by the method instead.
+         * The callback is bound to `thisArg` and invoked with one argument; (value).
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to clone.
+         * @param {boolean} [isDeep=false] Specify a deep clone.
+         * @param {Function} [callback] The function to customize cloning values.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {*} Returns the cloned value.
+         * @example
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 36 },
+         *   { 'name': 'fred',   'age': 40 }
+         * ];
+         *
+         * var shallow = _.clone(characters);
+         * shallow[0] === characters[0];
+         * // => true
+         *
+         * var deep = _.clone(characters, true);
+         * deep[0] === characters[0];
+         * // => false
+         *
+         * _.mixin({
+     *   'clone': _.partialRight(_.clone, function(value) {
+     *     return _.isElement(value) ? value.cloneNode(false) : undefined;
+     *   })
+     * });
+         *
+         * var clone = _.clone(document.body);
+         * clone.childNodes.length;
+         * // => 0
+         */
+        function clone(value, isDeep, callback, thisArg) {
+            // allows working with "Collections" methods without using their `index`
+            // and `collection` arguments for `isDeep` and `callback`
+            if (typeof isDeep != 'boolean' && isDeep != null) {
+                thisArg = callback;
+                callback = isDeep;
+                isDeep = false;
+            }
+            return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+        }
+
+        /**
+         * Creates a deep clone of `value`. If a callback is provided it will be
+         * executed to produce the cloned values. If the callback returns `undefined`
+         * cloning will be handled by the method instead. The callback is bound to
+         * `thisArg` and invoked with one argument; (value).
+         *
+         * Note: This method is loosely based on the structured clone algorithm. Functions
+         * and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and
+         * objects created by constructors other than `Object` are cloned to plain `Object` objects.
+         * See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to deep clone.
+         * @param {Function} [callback] The function to customize cloning values.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {*} Returns the deep cloned value.
+         * @example
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 36 },
+         *   { 'name': 'fred',   'age': 40 }
+         * ];
+         *
+         * var deep = _.cloneDeep(characters);
+         * deep[0] === characters[0];
+         * // => false
+         *
+         * var view = {
+     *   'label': 'docs',
+     *   'node': element
+     * };
+         *
+         * var clone = _.cloneDeep(view, function(value) {
+     *   return _.isElement(value) ? value.cloneNode(true) : undefined;
+     * });
+         *
+         * clone.node == view.node;
+         * // => false
+         */
+        function cloneDeep(value, callback, thisArg) {
+            return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+        }
+
+        /**
+         * Creates an object that inherits from the given `prototype` object. If a
+         * `properties` object is provided its own enumerable properties are assigned
+         * to the created object.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} prototype The object to inherit from.
+         * @param {Object} [properties] The properties to assign to the object.
+         * @returns {Object} Returns the new object.
+         * @example
+         *
+         * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+         *
+         * function Circle() {
+     *   Shape.call(this);
+     * }
+         *
+         * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle });
+         *
+         * var circle = new Circle;
+         * circle instanceof Circle;
+         * // => true
+         *
+         * circle instanceof Shape;
+         * // => true
+         */
+        function create(prototype, properties) {
+            var result = baseCreate(prototype);
+            return properties ? assign(result, properties) : result;
+        }
+
+        /**
+         * Assigns own enumerable properties of source object(s) to the destination
+         * object for all destination properties that resolve to `undefined`. Once a
+         * property is set, additional defaults of the same property will be ignored.
+         *
+         * @static
+         * @memberOf _
+         * @type Function
+         * @category Objects
+         * @param {Object} object The destination object.
+         * @param {...Object} [source] The source objects.
+         * @param- {Object} [guard] Allows working with `_.reduce` without using its
+         *  `key` and `object` arguments as sources.
+         * @returns {Object} Returns the destination object.
+         * @example
+         *
+         * var object = { 'name': 'barney' };
+         * _.defaults(object, { 'name': 'fred', 'employer': 'slate' });
+         * // => { 'name': 'barney', 'employer': 'slate' }
+         */
+        var defaults = function(object, source, guard) {
+            var index, iterable = object, result = iterable;
+            if (!iterable) return result;
+            var args = arguments,
+                    argsIndex = 0,
+                    argsLength = typeof guard == 'number' ? 2 : args.length;
+            while (++argsIndex < argsLength) {
+                iterable = args[argsIndex];
+                if (iterable && objectTypes[typeof iterable]) {
+                    var ownIndex = -1,
+                            ownProps = objectTypes[typeof iterable] && keys(iterable),
+                            length = ownProps ? ownProps.length : 0;
+
+                    while (++ownIndex < length) {
+                        index = ownProps[ownIndex];
+                        if (typeof result[index] == 'undefined') result[index] = iterable[index];
+                    }
+                }
+            }
+            return result
+        };
+
+        /**
+         * This method is like `_.findIndex` except that it returns the key of the
+         * first element that passes the callback check, instead of the element itself.
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} object The object to search.
+         * @param {Function|Object|string} [callback=identity] The function called per
+         *  iteration. If a property name or object is provided it will be used to
+         *  create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+         * @example
+         *
+         * var characters = {
+     *   'barney': {  'age': 36, 'blocked': false },
+     *   'fred': {    'age': 40, 'blocked': true },
+     *   'pebbles': { 'age': 1,  'blocked': false }
+     * };
+         *
+         * _.findKey(characters, function(chr) {
+     *   return chr.age < 40;
+     * });
+         * // => 'barney' (property order is not guaranteed across environments)
+         *
+         * // using "_.where" callback shorthand
+         * _.findKey(characters, { 'age': 1 });
+         * // => 'pebbles'
+         *
+         * // using "_.pluck" callback shorthand
+         * _.findKey(characters, 'blocked');
+         * // => 'fred'
+         */
+        function findKey(object, callback, thisArg) {
+            var result;
+            callback = lodash.createCallback(callback, thisArg, 3);
+            forOwn(object, function(value, key, object) {
+                if (callback(value, key, object)) {
+                    result = key;
+                    return false;
+                }
+            });
+            return result;
+        }
+
+        /**
+         * This method is like `_.findKey` except that it iterates over elements
+         * of a `collection` in the opposite order.
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} object The object to search.
+         * @param {Function|Object|string} [callback=identity] The function called per
+         *  iteration. If a property name or object is provided it will be used to
+         *  create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+         * @example
+         *
+         * var characters = {
+     *   'barney': {  'age': 36, 'blocked': true },
+     *   'fred': {    'age': 40, 'blocked': false },
+     *   'pebbles': { 'age': 1,  'blocked': true }
+     * };
+         *
+         * _.findLastKey(characters, function(chr) {
+     *   return chr.age < 40;
+     * });
+         * // => returns `pebbles`, assuming `_.findKey` returns `barney`
+         *
+         * // using "_.where" callback shorthand
+         * _.findLastKey(characters, { 'age': 40 });
+         * // => 'fred'
+         *
+         * // using "_.pluck" callback shorthand
+         * _.findLastKey(characters, 'blocked');
+         * // => 'pebbles'
+         */
+        function findLastKey(object, callback, thisArg) {
+            var result;
+            callback = lodash.createCallback(callback, thisArg, 3);
+            forOwnRight(object, function(value, key, object) {
+                if (callback(value, key, object)) {
+                    result = key;
+                    return false;
+                }
+            });
+            return result;
+        }
+
+        /**
+         * Iterates over own and inherited enumerable properties of an object,
+         * executing the callback for each property. The callback is bound to `thisArg`
+         * and invoked with three arguments; (value, key, object). Callbacks may exit
+         * iteration early by explicitly returning `false`.
+         *
+         * @static
+         * @memberOf _
+         * @type Function
+         * @category Objects
+         * @param {Object} object The object to iterate over.
+         * @param {Function} [callback=identity] The function called per iteration.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Object} Returns `object`.
+         * @example
+         *
+         * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+         *
+         * Shape.prototype.move = function(x, y) {
+     *   this.x += x;
+     *   this.y += y;
+     * };
+         *
+         * _.forIn(new Shape, function(value, key) {
+     *   console.log(key);
+     * });
+         * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
+         */
+        var forIn = function(collection, callback, thisArg) {
+            var index, iterable = collection, result = iterable;
+            if (!iterable) return result;
+            if (!objectTypes[typeof iterable]) return result;
+            callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+            for (index in iterable) {
+                if (callback(iterable[index], index, collection) === false) return result;
+            }
+            return result
+        };
+
+        /**
+         * This method is like `_.forIn` except that it iterates over elements
+         * of a `collection` in the opposite order.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} object The object to iterate over.
+         * @param {Function} [callback=identity] The function called per iteration.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Object} Returns `object`.
+         * @example
+         *
+         * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+         *
+         * Shape.prototype.move = function(x, y) {
+     *   this.x += x;
+     *   this.y += y;
+     * };
+         *
+         * _.forInRight(new Shape, function(value, key) {
+     *   console.log(key);
+     * });
+         * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move'
+         */
+        function forInRight(object, callback, thisArg) {
+            var pairs = [];
+
+            forIn(object, function(value, key) {
+                pairs.push(key, value);
+            });
+
+            var length = pairs.length;
+            callback = baseCreateCallback(callback, thisArg, 3);
+            while (length--) {
+                if (callback(pairs[length--], pairs[length], object) === false) {
+                    break;
+                }
+            }
+            return object;
+        }
+
+        /**
+         * Iterates over own enumerable properties of an object, executing the callback
+         * for each property. The callback is bound to `thisArg` and invoked with three
+         * arguments; (value, key, object). Callbacks may exit iteration early by
+         * explicitly returning `false`.
+         *
+         * @static
+         * @memberOf _
+         * @type Function
+         * @category Objects
+         * @param {Object} object The object to iterate over.
+         * @param {Function} [callback=identity] The function called per iteration.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Object} Returns `object`.
+         * @example
+         *
+         * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+     *   console.log(key);
+     * });
+         * // => logs '0', '1', and 'length' (property order is not guaranteed across environments)
+         */
+        var forOwn = function(collection, callback, thisArg) {
+            var index, iterable = collection, result = iterable;
+            if (!iterable) return result;
+            if (!objectTypes[typeof iterable]) return result;
+            callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+            var ownIndex = -1,
+                    ownProps = objectTypes[typeof iterable] && keys(iterable),
+                    length = ownProps ? ownProps.length : 0;
+
+            while (++ownIndex < length) {
+                index = ownProps[ownIndex];
+                if (callback(iterable[index], index, collection) === false) return result;
+            }
+            return result
+        };
+
+        /**
+         * This method is like `_.forOwn` except that it iterates over elements
+         * of a `collection` in the opposite order.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} object The object to iterate over.
+         * @param {Function} [callback=identity] The function called per iteration.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Object} Returns `object`.
+         * @example
+         *
+         * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+     *   console.log(key);
+     * });
+         * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length'
+         */
+        function forOwnRight(object, callback, thisArg) {
+            var props = keys(object),
+                    length = props.length;
+
+            callback = baseCreateCallback(callback, thisArg, 3);
+            while (length--) {
+                var key = props[length];
+                if (callback(object[key], key, object) === false) {
+                    break;
+                }
+            }
+            return object;
+        }
+
+        /**
+         * Creates a sorted array of property names of all enumerable properties,
+         * own and inherited, of `object` that have function values.
+         *
+         * @static
+         * @memberOf _
+         * @alias methods
+         * @category Objects
+         * @param {Object} object The object to inspect.
+         * @returns {Array} Returns an array of property names that have function values.
+         * @example
+         *
+         * _.functions(_);
+         * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
+         */
+        function functions(object) {
+            var result = [];
+            forIn(object, function(value, key) {
+                if (isFunction(value)) {
+                    result.push(key);
+                }
+            });
+            return result.sort();
+        }
+
+        /**
+         * Checks if the specified property name exists as a direct property of `object`,
+         * instead of an inherited property.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} object The object to inspect.
+         * @param {string} key The name of the property to check.
+         * @returns {boolean} Returns `true` if key is a direct property, else `false`.
+         * @example
+         *
+         * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
+         * // => true
+         */
+        function has(object, key) {
+            return object ? hasOwnProperty.call(object, key) : false;
+        }
+
+        /**
+         * Creates an object composed of the inverted keys and values of the given object.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} object The object to invert.
+         * @returns {Object} Returns the created inverted object.
+         * @example
+         *
+         * _.invert({ 'first': 'fred', 'second': 'barney' });
+         * // => { 'fred': 'first', 'barney': 'second' }
+         */
+        function invert(object) {
+            var index = -1,
+                    props = keys(object),
+                    length = props.length,
+                    result = {};
+
+            while (++index < length) {
+                var key = props[index];
+                result[object[key]] = key;
+            }
+            return result;
+        }
+
+        /**
+         * Checks if `value` is a boolean value.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`.
+         * @example
+         *
+         * _.isBoolean(null);
+         * // => false
+         */
+        function isBoolean(value) {
+            return value === true || value === false ||
+                    value && typeof value == 'object' && toString.call(value) == boolClass || false;
+        }
+
+        /**
+         * Checks if `value` is a date.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is a date, else `false`.
+         * @example
+         *
+         * _.isDate(new Date);
+         * // => true
+         */
+        function isDate(value) {
+            return value && typeof value == 'object' && toString.call(value) == dateClass || false;
+        }
+
+        /**
+         * Checks if `value` is a DOM element.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`.
+         * @example
+         *
+         * _.isElement(document.body);
+         * // => true
+         */
+        function isElement(value) {
+            return value && value.nodeType === 1 || false;
+        }
+
+        /**
+         * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a
+         * length of `0` and objects with no own enumerable properties are considered
+         * "empty".
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Array|Object|string} value The value to inspect.
+         * @returns {boolean} Returns `true` if the `value` is empty, else `false`.
+         * @example
+         *
+         * _.isEmpty([1, 2, 3]);
+         * // => false
+         *
+         * _.isEmpty({});
+         * // => true
+         *
+         * _.isEmpty('');
+         * // => true
+         */
+        function isEmpty(value) {
+            var result = true;
+            if (!value) {
+                return result;
+            }
+            var className = toString.call(value),
+                    length = value.length;
+
+            if ((className == arrayClass || className == stringClass || className == argsClass ) ||
+                    (className == objectClass && typeof length == 'number' && isFunction(value.splice))) {
+                return !length;
+            }
+            forOwn(value, function() {
+                return (result = false);
+            });
+            return result;
+        }
+
+        /**
+         * Performs a deep comparison between two values to determine if they are
+         * equivalent to each other. If a callback is provided it will be executed
+         * to compare values. If the callback returns `undefined` comparisons will
+         * be handled by the method instead. The callback is bound to `thisArg` and
+         * invoked with two arguments; (a, b).
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} a The value to compare.
+         * @param {*} b The other value to compare.
+         * @param {Function} [callback] The function to customize comparing values.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+         * @example
+         *
+         * var object = { 'name': 'fred' };
+         * var copy = { 'name': 'fred' };
+         *
+         * object == copy;
+         * // => false
+         *
+         * _.isEqual(object, copy);
+         * // => true
+         *
+         * var words = ['hello', 'goodbye'];
+         * var otherWords = ['hi', 'goodbye'];
+         *
+         * _.isEqual(words, otherWords, function(a, b) {
+     *   var reGreet = /^(?:hello|hi)$/i,
+     *       aGreet = _.isString(a) && reGreet.test(a),
+     *       bGreet = _.isString(b) && reGreet.test(b);
+     *
+     *   return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
+     * });
+         * // => true
+         */
+        function isEqual(a, b, callback, thisArg) {
+            return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2));
+        }
+
+        /**
+         * Checks if `value` is, or can be coerced to, a finite number.
+         *
+         * Note: This is not the same as native `isFinite` which will return true for
+         * booleans and empty strings. See http://es5.github.io/#x15.1.2.5.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is finite, else `false`.
+         * @example
+         *
+         * _.isFinite(-101);
+         * // => true
+         *
+         * _.isFinite('10');
+         * // => true
+         *
+         * _.isFinite(true);
+         * // => false
+         *
+         * _.isFinite('');
+         * // => false
+         *
+         * _.isFinite(Infinity);
+         * // => false
+         */
+        function isFinite(value) {
+            return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
+        }
+
+        /**
+         * Checks if `value` is a function.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is a function, else `false`.
+         * @example
+         *
+         * _.isFunction(_);
+         * // => true
+         */
+        function isFunction(value) {
+            return typeof value == 'function';
+        }
+
+        /**
+         * Checks if `value` is the language type of Object.
+         * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is an object, else `false`.
+         * @example
+         *
+         * _.isObject({});
+         * // => true
+         *
+         * _.isObject([1, 2, 3]);
+         * // => true
+         *
+         * _.isObject(1);
+         * // => false
+         */
+        function isObject(value) {
+            // check if the value is the ECMAScript language type of Object
+            // http://es5.github.io/#x8
+            // and avoid a V8 bug
+            // http://code.google.com/p/v8/issues/detail?id=2291
+            return !!(value && objectTypes[typeof value]);
+        }
+
+        /**
+         * Checks if `value` is `NaN`.
+         *
+         * Note: This is not the same as native `isNaN` which will return `true` for
+         * `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`.
+         * @example
+         *
+         * _.isNaN(NaN);
+         * // => true
+         *
+         * _.isNaN(new Number(NaN));
+         * // => true
+         *
+         * isNaN(undefined);
+         * // => true
+         *
+         * _.isNaN(undefined);
+         * // => false
+         */
+        function isNaN(value) {
+            // `NaN` as a primitive is the only value that is not equal to itself
+            // (perform the [[Class]] check first to avoid errors with some host objects in IE)
+            return isNumber(value) && value != +value;
+        }
+
+        /**
+         * Checks if `value` is `null`.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is `null`, else `false`.
+         * @example
+         *
+         * _.isNull(null);
+         * // => true
+         *
+         * _.isNull(undefined);
+         * // => false
+         */
+        function isNull(value) {
+            return value === null;
+        }
+
+        /**
+         * Checks if `value` is a number.
+         *
+         * Note: `NaN` is considered a number. See http://es5.github.io/#x8.5.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is a number, else `false`.
+         * @example
+         *
+         * _.isNumber(8.4 * 5);
+         * // => true
+         */
+        function isNumber(value) {
+            return typeof value == 'number' ||
+                    value && typeof value == 'object' && toString.call(value) == numberClass || false;
+        }
+
+        /**
+         * Checks if `value` is an object created by the `Object` constructor.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+         * @example
+         *
+         * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+         *
+         * _.isPlainObject(new Shape);
+         * // => false
+         *
+         * _.isPlainObject([1, 2, 3]);
+         * // => false
+         *
+         * _.isPlainObject({ 'x': 0, 'y': 0 });
+         * // => true
+         */
+        var isPlainObject = function(value) {
+            if (!(value && toString.call(value) == objectClass)) {
+                return false;
+            }
+            var valueOf = value.valueOf,
+                    objProto = typeof valueOf == 'function' && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
+
+            return objProto
+                    ? (value == objProto || getPrototypeOf(value) == objProto)
+                    : shimIsPlainObject(value);
+        };
+
+        /**
+         * Checks if `value` is a regular expression.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`.
+         * @example
+         *
+         * _.isRegExp(/fred/);
+         * // => true
+         */
+        function isRegExp(value) {
+            return value && typeof value == 'object' && toString.call(value) == regexpClass || false;
+        }
+
+        /**
+         * Checks if `value` is a string.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is a string, else `false`.
+         * @example
+         *
+         * _.isString('fred');
+         * // => true
+         */
+        function isString(value) {
+            return typeof value == 'string' ||
+                    value && typeof value == 'object' && toString.call(value) == stringClass || false;
+        }
+
+        /**
+         * Checks if `value` is `undefined`.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {*} value The value to check.
+         * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`.
+         * @example
+         *
+         * _.isUndefined(void 0);
+         * // => true
+         */
+        function isUndefined(value) {
+            return typeof value == 'undefined';
+        }
+
+        /**
+         * Creates an object with the same keys as `object` and values generated by
+         * running each own enumerable property of `object` through the callback.
+         * The callback is bound to `thisArg` and invoked with three arguments;
+         * (value, key, object).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} object The object to iterate over.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Array} Returns a new object with values of the results of each `callback` execution.
+         * @example
+         *
+         * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
+         * // => { 'a': 3, 'b': 6, 'c': 9 }
+         *
+         * var characters = {
+     *   'fred': { 'name': 'fred', 'age': 40 },
+     *   'pebbles': { 'name': 'pebbles', 'age': 1 }
+     * };
+         *
+         * // using "_.pluck" callback shorthand
+         * _.mapValues(characters, 'age');
+         * // => { 'fred': 40, 'pebbles': 1 }
+         */
+        function mapValues(object, callback, thisArg) {
+            var result = {};
+            callback = lodash.createCallback(callback, thisArg, 3);
+
+            forOwn(object, function(value, key, object) {
+                result[key] = callback(value, key, object);
+            });
+            return result;
+        }
+
+        /**
+         * Recursively merges own enumerable properties of the source object(s), that
+         * don't resolve to `undefined` into the destination object. Subsequent sources
+         * will overwrite property assignments of previous sources. If a callback is
+         * provided it will be executed to produce the merged values of the destination
+         * and source properties. If the callback returns `undefined` merging will
+         * be handled by the method instead. The callback is bound to `thisArg` and
+         * invoked with two arguments; (objectValue, sourceValue).
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} object The destination object.
+         * @param {...Object} [source] The source objects.
+         * @param {Function} [callback] The function to customize merging properties.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Object} Returns the destination object.
+         * @example
+         *
+         * var names = {
+     *   'characters': [
+     *     { 'name': 'barney' },
+     *     { 'name': 'fred' }
+     *   ]
+     * };
+         *
+         * var ages = {
+     *   'characters': [
+     *     { 'age': 36 },
+     *     { 'age': 40 }
+     *   ]
+     * };
+         *
+         * _.merge(names, ages);
+         * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] }
+         *
+         * var food = {
+     *   'fruits': ['apple'],
+     *   'vegetables': ['beet']
+     * };
+         *
+         * var otherFood = {
+     *   'fruits': ['banana'],
+     *   'vegetables': ['carrot']
+     * };
+         *
+         * _.merge(food, otherFood, function(a, b) {
+     *   return _.isArray(a) ? a.concat(b) : undefined;
+     * });
+         * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
+         */
+        function merge(object) {
+            var args = arguments,
+                    length = 2;
+
+            if (!isObject(object)) {
+                return object;
+            }
+            // allows working with `_.reduce` and `_.reduceRight` without using
+            // their `index` and `collection` arguments
+            if (typeof args[2] != 'number') {
+                length = args.length;
+            }
+            if (length > 3 && typeof args[length - 2] == 'function') {
+                var callback = baseCreateCallback(args[--length - 1], args[length--], 2);
+            } else if (length > 2 && typeof args[length - 1] == 'function') {
+                callback = args[--length];
+            }
+            var sources = slice(arguments, 1, length),
+                    index = -1,
+                    stackA = getArray(),
+                    stackB = getArray();
+
+            while (++index < length) {
+                baseMerge(object, sources[index], callback, stackA, stackB);
+            }
+            releaseArray(stackA);
+            releaseArray(stackB);
+            return object;
+        }
+
+        /**
+         * Creates a shallow clone of `object` excluding the specified properties.
+         * Property names may be specified as individual arguments or as arrays of
+         * property names. If a callback is provided it will be executed for each
+         * property of `object` omitting the properties the callback returns truey
+         * for. The callback is bound to `thisArg` and invoked with three arguments;
+         * (value, key, object).
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} object The source object.
+         * @param {Function|...string|string[]} [callback] The properties to omit or the
+         *  function called per iteration.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Object} Returns an object without the omitted properties.
+         * @example
+         *
+         * _.omit({ 'name': 'fred', 'age': 40 }, 'age');
+         * // => { 'name': 'fred' }
+         *
+         * _.omit({ 'name': 'fred', 'age': 40 }, function(value) {
+     *   return typeof value == 'number';
+     * });
+         * // => { 'name': 'fred' }
+         */
+        function omit(object, callback, thisArg) {
+            var result = {};
+            if (typeof callback != 'function') {
+                var props = [];
+                forIn(object, function(value, key) {
+                    props.push(key);
+                });
+                props = baseDifference(props, baseFlatten(arguments, true, false, 1));
+
+                var index = -1,
+                        length = props.length;
+
+                while (++index < length) {
+                    var key = props[index];
+                    result[key] = object[key];
+                }
+            } else {
+                callback = lodash.createCallback(callback, thisArg, 3);
+                forIn(object, function(value, key, object) {
+                    if (!callback(value, key, object)) {
+                        result[key] = value;
+                    }
+                });
+            }
+            return result;
+        }
+
+        /**
+         * Creates a two dimensional array of an object's key-value pairs,
+         * i.e. `[[key1, value1], [key2, value2]]`.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} object The object to inspect.
+         * @returns {Array} Returns new array of key-value pairs.
+         * @example
+         *
+         * _.pairs({ 'barney': 36, 'fred': 40 });
+         * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments)
+         */
+        function pairs(object) {
+            var index = -1,
+                    props = keys(object),
+                    length = props.length,
+                    result = Array(length);
+
+            while (++index < length) {
+                var key = props[index];
+                result[index] = [key, object[key]];
+            }
+            return result;
+        }
+
+        /**
+         * Creates a shallow clone of `object` composed of the specified properties.
+         * Property names may be specified as individual arguments or as arrays of
+         * property names. If a callback is provided it will be executed for each
+         * property of `object` picking the properties the callback returns truey
+         * for. The callback is bound to `thisArg` and invoked with three arguments;
+         * (value, key, object).
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} object The source object.
+         * @param {Function|...string|string[]} [callback] The function called per
+         *  iteration or property names to pick, specified as individual property
+         *  names or arrays of property names.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Object} Returns an object composed of the picked properties.
+         * @example
+         *
+         * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
+         * // => { 'name': 'fred' }
+         *
+         * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
+     *   return key.charAt(0) != '_';
+     * });
+         * // => { 'name': 'fred' }
+         */
+        function pick(object, callback, thisArg) {
+            var result = {};
+            if (typeof callback != 'function') {
+                var index = -1,
+                        props = baseFlatten(arguments, true, false, 1),
+                        length = isObject(object) ? props.length : 0;
+
+                while (++index < length) {
+                    var key = props[index];
+                    if (key in object) {
+                        result[key] = object[key];
+                    }
+                }
+            } else {
+                callback = lodash.createCallback(callback, thisArg, 3);
+                forIn(object, function(value, key, object) {
+                    if (callback(value, key, object)) {
+                        result[key] = value;
+                    }
+                });
+            }
+            return result;
+        }
+
+        /**
+         * An alternative to `_.reduce` this method transforms `object` to a new
+         * `accumulator` object which is the result of running each of its own
+         * enumerable properties through a callback, with each callback execution
+         * potentially mutating the `accumulator` object. The callback is bound to
+         * `thisArg` and invoked with four arguments; (accumulator, value, key, object).
+         * Callbacks may exit iteration early by explicitly returning `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Array|Object} object The object to iterate over.
+         * @param {Function} [callback=identity] The function called per iteration.
+         * @param {*} [accumulator] The custom accumulator value.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {*} Returns the accumulated value.
+         * @example
+         *
+         * var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) {
+     *   num *= num;
+     *   if (num % 2) {
+     *     return result.push(num) < 3;
+     *   }
+     * });
+         * // => [1, 9, 25]
+         *
+         * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+     *   result[key] = num * 3;
+     * });
+         * // => { 'a': 3, 'b': 6, 'c': 9 }
+         */
+        function transform(object, callback, accumulator, thisArg) {
+            var isArr = isArray(object);
+            if (accumulator == null) {
+                if (isArr) {
+                    accumulator = [];
+                } else {
+                    var ctor = object && object.constructor,
+                            proto = ctor && ctor.prototype;
+
+                    accumulator = baseCreate(proto);
+                }
+            }
+            if (callback) {
+                callback = lodash.createCallback(callback, thisArg, 4);
+                (isArr ? forEach : forOwn)(object, function(value, index, object) {
+                    return callback(accumulator, value, index, object);
+                });
+            }
+            return accumulator;
+        }
+
+        /**
+         * Creates an array composed of the own enumerable property values of `object`.
+         *
+         * @static
+         * @memberOf _
+         * @category Objects
+         * @param {Object} object The object to inspect.
+         * @returns {Array} Returns an array of property values.
+         * @example
+         *
+         * _.values({ 'one': 1, 'two': 2, 'three': 3 });
+         * // => [1, 2, 3] (property order is not guaranteed across environments)
+         */
+        function values(object) {
+            var index = -1,
+                    props = keys(object),
+                    length = props.length,
+                    result = Array(length);
+
+            while (++index < length) {
+                result[index] = object[props[index]];
+            }
+            return result;
+        }
+
+        /*--------------------------------------------------------------------------*/
+
+        /**
+         * Creates an array of elements from the specified indexes, or keys, of the
+         * `collection`. Indexes may be specified as individual arguments or as arrays
+         * of indexes.
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {...(number|number[]|string|string[])} [index] The indexes of `collection`
+         *   to retrieve, specified as individual indexes or arrays of indexes.
+         * @returns {Array} Returns a new array of elements corresponding to the
+         *  provided indexes.
+         * @example
+         *
+         * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
+         * // => ['a', 'c', 'e']
+         *
+         * _.at(['fred', 'barney', 'pebbles'], 0, 2);
+         * // => ['fred', 'pebbles']
+         */
+        function at(collection) {
+            var args = arguments,
+                    index = -1,
+                    props = baseFlatten(args, true, false, 1),
+                    length = (args[2] && args[2][args[1]] === collection) ? 1 : props.length,
+                    result = Array(length);
+
+            while(++index < length) {
+                result[index] = collection[props[index]];
+            }
+            return result;
+        }
+
+        /**
+         * Checks if a given value is present in a collection using strict equality
+         * for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the
+         * offset from the end of the collection.
+         *
+         * @static
+         * @memberOf _
+         * @alias include
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {*} target The value to check for.
+         * @param {number} [fromIndex=0] The index to search from.
+         * @returns {boolean} Returns `true` if the `target` element is found, else `false`.
+         * @example
+         *
+         * _.contains([1, 2, 3], 1);
+         * // => true
+         *
+         * _.contains([1, 2, 3], 1, 2);
+         * // => false
+         *
+         * _.contains({ 'name': 'fred', 'age': 40 }, 'fred');
+         * // => true
+         *
+         * _.contains('pebbles', 'eb');
+         * // => true
+         */
+        function contains(collection, target, fromIndex) {
+            var index = -1,
+                    indexOf = getIndexOf(),
+                    length = collection ? collection.length : 0,
+                    result = false;
+
+            fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0;
+            if (isArray(collection)) {
+                result = indexOf(collection, target, fromIndex) > -1;
+            } else if (typeof length == 'number') {
+                result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex)) > -1;
+            } else {
+                forOwn(collection, function(value) {
+                    if (++index >= fromIndex) {
+                        return !(result = value === target);
+                    }
+                });
+            }
+            return result;
+        }
+
+        /**
+         * Creates an object composed of keys generated from the results of running
+         * each element of `collection` through the callback. The corresponding value
+         * of each key is the number of times the key was returned by the callback.
+         * The callback is bound to `thisArg` and invoked with three arguments;
+         * (value, index|key, collection).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Object} Returns the composed aggregate object.
+         * @example
+         *
+         * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
+         * // => { '4': 1, '6': 2 }
+         *
+         * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+         * // => { '4': 1, '6': 2 }
+         *
+         * _.countBy(['one', 'two', 'three'], 'length');
+         * // => { '3': 2, '5': 1 }
+         */
+        var countBy = createAggregator(function(result, value, key) {
+            (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
+        });
+
+        /**
+         * Checks if the given callback returns truey value for **all** elements of
+         * a collection. The callback is bound to `thisArg` and invoked with three
+         * arguments; (value, index|key, collection).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @alias all
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {boolean} Returns `true` if all elements passed the callback check,
+         *  else `false`.
+         * @example
+         *
+         * _.every([true, 1, null, 'yes']);
+         * // => false
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 36 },
+         *   { 'name': 'fred',   'age': 40 }
+         * ];
+         *
+         * // using "_.pluck" callback shorthand
+         * _.every(characters, 'age');
+         * // => true
+         *
+         * // using "_.where" callback shorthand
+         * _.every(characters, { 'age': 36 });
+         * // => false
+         */
+        function every(collection, callback, thisArg) {
+            var result = true;
+            callback = lodash.createCallback(callback, thisArg, 3);
+
+            var index = -1,
+                    length = collection ? collection.length : 0;
+
+            if (typeof length == 'number') {
+                while (++index < length) {
+                    if (!(result = !!callback(collection[index], index, collection))) {
+                        break;
+                    }
+                }
+            } else {
+                forOwn(collection, function(value, index, collection) {
+                    return (result = !!callback(value, index, collection));
+                });
+            }
+            return result;
+        }
+
+        /**
+         * Iterates over elements of a collection, returning an array of all elements
+         * the callback returns truey for. The callback is bound to `thisArg` and
+         * invoked with three arguments; (value, index|key, collection).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @alias select
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Array} Returns a new array of elements that passed the callback check.
+         * @example
+         *
+         * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+         * // => [2, 4, 6]
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 36, 'blocked': false },
+         *   { 'name': 'fred',   'age': 40, 'blocked': true }
+         * ];
+         *
+         * // using "_.pluck" callback shorthand
+         * _.filter(characters, 'blocked');
+         * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+         *
+         * // using "_.where" callback shorthand
+         * _.filter(characters, { 'age': 36 });
+         * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+         */
+        function filter(collection, callback, thisArg) {
+            var result = [];
+            callback = lodash.createCallback(callback, thisArg, 3);
+
+            var index = -1,
+                    length = collection ? collection.length : 0;
+
+            if (typeof length == 'number') {
+                while (++index < length) {
+                    var value = collection[index];
+                    if (callback(value, index, collection)) {
+                        result.push(value);
+                    }
+                }
+            } else {
+                forOwn(collection, function(value, index, collection) {
+                    if (callback(value, index, collection)) {
+                        result.push(value);
+                    }
+                });
+            }
+            return result;
+        }
+
+        /**
+         * Iterates over elements of a collection, returning the first element that
+         * the callback returns truey for. The callback is bound to `thisArg` and
+         * invoked with three arguments; (value, index|key, collection).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @alias detect, findWhere
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {*} Returns the found element, else `undefined`.
+         * @example
+         *
+         * var characters = [
+         *   { 'name': 'barney',  'age': 36, 'blocked': false },
+         *   { 'name': 'fred',    'age': 40, 'blocked': true },
+         *   { 'name': 'pebbles', 'age': 1,  'blocked': false }
+         * ];
+         *
+         * _.find(characters, function(chr) {
+     *   return chr.age < 40;
+     * });
+         * // => { 'name': 'barney', 'age': 36, 'blocked': false }
+         *
+         * // using "_.where" callback shorthand
+         * _.find(characters, { 'age': 1 });
+         * // =>  { 'name': 'pebbles', 'age': 1, 'blocked': false }
+         *
+         * // using "_.pluck" callback shorthand
+         * _.find(characters, 'blocked');
+         * // => { 'name': 'fred', 'age': 40, 'blocked': true }
+         */
+        function find(collection, callback, thisArg) {
+            callback = lodash.createCallback(callback, thisArg, 3);
+
+            var index = -1,
+                    length = collection ? collection.length : 0;
+
+            if (typeof length == 'number') {
+                while (++index < length) {
+                    var value = collection[index];
+                    if (callback(value, index, collection)) {
+                        return value;
+                    }
+                }
+            } else {
+                var result;
+                forOwn(collection, function(value, index, collection) {
+                    if (callback(value, index, collection)) {
+                        result = value;
+                        return false;
+                    }
+                });
+                return result;
+            }
+        }
+
+        /**
+         * This method is like `_.find` except that it iterates over elements
+         * of a `collection` from right to left.
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {*} Returns the found element, else `undefined`.
+         * @example
+         *
+         * _.findLast([1, 2, 3, 4], function(num) {
+     *   return num % 2 == 1;
+     * });
+         * // => 3
+         */
+        function findLast(collection, callback, thisArg) {
+            var result;
+            callback = lodash.createCallback(callback, thisArg, 3);
+            forEachRight(collection, function(value, index, collection) {
+                if (callback(value, index, collection)) {
+                    result = value;
+                    return false;
+                }
+            });
+            return result;
+        }
+
+        /**
+         * Iterates over elements of a collection, executing the callback for each
+         * element. The callback is bound to `thisArg` and invoked with three arguments;
+         * (value, index|key, collection). Callbacks may exit iteration early by
+         * explicitly returning `false`.
+         *
+         * Note: As with other "Collections" methods, objects with a `length` property
+         * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
+         * may be used for object iteration.
+         *
+         * @static
+         * @memberOf _
+         * @alias each
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function} [callback=identity] The function called per iteration.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Array|Object|string} Returns `collection`.
+         * @example
+         *
+         * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
+         * // => logs each number and returns '1,2,3'
+         *
+         * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
+         * // => logs each number and returns the object (property order is not guaranteed across environments)
+         */
+        function forEach(collection, callback, thisArg) {
+            var index = -1,
+                    length = collection ? collection.length : 0;
+
+            callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+            if (typeof length == 'number') {
+                while (++index < length) {
+                    if (callback(collection[index], index, collection) === false) {
+                        break;
+                    }
+                }
+            } else {
+                forOwn(collection, callback);
+            }
+            return collection;
+        }
+
+        /**
+         * This method is like `_.forEach` except that it iterates over elements
+         * of a `collection` from right to left.
+         *
+         * @static
+         * @memberOf _
+         * @alias eachRight
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function} [callback=identity] The function called per iteration.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Array|Object|string} Returns `collection`.
+         * @example
+         *
+         * _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(',');
+         * // => logs each number from right to left and returns '3,2,1'
+         */
+        function forEachRight(collection, callback, thisArg) {
+            var length = collection ? collection.length : 0;
+            callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+            if (typeof length == 'number') {
+                while (length--) {
+                    if (callback(collection[length], length, collection) === false) {
+                        break;
+                    }
+                }
+            } else {
+                var props = keys(collection);
+                length = props.length;
+                forOwn(collection, function(value, key, collection) {
+                    key = props ? props[--length] : --length;
+                    return callback(collection[key], key, collection);
+                });
+            }
+            return collection;
+        }
+
+        /**
+         * Creates an object composed of keys generated from the results of running
+         * each element of a collection through the callback. The corresponding value
+         * of each key is an array of the elements responsible for generating the key.
+         * The callback is bound to `thisArg` and invoked with three arguments;
+         * (value, index|key, collection).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Object} Returns the composed aggregate object.
+         * @example
+         *
+         * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); });
+         * // => { '4': [4.2], '6': [6.1, 6.4] }
+         *
+         * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+         * // => { '4': [4.2], '6': [6.1, 6.4] }
+         *
+         * // using "_.pluck" callback shorthand
+         * _.groupBy(['one', 'two', 'three'], 'length');
+         * // => { '3': ['one', 'two'], '5': ['three'] }
+         */
+        var groupBy = createAggregator(function(result, value, key) {
+            (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
+        });
+
+        /**
+         * Creates an object composed of keys generated from the results of running
+         * each element of the collection through the given callback. The corresponding
+         * value of each key is the last element responsible for generating the key.
+         * The callback is bound to `thisArg` and invoked with three arguments;
+         * (value, index|key, collection).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Object} Returns the composed aggregate object.
+         * @example
+         *
+         * var keys = [
+         *   { 'dir': 'left', 'code': 97 },
+         *   { 'dir': 'right', 'code': 100 }
+         * ];
+         *
+         * _.indexBy(keys, 'dir');
+         * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
+         *
+         * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); });
+         * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+         *
+         * _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String);
+         * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+         */
+        var indexBy = createAggregator(function(result, value, key) {
+            result[key] = value;
+        });
+
+        /**
+         * Invokes the method named by `methodName` on each element in the `collection`
+         * returning an array of the results of each invoked method. Additional arguments
+         * will be provided to each invoked method. If `methodName` is a function it
+         * will be invoked for, and `this` bound to, each element in the `collection`.
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function|string} methodName The name of the method to invoke or
+         *  the function invoked per iteration.
+         * @param {...*} [arg] Arguments to invoke the method with.
+         * @returns {Array} Returns a new array of the results of each invoked method.
+         * @example
+         *
+         * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
+         * // => [[1, 5, 7], [1, 2, 3]]
+         *
+         * _.invoke([123, 456], String.prototype.split, '');
+         * // => [['1', '2', '3'], ['4', '5', '6']]
+         */
+        function invoke(collection, methodName) {
+            var args = slice(arguments, 2),
+                    index = -1,
+                    isFunc = typeof methodName == 'function',
+                    length = collection ? collection.length : 0,
+                    result = Array(typeof length == 'number' ? length : 0);
+
+            forEach(collection, function(value) {
+                result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
+            });
+            return result;
+        }
+
+        /**
+         * Creates an array of values by running each element in the collection
+         * through the callback. The callback is bound to `thisArg` and invoked with
+         * three arguments; (value, index|key, collection).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @alias collect
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Array} Returns a new array of the results of each `callback` execution.
+         * @example
+         *
+         * _.map([1, 2, 3], function(num) { return num * 3; });
+         * // => [3, 6, 9]
+         *
+         * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
+         * // => [3, 6, 9] (property order is not guaranteed across environments)
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 36 },
+         *   { 'name': 'fred',   'age': 40 }
+         * ];
+         *
+         * // using "_.pluck" callback shorthand
+         * _.map(characters, 'name');
+         * // => ['barney', 'fred']
+         */
+        function map(collection, callback, thisArg) {
+            var index = -1,
+                    length = collection ? collection.length : 0;
+
+            callback = lodash.createCallback(callback, thisArg, 3);
+            if (typeof length == 'number') {
+                var result = Array(length);
+                while (++index < length) {
+                    result[index] = callback(collection[index], index, collection);
+                }
+            } else {
+                result = [];
+                forOwn(collection, function(value, key, collection) {
+                    result[++index] = callback(value, key, collection);
+                });
+            }
+            return result;
+        }
+
+        /**
+         * Retrieves the maximum value of a collection. If the collection is empty or
+         * falsey `-Infinity` is returned. If a callback is provided it will be executed
+         * for each value in the collection to generate the criterion by which the value
+         * is ranked. The callback is bound to `thisArg` and invoked with three
+         * arguments; (value, index, collection).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {*} Returns the maximum value.
+         * @example
+         *
+         * _.max([4, 2, 8, 6]);
+         * // => 8
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 36 },
+         *   { 'name': 'fred',   'age': 40 }
+         * ];
+         *
+         * _.max(characters, function(chr) { return chr.age; });
+         * // => { 'name': 'fred', 'age': 40 };
+         *
+         * // using "_.pluck" callback shorthand
+         * _.max(characters, 'age');
+         * // => { 'name': 'fred', 'age': 40 };
+         */
+        function max(collection, callback, thisArg) {
+            var computed = -Infinity,
+                    result = computed;
+
+            // allows working with functions like `_.map` without using
+            // their `index` argument as a callback
+            if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+                callback = null;
+            }
+            if (callback == null && isArray(collection)) {
+                var index = -1,
+                        length = collection.length;
+
+                while (++index < length) {
+                    var value = collection[index];
+                    if (value > result) {
+                        result = value;
+                    }
+                }
+            } else {
+                callback = (callback == null && isString(collection))
+                        ? charAtCallback
+                        : lodash.createCallback(callback, thisArg, 3);
+
+                forEach(collection, function(value, index, collection) {
+                    var current = callback(value, index, collection);
+                    if (current > computed) {
+                        computed = current;
+                        result = value;
+                    }
+                });
+            }
+            return result;
+        }
+
+        /**
+         * Retrieves the minimum value of a collection. If the collection is empty or
+         * falsey `Infinity` is returned. If a callback is provided it will be executed
+         * for each value in the collection to generate the criterion by which the value
+         * is ranked. The callback is bound to `thisArg` and invoked with three
+         * arguments; (value, index, collection).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {*} Returns the minimum value.
+         * @example
+         *
+         * _.min([4, 2, 8, 6]);
+         * // => 2
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 36 },
+         *   { 'name': 'fred',   'age': 40 }
+         * ];
+         *
+         * _.min(characters, function(chr) { return chr.age; });
+         * // => { 'name': 'barney', 'age': 36 };
+         *
+         * // using "_.pluck" callback shorthand
+         * _.min(characters, 'age');
+         * // => { 'name': 'barney', 'age': 36 };
+         */
+        function min(collection, callback, thisArg) {
+            var computed = Infinity,
+                    result = computed;
+
+            // allows working with functions like `_.map` without using
+            // their `index` argument as a callback
+            if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+                callback = null;
+            }
+            if (callback == null && isArray(collection)) {
+                var index = -1,
+                        length = collection.length;
+
+                while (++index < length) {
+                    var value = collection[index];
+                    if (value < result) {
+                        result = value;
+                    }
+                }
+            } else {
+                callback = (callback == null && isString(collection))
+                        ? charAtCallback
+                        : lodash.createCallback(callback, thisArg, 3);
+
+                forEach(collection, function(value, index, collection) {
+                    var current = callback(value, index, collection);
+                    if (current < computed) {
+                        computed = current;
+                        result = value;
+                    }
+                });
+            }
+            return result;
+        }
+
+        /**
+         * Retrieves the value of a specified property from all elements in the collection.
+         *
+         * @static
+         * @memberOf _
+         * @type Function
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {string} property The name of the property to pluck.
+         * @returns {Array} Returns a new array of property values.
+         * @example
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 36 },
+         *   { 'name': 'fred',   'age': 40 }
+         * ];
+         *
+         * _.pluck(characters, 'name');
+         * // => ['barney', 'fred']
+         */
+        var pluck = map;
+
+        /**
+         * Reduces a collection to a value which is the accumulated result of running
+         * each element in the collection through the callback, where each successive
+         * callback execution consumes the return value of the previous execution. If
+         * `accumulator` is not provided the first element of the collection will be
+         * used as the initial `accumulator` value. The callback is bound to `thisArg`
+         * and invoked with four arguments; (accumulator, value, index|key, collection).
+         *
+         * @static
+         * @memberOf _
+         * @alias foldl, inject
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function} [callback=identity] The function called per iteration.
+         * @param {*} [accumulator] Initial value of the accumulator.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {*} Returns the accumulated value.
+         * @example
+         *
+         * var sum = _.reduce([1, 2, 3], function(sum, num) {
+     *   return sum + num;
+     * });
+         * // => 6
+         *
+         * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+     *   result[key] = num * 3;
+     *   return result;
+     * }, {});
+         * // => { 'a': 3, 'b': 6, 'c': 9 }
+         */
+        function reduce(collection, callback, accumulator, thisArg) {
+            if (!collection) return accumulator;
+            var noaccum = arguments.length < 3;
+            callback = lodash.createCallback(callback, thisArg, 4);
+
+            var index = -1,
+                    length = collection.length;
+
+            if (typeof length == 'number') {
+                if (noaccum) {
+                    accumulator = collection[++index];
+                }
+                while (++index < length) {
+                    accumulator = callback(accumulator, collection[index], index, collection);
+                }
+            } else {
+                forOwn(collection, function(value, index, collection) {
+                    accumulator = noaccum
+                            ? (noaccum = false, value)
+                            : callback(accumulator, value, index, collection)
+                });
+            }
+            return accumulator;
+        }
+
+        /**
+         * This method is like `_.reduce` except that it iterates over elements
+         * of a `collection` from right to left.
+         *
+         * @static
+         * @memberOf _
+         * @alias foldr
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function} [callback=identity] The function called per iteration.
+         * @param {*} [accumulator] Initial value of the accumulator.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {*} Returns the accumulated value.
+         * @example
+         *
+         * var list = [[0, 1], [2, 3], [4, 5]];
+         * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
+         * // => [4, 5, 2, 3, 0, 1]
+         */
+        function reduceRight(collection, callback, accumulator, thisArg) {
+            var noaccum = arguments.length < 3;
+            callback = lodash.createCallback(callback, thisArg, 4);
+            forEachRight(collection, function(value, index, collection) {
+                accumulator = noaccum
+                        ? (noaccum = false, value)
+                        : callback(accumulator, value, index, collection);
+            });
+            return accumulator;
+        }
+
+        /**
+         * The opposite of `_.filter` this method returns the elements of a
+         * collection that the callback does **not** return truey for.
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Array} Returns a new array of elements that failed the callback check.
+         * @example
+         *
+         * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+         * // => [1, 3, 5]
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 36, 'blocked': false },
+         *   { 'name': 'fred',   'age': 40, 'blocked': true }
+         * ];
+         *
+         * // using "_.pluck" callback shorthand
+         * _.reject(characters, 'blocked');
+         * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+         *
+         * // using "_.where" callback shorthand
+         * _.reject(characters, { 'age': 36 });
+         * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+         */
+        function reject(collection, callback, thisArg) {
+            callback = lodash.createCallback(callback, thisArg, 3);
+            return filter(collection, function(value, index, collection) {
+                return !callback(value, index, collection);
+            });
+        }
+
+        /**
+         * Retrieves a random element or `n` random elements from a collection.
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to sample.
+         * @param {number} [n] The number of elements to sample.
+         * @param- {Object} [guard] Allows working with functions like `_.map`
+         *  without using their `index` arguments as `n`.
+         * @returns {Array} Returns the random sample(s) of `collection`.
+         * @example
+         *
+         * _.sample([1, 2, 3, 4]);
+         * // => 2
+         *
+         * _.sample([1, 2, 3, 4], 2);
+         * // => [3, 1]
+         */
+        function sample(collection, n, guard) {
+            if (collection && typeof collection.length != 'number') {
+                collection = values(collection);
+            }
+            if (n == null || guard) {
+                return collection ? collection[baseRandom(0, collection.length - 1)] : undefined;
+            }
+            var result = shuffle(collection);
+            result.length = nativeMin(nativeMax(0, n), result.length);
+            return result;
+        }
+
+        /**
+         * Creates an array of shuffled values, using a version of the Fisher-Yates
+         * shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to shuffle.
+         * @returns {Array} Returns a new shuffled collection.
+         * @example
+         *
+         * _.shuffle([1, 2, 3, 4, 5, 6]);
+         * // => [4, 1, 6, 3, 5, 2]
+         */
+        function shuffle(collection) {
+            var index = -1,
+                    length = collection ? collection.length : 0,
+                    result = Array(typeof length == 'number' ? length : 0);
+
+            forEach(collection, function(value) {
+                var rand = baseRandom(0, ++index);
+                result[index] = result[rand];
+                result[rand] = value;
+            });
+            return result;
+        }
+
+        /**
+         * Gets the size of the `collection` by returning `collection.length` for arrays
+         * and array-like objects or the number of own enumerable properties for objects.
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to inspect.
+         * @returns {number} Returns `collection.length` or number of own enumerable properties.
+         * @example
+         *
+         * _.size([1, 2]);
+         * // => 2
+         *
+         * _.size({ 'one': 1, 'two': 2, 'three': 3 });
+         * // => 3
+         *
+         * _.size('pebbles');
+         * // => 7
+         */
+        function size(collection) {
+            var length = collection ? collection.length : 0;
+            return typeof length == 'number' ? length : keys(collection).length;
+        }
+
+        /**
+         * Checks if the callback returns a truey value for **any** element of a
+         * collection. The function returns as soon as it finds a passing value and
+         * does not iterate over the entire collection. The callback is bound to
+         * `thisArg` and invoked with three arguments; (value, index|key, collection).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @alias any
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {boolean} Returns `true` if any element passed the callback check,
+         *  else `false`.
+         * @example
+         *
+         * _.some([null, 0, 'yes', false], Boolean);
+         * // => true
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 36, 'blocked': false },
+         *   { 'name': 'fred',   'age': 40, 'blocked': true }
+         * ];
+         *
+         * // using "_.pluck" callback shorthand
+         * _.some(characters, 'blocked');
+         * // => true
+         *
+         * // using "_.where" callback shorthand
+         * _.some(characters, { 'age': 1 });
+         * // => false
+         */
+        function some(collection, callback, thisArg) {
+            var result;
+            callback = lodash.createCallback(callback, thisArg, 3);
+
+            var index = -1,
+                    length = collection ? collection.length : 0;
+
+            if (typeof length == 'number') {
+                while (++index < length) {
+                    if ((result = callback(collection[index], index, collection))) {
+                        break;
+                    }
+                }
+            } else {
+                forOwn(collection, function(value, index, collection) {
+                    return !(result = callback(value, index, collection));
+                });
+            }
+            return !!result;
+        }
+
+        /**
+         * Creates an array of elements, sorted in ascending order by the results of
+         * running each element in a collection through the callback. This method
+         * performs a stable sort, that is, it will preserve the original sort order
+         * of equal elements. The callback is bound to `thisArg` and invoked with
+         * three arguments; (value, index|key, collection).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an array of property names is provided for `callback` the collection
+         * will be sorted by each property value.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Array|Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Array} Returns a new array of sorted elements.
+         * @example
+         *
+         * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
+         * // => [3, 1, 2]
+         *
+         * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
+         * // => [3, 1, 2]
+         *
+         * var characters = [
+         *   { 'name': 'barney',  'age': 36 },
+         *   { 'name': 'fred',    'age': 40 },
+         *   { 'name': 'barney',  'age': 26 },
+         *   { 'name': 'fred',    'age': 30 }
+         * ];
+         *
+         * // using "_.pluck" callback shorthand
+         * _.map(_.sortBy(characters, 'age'), _.values);
+         * // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]]
+         *
+         * // sorting by multiple properties
+         * _.map(_.sortBy(characters, ['name', 'age']), _.values);
+         * // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
+         */
+        function sortBy(collection, callback, thisArg) {
+            var index = -1,
+                    isArr = isArray(callback),
+                    length = collection ? collection.length : 0,
+                    result = Array(typeof length == 'number' ? length : 0);
+
+            if (!isArr) {
+                callback = lodash.createCallback(callback, thisArg, 3);
+            }
+            forEach(collection, function(value, key, collection) {
+                var object = result[++index] = getObject();
+                if (isArr) {
+                    object.criteria = map(callback, function(key) { return value[key]; });
+                } else {
+                    (object.criteria = getArray())[0] = callback(value, key, collection);
+                }
+                object.index = index;
+                object.value = value;
+            });
+
+            length = result.length;
+            result.sort(compareAscending);
+            while (length--) {
+                var object = result[length];
+                result[length] = object.value;
+                if (!isArr) {
+                    releaseArray(object.criteria);
+                }
+                releaseObject(object);
+            }
+            return result;
+        }
+
+        /**
+         * Converts the `collection` to an array.
+         *
+         * @static
+         * @memberOf _
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to convert.
+         * @returns {Array} Returns the new converted array.
+         * @example
+         *
+         * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
+         * // => [2, 3, 4]
+         */
+        function toArray(collection) {
+            if (collection && typeof collection.length == 'number') {
+                return slice(collection);
+            }
+            return values(collection);
+        }
+
+        /**
+         * Performs a deep comparison of each element in a `collection` to the given
+         * `properties` object, returning an array of all elements that have equivalent
+         * property values.
+         *
+         * @static
+         * @memberOf _
+         * @type Function
+         * @category Collections
+         * @param {Array|Object|string} collection The collection to iterate over.
+         * @param {Object} props The object of property values to filter by.
+         * @returns {Array} Returns a new array of elements that have the given properties.
+         * @example
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
+         *   { 'name': 'fred',   'age': 40, 'pets': ['baby puss', 'dino'] }
+         * ];
+         *
+         * _.where(characters, { 'age': 36 });
+         * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
+         *
+         * _.where(characters, { 'pets': ['dino'] });
+         * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
+         */
+        var where = filter;
+
+        /*--------------------------------------------------------------------------*/
+
+        /**
+         * Creates an array with all falsey values removed. The values `false`, `null`,
+         * `0`, `""`, `undefined`, and `NaN` are all falsey.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {Array} array The array to compact.
+         * @returns {Array} Returns a new array of filtered values.
+         * @example
+         *
+         * _.compact([0, 1, false, 2, '', 3]);
+         * // => [1, 2, 3]
+         */
+        function compact(array) {
+            var index = -1,
+                    length = array ? array.length : 0,
+                    result = [];
+
+            while (++index < length) {
+                var value = array[index];
+                if (value) {
+                    result.push(value);
+                }
+            }
+            return result;
+        }
+
+        /**
+         * Creates an array excluding all values of the provided arrays using strict
+         * equality for comparisons, i.e. `===`.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {Array} array The array to process.
+         * @param {...Array} [values] The arrays of values to exclude.
+         * @returns {Array} Returns a new array of filtered values.
+         * @example
+         *
+         * _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
+         * // => [1, 3, 4]
+         */
+        function difference(array) {
+            return baseDifference(array, baseFlatten(arguments, true, true, 1));
+        }
+
+        /**
+         * This method is like `_.find` except that it returns the index of the first
+         * element that passes the callback check, instead of the element itself.
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {Array} array The array to search.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {number} Returns the index of the found element, else `-1`.
+         * @example
+         *
+         * var characters = [
+         *   { 'name': 'barney',  'age': 36, 'blocked': false },
+         *   { 'name': 'fred',    'age': 40, 'blocked': true },
+         *   { 'name': 'pebbles', 'age': 1,  'blocked': false }
+         * ];
+         *
+         * _.findIndex(characters, function(chr) {
+     *   return chr.age < 20;
+     * });
+         * // => 2
+         *
+         * // using "_.where" callback shorthand
+         * _.findIndex(characters, { 'age': 36 });
+         * // => 0
+         *
+         * // using "_.pluck" callback shorthand
+         * _.findIndex(characters, 'blocked');
+         * // => 1
+         */
+        function findIndex(array, callback, thisArg) {
+            var index = -1,
+                    length = array ? array.length : 0;
+
+            callback = lodash.createCallback(callback, thisArg, 3);
+            while (++index < length) {
+                if (callback(array[index], index, array)) {
+                    return index;
+                }
+            }
+            return -1;
+        }
+
+        /**
+         * This method is like `_.findIndex` except that it iterates over elements
+         * of a `collection` from right to left.
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {Array} array The array to search.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {number} Returns the index of the found element, else `-1`.
+         * @example
+         *
+         * var characters = [
+         *   { 'name': 'barney',  'age': 36, 'blocked': true },
+         *   { 'name': 'fred',    'age': 40, 'blocked': false },
+         *   { 'name': 'pebbles', 'age': 1,  'blocked': true }
+         * ];
+         *
+         * _.findLastIndex(characters, function(chr) {
+     *   return chr.age > 30;
+     * });
+         * // => 1
+         *
+         * // using "_.where" callback shorthand
+         * _.findLastIndex(characters, { 'age': 36 });
+         * // => 0
+         *
+         * // using "_.pluck" callback shorthand
+         * _.findLastIndex(characters, 'blocked');
+         * // => 2
+         */
+        function findLastIndex(array, callback, thisArg) {
+            var length = array ? array.length : 0;
+            callback = lodash.createCallback(callback, thisArg, 3);
+            while (length--) {
+                if (callback(array[length], length, array)) {
+                    return length;
+                }
+            }
+            return -1;
+        }
+
+        /**
+         * Gets the first element or first `n` elements of an array. If a callback
+         * is provided elements at the beginning of the array are returned as long
+         * as the callback returns truey. The callback is bound to `thisArg` and
+         * invoked with three arguments; (value, index, array).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @alias head, take
+         * @category Arrays
+         * @param {Array} array The array to query.
+         * @param {Function|Object|number|string} [callback] The function called
+         *  per element or the number of elements to return. If a property name or
+         *  object is provided it will be used to create a "_.pluck" or "_.where"
+         *  style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {*} Returns the first element(s) of `array`.
+         * @example
+         *
+         * _.first([1, 2, 3]);
+         * // => 1
+         *
+         * _.first([1, 2, 3], 2);
+         * // => [1, 2]
+         *
+         * _.first([1, 2, 3], function(num) {
+     *   return num < 3;
+     * });
+         * // => [1, 2]
+         *
+         * var characters = [
+         *   { 'name': 'barney',  'blocked': true,  'employer': 'slate' },
+         *   { 'name': 'fred',    'blocked': false, 'employer': 'slate' },
+         *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
+         * ];
+         *
+         * // using "_.pluck" callback shorthand
+         * _.first(characters, 'blocked');
+         * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }]
+         *
+         * // using "_.where" callback shorthand
+         * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name');
+         * // => ['barney', 'fred']
+         */
+        function first(array, callback, thisArg) {
+            var n = 0,
+                    length = array ? array.length : 0;
+
+            if (typeof callback != 'number' && callback != null) {
+                var index = -1;
+                callback = lodash.createCallback(callback, thisArg, 3);
+                while (++index < length && callback(array[index], index, array)) {
+                    n++;
+                }
+            } else {
+                n = callback;
+                if (n == null || thisArg) {
+                    return array ? array[0] : undefined;
+                }
+            }
+            return slice(array, 0, nativeMin(nativeMax(0, n), length));
+        }
+
+        /**
+         * Flattens a nested array (the nesting can be to any depth). If `isShallow`
+         * is truey, the array will only be flattened a single level. If a callback
+         * is provided each element of the array is passed through the callback before
+         * flattening. The callback is bound to `thisArg` and invoked with three
+         * arguments; (value, index, array).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {Array} array The array to flatten.
+         * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Array} Returns a new flattened array.
+         * @example
+         *
+         * _.flatten([1, [2], [3, [[4]]]]);
+         * // => [1, 2, 3, 4];
+         *
+         * _.flatten([1, [2], [3, [[4]]]], true);
+         * // => [1, 2, 3, [[4]]];
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
+         *   { 'name': 'fred',   'age': 40, 'pets': ['baby puss', 'dino'] }
+         * ];
+         *
+         * // using "_.pluck" callback shorthand
+         * _.flatten(characters, 'pets');
+         * // => ['hoppy', 'baby puss', 'dino']
+         */
+        function flatten(array, isShallow, callback, thisArg) {
+            // juggle arguments
+            if (typeof isShallow != 'boolean' && isShallow != null) {
+                thisArg = callback;
+                callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow;
+                isShallow = false;
+            }
+            if (callback != null) {
+                array = map(array, callback, thisArg);
+            }
+            return baseFlatten(array, isShallow);
+        }
+
+        /**
+         * Gets the index at which the first occurrence of `value` is found using
+         * strict equality for comparisons, i.e. `===`. If the array is already sorted
+         * providing `true` for `fromIndex` will run a faster binary search.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {Array} array The array to search.
+         * @param {*} value The value to search for.
+         * @param {boolean|number} [fromIndex=0] The index to search from or `true`
+         *  to perform a binary search on a sorted array.
+         * @returns {number} Returns the index of the matched value or `-1`.
+         * @example
+         *
+         * _.indexOf([1, 2, 3, 1, 2, 3], 2);
+         * // => 1
+         *
+         * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
+         * // => 4
+         *
+         * _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
+         * // => 2
+         */
+        function indexOf(array, value, fromIndex) {
+            if (typeof fromIndex == 'number') {
+                var length = array ? array.length : 0;
+                fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0);
+            } else if (fromIndex) {
+                var index = sortedIndex(array, value);
+                return array[index] === value ? index : -1;
+            }
+            return baseIndexOf(array, value, fromIndex);
+        }
+
+        /**
+         * Gets all but the last element or last `n` elements of an array. If a
+         * callback is provided elements at the end of the array are excluded from
+         * the result as long as the callback returns truey. The callback is bound
+         * to `thisArg` and invoked with three arguments; (value, index, array).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {Array} array The array to query.
+         * @param {Function|Object|number|string} [callback=1] The function called
+         *  per element or the number of elements to exclude. If a property name or
+         *  object is provided it will be used to create a "_.pluck" or "_.where"
+         *  style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Array} Returns a slice of `array`.
+         * @example
+         *
+         * _.initial([1, 2, 3]);
+         * // => [1, 2]
+         *
+         * _.initial([1, 2, 3], 2);
+         * // => [1]
+         *
+         * _.initial([1, 2, 3], function(num) {
+     *   return num > 1;
+     * });
+         * // => [1]
+         *
+         * var characters = [
+         *   { 'name': 'barney',  'blocked': false, 'employer': 'slate' },
+         *   { 'name': 'fred',    'blocked': true,  'employer': 'slate' },
+         *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
+         * ];
+         *
+         * // using "_.pluck" callback shorthand
+         * _.initial(characters, 'blocked');
+         * // => [{ 'name': 'barney',  'blocked': false, 'employer': 'slate' }]
+         *
+         * // using "_.where" callback shorthand
+         * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name');
+         * // => ['barney', 'fred']
+         */
+        function initial(array, callback, thisArg) {
+            var n = 0,
+                    length = array ? array.length : 0;
+
+            if (typeof callback != 'number' && callback != null) {
+                var index = length;
+                callback = lodash.createCallback(callback, thisArg, 3);
+                while (index-- && callback(array[index], index, array)) {
+                    n++;
+                }
+            } else {
+                n = (callback == null || thisArg) ? 1 : callback || n;
+            }
+            return slice(array, 0, nativeMin(nativeMax(0, length - n), length));
+        }
+
+        /**
+         * Creates an array of unique values present in all provided arrays using
+         * strict equality for comparisons, i.e. `===`.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {...Array} [array] The arrays to inspect.
+         * @returns {Array} Returns an array of shared values.
+         * @example
+         *
+         * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+         * // => [1, 2]
+         */
+        function intersection() {
+            var args = [],
+                    argsIndex = -1,
+                    argsLength = arguments.length,
+                    caches = getArray(),
+                    indexOf = getIndexOf(),
+                    trustIndexOf = indexOf === baseIndexOf,
+                    seen = getArray();
+
+            while (++argsIndex < argsLength) {
+                var value = arguments[argsIndex];
+                if (isArray(value) || isArguments(value)) {
+                    args.push(value);
+                    caches.push(trustIndexOf && value.length >= largeArraySize &&
+                            createCache(argsIndex ? args[argsIndex] : seen));
+                }
+            }
+            var array = args[0],
+                    index = -1,
+                    length = array ? array.length : 0,
+                    result = [];
+
+            outer:
+                    while (++index < length) {
+                        var cache = caches[0];
+                        value = array[index];
+
+                        if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) {
+                            argsIndex = argsLength;
+                            (cache || seen).push(value);
+                            while (--argsIndex) {
+                                cache = caches[argsIndex];
+                                if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) {
+                                    continue outer;
+                                }
+                            }
+                            result.push(value);
+                        }
+                    }
+            while (argsLength--) {
+                cache = caches[argsLength];
+                if (cache) {
+                    releaseObject(cache);
+                }
+            }
+            releaseArray(caches);
+            releaseArray(seen);
+            return result;
+        }
+
+        /**
+         * Gets the last element or last `n` elements of an array. If a callback is
+         * provided elements at the end of the array are returned as long as the
+         * callback returns truey. The callback is bound to `thisArg` and invoked
+         * with three arguments; (value, index, array).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {Array} array The array to query.
+         * @param {Function|Object|number|string} [callback] The function called
+         *  per element or the number of elements to return. If a property name or
+         *  object is provided it will be used to create a "_.pluck" or "_.where"
+         *  style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {*} Returns the last element(s) of `array`.
+         * @example
+         *
+         * _.last([1, 2, 3]);
+         * // => 3
+         *
+         * _.last([1, 2, 3], 2);
+         * // => [2, 3]
+         *
+         * _.last([1, 2, 3], function(num) {
+     *   return num > 1;
+     * });
+         * // => [2, 3]
+         *
+         * var characters = [
+         *   { 'name': 'barney',  'blocked': false, 'employer': 'slate' },
+         *   { 'name': 'fred',    'blocked': true,  'employer': 'slate' },
+         *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
+         * ];
+         *
+         * // using "_.pluck" callback shorthand
+         * _.pluck(_.last(characters, 'blocked'), 'name');
+         * // => ['fred', 'pebbles']
+         *
+         * // using "_.where" callback shorthand
+         * _.last(characters, { 'employer': 'na' });
+         * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+         */
+        function last(array, callback, thisArg) {
+            var n = 0,
+                    length = array ? array.length : 0;
+
+            if (typeof callback != 'number' && callback != null) {
+                var index = length;
+                callback = lodash.createCallback(callback, thisArg, 3);
+                while (index-- && callback(array[index], index, array)) {
+                    n++;
+                }
+            } else {
+                n = callback;
+                if (n == null || thisArg) {
+                    return array ? array[length - 1] : undefined;
+                }
+            }
+            return slice(array, nativeMax(0, length - n));
+        }
+
+        /**
+         * Gets the index at which the last occurrence of `value` is found using strict
+         * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
+         * as the offset from the end of the collection.
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {Array} array The array to search.
+         * @param {*} value The value to search for.
+         * @param {number} [fromIndex=array.length-1] The index to search from.
+         * @returns {number} Returns the index of the matched value or `-1`.
+         * @example
+         *
+         * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
+         * // => 4
+         *
+         * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
+         * // => 1
+         */
+        function lastIndexOf(array, value, fromIndex) {
+            var index = array ? array.length : 0;
+            if (typeof fromIndex == 'number') {
+                index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
+            }
+            while (index--) {
+                if (array[index] === value) {
+                    return index;
+                }
+            }
+            return -1;
+        }
+
+        /**
+         * Removes all provided values from the given array using strict equality for
+         * comparisons, i.e. `===`.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {Array} array The array to modify.
+         * @param {...*} [value] The values to remove.
+         * @returns {Array} Returns `array`.
+         * @example
+         *
+         * var array = [1, 2, 3, 1, 2, 3];
+         * _.pull(array, 2, 3);
+         * console.log(array);
+         * // => [1, 1]
+         */
+        function pull(array) {
+            var args = arguments,
+                    argsIndex = 0,
+                    argsLength = args.length,
+                    length = array ? array.length : 0;
+
+            while (++argsIndex < argsLength) {
+                var index = -1,
+                        value = args[argsIndex];
+                while (++index < length) {
+                    if (array[index] === value) {
+                        splice.call(array, index--, 1);
+                        length--;
+                    }
+                }
+            }
+            return array;
+        }
+
+        /**
+         * Creates an array of numbers (positive and/or negative) progressing from
+         * `start` up to but not including `end`. If `start` is less than `stop` a
+         * zero-length range is created unless a negative `step` is specified.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {number} [start=0] The start of the range.
+         * @param {number} end The end of the range.
+         * @param {number} [step=1] The value to increment or decrement by.
+         * @returns {Array} Returns a new range array.
+         * @example
+         *
+         * _.range(4);
+         * // => [0, 1, 2, 3]
+         *
+         * _.range(1, 5);
+         * // => [1, 2, 3, 4]
+         *
+         * _.range(0, 20, 5);
+         * // => [0, 5, 10, 15]
+         *
+         * _.range(0, -4, -1);
+         * // => [0, -1, -2, -3]
+         *
+         * _.range(1, 4, 0);
+         * // => [1, 1, 1]
+         *
+         * _.range(0);
+         * // => []
+         */
+        function range(start, end, step) {
+            start = +start || 0;
+            step = typeof step == 'number' ? step : (+step || 1);
+
+            if (end == null) {
+                end = start;
+                start = 0;
+            }
+            // use `Array(length)` so engines like Chakra and V8 avoid slower modes
+            // http://youtu.be/XAqIpGU8ZZk#t=17m25s
+            var index = -1,
+                    length = nativeMax(0, ceil((end - start) / (step || 1))),
+                    result = Array(length);
+
+            while (++index < length) {
+                result[index] = start;
+                start += step;
+            }
+            return result;
+        }
+
+        /**
+         * Removes all elements from an array that the callback returns truey for
+         * and returns an array of removed elements. The callback is bound to `thisArg`
+         * and invoked with three arguments; (value, index, array).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {Array} array The array to modify.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Array} Returns a new array of removed elements.
+         * @example
+         *
+         * var array = [1, 2, 3, 4, 5, 6];
+         * var evens = _.remove(array, function(num) { return num % 2 == 0; });
+         *
+         * console.log(array);
+         * // => [1, 3, 5]
+         *
+         * console.log(evens);
+         * // => [2, 4, 6]
+         */
+        function remove(array, callback, thisArg) {
+            var index = -1,
+                    length = array ? array.length : 0,
+                    result = [];
+
+            callback = lodash.createCallback(callback, thisArg, 3);
+            while (++index < length) {
+                var value = array[index];
+                if (callback(value, index, array)) {
+                    result.push(value);
+                    splice.call(array, index--, 1);
+                    length--;
+                }
+            }
+            return result;
+        }
+
+        /**
+         * The opposite of `_.initial` this method gets all but the first element or
+         * first `n` elements of an array. If a callback function is provided elements
+         * at the beginning of the array are excluded from the result as long as the
+         * callback returns truey. The callback is bound to `thisArg` and invoked
+         * with three arguments; (value, index, array).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @alias drop, tail
+         * @category Arrays
+         * @param {Array} array The array to query.
+         * @param {Function|Object|number|string} [callback=1] The function called
+         *  per element or the number of elements to exclude. If a property name or
+         *  object is provided it will be used to create a "_.pluck" or "_.where"
+         *  style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Array} Returns a slice of `array`.
+         * @example
+         *
+         * _.rest([1, 2, 3]);
+         * // => [2, 3]
+         *
+         * _.rest([1, 2, 3], 2);
+         * // => [3]
+         *
+         * _.rest([1, 2, 3], function(num) {
+     *   return num < 3;
+     * });
+         * // => [3]
+         *
+         * var characters = [
+         *   { 'name': 'barney',  'blocked': true,  'employer': 'slate' },
+         *   { 'name': 'fred',    'blocked': false,  'employer': 'slate' },
+         *   { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+         * ];
+         *
+         * // using "_.pluck" callback shorthand
+         * _.pluck(_.rest(characters, 'blocked'), 'name');
+         * // => ['fred', 'pebbles']
+         *
+         * // using "_.where" callback shorthand
+         * _.rest(characters, { 'employer': 'slate' });
+         * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+         */
+        function rest(array, callback, thisArg) {
+            if (typeof callback != 'number' && callback != null) {
+                var n = 0,
+                        index = -1,
+                        length = array ? array.length : 0;
+
+                callback = lodash.createCallback(callback, thisArg, 3);
+                while (++index < length && callback(array[index], index, array)) {
+                    n++;
+                }
+            } else {
+                n = (callback == null || thisArg) ? 1 : nativeMax(0, callback);
+            }
+            return slice(array, n);
+        }
+
+        /**
+         * Uses a binary search to determine the smallest index at which a value
+         * should be inserted into a given sorted array in order to maintain the sort
+         * order of the array. If a callback is provided it will be executed for
+         * `value` and each element of `array` to compute their sort ranking. The
+         * callback is bound to `thisArg` and invoked with one argument; (value).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {Array} array The array to inspect.
+         * @param {*} value The value to evaluate.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {number} Returns the index at which `value` should be inserted
+         *  into `array`.
+         * @example
+         *
+         * _.sortedIndex([20, 30, 50], 40);
+         * // => 2
+         *
+         * // using "_.pluck" callback shorthand
+         * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
+         * // => 2
+         *
+         * var dict = {
+     *   'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
+     * };
+         *
+         * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+     *   return dict.wordToNumber[word];
+     * });
+         * // => 2
+         *
+         * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+     *   return this.wordToNumber[word];
+     * }, dict);
+         * // => 2
+         */
+        function sortedIndex(array, value, callback, thisArg) {
+            var low = 0,
+                    high = array ? array.length : low;
+
+            // explicitly reference `identity` for better inlining in Firefox
+            callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity;
+            value = callback(value);
+
+            while (low < high) {
+                var mid = (low + high) >>> 1;
+                (callback(array[mid]) < value)
+                        ? low = mid + 1
+                        : high = mid;
+            }
+            return low;
+        }
+
+        /**
+         * Creates an array of unique values, in order, of the provided arrays using
+         * strict equality for comparisons, i.e. `===`.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {...Array} [array] The arrays to inspect.
+         * @returns {Array} Returns an array of combined values.
+         * @example
+         *
+         * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+         * // => [1, 2, 3, 5, 4]
+         */
+        function union() {
+            return baseUniq(baseFlatten(arguments, true, true));
+        }
+
+        /**
+         * Creates a duplicate-value-free version of an array using strict equality
+         * for comparisons, i.e. `===`. If the array is sorted, providing
+         * `true` for `isSorted` will use a faster algorithm. If a callback is provided
+         * each element of `array` is passed through the callback before uniqueness
+         * is computed. The callback is bound to `thisArg` and invoked with three
+         * arguments; (value, index, array).
+         *
+         * If a property name is provided for `callback` the created "_.pluck" style
+         * callback will return the property value of the given element.
+         *
+         * If an object is provided for `callback` the created "_.where" style callback
+         * will return `true` for elements that have the properties of the given object,
+         * else `false`.
+         *
+         * @static
+         * @memberOf _
+         * @alias unique
+         * @category Arrays
+         * @param {Array} array The array to process.
+         * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+         * @param {Function|Object|string} [callback=identity] The function called
+         *  per iteration. If a property name or object is provided it will be used
+         *  to create a "_.pluck" or "_.where" style callback, respectively.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Array} Returns a duplicate-value-free array.
+         * @example
+         *
+         * _.uniq([1, 2, 1, 3, 1]);
+         * // => [1, 2, 3]
+         *
+         * _.uniq([1, 1, 2, 2, 3], true);
+         * // => [1, 2, 3]
+         *
+         * _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); });
+         * // => ['A', 'b', 'C']
+         *
+         * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math);
+         * // => [1, 2.5, 3]
+         *
+         * // using "_.pluck" callback shorthand
+         * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+         * // => [{ 'x': 1 }, { 'x': 2 }]
+         */
+        function uniq(array, isSorted, callback, thisArg) {
+            // juggle arguments
+            if (typeof isSorted != 'boolean' && isSorted != null) {
+                thisArg = callback;
+                callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted;
+                isSorted = false;
+            }
+            if (callback != null) {
+                callback = lodash.createCallback(callback, thisArg, 3);
+            }
+            return baseUniq(array, isSorted, callback);
+        }
+
+        /**
+         * Creates an array excluding all provided values using strict equality for
+         * comparisons, i.e. `===`.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {Array} array The array to filter.
+         * @param {...*} [value] The values to exclude.
+         * @returns {Array} Returns a new array of filtered values.
+         * @example
+         *
+         * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
+         * // => [2, 3, 4]
+         */
+        function without(array) {
+            return baseDifference(array, slice(arguments, 1));
+        }
+
+        /**
+         * Creates an array that is the smymetric difference of the provided arrays.
+         * See http://en.wikipedia.org/wiki/Symmetric_difference.
+         *
+         * @static
+         * @memberOf _
+         * @category Arrays
+         * @param {...Array} [array] The arrays to inspect.
+         * @returns {Array} Returns an array of values.
+         * @example
+         *
+         * _.xor([1, 2, 3], [5, 2, 1, 4]);
+         * // => [3, 5, 4]
+         *
+         * _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
+         * // => [1, 4, 5]
+         */
+        function xor() {
+            var index = -1,
+                    length = arguments.length;
+
+            while (++index < length) {
+                var array = arguments[index];
+                if (isArray(array) || isArguments(array)) {
+                    var result = result
+                            ? baseUniq(baseDifference(result, array).concat(baseDifference(array, result)))
+                            : array;
+                }
+            }
+            return result || [];
+        }
+
+        /**
+         * Creates an array of grouped elements, the first of which contains the first
+         * elements of the given arrays, the second of which contains the second
+         * elements of the given arrays, and so on.
+         *
+         * @static
+         * @memberOf _
+         * @alias unzip
+         * @category Arrays
+         * @param {...Array} [array] Arrays to process.
+         * @returns {Array} Returns a new array of grouped elements.
+         * @example
+         *
+         * _.zip(['fred', 'barney'], [30, 40], [true, false]);
+         * // => [['fred', 30, true], ['barney', 40, false]]
+         */
+        function zip() {
+            var array = arguments.length > 1 ? arguments : arguments[0],
+                    index = -1,
+                    length = array ? max(pluck(array, 'length')) : 0,
+                    result = Array(length < 0 ? 0 : length);
+
+            while (++index < length) {
+                result[index] = pluck(array, index);
+            }
+            return result;
+        }
+
+        /**
+         * Creates an object composed from arrays of `keys` and `values`. Provide
+         * either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`
+         * or two arrays, one of `keys` and one of corresponding `values`.
+         *
+         * @static
+         * @memberOf _
+         * @alias object
+         * @category Arrays
+         * @param {Array} keys The array of keys.
+         * @param {Array} [values=[]] The array of values.
+         * @returns {Object} Returns an object composed of the given keys and
+         *  corresponding values.
+         * @example
+         *
+         * _.zipObject(['fred', 'barney'], [30, 40]);
+         * // => { 'fred': 30, 'barney': 40 }
+         */
+        function zipObject(keys, values) {
+            var index = -1,
+                    length = keys ? keys.length : 0,
+                    result = {};
+
+            if (!values && length && !isArray(keys[0])) {
+                values = [];
+            }
+            while (++index < length) {
+                var key = keys[index];
+                if (values) {
+                    result[key] = values[index];
+                } else if (key) {
+                    result[key[0]] = key[1];
+                }
+            }
+            return result;
+        }
+
+        /*--------------------------------------------------------------------------*/
+
+        /**
+         * Creates a function that executes `func`, with  the `this` binding and
+         * arguments of the created function, only after being called `n` times.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {number} n The number of times the function must be called before
+         *  `func` is executed.
+         * @param {Function} func The function to restrict.
+         * @returns {Function} Returns the new restricted function.
+         * @example
+         *
+         * var saves = ['profile', 'settings'];
+         *
+         * var done = _.after(saves.length, function() {
+     *   console.log('Done saving!');
+     * });
+         *
+         * _.forEach(saves, function(type) {
+     *   asyncSave({ 'type': type, 'complete': done });
+     * });
+         * // => logs 'Done saving!', after all saves have completed
+         */
+        function after(n, func) {
+            if (!isFunction(func)) {
+                throw new TypeError;
+            }
+            return function() {
+                if (--n < 1) {
+                    return func.apply(this, arguments);
+                }
+            };
+        }
+
+        /**
+         * Creates a function that, when called, invokes `func` with the `this`
+         * binding of `thisArg` and prepends any additional `bind` arguments to those
+         * provided to the bound function.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {Function} func The function to bind.
+         * @param {*} [thisArg] The `this` binding of `func`.
+         * @param {...*} [arg] Arguments to be partially applied.
+         * @returns {Function} Returns the new bound function.
+         * @example
+         *
+         * var func = function(greeting) {
+     *   return greeting + ' ' + this.name;
+     * };
+         *
+         * func = _.bind(func, { 'name': 'fred' }, 'hi');
+         * func();
+         * // => 'hi fred'
+         */
+        function bind(func, thisArg) {
+            return arguments.length > 2
+                    ? createWrapper(func, 17, slice(arguments, 2), null, thisArg)
+                    : createWrapper(func, 1, null, null, thisArg);
+        }
+
+        /**
+         * Binds methods of an object to the object itself, overwriting the existing
+         * method. Method names may be specified as individual arguments or as arrays
+         * of method names. If no method names are provided all the function properties
+         * of `object` will be bound.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {Object} object The object to bind and assign the bound methods to.
+         * @param {...string} [methodName] The object method names to
+         *  bind, specified as individual method names or arrays of method names.
+         * @returns {Object} Returns `object`.
+         * @example
+         *
+         * var view = {
+     *   'label': 'docs',
+     *   'onClick': function() { console.log('clicked ' + this.label); }
+     * };
+         *
+         * _.bindAll(view);
+         * jQuery('#docs').on('click', view.onClick);
+         * // => logs 'clicked docs', when the button is clicked
+         */
+        function bindAll(object) {
+            var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object),
+                    index = -1,
+                    length = funcs.length;
+
+            while (++index < length) {
+                var key = funcs[index];
+                object[key] = createWrapper(object[key], 1, null, null, object);
+            }
+            return object;
+        }
+
+        /**
+         * Creates a function that, when called, invokes the method at `object[key]`
+         * and prepends any additional `bindKey` arguments to those provided to the bound
+         * function. This method differs from `_.bind` by allowing bound functions to
+         * reference methods that will be redefined or don't yet exist.
+         * See http://michaux.ca/articles/lazy-function-definition-pattern.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {Object} object The object the method belongs to.
+         * @param {string} key The key of the method.
+         * @param {...*} [arg] Arguments to be partially applied.
+         * @returns {Function} Returns the new bound function.
+         * @example
+         *
+         * var object = {
+     *   'name': 'fred',
+     *   'greet': function(greeting) {
+     *     return greeting + ' ' + this.name;
+     *   }
+     * };
+         *
+         * var func = _.bindKey(object, 'greet', 'hi');
+         * func();
+         * // => 'hi fred'
+         *
+         * object.greet = function(greeting) {
+     *   return greeting + 'ya ' + this.name + '!';
+     * };
+         *
+         * func();
+         * // => 'hiya fred!'
+         */
+        function bindKey(object, key) {
+            return arguments.length > 2
+                    ? createWrapper(key, 19, slice(arguments, 2), null, object)
+                    : createWrapper(key, 3, null, null, object);
+        }
+
+        /**
+         * Creates a function that is the composition of the provided functions,
+         * where each function consumes the return value of the function that follows.
+         * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
+         * Each function is executed with the `this` binding of the composed function.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {...Function} [func] Functions to compose.
+         * @returns {Function} Returns the new composed function.
+         * @example
+         *
+         * var realNameMap = {
+     *   'pebbles': 'penelope'
+     * };
+         *
+         * var format = function(name) {
+     *   name = realNameMap[name.toLowerCase()] || name;
+     *   return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
+     * };
+         *
+         * var greet = function(formatted) {
+     *   return 'Hiya ' + formatted + '!';
+     * };
+         *
+         * var welcome = _.compose(greet, format);
+         * welcome('pebbles');
+         * // => 'Hiya Penelope!'
+         */
+        function compose() {
+            var funcs = arguments,
+                    length = funcs.length;
+
+            while (length--) {
+                if (!isFunction(funcs[length])) {
+                    throw new TypeError;
+                }
+            }
+            return function() {
+                var args = arguments,
+                        length = funcs.length;
+
+                while (length--) {
+                    args = [funcs[length].apply(this, args)];
+                }
+                return args[0];
+            };
+        }
+
+        /**
+         * Creates a function which accepts one or more arguments of `func` that when
+         * invoked either executes `func` returning its result, if all `func` arguments
+         * have been provided, or returns a function that accepts one or more of the
+         * remaining `func` arguments, and so on. The arity of `func` can be specified
+         * if `func.length` is not sufficient.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {Function} func The function to curry.
+         * @param {number} [arity=func.length] The arity of `func`.
+         * @returns {Function} Returns the new curried function.
+         * @example
+         *
+         * var curried = _.curry(function(a, b, c) {
+     *   console.log(a + b + c);
+     * });
+         *
+         * curried(1)(2)(3);
+         * // => 6
+         *
+         * curried(1, 2)(3);
+         * // => 6
+         *
+         * curried(1, 2, 3);
+         * // => 6
+         */
+        function curry(func, arity) {
+            arity = typeof arity == 'number' ? arity : (+arity || func.length);
+            return createWrapper(func, 4, null, null, null, arity);
+        }
+
+        /**
+         * Creates a function that will delay the execution of `func` until after
+         * `wait` milliseconds have elapsed since the last time it was invoked.
+         * Provide an options object to indicate that `func` should be invoked on
+         * the leading and/or trailing edge of the `wait` timeout. Subsequent calls
+         * to the debounced function will return the result of the last `func` call.
+         *
+         * Note: If `leading` and `trailing` options are `true` `func` will be called
+         * on the trailing edge of the timeout only if the the debounced function is
+         * invoked more than once during the `wait` timeout.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {Function} func The function to debounce.
+         * @param {number} wait The number of milliseconds to delay.
+         * @param {Object} [options] The options object.
+         * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout.
+         * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called.
+         * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+         * @returns {Function} Returns the new debounced function.
+         * @example
+         *
+         * // avoid costly calculations while the window size is in flux
+         * var lazyLayout = _.debounce(calculateLayout, 150);
+         * jQuery(window).on('resize', lazyLayout);
+         *
+         * // execute `sendMail` when the click event is fired, debouncing subsequent calls
+         * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
+     *   'leading': true,
+     *   'trailing': false
+     * });
+         *
+         * // ensure `batchLog` is executed once after 1 second of debounced calls
+         * var source = new EventSource('/stream');
+         * source.addEventListener('message', _.debounce(batchLog, 250, {
+     *   'maxWait': 1000
+     * }, false);
+         */
+        function debounce(func, wait, options) {
+            var args,
+                    maxTimeoutId,
+                    result,
+                    stamp,
+                    thisArg,
+                    timeoutId,
+                    trailingCall,
+                    lastCalled = 0,
+                    maxWait = false,
+                    trailing = true;
+
+            if (!isFunction(func)) {
+                throw new TypeError;
+            }
+            wait = nativeMax(0, wait) || 0;
+            if (options === true) {
+                var leading = true;
+                trailing = false;
+            } else if (isObject(options)) {
+                leading = options.leading;
+                maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0);
+                trailing = 'trailing' in options ? options.trailing : trailing;
+            }
+            var delayed = function() {
+                var remaining = wait - (now() - stamp);
+                if (remaining <= 0) {
+                    if (maxTimeoutId) {
+                        clearTimeout(maxTimeoutId);
+                    }
+                    var isCalled = trailingCall;
+                    maxTimeoutId = timeoutId = trailingCall = undefined;
+                    if (isCalled) {
+                        lastCalled = now();
+                        result = func.apply(thisArg, args);
+                        if (!timeoutId && !maxTimeoutId) {
+                            args = thisArg = null;
+                        }
+                    }
+                } else {
+                    timeoutId = setTimeout(delayed, remaining);
+                }
+            };
+
+            var maxDelayed = function() {
+                if (timeoutId) {
+                    clearTimeout(timeoutId);
+                }
+                maxTimeoutId = timeoutId = trailingCall = undefined;
+                if (trailing || (maxWait !== wait)) {
+                    lastCalled = now();
+                    result = func.apply(thisArg, args);
+                    if (!timeoutId && !maxTimeoutId) {
+                        args = thisArg = null;
+                    }
+                }
+            };
+
+            return function() {
+                args = arguments;
+                stamp = now();
+                thisArg = this;
+                trailingCall = trailing && (timeoutId || !leading);
+
+                if (maxWait === false) {
+                    var leadingCall = leading && !timeoutId;
+                } else {
+                    if (!maxTimeoutId && !leading) {
+                        lastCalled = stamp;
+                    }
+                    var remaining = maxWait - (stamp - lastCalled),
+                            isCalled = remaining <= 0;
+
+                    if (isCalled) {
+                        if (maxTimeoutId) {
+                            maxTimeoutId = clearTimeout(maxTimeoutId);
+                        }
+                        lastCalled = stamp;
+                        result = func.apply(thisArg, args);
+                    }
+                    else if (!maxTimeoutId) {
+                        maxTimeoutId = setTimeout(maxDelayed, remaining);
+                    }
+                }
+                if (isCalled && timeoutId) {
+                    timeoutId = clearTimeout(timeoutId);
+                }
+                else if (!timeoutId && wait !== maxWait) {
+                    timeoutId = setTimeout(delayed, wait);
+                }
+                if (leadingCall) {
+                    isCalled = true;
+                    result = func.apply(thisArg, args);
+                }
+                if (isCalled && !timeoutId && !maxTimeoutId) {
+                    args = thisArg = null;
+                }
+                return result;
+            };
+        }
+
+        /**
+         * Defers executing the `func` function until the current call stack has cleared.
+         * Additional arguments will be provided to `func` when it is invoked.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {Function} func The function to defer.
+         * @param {...*} [arg] Arguments to invoke the function with.
+         * @returns {number} Returns the timer id.
+         * @example
+         *
+         * _.defer(function(text) { console.log(text); }, 'deferred');
+         * // logs 'deferred' after one or more milliseconds
+         */
+        function defer(func) {
+            if (!isFunction(func)) {
+                throw new TypeError;
+            }
+            var args = slice(arguments, 1);
+            return setTimeout(function() { func.apply(undefined, args); }, 1);
+        }
+
+        /**
+         * Executes the `func` function after `wait` milliseconds. Additional arguments
+         * will be provided to `func` when it is invoked.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {Function} func The function to delay.
+         * @param {number} wait The number of milliseconds to delay execution.
+         * @param {...*} [arg] Arguments to invoke the function with.
+         * @returns {number} Returns the timer id.
+         * @example
+         *
+         * _.delay(function(text) { console.log(text); }, 1000, 'later');
+         * // => logs 'later' after one second
+         */
+        function delay(func, wait) {
+            if (!isFunction(func)) {
+                throw new TypeError;
+            }
+            var args = slice(arguments, 2);
+            return setTimeout(function() { func.apply(undefined, args); }, wait);
+        }
+
+        /**
+         * Creates a function that memoizes the result of `func`. If `resolver` is
+         * provided it will be used to determine the cache key for storing the result
+         * based on the arguments provided to the memoized function. By default, the
+         * first argument provided to the memoized function is used as the cache key.
+         * The `func` is executed with the `this` binding of the memoized function.
+         * The result cache is exposed as the `cache` property on the memoized function.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {Function} func The function to have its output memoized.
+         * @param {Function} [resolver] A function used to resolve the cache key.
+         * @returns {Function} Returns the new memoizing function.
+         * @example
+         *
+         * var fibonacci = _.memoize(function(n) {
+     *   return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
+     * });
+         *
+         * fibonacci(9)
+         * // => 34
+         *
+         * var data = {
+     *   'fred': { 'name': 'fred', 'age': 40 },
+     *   'pebbles': { 'name': 'pebbles', 'age': 1 }
+     * };
+         *
+         * // modifying the result cache
+         * var get = _.memoize(function(name) { return data[name]; }, _.identity);
+         * get('pebbles');
+         * // => { 'name': 'pebbles', 'age': 1 }
+         *
+         * get.cache.pebbles.name = 'penelope';
+         * get('pebbles');
+         * // => { 'name': 'penelope', 'age': 1 }
+         */
+        function memoize(func, resolver) {
+            if (!isFunction(func)) {
+                throw new TypeError;
+            }
+            var memoized = function() {
+                var cache = memoized.cache,
+                        key = resolver ? resolver.apply(this, arguments) : keyPrefix + arguments[0];
+
+                return hasOwnProperty.call(cache, key)
+                        ? cache[key]
+                        : (cache[key] = func.apply(this, arguments));
+            }
+            memoized.cache = {};
+            return memoized;
+        }
+
+        /**
+         * Creates a function that is restricted to execute `func` once. Repeat calls to
+         * the function will return the value of the first call. The `func` is executed
+         * with the `this` binding of the created function.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {Function} func The function to restrict.
+         * @returns {Function} Returns the new restricted function.
+         * @example
+         *
+         * var initialize = _.once(createApplication);
+         * initialize();
+         * initialize();
+         * // `initialize` executes `createApplication` once
+         */
+        function once(func) {
+            var ran,
+                    result;
+
+            if (!isFunction(func)) {
+                throw new TypeError;
+            }
+            return function() {
+                if (ran) {
+                    return result;
+                }
+                ran = true;
+                result = func.apply(this, arguments);
+
+                // clear the `func` variable so the function may be garbage collected
+                func = null;
+                return result;
+            };
+        }
+
+        /**
+         * Creates a function that, when called, invokes `func` with any additional
+         * `partial` arguments prepended to those provided to the new function. This
+         * method is similar to `_.bind` except it does **not** alter the `this` binding.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {Function} func The function to partially apply arguments to.
+         * @param {...*} [arg] Arguments to be partially applied.
+         * @returns {Function} Returns the new partially applied function.
+         * @example
+         *
+         * var greet = function(greeting, name) { return greeting + ' ' + name; };
+         * var hi = _.partial(greet, 'hi');
+         * hi('fred');
+         * // => 'hi fred'
+         */
+        function partial(func) {
+            return createWrapper(func, 16, slice(arguments, 1));
+        }
+
+        /**
+         * This method is like `_.partial` except that `partial` arguments are
+         * appended to those provided to the new function.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {Function} func The function to partially apply arguments to.
+         * @param {...*} [arg] Arguments to be partially applied.
+         * @returns {Function} Returns the new partially applied function.
+         * @example
+         *
+         * var defaultsDeep = _.partialRight(_.merge, _.defaults);
+         *
+         * var options = {
+     *   'variable': 'data',
+     *   'imports': { 'jq': $ }
+     * };
+         *
+         * defaultsDeep(options, _.templateSettings);
+         *
+         * options.variable
+         * // => 'data'
+         *
+         * options.imports
+         * // => { '_': _, 'jq': $ }
+         */
+        function partialRight(func) {
+            return createWrapper(func, 32, null, slice(arguments, 1));
+        }
+
+        /**
+         * Creates a function that, when executed, will only call the `func` function
+         * at most once per every `wait` milliseconds. Provide an options object to
+         * indicate that `func` should be invoked on the leading and/or trailing edge
+         * of the `wait` timeout. Subsequent calls to the throttled function will
+         * return the result of the last `func` call.
+         *
+         * Note: If `leading` and `trailing` options are `true` `func` will be called
+         * on the trailing edge of the timeout only if the the throttled function is
+         * invoked more than once during the `wait` timeout.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {Function} func The function to throttle.
+         * @param {number} wait The number of milliseconds to throttle executions to.
+         * @param {Object} [options] The options object.
+         * @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout.
+         * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+         * @returns {Function} Returns the new throttled function.
+         * @example
+         *
+         * // avoid excessively updating the position while scrolling
+         * var throttled = _.throttle(updatePosition, 100);
+         * jQuery(window).on('scroll', throttled);
+         *
+         * // execute `renewToken` when the click event is fired, but not more than once every 5 minutes
+         * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
+     *   'trailing': false
+     * }));
+         */
+        function throttle(func, wait, options) {
+            var leading = true,
+                    trailing = true;
+
+            if (!isFunction(func)) {
+                throw new TypeError;
+            }
+            if (options === false) {
+                leading = false;
+            } else if (isObject(options)) {
+                leading = 'leading' in options ? options.leading : leading;
+                trailing = 'trailing' in options ? options.trailing : trailing;
+            }
+            debounceOptions.leading = leading;
+            debounceOptions.maxWait = wait;
+            debounceOptions.trailing = trailing;
+
+            return debounce(func, wait, debounceOptions);
+        }
+
+        /**
+         * Creates a function that provides `value` to the wrapper function as its
+         * first argument. Additional arguments provided to the function are appended
+         * to those provided to the wrapper function. The wrapper is executed with
+         * the `this` binding of the created function.
+         *
+         * @static
+         * @memberOf _
+         * @category Functions
+         * @param {*} value The value to wrap.
+         * @param {Function} wrapper The wrapper function.
+         * @returns {Function} Returns the new function.
+         * @example
+         *
+         * var p = _.wrap(_.escape, function(func, text) {
+     *   return '<p>' + func(text) + '</p>';
+     * });
+         *
+         * p('Fred, Wilma, & Pebbles');
+         * // => '<p>Fred, Wilma, &amp; Pebbles</p>'
+         */
+        function wrap(value, wrapper) {
+            return createWrapper(wrapper, 16, [value]);
+        }
+
+        /*--------------------------------------------------------------------------*/
+
+        /**
+         * Creates a function that returns `value`.
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @param {*} value The value to return from the new function.
+         * @returns {Function} Returns the new function.
+         * @example
+         *
+         * var object = { 'name': 'fred' };
+         * var getter = _.constant(object);
+         * getter() === object;
+         * // => true
+         */
+        function constant(value) {
+            return function() {
+                return value;
+            };
+        }
+
+        /**
+         * Produces a callback bound to an optional `thisArg`. If `func` is a property
+         * name the created callback will return the property value for a given element.
+         * If `func` is an object the created callback will return `true` for elements
+         * that contain the equivalent object properties, otherwise it will return `false`.
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @param {*} [func=identity] The value to convert to a callback.
+         * @param {*} [thisArg] The `this` binding of the created callback.
+         * @param {number} [argCount] The number of arguments the callback accepts.
+         * @returns {Function} Returns a callback function.
+         * @example
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 36 },
+         *   { 'name': 'fred',   'age': 40 }
+         * ];
+         *
+         * // wrap to create custom callback shorthands
+         * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) {
+     *   var match = /^(.+?)__([gl]t)(.+)$/.exec(callback);
+     *   return !match ? func(callback, thisArg) : function(object) {
+     *     return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3];
+     *   };
+     * });
+         *
+         * _.filter(characters, 'age__gt38');
+         * // => [{ 'name': 'fred', 'age': 40 }]
+         */
+        function createCallback(func, thisArg, argCount) {
+            var type = typeof func;
+            if (func == null || type == 'function') {
+                return baseCreateCallback(func, thisArg, argCount);
+            }
+            // handle "_.pluck" style callback shorthands
+            if (type != 'object') {
+                return property(func);
+            }
+            var props = keys(func),
+                    key = props[0],
+                    a = func[key];
+
+            // handle "_.where" style callback shorthands
+            if (props.length == 1 && a === a && !isObject(a)) {
+                // fast path the common case of providing an object with a single
+                // property containing a primitive value
+                return function(object) {
+                    var b = object[key];
+                    return a === b && (a !== 0 || (1 / a == 1 / b));
+                };
+            }
+            return function(object) {
+                var length = props.length,
+                        result = false;
+
+                while (length--) {
+                    if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) {
+                        break;
+                    }
+                }
+                return result;
+            };
+        }
+
+        /**
+         * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
+         * corresponding HTML entities.
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @param {string} string The string to escape.
+         * @returns {string} Returns the escaped string.
+         * @example
+         *
+         * _.escape('Fred, Wilma, & Pebbles');
+         * // => 'Fred, Wilma, &amp; Pebbles'
+         */
+        function escape(string) {
+            return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
+        }
+
+        /**
+         * This method returns the first argument provided to it.
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @param {*} value Any value.
+         * @returns {*} Returns `value`.
+         * @example
+         *
+         * var object = { 'name': 'fred' };
+         * _.identity(object) === object;
+         * // => true
+         */
+        function identity(value) {
+            return value;
+        }
+
+        /**
+         * Adds function properties of a source object to the destination object.
+         * If `object` is a function methods will be added to its prototype as well.
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @param {Function|Object} [object=lodash] object The destination object.
+         * @param {Object} source The object of functions to add.
+         * @param {Object} [options] The options object.
+         * @param {boolean} [options.chain=true] Specify whether the functions added are chainable.
+         * @example
+         *
+         * function capitalize(string) {
+     *   return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
+     * }
+         *
+         * _.mixin({ 'capitalize': capitalize });
+         * _.capitalize('fred');
+         * // => 'Fred'
+         *
+         * _('fred').capitalize().value();
+         * // => 'Fred'
+         *
+         * _.mixin({ 'capitalize': capitalize }, { 'chain': false });
+         * _('fred').capitalize();
+         * // => 'Fred'
+         */
+        function mixin(object, source, options) {
+            var chain = true,
+                    methodNames = source && functions(source);
+
+            if (!source || (!options && !methodNames.length)) {
+                if (options == null) {
+                    options = source;
+                }
+                ctor = lodashWrapper;
+                source = object;
+                object = lodash;
+                methodNames = functions(source);
+            }
+            if (options === false) {
+                chain = false;
+            } else if (isObject(options) && 'chain' in options) {
+                chain = options.chain;
+            }
+            var ctor = object,
+                    isFunc = isFunction(ctor);
+
+            forEach(methodNames, function(methodName) {
+                var func = object[methodName] = source[methodName];
+                if (isFunc) {
+                    ctor.prototype[methodName] = function() {
+                        var chainAll = this.__chain__,
+                                value = this.__wrapped__,
+                                args = [value];
+
+                        push.apply(args, arguments);
+                        var result = func.apply(object, args);
+                        if (chain || chainAll) {
+                            if (value === result && isObject(result)) {
+                                return this;
+                            }
+                            result = new ctor(result);
+                            result.__chain__ = chainAll;
+                        }
+                        return result;
+                    };
+                }
+            });
+        }
+
+        /**
+         * Reverts the '_' variable to its previous value and returns a reference to
+         * the `lodash` function.
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @returns {Function} Returns the `lodash` function.
+         * @example
+         *
+         * var lodash = _.noConflict();
+         */
+        function noConflict() {
+            context._ = oldDash;
+            return this;
+        }
+
+        /**
+         * A no-operation function.
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @example
+         *
+         * var object = { 'name': 'fred' };
+         * _.noop(object) === undefined;
+         * // => true
+         */
+        function noop() {
+            // no operation performed
+        }
+
+        /**
+         * Gets the number of milliseconds that have elapsed since the Unix epoch
+         * (1 January 1970 00:00:00 UTC).
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @example
+         *
+         * var stamp = _.now();
+         * _.defer(function() { console.log(_.now() - stamp); });
+         * // => logs the number of milliseconds it took for the deferred function to be called
+         */
+        var now = reNative.test(now = Date.now) && now || function() {
+            return new Date().getTime();
+        };
+
+        /**
+         * Converts the given value into an integer of the specified radix.
+         * If `radix` is `undefined` or `0` a `radix` of `10` is used unless the
+         * `value` is a hexadecimal, in which case a `radix` of `16` is used.
+         *
+         * Note: This method avoids differences in native ES3 and ES5 `parseInt`
+         * implementations. See http://es5.github.io/#E.
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @param {string} value The value to parse.
+         * @param {number} [radix] The radix used to interpret the value to parse.
+         * @returns {number} Returns the new integer value.
+         * @example
+         *
+         * _.parseInt('08');
+         * // => 8
+         */
+        var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) {
+            // Firefox < 21 and Opera < 15 follow the ES3 specified implementation of `parseInt`
+            return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0);
+        };
+
+        /**
+         * Creates a "_.pluck" style function, which returns the `key` value of a
+         * given object.
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @param {string} key The name of the property to retrieve.
+         * @returns {Function} Returns the new function.
+         * @example
+         *
+         * var characters = [
+         *   { 'name': 'fred',   'age': 40 },
+         *   { 'name': 'barney', 'age': 36 }
+         * ];
+         *
+         * var getName = _.property('name');
+         *
+         * _.map(characters, getName);
+         * // => ['barney', 'fred']
+         *
+         * _.sortBy(characters, getName);
+         * // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred',   'age': 40 }]
+         */
+        function property(key) {
+            return function(object) {
+                return object[key];
+            };
+        }
+
+        /**
+         * Produces a random number between `min` and `max` (inclusive). If only one
+         * argument is provided a number between `0` and the given number will be
+         * returned. If `floating` is truey or either `min` or `max` are floats a
+         * floating-point number will be returned instead of an integer.
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @param {number} [min=0] The minimum possible value.
+         * @param {number} [max=1] The maximum possible value.
+         * @param {boolean} [floating=false] Specify returning a floating-point number.
+         * @returns {number} Returns a random number.
+         * @example
+         *
+         * _.random(0, 5);
+         * // => an integer between 0 and 5
+         *
+         * _.random(5);
+         * // => also an integer between 0 and 5
+         *
+         * _.random(5, true);
+         * // => a floating-point number between 0 and 5
+         *
+         * _.random(1.2, 5.2);
+         * // => a floating-point number between 1.2 and 5.2
+         */
+        function random(min, max, floating) {
+            var noMin = min == null,
+                    noMax = max == null;
+
+            if (floating == null) {
+                if (typeof min == 'boolean' && noMax) {
+                    floating = min;
+                    min = 1;
+                }
+                else if (!noMax && typeof max == 'boolean') {
+                    floating = max;
+                    noMax = true;
+                }
+            }
+            if (noMin && noMax) {
+                max = 1;
+            }
+            min = +min || 0;
+            if (noMax) {
+                max = min;
+                min = 0;
+            } else {
+                max = +max || 0;
+            }
+            if (floating || min % 1 || max % 1) {
+                var rand = nativeRandom();
+                return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max);
+            }
+            return baseRandom(min, max);
+        }
+
+        /**
+         * Resolves the value of property `key` on `object`. If `key` is a function
+         * it will be invoked with the `this` binding of `object` and its result returned,
+         * else the property value is returned. If `object` is falsey then `undefined`
+         * is returned.
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @param {Object} object The object to inspect.
+         * @param {string} key The name of the property to resolve.
+         * @returns {*} Returns the resolved value.
+         * @example
+         *
+         * var object = {
+     *   'cheese': 'crumpets',
+     *   'stuff': function() {
+     *     return 'nonsense';
+     *   }
+     * };
+         *
+         * _.result(object, 'cheese');
+         * // => 'crumpets'
+         *
+         * _.result(object, 'stuff');
+         * // => 'nonsense'
+         */
+        function result(object, key) {
+            if (object) {
+                var value = object[key];
+                return isFunction(value) ? object[key]() : value;
+            }
+        }
+
+        /**
+         * A micro-templating method that handles arbitrary delimiters, preserves
+         * whitespace, and correctly escapes quotes within interpolated code.
+         *
+         * Note: In the development build, `_.template` utilizes sourceURLs for easier
+         * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+         *
+         * For more information on precompiling templates see:
+         * http://lodash.com/custom-builds
+         *
+         * For more information on Chrome extension sandboxes see:
+         * http://developer.chrome.com/stable/extensions/sandboxingEval.html
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @param {string} text The template text.
+         * @param {Object} data The data object used to populate the text.
+         * @param {Object} [options] The options object.
+         * @param {RegExp} [options.escape] The "escape" delimiter.
+         * @param {RegExp} [options.evaluate] The "evaluate" delimiter.
+         * @param {Object} [options.imports] An object to import into the template as local variables.
+         * @param {RegExp} [options.interpolate] The "interpolate" delimiter.
+         * @param {string} [sourceURL] The sourceURL of the template's compiled source.
+         * @param {string} [variable] The data object variable name.
+         * @returns {Function|string} Returns a compiled function when no `data` object
+         *  is given, else it returns the interpolated text.
+         * @example
+         *
+         * // using the "interpolate" delimiter to create a compiled template
+         * var compiled = _.template('hello <%= name %>');
+         * compiled({ 'name': 'fred' });
+         * // => 'hello fred'
+         *
+         * // using the "escape" delimiter to escape HTML in data property values
+         * _.template('<b><%- value %></b>', { 'value': '<script>' });
+         * // => '<b>&lt;script&gt;</b>'
+         *
+         * // using the "evaluate" delimiter to generate HTML
+         * var list = '<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>';
+         * _.template(list, { 'people': ['fred', 'barney'] });
+         * // => '<li>fred</li><li>barney</li>'
+         *
+         * // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
+         * _.template('hello ${ name }', { 'name': 'pebbles' });
+         * // => 'hello pebbles'
+         *
+         * // using the internal `print` function in "evaluate" delimiters
+         * _.template('<% print("hello " + name); %>!', { 'name': 'barney' });
+         * // => 'hello barney!'
+         *
+         * // using a custom template delimiters
+         * _.templateSettings = {
+     *   'interpolate': /{{([\s\S]+?)}}/g
+     * };
+         *
+         * _.template('hello {{ name }}!', { 'name': 'mustache' });
+         * // => 'hello mustache!'
+         *
+         * // using the `imports` option to import jQuery
+         * var list = '<% jq.each(people, function(name) { %><li><%- name %></li><% }); %>';
+         * _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
+         * // => '<li>fred</li><li>barney</li>'
+         *
+         * // using the `sourceURL` option to specify a custom sourceURL for the template
+         * var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
+         * compiled(data);
+         * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
+         *
+         * // using the `variable` option to ensure a with-statement isn't used in the compiled template
+         * var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' });
+         * compiled.source;
+         * // => function(data) {
+     *   var __t, __p = '', __e = _.escape;
+     *   __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!';
+     *   return __p;
+     * }
+         *
+         * // using the `source` property to inline compiled templates for meaningful
+         * // line numbers in error messages and a stack trace
+         * fs.writeFileSync(path.join(cwd, 'jst.js'), '\
+         *   var JST = {\
+     *     "main": ' + _.template(mainText).source + '\
+     *   };\
+         * ');
+         */
+        function template(text, data, options) {
+            // based on John Resig's `tmpl` implementation
+            // http://ejohn.org/blog/javascript-micro-templating/
+            // and Laura Doktorova's doT.js
+            // https://github.com/olado/doT
+            var settings = lodash.templateSettings;
+            text = String(text || '');
+
+            // avoid missing dependencies when `iteratorTemplate` is not defined
+            options = defaults({}, options, settings);
+
+            var imports = defaults({}, options.imports, settings.imports),
+                    importsKeys = keys(imports),
+                    importsValues = values(imports);
+
+            var isEvaluating,
+                    index = 0,
+                    interpolate = options.interpolate || reNoMatch,
+                    source = "__p += '";
+
+            // compile the regexp to match each delimiter
+            var reDelimiters = RegExp(
+                    (options.escape || reNoMatch).source + '|' +
+                            interpolate.source + '|' +
+                            (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
+                            (options.evaluate || reNoMatch).source + '|$'
+                    , 'g');
+
+            text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
+                interpolateValue || (interpolateValue = esTemplateValue);
+
+                // escape characters that cannot be included in string literals
+                source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);
+
+                // replace delimiters with snippets
+                if (escapeValue) {
+                    source += "' +\n__e(" + escapeValue + ") +\n'";
+                }
+                if (evaluateValue) {
+                    isEvaluating = true;
+                    source += "';\n" + evaluateValue + ";\n__p += '";
+                }
+                if (interpolateValue) {
+                    source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
+                }
+                index = offset + match.length;
+
+                // the JS engine embedded in Adobe products requires returning the `match`
+                // string in order to produce the correct `offset` value
+                return match;
+            });
+
+            source += "';\n";
+
+            // if `variable` is not specified, wrap a with-statement around the generated
+            // code to add the data object to the top of the scope chain
+            var variable = options.variable,
+                    hasVariable = variable;
+
+            if (!hasVariable) {
+                variable = 'obj';
+                source = 'with (' + variable + ') {\n' + source + '\n}\n';
+            }
+            // cleanup code by stripping empty strings
+            source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
+                    .replace(reEmptyStringMiddle, '$1')
+                    .replace(reEmptyStringTrailing, '$1;');
+
+            // frame code as the function body
+            source = 'function(' + variable + ') {\n' +
+                    (hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') +
+                    "var __t, __p = '', __e = _.escape" +
+                    (isEvaluating
+                            ? ', __j = Array.prototype.join;\n' +
+                            "function print() { __p += __j.call(arguments, '') }\n"
+                            : ';\n'
+                            ) +
+                    source +
+                    'return __p\n}';
+
+            // Use a sourceURL for easier debugging.
+            // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+            var sourceURL = '\n/*\n//# sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']') + '\n*/';
+
+            try {
+                var result = Function(importsKeys, 'return ' + source + sourceURL).apply(undefined, importsValues);
+            } catch(e) {
+                e.source = source;
+                throw e;
+            }
+            if (data) {
+                return result(data);
+            }
+            // provide the compiled function's source by its `toString` method, in
+            // supported environments, or the `source` property as a convenience for
+            // inlining compiled templates during the build process
+            result.source = source;
+            return result;
+        }
+
+        /**
+         * Executes the callback `n` times, returning an array of the results
+         * of each callback execution. The callback is bound to `thisArg` and invoked
+         * with one argument; (index).
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @param {number} n The number of times to execute the callback.
+         * @param {Function} callback The function called per iteration.
+         * @param {*} [thisArg] The `this` binding of `callback`.
+         * @returns {Array} Returns an array of the results of each `callback` execution.
+         * @example
+         *
+         * var diceRolls = _.times(3, _.partial(_.random, 1, 6));
+         * // => [3, 6, 4]
+         *
+         * _.times(3, function(n) { mage.castSpell(n); });
+         * // => calls `mage.castSpell(n)` three times, passing `n` of `0`, `1`, and `2` respectively
+         *
+         * _.times(3, function(n) { this.cast(n); }, mage);
+         * // => also calls `mage.castSpell(n)` three times
+         */
+        function times(n, callback, thisArg) {
+            n = (n = +n) > -1 ? n : 0;
+            var index = -1,
+                    result = Array(n);
+
+            callback = baseCreateCallback(callback, thisArg, 1);
+            while (++index < n) {
+                result[index] = callback(index);
+            }
+            return result;
+        }
+
+        /**
+         * The inverse of `_.escape` this method converts the HTML entities
+         * `&amp;`, `&lt;`, `&gt;`, `&quot;`, and `&#39;` in `string` to their
+         * corresponding characters.
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @param {string} string The string to unescape.
+         * @returns {string} Returns the unescaped string.
+         * @example
+         *
+         * _.unescape('Fred, Barney &amp; Pebbles');
+         * // => 'Fred, Barney & Pebbles'
+         */
+        function unescape(string) {
+            return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar);
+        }
+
+        /**
+         * Generates a unique ID. If `prefix` is provided the ID will be appended to it.
+         *
+         * @static
+         * @memberOf _
+         * @category Utilities
+         * @param {string} [prefix] The value to prefix the ID with.
+         * @returns {string} Returns the unique ID.
+         * @example
+         *
+         * _.uniqueId('contact_');
+         * // => 'contact_104'
+         *
+         * _.uniqueId();
+         * // => '105'
+         */
+        function uniqueId(prefix) {
+            var id = ++idCounter;
+            return String(prefix == null ? '' : prefix) + id;
+        }
+
+        /*--------------------------------------------------------------------------*/
+
+        /**
+         * Creates a `lodash` object that wraps the given value with explicit
+         * method chaining enabled.
+         *
+         * @static
+         * @memberOf _
+         * @category Chaining
+         * @param {*} value The value to wrap.
+         * @returns {Object} Returns the wrapper object.
+         * @example
+         *
+         * var characters = [
+         *   { 'name': 'barney',  'age': 36 },
+         *   { 'name': 'fred',    'age': 40 },
+         *   { 'name': 'pebbles', 'age': 1 }
+         * ];
+         *
+         * var youngest = _.chain(characters)
+         *     .sortBy('age')
+         *     .map(function(chr) { return chr.name + ' is ' + chr.age; })
+         *     .first()
+         *     .value();
+         * // => 'pebbles is 1'
+         */
+        function chain(value) {
+            value = new lodashWrapper(value);
+            value.__chain__ = true;
+            return value;
+        }
+
+        /**
+         * Invokes `interceptor` with the `value` as the first argument and then
+         * returns `value`. The purpose of this method is to "tap into" a method
+         * chain in order to perform operations on intermediate results within
+         * the chain.
+         *
+         * @static
+         * @memberOf _
+         * @category Chaining
+         * @param {*} value The value to provide to `interceptor`.
+         * @param {Function} interceptor The function to invoke.
+         * @returns {*} Returns `value`.
+         * @example
+         *
+         * _([1, 2, 3, 4])
+         *  .tap(function(array) { array.pop(); })
+         *  .reverse()
+         *  .value();
+         * // => [3, 2, 1]
+         */
+        function tap(value, interceptor) {
+            interceptor(value);
+            return value;
+        }
+
+        /**
+         * Enables explicit method chaining on the wrapper object.
+         *
+         * @name chain
+         * @memberOf _
+         * @category Chaining
+         * @returns {*} Returns the wrapper object.
+         * @example
+         *
+         * var characters = [
+         *   { 'name': 'barney', 'age': 36 },
+         *   { 'name': 'fred',   'age': 40 }
+         * ];
+         *
+         * // without explicit chaining
+         * _(characters).first();
+         * // => { 'name': 'barney', 'age': 36 }
+         *
+         * // with explicit chaining
+         * _(characters).chain()
+         *   .first()
+         *   .pick('age')
+         *   .value();
+         * // => { 'age': 36 }
+         */
+        function wrapperChain() {
+            this.__chain__ = true;
+            return this;
+        }
+
+        /**
+         * Produces the `toString` result of the wrapped value.
+         *
+         * @name toString
+         * @memberOf _
+         * @category Chaining
+         * @returns {string} Returns the string result.
+         * @example
+         *
+         * _([1, 2, 3]).toString();
+         * // => '1,2,3'
+         */
+        function wrapperToString() {
+            return String(this.__wrapped__);
+        }
+
+        /**
+         * Extracts the wrapped value.
+         *
+         * @name valueOf
+         * @memberOf _
+         * @alias value
+         * @category Chaining
+         * @returns {*} Returns the wrapped value.
+         * @example
+         *
+         * _([1, 2, 3]).valueOf();
+         * // => [1, 2, 3]
+         */
+        function wrapperValueOf() {
+            return this.__wrapped__;
+        }
+
+        /*--------------------------------------------------------------------------*/
+
+        // add functions that return wrapped values when chaining
+        lodash.after = after;
+        lodash.assign = assign;
+        lodash.at = at;
+        lodash.bind = bind;
+        lodash.bindAll = bindAll;
+        lodash.bindKey = bindKey;
+        lodash.chain = chain;
+        lodash.compact = compact;
+        lodash.compose = compose;
+        lodash.constant = constant;
+        lodash.countBy = countBy;
+        lodash.create = create;
+        lodash.createCallback = createCallback;
+        lodash.curry = curry;
+        lodash.debounce = debounce;
+        lodash.defaults = defaults;
+        lodash.defer = defer;
+        lodash.delay = delay;
+        lodash.difference = difference;
+        lodash.filter = filter;
+        lodash.flatten = flatten;
+        lodash.forEach = forEach;
+        lodash.forEachRight = forEachRight;
+        lodash.forIn = forIn;
+        lodash.forInRight = forInRight;
+        lodash.forOwn = forOwn;
+        lodash.forOwnRight = forOwnRight;
+        lodash.functions = functions;
+        lodash.groupBy = groupBy;
+        lodash.indexBy = indexBy;
+        lodash.initial = initial;
+        lodash.intersection = intersection;
+        lodash.invert = invert;
+        lodash.invoke = invoke;
+        lodash.keys = keys;
+        lodash.map = map;
+        lodash.mapValues = mapValues;
+        lodash.max = max;
+        lodash.memoize = memoize;
+        lodash.merge = merge;
+        lodash.min = min;
+        lodash.omit = omit;
+        lodash.once = once;
+        lodash.pairs = pairs;
+        lodash.partial = partial;
+        lodash.partialRight = partialRight;
+        lodash.pick = pick;
+        lodash.pluck = pluck;
+        lodash.property = property;
+        lodash.pull = pull;
+        lodash.range = range;
+        lodash.reject = reject;
+        lodash.remove = remove;
+        lodash.rest = rest;
+        lodash.shuffle = shuffle;
+        lodash.sortBy = sortBy;
+        lodash.tap = tap;
+        lodash.throttle = throttle;
+        lodash.times = times;
+        lodash.toArray = toArray;
+        lodash.transform = transform;
+        lodash.union = union;
+        lodash.uniq = uniq;
+        lodash.values = values;
+        lodash.where = where;
+        lodash.without = without;
+        lodash.wrap = wrap;
+        lodash.xor = xor;
+        lodash.zip = zip;
+        lodash.zipObject = zipObject;
+
+        // add aliases
+        lodash.collect = map;
+        lodash.drop = rest;
+        lodash.each = forEach;
+        lodash.eachRight = forEachRight;
+        lodash.extend = assign;
+        lodash.methods = functions;
+        lodash.object = zipObject;
+        lodash.select = filter;
+        lodash.tail = rest;
+        lodash.unique = uniq;
+        lodash.unzip = zip;
+
+        // add functions to `lodash.prototype`
+        mixin(lodash);
+
+        /*--------------------------------------------------------------------------*/
+
+        // add functions that return unwrapped values when chaining
+        lodash.clone = clone;
+        lodash.cloneDeep = cloneDeep;
+        lodash.contains = contains;
+        lodash.escape = escape;
+        lodash.every = every;
+        lodash.find = find;
+        lodash.findIndex = findIndex;
+        lodash.findKey = findKey;
+        lodash.findLast = findLast;
+        lodash.findLastIndex = findLastIndex;
+        lodash.findLastKey = findLastKey;
+        lodash.has = has;
+        lodash.identity = identity;
+        lodash.indexOf = indexOf;
+        lodash.isArguments = isArguments;
+        lodash.isArray = isArray;
+        lodash.isBoolean = isBoolean;
+        lodash.isDate = isDate;
+        lodash.isElement = isElement;
+        lodash.isEmpty = isEmpty;
+        lodash.isEqual = isEqual;
+        lodash.isFinite = isFinite;
+        lodash.isFunction = isFunction;
+        lodash.isNaN = isNaN;
+        lodash.isNull = isNull;
+        lodash.isNumber = isNumber;
+        lodash.isObject = isObject;
+        lodash.isPlainObject = isPlainObject;
+        lodash.isRegExp = isRegExp;
+        lodash.isString = isString;
+        lodash.isUndefined = isUndefined;
+        lodash.lastIndexOf = lastIndexOf;
+        lodash.mixin = mixin;
+        lodash.noConflict = noConflict;
+        lodash.noop = noop;
+        lodash.now = now;
+        lodash.parseInt = parseInt;
+        lodash.random = random;
+        lodash.reduce = reduce;
+        lodash.reduceRight = reduceRight;
+        lodash.result = result;
+        lodash.runInContext = runInContext;
+        lodash.size = size;
+        lodash.some = some;
+        lodash.sortedIndex = sortedIndex;
+        lodash.template = template;
+        lodash.unescape = unescape;
+        lodash.uniqueId = uniqueId;
+
+        // add aliases
+        lodash.all = every;
+        lodash.any = some;
+        lodash.detect = find;
+        lodash.findWhere = find;
+        lodash.foldl = reduce;
+        lodash.foldr = reduceRight;
+        lodash.include = contains;
+        lodash.inject = reduce;
+
+        mixin(function() {
+            var source = {}
+            forOwn(lodash, function(func, methodName) {
+                if (!lodash.prototype[methodName]) {
+                    source[methodName] = func;
+                }
+            });
+            return source;
+        }(), false);
+
+        /*--------------------------------------------------------------------------*/
+
+        // add functions capable of returning wrapped and unwrapped values when chaining
+        lodash.first = first;
+        lodash.last = last;
+        lodash.sample = sample;
+
+        // add aliases
+        lodash.take = first;
+        lodash.head = first;
+
+        forOwn(lodash, function(func, methodName) {
+            var callbackable = methodName !== 'sample';
+            if (!lodash.prototype[methodName]) {
+                lodash.prototype[methodName]= function(n, guard) {
+                    var chainAll = this.__chain__,
+                            result = func(this.__wrapped__, n, guard);
+
+                    return !chainAll && (n == null || (guard && !(callbackable && typeof n == 'function')))
+                            ? result
+                            : new lodashWrapper(result, chainAll);
+                };
+            }
+        });
+
+        /*--------------------------------------------------------------------------*/
+
+        /**
+         * The semantic version number.
+         *
+         * @static
+         * @memberOf _
+         * @type string
+         */
+        lodash.VERSION = '2.4.0';
+
+        // add "Chaining" functions to the wrapper
+        lodash.prototype.chain = wrapperChain;
+        lodash.prototype.toString = wrapperToString;
+        lodash.prototype.value = wrapperValueOf;
+        lodash.prototype.valueOf = wrapperValueOf;
+
+        // add `Array` functions that return unwrapped values
+        forEach(['join', 'pop', 'shift'], function(methodName) {
+            var func = arrayRef[methodName];
+            lodash.prototype[methodName] = function() {
+                var chainAll = this.__chain__,
+                        result = func.apply(this.__wrapped__, arguments);
+
+                return chainAll
+                        ? new lodashWrapper(result, chainAll)
+                        : result;
+            };
+        });
+
+        // add `Array` functions that return the existing wrapped value
+        forEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
+            var func = arrayRef[methodName];
+            lodash.prototype[methodName] = function() {
+                func.apply(this.__wrapped__, arguments);
+                return this;
+            };
+        });
+
+        // add `Array` functions that return new wrapped values
+        forEach(['concat', 'slice', 'splice'], function(methodName) {
+            var func = arrayRef[methodName];
+            lodash.prototype[methodName] = function() {
+                return new lodashWrapper(func.apply(this.__wrapped__, arguments), this.__chain__);
+            };
+        });
+
+        return lodash;
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    // expose Lo-Dash
+    var _ = runInContext();
+
+    // some AMD build optimizers like r.js check for condition patterns like the following:
+    if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+        // Expose Lo-Dash to the global object even when an AMD loader is present in
+        // case Lo-Dash is loaded with a RequireJS shim config.
+        // See http://requirejs.org/docs/api.html#config-shim
+        root._ = _;
+
+        // define as an anonymous module so, through path mapping, it can be
+        // referenced as the "underscore" module
+        define(function() {
+            return _;
+        });
+    }
+    // check for `exports` after `define` in case a build optimizer adds an `exports` object
+    else if (freeExports && freeModule) {
+        // in Node.js or RingoJS
+        if (moduleExports) {
+            (freeModule.exports = _)._ = _;
+        }
+        // in Narwhal or Rhino -require
+        else {
+            freeExports._ = _;
+        }
+    }
+    else {
+        // in a browser or Rhino
+        root._ = _;
+    }
+}.call(this));
\ No newline at end of file
diff --git a/lib/underscore-1.5.1.js b/lib/underscore-1.5.1.js
deleted file mode 100755
index 7d4ee27c7..000000000
--- a/lib/underscore-1.5.1.js
+++ /dev/null
@@ -1,1246 +0,0 @@
-//     Underscore.js 1.5.1
-//     http://underscorejs.org
-//     (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
-//     Underscore may be freely distributed under the MIT license.
-
-(function() {
-
-  // Baseline setup
-  // --------------
-
-  // Establish the root object, `window` in the browser, or `global` on the server.
-  var root = this;
-
-  // Save the previous value of the `_` variable.
-  var previousUnderscore = root._;
-
-  // Establish the object that gets returned to break out of a loop iteration.
-  var breaker = {};
-
-  // Save bytes in the minified (but not gzipped) version:
-  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
-
-  // Create quick reference variables for speed access to core prototypes.
-  var
-    push             = ArrayProto.push,
-    slice            = ArrayProto.slice,
-    concat           = ArrayProto.concat,
-    toString         = ObjProto.toString,
-    hasOwnProperty   = ObjProto.hasOwnProperty;
-
-  // All **ECMAScript 5** native function implementations that we hope to use
-  // are declared here.
-  var
-    nativeForEach      = ArrayProto.forEach,
-    nativeMap          = ArrayProto.map,
-    nativeReduce       = ArrayProto.reduce,
-    nativeReduceRight  = ArrayProto.reduceRight,
-    nativeFilter       = ArrayProto.filter,
-    nativeEvery        = ArrayProto.every,
-    nativeSome         = ArrayProto.some,
-    nativeIndexOf      = ArrayProto.indexOf,
-    nativeLastIndexOf  = ArrayProto.lastIndexOf,
-    nativeIsArray      = Array.isArray,
-    nativeKeys         = Object.keys,
-    nativeBind         = FuncProto.bind;
-
-  // Create a safe reference to the Underscore object for use below.
-  var _ = function(obj) {
-    if (obj instanceof _) return obj;
-    if (!(this instanceof _)) return new _(obj);
-    this._wrapped = obj;
-  };
-
-  // Export the Underscore object for **Node.js**, with
-  // backwards-compatibility for the old `require()` API. If we're in
-  // the browser, add `_` as a global object via a string identifier,
-  // for Closure Compiler "advanced" mode.
-  if (typeof exports !== 'undefined') {
-    if (typeof module !== 'undefined' && module.exports) {
-      exports = module.exports = _;
-    }
-    exports._ = _;
-  } else {
-    root._ = _;
-  }
-
-  // Current version.
-  _.VERSION = '1.5.1';
-
-  // Collection Functions
-  // --------------------
-
-  // The cornerstone, an `each` implementation, aka `forEach`.
-  // Handles objects with the built-in `forEach`, arrays, and raw objects.
-  // Delegates to **ECMAScript 5**'s native `forEach` if available.
-  var each = _.each = _.forEach = function(obj, iterator, context) {
-    if (obj == null) return;
-    if (nativeForEach && obj.forEach === nativeForEach) {
-      obj.forEach(iterator, context);
-    } else if (obj.length === +obj.length) {
-      for (var i = 0, l = obj.length; i < l; i++) {
-        if (iterator.call(context, obj[i], i, obj) === breaker) return;
-      }
-    } else {
-      for (var key in obj) {
-        if (_.has(obj, key)) {
-          if (iterator.call(context, obj[key], key, obj) === breaker) return;
-        }
-      }
-    }
-  };
-
-  // Return the results of applying the iterator to each element.
-  // Delegates to **ECMAScript 5**'s native `map` if available.
-  _.map = _.collect = function(obj, iterator, context) {
-    var results = [];
-    if (obj == null) return results;
-    if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
-    each(obj, function(value, index, list) {
-      results.push(iterator.call(context, value, index, list));
-    });
-    return results;
-  };
-
-  var reduceError = 'Reduce of empty array with no initial value';
-
-  // **Reduce** builds up a single result from a list of values, aka `inject`,
-  // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
-  _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
-    var initial = arguments.length > 2;
-    if (obj == null) obj = [];
-    if (nativeReduce && obj.reduce === nativeReduce) {
-      if (context) iterator = _.bind(iterator, context);
-      return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
-    }
-    each(obj, function(value, index, list) {
-      if (!initial) {
-        memo = value;
-        initial = true;
-      } else {
-        memo = iterator.call(context, memo, value, index, list);
-      }
-    });
-    if (!initial) throw new TypeError(reduceError);
-    return memo;
-  };
-
-  // The right-associative version of reduce, also known as `foldr`.
-  // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
-  _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
-    var initial = arguments.length > 2;
-    if (obj == null) obj = [];
-    if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
-      if (context) iterator = _.bind(iterator, context);
-      return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
-    }
-    var length = obj.length;
-    if (length !== +length) {
-      var keys = _.keys(obj);
-      length = keys.length;
-    }
-    each(obj, function(value, index, list) {
-      index = keys ? keys[--length] : --length;
-      if (!initial) {
-        memo = obj[index];
-        initial = true;
-      } else {
-        memo = iterator.call(context, memo, obj[index], index, list);
-      }
-    });
-    if (!initial) throw new TypeError(reduceError);
-    return memo;
-  };
-
-  // Return the first value which passes a truth test. Aliased as `detect`.
-  _.find = _.detect = function(obj, iterator, context) {
-    var result;
-    any(obj, function(value, index, list) {
-      if (iterator.call(context, value, index, list)) {
-        result = value;
-        return true;
-      }
-    });
-    return result;
-  };
-
-  // Return all the elements that pass a truth test.
-  // Delegates to **ECMAScript 5**'s native `filter` if available.
-  // Aliased as `select`.
-  _.filter = _.select = function(obj, iterator, context) {
-    var results = [];
-    if (obj == null) return results;
-    if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
-    each(obj, function(value, index, list) {
-      if (iterator.call(context, value, index, list)) results.push(value);
-    });
-    return results;
-  };
-
-  // Return all the elements for which a truth test fails.
-  _.reject = function(obj, iterator, context) {
-    return _.filter(obj, function(value, index, list) {
-      return !iterator.call(context, value, index, list);
-    }, context);
-  };
-
-  // Determine whether all of the elements match a truth test.
-  // Delegates to **ECMAScript 5**'s native `every` if available.
-  // Aliased as `all`.
-  _.every = _.all = function(obj, iterator, context) {
-    iterator || (iterator = _.identity);
-    var result = true;
-    if (obj == null) return result;
-    if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
-    each(obj, function(value, index, list) {
-      if (!(result = result && iterator.call(context, value, index, list))) return breaker;
-    });
-    return !!result;
-  };
-
-  // Determine if at least one element in the object matches a truth test.
-  // Delegates to **ECMAScript 5**'s native `some` if available.
-  // Aliased as `any`.
-  var any = _.some = _.any = function(obj, iterator, context) {
-    iterator || (iterator = _.identity);
-    var result = false;
-    if (obj == null) return result;
-    if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
-    each(obj, function(value, index, list) {
-      if (result || (result = iterator.call(context, value, index, list))) return breaker;
-    });
-    return !!result;
-  };
-
-  // Determine if the array or object contains a given value (using `===`).
-  // Aliased as `include`.
-  _.contains = _.include = function(obj, target) {
-    if (obj == null) return false;
-    if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
-    return any(obj, function(value) {
-      return value === target;
-    });
-  };
-
-  // Invoke a method (with arguments) on every item in a collection.
-  _.invoke = function(obj, method) {
-    var args = slice.call(arguments, 2);
-    var isFunc = _.isFunction(method);
-    return _.map(obj, function(value) {
-      return (isFunc ? method : value[method]).apply(value, args);
-    });
-  };
-
-  // Convenience version of a common use case of `map`: fetching a property.
-  _.pluck = function(obj, key) {
-    return _.map(obj, function(value){ return value[key]; });
-  };
-
-  // Convenience version of a common use case of `filter`: selecting only objects
-  // containing specific `key:value` pairs.
-  _.where = function(obj, attrs, first) {
-    if (_.isEmpty(attrs)) return first ? void 0 : [];
-    return _[first ? 'find' : 'filter'](obj, function(value) {
-      for (var key in attrs) {
-        if (attrs[key] !== value[key]) return false;
-      }
-      return true;
-    });
-  };
-
-  // Convenience version of a common use case of `find`: getting the first object
-  // containing specific `key:value` pairs.
-  _.findWhere = function(obj, attrs) {
-    return _.where(obj, attrs, true);
-  };
-
-  // Return the maximum element or (element-based computation).
-  // Can't optimize arrays of integers longer than 65,535 elements.
-  // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
-  _.max = function(obj, iterator, context) {
-    if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
-      return Math.max.apply(Math, obj);
-    }
-    if (!iterator && _.isEmpty(obj)) return -Infinity;
-    var result = {computed : -Infinity, value: -Infinity};
-    each(obj, function(value, index, list) {
-      var computed = iterator ? iterator.call(context, value, index, list) : value;
-      computed > result.computed && (result = {value : value, computed : computed});
-    });
-    return result.value;
-  };
-
-  // Return the minimum element (or element-based computation).
-  _.min = function(obj, iterator, context) {
-    if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
-      return Math.min.apply(Math, obj);
-    }
-    if (!iterator && _.isEmpty(obj)) return Infinity;
-    var result = {computed : Infinity, value: Infinity};
-    each(obj, function(value, index, list) {
-      var computed = iterator ? iterator.call(context, value, index, list) : value;
-      computed < result.computed && (result = {value : value, computed : computed});
-    });
-    return result.value;
-  };
-
-  // Shuffle an array.
-  _.shuffle = function(obj) {
-    var rand;
-    var index = 0;
-    var shuffled = [];
-    each(obj, function(value) {
-      rand = _.random(index++);
-      shuffled[index - 1] = shuffled[rand];
-      shuffled[rand] = value;
-    });
-    return shuffled;
-  };
-
-  // An internal function to generate lookup iterators.
-  var lookupIterator = function(value) {
-    return _.isFunction(value) ? value : function(obj){ return obj[value]; };
-  };
-
-  // Sort the object's values by a criterion produced by an iterator.
-  _.sortBy = function(obj, value, context) {
-    var iterator = lookupIterator(value);
-    return _.pluck(_.map(obj, function(value, index, list) {
-      return {
-        value : value,
-        index : index,
-        criteria : iterator.call(context, value, index, list)
-      };
-    }).sort(function(left, right) {
-      var a = left.criteria;
-      var b = right.criteria;
-      if (a !== b) {
-        if (a > b || a === void 0) return 1;
-        if (a < b || b === void 0) return -1;
-      }
-      return left.index < right.index ? -1 : 1;
-    }), 'value');
-  };
-
-  // An internal function used for aggregate "group by" operations.
-  var group = function(obj, value, context, behavior) {
-    var result = {};
-    var iterator = lookupIterator(value == null ? _.identity : value);
-    each(obj, function(value, index) {
-      var key = iterator.call(context, value, index, obj);
-      behavior(result, key, value);
-    });
-    return result;
-  };
-
-  // Groups the object's values by a criterion. Pass either a string attribute
-  // to group by, or a function that returns the criterion.
-  _.groupBy = function(obj, value, context) {
-    return group(obj, value, context, function(result, key, value) {
-      (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
-    });
-  };
-
-  // Counts instances of an object that group by a certain criterion. Pass
-  // either a string attribute to count by, or a function that returns the
-  // criterion.
-  _.countBy = function(obj, value, context) {
-    return group(obj, value, context, function(result, key) {
-      if (!_.has(result, key)) result[key] = 0;
-      result[key]++;
-    });
-  };
-
-  // Use a comparator function to figure out the smallest index at which
-  // an object should be inserted so as to maintain order. Uses binary search.
-  _.sortedIndex = function(array, obj, iterator, context) {
-    iterator = iterator == null ? _.identity : lookupIterator(iterator);
-    var value = iterator.call(context, obj);
-    var low = 0, high = array.length;
-    while (low < high) {
-      var mid = (low + high) >>> 1;
-      iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
-    }
-    return low;
-  };
-
-  // Safely create a real, live array from anything iterable.
-  _.toArray = function(obj) {
-    if (!obj) return [];
-    if (_.isArray(obj)) return slice.call(obj);
-    if (obj.length === +obj.length) return _.map(obj, _.identity);
-    return _.values(obj);
-  };
-
-  // Return the number of elements in an object.
-  _.size = function(obj) {
-    if (obj == null) return 0;
-    return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
-  };
-
-  // Array Functions
-  // ---------------
-
-  // Get the first element of an array. Passing **n** will return the first N
-  // values in the array. Aliased as `head` and `take`. The **guard** check
-  // allows it to work with `_.map`.
-  _.first = _.head = _.take = function(array, n, guard) {
-    if (array == null) return void 0;
-    return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
-  };
-
-  // Returns everything but the last entry of the array. Especially useful on
-  // the arguments object. Passing **n** will return all the values in
-  // the array, excluding the last N. The **guard** check allows it to work with
-  // `_.map`.
-  _.initial = function(array, n, guard) {
-    return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
-  };
-
-  // Get the last element of an array. Passing **n** will return the last N
-  // values in the array. The **guard** check allows it to work with `_.map`.
-  _.last = function(array, n, guard) {
-    if (array == null) return void 0;
-    if ((n != null) && !guard) {
-      return slice.call(array, Math.max(array.length - n, 0));
-    } else {
-      return array[array.length - 1];
-    }
-  };
-
-  // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
-  // Especially useful on the arguments object. Passing an **n** will return
-  // the rest N values in the array. The **guard**
-  // check allows it to work with `_.map`.
-  _.rest = _.tail = _.drop = function(array, n, guard) {
-    return slice.call(array, (n == null) || guard ? 1 : n);
-  };
-
-  // Trim out all falsy values from an array.
-  _.compact = function(array) {
-    return _.filter(array, _.identity);
-  };
-
-  // Internal implementation of a recursive `flatten` function.
-  var flatten = function(input, shallow, output) {
-    if (shallow && _.every(input, _.isArray)) {
-      return concat.apply(output, input);
-    }
-    each(input, function(value) {
-      if (_.isArray(value) || _.isArguments(value)) {
-        shallow ? push.apply(output, value) : flatten(value, shallow, output);
-      } else {
-        output.push(value);
-      }
-    });
-    return output;
-  };
-
-  // Return a completely flattened version of an array.
-  _.flatten = function(array, shallow) {
-    return flatten(array, shallow, []);
-  };
-
-  // Return a version of the array that does not contain the specified value(s).
-  _.without = function(array) {
-    return _.difference(array, slice.call(arguments, 1));
-  };
-
-  // Produce a duplicate-free version of the array. If the array has already
-  // been sorted, you have the option of using a faster algorithm.
-  // Aliased as `unique`.
-  _.uniq = _.unique = function(array, isSorted, iterator, context) {
-    if (_.isFunction(isSorted)) {
-      context = iterator;
-      iterator = isSorted;
-      isSorted = false;
-    }
-    var initial = iterator ? _.map(array, iterator, context) : array;
-    var results = [];
-    var seen = [];
-    each(initial, function(value, index) {
-      if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
-        seen.push(value);
-        results.push(array[index]);
-      }
-    });
-    return results;
-  };
-
-  // Produce an array that contains the union: each distinct element from all of
-  // the passed-in arrays.
-  _.union = function() {
-    return _.uniq(_.flatten(arguments, true));
-  };
-
-  // Produce an array that contains every item shared between all the
-  // passed-in arrays.
-  _.intersection = function(array) {
-    var rest = slice.call(arguments, 1);
-    return _.filter(_.uniq(array), function(item) {
-      return _.every(rest, function(other) {
-        return _.indexOf(other, item) >= 0;
-      });
-    });
-  };
-
-  // Take the difference between one array and a number of other arrays.
-  // Only the elements present in just the first array will remain.
-  _.difference = function(array) {
-    var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
-    return _.filter(array, function(value){ return !_.contains(rest, value); });
-  };
-
-  // Zip together multiple lists into a single array -- elements that share
-  // an index go together.
-  _.zip = function() {
-    var length = _.max(_.pluck(arguments, "length").concat(0));
-    var results = new Array(length);
-    for (var i = 0; i < length; i++) {
-      results[i] = _.pluck(arguments, '' + i);
-    }
-    return results;
-  };
-
-  // Converts lists into objects. Pass either a single array of `[key, value]`
-  // pairs, or two parallel arrays of the same length -- one of keys, and one of
-  // the corresponding values.
-  _.object = function(list, values) {
-    if (list == null) return {};
-    var result = {};
-    for (var i = 0, l = list.length; i < l; i++) {
-      if (values) {
-        result[list[i]] = values[i];
-      } else {
-        result[list[i][0]] = list[i][1];
-      }
-    }
-    return result;
-  };
-
-  // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
-  // we need this function. Return the position of the first occurrence of an
-  // item in an array, or -1 if the item is not included in the array.
-  // Delegates to **ECMAScript 5**'s native `indexOf` if available.
-  // If the array is large and already in sort order, pass `true`
-  // for **isSorted** to use binary search.
-  _.indexOf = function(array, item, isSorted) {
-    if (array == null) return -1;
-    var i = 0, l = array.length;
-    if (isSorted) {
-      if (typeof isSorted == 'number') {
-        i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
-      } else {
-        i = _.sortedIndex(array, item);
-        return array[i] === item ? i : -1;
-      }
-    }
-    if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
-    for (; i < l; i++) if (array[i] === item) return i;
-    return -1;
-  };
-
-  // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
-  _.lastIndexOf = function(array, item, from) {
-    if (array == null) return -1;
-    var hasIndex = from != null;
-    if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
-      return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
-    }
-    var i = (hasIndex ? from : array.length);
-    while (i--) if (array[i] === item) return i;
-    return -1;
-  };
-
-  // Generate an integer Array containing an arithmetic progression. A port of
-  // the native Python `range()` function. See
-  // [the Python documentation](http://docs.python.org/library/functions.html#range).
-  _.range = function(start, stop, step) {
-    if (arguments.length <= 1) {
-      stop = start || 0;
-      start = 0;
-    }
-    step = arguments[2] || 1;
-
-    var len = Math.max(Math.ceil((stop - start) / step), 0);
-    var idx = 0;
-    var range = new Array(len);
-
-    while(idx < len) {
-      range[idx++] = start;
-      start += step;
-    }
-
-    return range;
-  };
-
-  // Function (ahem) Functions
-  // ------------------
-
-  // Reusable constructor function for prototype setting.
-  var ctor = function(){};
-
-  // Create a function bound to a given object (assigning `this`, and arguments,
-  // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
-  // available.
-  _.bind = function(func, context) {
-    var args, bound;
-    if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
-    if (!_.isFunction(func)) throw new TypeError;
-    args = slice.call(arguments, 2);
-    return bound = function() {
-      if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
-      ctor.prototype = func.prototype;
-      var self = new ctor;
-      ctor.prototype = null;
-      var result = func.apply(self, args.concat(slice.call(arguments)));
-      if (Object(result) === result) return result;
-      return self;
-    };
-  };
-
-  // Partially apply a function by creating a version that has had some of its
-  // arguments pre-filled, without changing its dynamic `this` context.
-  _.partial = function(func) {
-    var args = slice.call(arguments, 1);
-    return function() {
-      return func.apply(this, args.concat(slice.call(arguments)));
-    };
-  };
-
-  // Bind all of an object's methods to that object. Useful for ensuring that
-  // all callbacks defined on an object belong to it.
-  _.bindAll = function(obj) {
-    var funcs = slice.call(arguments, 1);
-    if (funcs.length === 0) throw new Error("bindAll must be passed function names");
-    each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
-    return obj;
-  };
-
-  // Memoize an expensive function by storing its results.
-  _.memoize = function(func, hasher) {
-    var memo = {};
-    hasher || (hasher = _.identity);
-    return function() {
-      var key = hasher.apply(this, arguments);
-      return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
-    };
-  };
-
-  // Delays a function for the given number of milliseconds, and then calls
-  // it with the arguments supplied.
-  _.delay = function(func, wait) {
-    var args = slice.call(arguments, 2);
-    return setTimeout(function(){ return func.apply(null, args); }, wait);
-  };
-
-  // Defers a function, scheduling it to run after the current call stack has
-  // cleared.
-  _.defer = function(func) {
-    return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
-  };
-
-  // Returns a function, that, when invoked, will only be triggered at most once
-  // during a given window of time. Normally, the throttled function will run
-  // as much as it can, without ever going more than once per `wait` duration;
-  // but if you'd like to disable the execution on the leading edge, pass
-  // `{leading: false}`. To disable execution on the trailing edge, ditto.
-  _.throttle = function(func, wait, options) {
-    var context, args, result;
-    var timeout = null;
-    var previous = 0;
-    options || (options = {});
-    var later = function() {
-      previous = options.leading === false ? 0 : new Date;
-      timeout = null;
-      result = func.apply(context, args);
-    };
-    return function() {
-      var now = new Date;
-      if (!previous && options.leading === false) previous = now;
-      var remaining = wait - (now - previous);
-      context = this;
-      args = arguments;
-      if (remaining <= 0) {
-        clearTimeout(timeout);
-        timeout = null;
-        previous = now;
-        result = func.apply(context, args);
-      } else if (!timeout && options.trailing !== false) {
-        timeout = setTimeout(later, remaining);
-      }
-      return result;
-    };
-  };
-
-  // Returns a function, that, as long as it continues to be invoked, will not
-  // be triggered. The function will be called after it stops being called for
-  // N milliseconds. If `immediate` is passed, trigger the function on the
-  // leading edge, instead of the trailing.
-  _.debounce = function(func, wait, immediate) {
-    var result;
-    var timeout = null;
-    return function() {
-      var context = this, args = arguments;
-      var later = function() {
-        timeout = null;
-        if (!immediate) result = func.apply(context, args);
-      };
-      var callNow = immediate && !timeout;
-      clearTimeout(timeout);
-      timeout = setTimeout(later, wait);
-      if (callNow) result = func.apply(context, args);
-      return result;
-    };
-  };
-
-  // Returns a function that will be executed at most one time, no matter how
-  // often you call it. Useful for lazy initialization.
-  _.once = function(func) {
-    var ran = false, memo;
-    return function() {
-      if (ran) return memo;
-      ran = true;
-      memo = func.apply(this, arguments);
-      func = null;
-      return memo;
-    };
-  };
-
-  // Returns the first function passed as an argument to the second,
-  // allowing you to adjust arguments, run code before and after, and
-  // conditionally execute the original function.
-  _.wrap = function(func, wrapper) {
-    return function() {
-      var args = [func];
-      push.apply(args, arguments);
-      return wrapper.apply(this, args);
-    };
-  };
-
-  // Returns a function that is the composition of a list of functions, each
-  // consuming the return value of the function that follows.
-  _.compose = function() {
-    var funcs = arguments;
-    return function() {
-      var args = arguments;
-      for (var i = funcs.length - 1; i >= 0; i--) {
-        args = [funcs[i].apply(this, args)];
-      }
-      return args[0];
-    };
-  };
-
-  // Returns a function that will only be executed after being called N times.
-  _.after = function(times, func) {
-    return function() {
-      if (--times < 1) {
-        return func.apply(this, arguments);
-      }
-    };
-  };
-
-  // Object Functions
-  // ----------------
-
-  // Retrieve the names of an object's properties.
-  // Delegates to **ECMAScript 5**'s native `Object.keys`
-  _.keys = nativeKeys || function(obj) {
-    if (obj !== Object(obj)) throw new TypeError('Invalid object');
-    var keys = [];
-    for (var key in obj) if (_.has(obj, key)) keys.push(key);
-    return keys;
-  };
-
-  // Retrieve the values of an object's properties.
-  _.values = function(obj) {
-    var values = [];
-    for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
-    return values;
-  };
-
-  // Convert an object into a list of `[key, value]` pairs.
-  _.pairs = function(obj) {
-    var pairs = [];
-    for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
-    return pairs;
-  };
-
-  // Invert the keys and values of an object. The values must be serializable.
-  _.invert = function(obj) {
-    var result = {};
-    for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
-    return result;
-  };
-
-  // Return a sorted list of the function names available on the object.
-  // Aliased as `methods`
-  _.functions = _.methods = function(obj) {
-    var names = [];
-    for (var key in obj) {
-      if (_.isFunction(obj[key])) names.push(key);
-    }
-    return names.sort();
-  };
-
-  // Extend a given object with all the properties in passed-in object(s).
-  _.extend = function(obj) {
-    each(slice.call(arguments, 1), function(source) {
-      if (source) {
-        for (var prop in source) {
-          obj[prop] = source[prop];
-        }
-      }
-    });
-    return obj;
-  };
-
-  // Return a copy of the object only containing the whitelisted properties.
-  _.pick = function(obj) {
-    var copy = {};
-    var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
-    each(keys, function(key) {
-      if (key in obj) copy[key] = obj[key];
-    });
-    return copy;
-  };
-
-   // Return a copy of the object without the blacklisted properties.
-  _.omit = function(obj) {
-    var copy = {};
-    var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
-    for (var key in obj) {
-      if (!_.contains(keys, key)) copy[key] = obj[key];
-    }
-    return copy;
-  };
-
-  // Fill in a given object with default properties.
-  _.defaults = function(obj) {
-    each(slice.call(arguments, 1), function(source) {
-      if (source) {
-        for (var prop in source) {
-          if (obj[prop] === void 0) obj[prop] = source[prop];
-        }
-      }
-    });
-    return obj;
-  };
-
-  // Create a (shallow-cloned) duplicate of an object.
-  _.clone = function(obj) {
-    if (!_.isObject(obj)) return obj;
-    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
-  };
-
-  // Invokes interceptor with the obj, and then returns obj.
-  // The primary purpose of this method is to "tap into" a method chain, in
-  // order to perform operations on intermediate results within the chain.
-  _.tap = function(obj, interceptor) {
-    interceptor(obj);
-    return obj;
-  };
-
-  // Internal recursive comparison function for `isEqual`.
-  var eq = function(a, b, aStack, bStack) {
-    // Identical objects are equal. `0 === -0`, but they aren't identical.
-    // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
-    if (a === b) return a !== 0 || 1 / a == 1 / b;
-    // A strict comparison is necessary because `null == undefined`.
-    if (a == null || b == null) return a === b;
-    // Unwrap any wrapped objects.
-    if (a instanceof _) a = a._wrapped;
-    if (b instanceof _) b = b._wrapped;
-    // Compare `[[Class]]` names.
-    var className = toString.call(a);
-    if (className != toString.call(b)) return false;
-    switch (className) {
-      // Strings, numbers, dates, and booleans are compared by value.
-      case '[object String]':
-        // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
-        // equivalent to `new String("5")`.
-        return a == String(b);
-      case '[object Number]':
-        // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
-        // other numeric values.
-        return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
-      case '[object Date]':
-      case '[object Boolean]':
-        // Coerce dates and booleans to numeric primitive values. Dates are compared by their
-        // millisecond representations. Note that invalid dates with millisecond representations
-        // of `NaN` are not equivalent.
-        return +a == +b;
-      // RegExps are compared by their source patterns and flags.
-      case '[object RegExp]':
-        return a.source == b.source &&
-               a.global == b.global &&
-               a.multiline == b.multiline &&
-               a.ignoreCase == b.ignoreCase;
-    }
-    if (typeof a != 'object' || typeof b != 'object') return false;
-    // Assume equality for cyclic structures. The algorithm for detecting cyclic
-    // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
-    var length = aStack.length;
-    while (length--) {
-      // Linear search. Performance is inversely proportional to the number of
-      // unique nested structures.
-      if (aStack[length] == a) return bStack[length] == b;
-    }
-    // Objects with different constructors are not equivalent, but `Object`s
-    // from different frames are.
-    var aCtor = a.constructor, bCtor = b.constructor;
-    if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
-                             _.isFunction(bCtor) && (bCtor instanceof bCtor))) {
-      return false;
-    }
-    // Add the first object to the stack of traversed objects.
-    aStack.push(a);
-    bStack.push(b);
-    var size = 0, result = true;
-    // Recursively compare objects and arrays.
-    if (className == '[object Array]') {
-      // Compare array lengths to determine if a deep comparison is necessary.
-      size = a.length;
-      result = size == b.length;
-      if (result) {
-        // Deep compare the contents, ignoring non-numeric properties.
-        while (size--) {
-          if (!(result = eq(a[size], b[size], aStack, bStack))) break;
-        }
-      }
-    } else {
-      // Deep compare objects.
-      for (var key in a) {
-        if (_.has(a, key)) {
-          // Count the expected number of properties.
-          size++;
-          // Deep compare each member.
-          if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
-        }
-      }
-      // Ensure that both objects contain the same number of properties.
-      if (result) {
-        for (key in b) {
-          if (_.has(b, key) && !(size--)) break;
-        }
-        result = !size;
-      }
-    }
-    // Remove the first object from the stack of traversed objects.
-    aStack.pop();
-    bStack.pop();
-    return result;
-  };
-
-  // Perform a deep comparison to check if two objects are equal.
-  _.isEqual = function(a, b) {
-    return eq(a, b, [], []);
-  };
-
-  // Is a given array, string, or object empty?
-  // An "empty" object has no enumerable own-properties.
-  _.isEmpty = function(obj) {
-    if (obj == null) return true;
-    if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
-    for (var key in obj) if (_.has(obj, key)) return false;
-    return true;
-  };
-
-  // Is a given value a DOM element?
-  _.isElement = function(obj) {
-    return !!(obj && obj.nodeType === 1);
-  };
-
-  // Is a given value an array?
-  // Delegates to ECMA5's native Array.isArray
-  _.isArray = nativeIsArray || function(obj) {
-    return toString.call(obj) == '[object Array]';
-  };
-
-  // Is a given variable an object?
-  _.isObject = function(obj) {
-    return obj === Object(obj);
-  };
-
-  // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
-  each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
-    _['is' + name] = function(obj) {
-      return toString.call(obj) == '[object ' + name + ']';
-    };
-  });
-
-  // Define a fallback version of the method in browsers (ahem, IE), where
-  // there isn't any inspectable "Arguments" type.
-  if (!_.isArguments(arguments)) {
-    _.isArguments = function(obj) {
-      return !!(obj && _.has(obj, 'callee'));
-    };
-  }
-
-  // Optimize `isFunction` if appropriate.
-  if (typeof (/./) !== 'function') {
-    _.isFunction = function(obj) {
-      return typeof obj === 'function';
-    };
-  }
-
-  // Is a given object a finite number?
-  _.isFinite = function(obj) {
-    return isFinite(obj) && !isNaN(parseFloat(obj));
-  };
-
-  // Is the given value `NaN`? (NaN is the only number which does not equal itself).
-  _.isNaN = function(obj) {
-    return _.isNumber(obj) && obj != +obj;
-  };
-
-  // Is a given value a boolean?
-  _.isBoolean = function(obj) {
-    return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
-  };
-
-  // Is a given value equal to null?
-  _.isNull = function(obj) {
-    return obj === null;
-  };
-
-  // Is a given variable undefined?
-  _.isUndefined = function(obj) {
-    return obj === void 0;
-  };
-
-  // Shortcut function for checking if an object has a given property directly
-  // on itself (in other words, not on a prototype).
-  _.has = function(obj, key) {
-    return hasOwnProperty.call(obj, key);
-  };
-
-  // Utility Functions
-  // -----------------
-
-  // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
-  // previous owner. Returns a reference to the Underscore object.
-  _.noConflict = function() {
-    root._ = previousUnderscore;
-    return this;
-  };
-
-  // Keep the identity function around for default iterators.
-  _.identity = function(value) {
-    return value;
-  };
-
-  // Run a function **n** times.
-  _.times = function(n, iterator, context) {
-    var accum = Array(Math.max(0, n));
-    for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
-    return accum;
-  };
-
-  // Return a random integer between min and max (inclusive).
-  _.random = function(min, max) {
-    if (max == null) {
-      max = min;
-      min = 0;
-    }
-    return min + Math.floor(Math.random() * (max - min + 1));
-  };
-
-  // List of HTML entities for escaping.
-  var entityMap = {
-    escape: {
-      '&': '&amp;',
-      '<': '&lt;',
-      '>': '&gt;',
-      '"': '&quot;',
-      "'": '&#x27;',
-      '/': '&#x2F;'
-    }
-  };
-  entityMap.unescape = _.invert(entityMap.escape);
-
-  // Regexes containing the keys and values listed immediately above.
-  var entityRegexes = {
-    escape:   new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
-    unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
-  };
-
-  // Functions for escaping and unescaping strings to/from HTML interpolation.
-  _.each(['escape', 'unescape'], function(method) {
-    _[method] = function(string) {
-      if (string == null) return '';
-      return ('' + string).replace(entityRegexes[method], function(match) {
-        return entityMap[method][match];
-      });
-    };
-  });
-
-  // If the value of the named `property` is a function then invoke it with the
-  // `object` as context; otherwise, return it.
-  _.result = function(object, property) {
-    if (object == null) return void 0;
-    var value = object[property];
-    return _.isFunction(value) ? value.call(object) : value;
-  };
-
-  // Add your own custom functions to the Underscore object.
-  _.mixin = function(obj) {
-    each(_.functions(obj), function(name){
-      var func = _[name] = obj[name];
-      _.prototype[name] = function() {
-        var args = [this._wrapped];
-        push.apply(args, arguments);
-        return result.call(this, func.apply(_, args));
-      };
-    });
-  };
-
-  // Generate a unique integer id (unique within the entire client session).
-  // Useful for temporary DOM ids.
-  var idCounter = 0;
-  _.uniqueId = function(prefix) {
-    var id = ++idCounter + '';
-    return prefix ? prefix + id : id;
-  };
-
-  // By default, Underscore uses ERB-style template delimiters, change the
-  // following template settings to use alternative delimiters.
-  _.templateSettings = {
-    evaluate    : /<%([\s\S]+?)%>/g,
-    interpolate : /<%=([\s\S]+?)%>/g,
-    escape      : /<%-([\s\S]+?)%>/g
-  };
-
-  // When customizing `templateSettings`, if you don't want to define an
-  // interpolation, evaluation or escaping regex, we need one that is
-  // guaranteed not to match.
-  var noMatch = /(.)^/;
-
-  // Certain characters need to be escaped so that they can be put into a
-  // string literal.
-  var escapes = {
-    "'":      "'",
-    '\\':     '\\',
-    '\r':     'r',
-    '\n':     'n',
-    '\t':     't',
-    '\u2028': 'u2028',
-    '\u2029': 'u2029'
-  };
-
-  var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
-
-  // JavaScript micro-templating, similar to John Resig's implementation.
-  // Underscore templating handles arbitrary delimiters, preserves whitespace,
-  // and correctly escapes quotes within interpolated code.
-  _.template = function(text, data, settings) {
-    var render;
-    settings = _.defaults({}, settings, _.templateSettings);
-
-    // Combine delimiters into one regular expression via alternation.
-    var matcher = new RegExp([
-      (settings.escape || noMatch).source,
-      (settings.interpolate || noMatch).source,
-      (settings.evaluate || noMatch).source
-    ].join('|') + '|$', 'g');
-
-    // Compile the template source, escaping string literals appropriately.
-    var index = 0;
-    var source = "__p+='";
-    text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
-      source += text.slice(index, offset)
-        .replace(escaper, function(match) { return '\\' + escapes[match]; });
-
-      if (escape) {
-        source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
-      }
-      if (interpolate) {
-        source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
-      }
-      if (evaluate) {
-        source += "';\n" + evaluate + "\n__p+='";
-      }
-      index = offset + match.length;
-      return match;
-    });
-    source += "';\n";
-
-    // If a variable is not specified, place data values in local scope.
-    if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
-
-    source = "var __t,__p='',__j=Array.prototype.join," +
-      "print=function(){__p+=__j.call(arguments,'');};\n" +
-      source + "return __p;\n";
-
-    try {
-      render = new Function(settings.variable || 'obj', '_', source);
-    } catch (e) {
-      e.source = source;
-      throw e;
-    }
-
-    if (data) return render(data, _);
-    var template = function(data) {
-      return render.call(this, data, _);
-    };
-
-    // Provide the compiled function source as a convenience for precompilation.
-    template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
-
-    return template;
-  };
-
-  // Add a "chain" function, which will delegate to the wrapper.
-  _.chain = function(obj) {
-    return _(obj).chain();
-  };
-
-  // OOP
-  // ---------------
-  // If Underscore is called as a function, it returns a wrapped object that
-  // can be used OO-style. This wrapper holds altered versions of all the
-  // underscore functions. Wrapped objects may be chained.
-
-  // Helper function to continue chaining intermediate results.
-  var result = function(obj) {
-    return this._chain ? _(obj).chain() : obj;
-  };
-
-  // Add all of the Underscore functions to the wrapper object.
-  _.mixin(_);
-
-  // Add all mutator Array functions to the wrapper.
-  each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
-    var method = ArrayProto[name];
-    _.prototype[name] = function() {
-      var obj = this._wrapped;
-      method.apply(obj, arguments);
-      if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
-      return result.call(this, obj);
-    };
-  });
-
-  // Add all accessor Array functions to the wrapper.
-  each(['concat', 'join', 'slice'], function(name) {
-    var method = ArrayProto[name];
-    _.prototype[name] = function() {
-      return result.call(this, method.apply(this._wrapped, arguments));
-    };
-  });
-
-  _.extend(_.prototype, {
-
-    // Start chaining a wrapped Underscore object.
-    chain: function() {
-      this._chain = true;
-      return this;
-    },
-
-    // Extracts the result from a wrapped and chained object.
-    value: function() {
-      return this._wrapped;
-    }
-
-  });
-
-}).call(this);
diff --git a/src/coffee/directives/api/i-marker.coffee b/src/coffee/directives/api/i-marker.coffee
index ba9c5c7b9..1efb166e0 100644
--- a/src/coffee/directives/api/i-marker.coffee
+++ b/src/coffee/directives/api/i-marker.coffee
@@ -13,7 +13,6 @@
 			@$log = directives.api.utils.Logger
 			@$timeout = $timeout
 			@restrict = 'ECMA'
-			@replace = true
 			@require = '^googleMap'
 			@priority = -1
 			@transclude = true
diff --git a/src/coffee/directives/api/marker.coffee b/src/coffee/directives/api/marker.coffee
index bffb6b5d4..29dbcc045 100644
--- a/src/coffee/directives/api/marker.coffee
+++ b/src/coffee/directives/api/marker.coffee
@@ -3,16 +3,16 @@
 	Thus there will be one html element per marker within the directive.
 ###
 @ngGmapModule "directives.api", ->
-	class @Marker extends directives.api.IMarker
-		constructor: ($timeout) ->
-			super($timeout)
-			self = @
-			@template = '<span class="angular-google-map-marker" ng-transclude></span>'
-			@$log.info(@)
-		
-		controller: ['$scope','$element', ($scope, $element) ->
-			@getMarker = ->
-				$element.data('instance')
-		]
-		link: (scope, element, attrs, ctrl) =>
-			new directives.api.models.parent.MarkerParentModel(scope, element, attrs, ctrl, @$timeout)
\ No newline at end of file
+    class @Marker extends directives.api.IMarker
+        constructor: ($timeout) ->
+            super($timeout)
+            self = @
+            @template = '<span class="angular-google-map-marker" ng-transclude></span>'
+            @$log.info(@)
+
+        controller: ['$scope', '$element', ($scope, $element) ->
+            getMarker: ->
+                $element.data('instance')
+        ]
+        link: (scope, element, attrs, ctrl) =>
+            new directives.api.models.parent.MarkerParentModel(scope, element, attrs, ctrl, @$timeout)
\ No newline at end of file
diff --git a/src/coffee/directives/api/markers.coffee b/src/coffee/directives/api/markers.coffee
index e478ea547..d5510fb8e 100644
--- a/src/coffee/directives/api/markers.coffee
+++ b/src/coffee/directives/api/markers.coffee
@@ -10,27 +10,27 @@ not 1:1 in this setting.
 
 ###
 @ngGmapModule "directives.api", ->
-	class @Markers extends directives.api.IMarker
-		constructor: ($timeout) ->
-			super($timeout)
-			self = @
-			@template = '<span class="angular-google-map-markers" ng-transclude></span>'
-			
-			@scope.models = '=models'
-			@scope.doCluster= '=docluster'
-			@scope.clusterOptions= '=clusteroptions'
-			@scope.fit= '=fit'
-			@scope.labelContent= '=labelcontent'
-			@scope.labelAnchor= '@labelanchor'
-			@scope.labelClass= '@labelclass'
+    class @Markers extends directives.api.IMarker
+        constructor: ($timeout) ->
+            super($timeout)
+            self = @
+            @template = '<span class="angular-google-map-markers" ng-transclude></span>'
 
-			@$timeout = $timeout
-			@$log.info(@)
-		
-		controller: ['$scope','$element',($scope, $element) ->
-			@getMarkersScope = ->
-				$scope
-		]
+            @scope.models = '=models'
+            @scope.doCluster = '=docluster'
+            @scope.clusterOptions = '=clusteroptions'
+            @scope.fit = '=fit'
+            @scope.labelContent = '=labelcontent'
+            @scope.labelAnchor = '@labelanchor'
+            @scope.labelClass = '@labelclass'
 
-		link: (scope, element, attrs, ctrl) =>
-			new directives.api.models.parent.MarkersParentModel(scope, element, attrs, ctrl, @$timeout)
\ No newline at end of file
+            @$timeout = $timeout
+            @$log.info(@)
+
+        controller: ['$scope', '$element', ($scope, $element) ->
+            getMarkersScope: ->
+                $scope
+        ]
+
+        link: (scope, element, attrs, ctrl) =>
+            new directives.api.models.parent.MarkersParentModel(scope, element, attrs, ctrl, @$timeout)
\ No newline at end of file