You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by da...@apache.org on 2017/06/30 22:41:55 UTC

[25/52] [partial] incubator-trafficcontrol git commit: promotes TO experimental UI to the new Traffic Portal

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/assets/js/ng-map_1.17.6.min.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/assets/js/ng-map_1.17.6.min.js b/traffic_portal/app/src/assets/js/ng-map_1.17.6.min.js
new file mode 100644
index 0000000..d6608ef
--- /dev/null
+++ b/traffic_portal/app/src/assets/js/ng-map_1.17.6.min.js
@@ -0,0 +1,26 @@
+!function(e,t){"object"==typeof exports?module.exports=t(require("angular")):"function"==typeof define&&define.amd?define(["angular"],t):t(e.angular)}(this,function(angular){/**
+ * AngularJS Google Maps Ver. 1.17.3
+ *
+ * The MIT License (MIT)
+ * 
+ * Copyright (c) 2014, 2015, 1016 Allen Kim
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+return angular.module("ngMap",[]),function(){"use strict";var e,t=function(t,n,o,i,a,r,s){e=a;var p=this;p.mapOptions,p.mapEvents,p.eventListeners,p.addObject=function(e,t){if(p.map){p.map[e]=p.map[e]||{};var n=Object.keys(p.map[e]).length;p.map[e][t.id||n]=t,p.map instanceof google.maps.Map&&("infoWindows"!=e&&t.setMap&&t.setMap&&t.setMap(p.map),t.centered&&t.position&&p.map.setCenter(t.position),"markers"==e&&p.objectChanged("markers"),"customMarkers"==e&&p.objectChanged("customMarkers"))}},p.deleteObject=function(e,t){if(t.map){var n=t.map[e];for(var o in n)n[o]===t&&(google.maps.event.clearInstanceListeners(t),delete n[o]);t.map&&t.setMap&&t.setMap(null),"markers"==e&&p.objectChanged("markers"),"customMarkers"==e&&p.objectChanged("customMarkers")}},p.observeAttrSetObj=function(t,n,o){if(n.noWatcher)return!1;for(var i=e.getAttrsToObserve(t),a=0;a<i.length;a++){var s=i[a];n.$observe(s,r.observeAndSet(s,o))}},p.zoomToIncludeMarkers=function(){if(null!=p.map.markers&&Object.keys(p.m
 ap.markers).length>0||null!=p.map.customMarkers&&Object.keys(p.map.customMarkers).length>0){var e=new google.maps.LatLngBounds;for(var t in p.map.markers)e.extend(p.map.markers[t].getPosition());for(var n in p.map.customMarkers)e.extend(p.map.customMarkers[n].getPosition());p.mapOptions.maximumZoom&&(p.enableMaximumZoomCheck=!0),p.map.fitBounds(e)}},p.objectChanged=function(e){!p.map||"markers"!=e&&"customMarkers"!=e||"auto"!=p.map.zoomToIncludeMarkers||p.zoomToIncludeMarkers()},p.initializeMap=function(){var a=p.mapOptions,u=p.mapEvents,l=p.map;if(p.map=s.getMapInstance(n[0]),r.setStyle(n[0]),l){var g=e.filter(o),d=e.getOptions(g),m=e.getControlOptions(g);a=angular.extend(d,m);for(var f in l){var v=l[f];if("object"==typeof v)for(var y in v)p.addObject(f,v[y])}p.map.showInfoWindow=p.showInfoWindow,p.map.hideInfoWindow=p.hideInfoWindow}a.zoom=a.zoom||15;var h=a.center;if(!a.center||"string"==typeof h&&h.match(/\{\{.*\}\}/))a.center=new google.maps.LatLng(0,0);else if(!(h instanceof g
 oogle.maps.LatLng)){var M=a.center;delete a.center,r.getGeoLocation(M,a.geoLocationOptions).then(function(e){p.map.setCenter(e);var n=a.geoCallback;n&&i(n)(t)},function(){a.geoFallbackCenter&&p.map.setCenter(a.geoFallbackCenter)})}p.map.setOptions(a);for(var b in u){var O=u[b],w=google.maps.event.addListener(p.map,b,O);p.eventListeners[b]=w}p.observeAttrSetObj(c,o,p.map),p.singleInfoWindow=a.singleInfoWindow,google.maps.event.trigger(p.map,"resize"),google.maps.event.addListenerOnce(p.map,"idle",function(){r.addMap(p),a.zoomToIncludeMarkers&&p.zoomToIncludeMarkers(),t.map=p.map,t.$emit("mapInitialized",p.map),o.mapInitialized&&i(o.mapInitialized)(t,{map:p.map})}),a.zoomToIncludeMarkers&&a.maximumZoom&&google.maps.event.addListener(p.map,"zoom_changed",function(){1==p.enableMaximumZoomCheck&&(p.enableMaximumZoomCheck=!1,google.maps.event.addListenerOnce(p.map,"bounds_changed",function(){p.map.setZoom(Math.min(a.maximumZoom,p.map.getZoom()))}))})},t.google=google;var c=e.orgAttributes
 (n),u=e.filter(o),l=e.getOptions(u,{scope:t}),g=e.getControlOptions(u),d=angular.extend(l,g),m=e.getEvents(t,u);if(Object.keys(m).length&&void 0,p.mapOptions=d,p.mapEvents=m,p.eventListeners={},l.lazyInit){if(o.id&&0===o.id.indexOf("{{",0)&&-1!==o.id.indexOf("}}",o.id.length-"}}".length))var f=o.id.slice(2,-2),v=i(f)(t);else var v=o.id;p.map={id:v},r.addMap(p)}else p.initializeMap();l.triggerResize&&google.maps.event.trigger(p.map,"resize"),n.bind("$destroy",function(){s.returnMapInstance(p.map),r.deleteMap(p)})};t.$inject=["$scope","$element","$attrs","$parse","Attr2MapOptions","NgMap","NgMapPool"],angular.module("ngMap").controller("__MapController",t)}(),function(){"use strict";var e,t=function(t,o,i,a){a=a[0]||a[1];var r=e.orgAttributes(o),s=e.filter(i),p=e.getOptions(s,{scope:t}),c=e.getEvents(t,s),u=n(p,c);a.addObject("bicyclingLayers",u),a.observeAttrSetObj(r,i,u),o.bind("$destroy",function(){a.deleteObject("bicyclingLayers",u)})},n=function(e,t){var n=new google.maps.Bicycli
 ngLayer(e);for(var o in t)google.maps.event.addListener(n,o,t[o]);return n},o=function(n){return e=n,{restrict:"E",require:["?^map","?^ngMap"],link:t}};o.$inject=["Attr2MapOptions"],angular.module("ngMap").directive("bicyclingLayer",o)}(),function(){"use strict";var e,t,n,o=function(n,o,i,a){a=a[0]||a[1];var r=e.filter(i),s=e.getOptions(r,{scope:n}),p=e.getEvents(n,r),c=o[0].parentElement.removeChild(o[0]);t(c.innerHTML.trim())(n);for(var u in p)google.maps.event.addDomListener(c,u,p[u]);a.addObject("customControls",c);var l=s.position;a.map.controls[google.maps.ControlPosition[l]].push(c),o.bind("$destroy",function(){a.deleteObject("customControls",c)})},i=function(i,a,r){return e=i,t=a,n=r,{restrict:"E",require:["?^map","?^ngMap"],link:o}};i.$inject=["Attr2MapOptions","$compile","NgMap"],angular.module("ngMap").directive("customControl",i)}(),function(){"use strict";var e,t,n,o,i=function(e){e=e||{},this.el=document.createElement("div"),this.el.style.display="inline-block",this.el
 .style.visibility="hidden",this.visible=!0;for(var t in e)this[t]=e[t]},a=function(){i.prototype=new google.maps.OverlayView,i.prototype.setContent=function(e,t){this.el.innerHTML=e,this.el.style.position="absolute",t&&n(angular.element(this.el).contents())(t)},i.prototype.getDraggable=function(){return this.draggable},i.prototype.setDraggable=function(e){this.draggable=e},i.prototype.getPosition=function(){return this.position},i.prototype.setPosition=function(e){if(e&&(this.position=e),this.getProjection()&&"function"==typeof this.position.lng){var n=this.getProjection().fromLatLngToDivPixel(this.position),o=this,i=function(){var e=Math.round(n.x-o.el.offsetWidth/2),t=Math.round(n.y-o.el.offsetHeight-10);o.el.style.left=e+"px",o.el.style.top=t+"px",o.el.style.visibility="visible"};o.el.offsetWidth&&o.el.offsetHeight?i():t(i,300)}},i.prototype.setZIndex=function(e){e&&(this.zIndex=e),this.el.style.zIndex=this.zIndex},i.prototype.getVisible=function(){return this.visible},i.prototyp
 e.setVisible=function(e){this.el.style.display=e?"inline-block":"none",this.visible=e},i.prototype.addClass=function(e){var t=this.el.className.trim().split(" ");-1==t.indexOf(e)&&t.push(e),this.el.className=t.join(" ")},i.prototype.removeClass=function(e){var t=this.el.className.split(" "),n=t.indexOf(e);n>-1&&t.splice(n,1),this.el.className=t.join(" ")},i.prototype.onAdd=function(){this.getPanes().overlayMouseTarget.appendChild(this.el)},i.prototype.draw=function(){this.setPosition(),this.setZIndex(this.zIndex),this.setVisible(this.visible)},i.prototype.onRemove=function(){this.el.parentNode.removeChild(this.el)}},r=function(n,a){return function(r,s,p,c){c=c[0]||c[1];var u=e.orgAttributes(s),l=e.filter(p),g=e.getOptions(l,{scope:r}),d=e.getEvents(r,l);s[0].style.display="none";var m=new i(g);t(function(){r.$watch("["+a.join(",")+"]",function(){m.setContent(n,r)},!0),m.setContent(s[0].innerHTML,r);var e=s[0].firstElementChild.className;m.addClass("custom-marker"),m.addClass(e),g.po
 sition instanceof google.maps.LatLng||o.getGeoLocation(g.position).then(function(e){m.setPosition(e)})});for(var f in d)google.maps.event.addDomListener(m.el,f,d[f]);c.addObject("customMarkers",m),c.observeAttrSetObj(u,p,m),s.bind("$destroy",function(){c.deleteObject("customMarkers",m)})}},s=function(i,s,p,c){return e=p,t=i,n=s,o=c,{restrict:"E",require:["?^map","?^ngMap"],compile:function(e){a(),e[0].style.display="none";var t=e.html(),n=t.match(/{{([^}]+)}}/g),o=[];return(n||[]).forEach(function(e){var t=e.replace("{{","").replace("}}","");-1==e.indexOf("::")&&-1==e.indexOf("this.")&&-1==o.indexOf(t)&&o.push(e.replace("{{","").replace("}}",""))}),r(t,o)}}};s.$inject=["$timeout","$compile","Attr2MapOptions","NgMap"],angular.module("ngMap").directive("customMarker",s)}(),function(){"use strict";var e,t,n,o=function(e,t){e.panel&&(e.panel=document.getElementById(e.panel)||document.querySelector(e.panel));var n=new google.maps.DirectionsRenderer(e);for(var o in t)google.maps.event.add
 Listener(n,o,t[o]);return n},i=function(e,o){var i=new google.maps.DirectionsService,a=o;a.travelMode=a.travelMode||"DRIVING";var r=["origin","destination","travelMode","transitOptions","unitSystem","durationInTraffic","waypoints","optimizeWaypoints","provideRouteAlternatives","avoidHighways","avoidTolls","region"];for(var s in a)-1===r.indexOf(s)&&delete a[s];a.waypoints&&("[]"==a.waypoints||""===a.waypoints)&&delete a.waypoints;var p=function(n){i.route(n,function(n,o){o==google.maps.DirectionsStatus.OK&&t(function(){e.setDirections(n)})})};a.origin&&a.destination&&("current-location"==a.origin?n.getCurrentPosition().then(function(e){a.origin=new google.maps.LatLng(e.coords.latitude,e.coords.longitude),p(a)}):"current-location"==a.destination?n.getCurrentPosition().then(function(e){a.destination=new google.maps.LatLng(e.coords.latitude,e.coords.longitude),p(a)}):p(a))},a=function(a,r,s,p){var c=a;e=p,t=r,n=s;var u=function(n,a,r,s){s=s[0]||s[1];var p=c.orgAttributes(a),u=c.filter(
 r),l=c.getOptions(u,{scope:n}),g=c.getEvents(n,u),d=c.getAttrsToObserve(p),m=o(l,g);s.addObject("directionsRenderers",m),d.forEach(function(e){!function(e){r.$observe(e,function(n){if("panel"==e)t(function(){var e=document.getElementById(n)||document.querySelector(n);e&&m.setPanel(e)});else if(l[e]!==n){var o=c.toOptionValue(n,{key:e});l[e]=o,i(m,l)}})}(e)}),e.getMap().then(function(){i(m,l)}),a.bind("$destroy",function(){s.deleteObject("directionsRenderers",m)})};return{restrict:"E",require:["?^map","?^ngMap"],link:u}};a.$inject=["Attr2MapOptions","$timeout","NavigatorGeolocation","NgMap"],angular.module("ngMap").directive("directions",a)}(),function(){"use strict";angular.module("ngMap").directive("drawingManager",["Attr2MapOptions",function(e){var t=e;return{restrict:"E",require:["?^map","?^ngMap"],link:function(e,n,o,i){i=i[0]||i[1];var a=t.filter(o),r=t.getOptions(a,{scope:e}),s=t.getControlOptions(a),p=t.getEvents(e,a),c=new google.maps.drawing.DrawingManager({drawingMode:r.dr
 awingmode,drawingControl:r.drawingcontrol,drawingControlOptions:s.drawingControlOptions,circleOptions:r.circleoptions,markerOptions:r.markeroptions,polygonOptions:r.polygonoptions,polylineOptions:r.polylineoptions,rectangleOptions:r.rectangleoptions});o.$observe("drawingControlOptions",function(e){c.drawingControlOptions=t.getControlOptions({drawingControlOptions:e}).drawingControlOptions,c.setDrawingMode(null),c.setMap(i.map)});for(var u in p)google.maps.event.addListener(c,u,p[u]);i.addObject("mapDrawingManager",c),n.bind("$destroy",function(){i.deleteObject("mapDrawingManager",c)})}}}])}(),function(){"use strict";angular.module("ngMap").directive("dynamicMapsEngineLayer",["Attr2MapOptions",function(e){var t=e,n=function(e,t){var n=new google.maps.visualization.DynamicMapsEngineLayer(e);for(var o in t)google.maps.event.addListener(n,o,t[o]);return n};return{restrict:"E",require:["?^map","?^ngMap"],link:function(e,o,i,a){a=a[0]||a[1];var r=t.filter(i),s=t.getOptions(r,{scope:e}),p=
 t.getEvents(e,r,p),c=n(s,p);a.addObject("mapsEngineLayers",c)}}}])}(),function(){"use strict";angular.module("ngMap").directive("fusionTablesLayer",["Attr2MapOptions",function(e){var t=e,n=function(e,t){var n=new google.maps.FusionTablesLayer(e);for(var o in t)google.maps.event.addListener(n,o,t[o]);return n};return{restrict:"E",require:["?^map","?^ngMap"],link:function(e,o,i,a){a=a[0]||a[1];var r=t.filter(i),s=t.getOptions(r,{scope:e}),p=t.getEvents(e,r,p),c=n(s,p);a.addObject("fusionTablesLayers",c)}}}])}(),function(){"use strict";angular.module("ngMap").directive("heatmapLayer",["Attr2MapOptions","$window",function(e,t){var n=e;return{restrict:"E",require:["?^map","?^ngMap"],link:function(e,o,i,a){a=a[0]||a[1];var r=n.filter(i),s=n.getOptions(r,{scope:e});if(s.data=t[i.data]||e[i.data],!(s.data instanceof Array))throw"invalid heatmap data";s.data=new google.maps.MVCArray(s.data);{var p=new google.maps.visualization.HeatmapLayer(s);n.getEvents(e,r)}a.addObject("heatmapLayers",p)}}
 }])}(),function(){"use strict";var e=function(e,t,n,o,i,a,r){var s=e,p=function(e,a,r){var s;!e.position||e.position instanceof google.maps.LatLng||delete e.position,s=new google.maps.InfoWindow(e);for(var p in a)p&&google.maps.event.addListener(s,p,a[p]);var c=n(function(e){angular.isString(r)?o(r).then(function(t){e(angular.element(t).wrap("<div>").parent())},function(e){throw"info-window template request failed: "+e}):e(r)}).then(function(e){var t=e.html().trim();if(1!=angular.element(t).length)throw"info-window working as a template must have a container";s.__template=t.replace(/\s?ng-non-bindable[='"]+/,"")});return s.__open=function(e,n,o){c.then(function(){i(function(){o&&(n.anchor=o);var i=t(s.__template)(n);s.setContent(i[0]),n.$apply(),o&&o.getPosition?s.open(e,o):o&&o instanceof google.maps.LatLng?(s.open(e),s.setPosition(o)):s.open(e);var a=s.content.parentElement.parentElement.parentElement;a.className="ng-map-info-window"})})},s},c=function(e,t,n,o){o=o[0]||o[1],t.css(
 "display","none");var i,c=s.orgAttributes(t),u=s.filter(n),l=s.getOptions(u,{scope:e}),g=s.getEvents(e,u),d=p(l,g,l.template||t);!l.position||l.position instanceof google.maps.LatLng||(i=l.position),i&&r.getGeoLocation(i).then(function(t){d.setPosition(t),d.__open(o.map,e,t);var i=n.geoCallback;i&&a(i)(e)}),o.addObject("infoWindows",d),o.observeAttrSetObj(c,n,d),o.showInfoWindow=o.map.showInfoWindow=o.showInfoWindow||function(t,n,i){var a="string"==typeof t?t:n,r="string"==typeof t?n:i;if("string"==typeof r)if("undefined"!=typeof o.map.markers&&"undefined"!=typeof o.map.markers[r])r=o.map.markers[r];else{if("undefined"==typeof o.map.customMarkers[r])throw new Error("Cant open info window for id "+r+". Marker or CustomMarker is not defined");r=o.map.customMarkers[r]}var s=o.map.infoWindows[a],p=r?r:this.getPosition?this:null;s.__open(o.map,e,p),o.singleInfoWindow&&(o.lastInfoWindow&&e.hideInfoWindow(o.lastInfoWindow),o.lastInfoWindow=a)},o.hideInfoWindow=o.map.hideInfoWindow=o.hideIn
 foWindow||function(e,t){var n="string"==typeof e?e:t,i=o.map.infoWindows[n];i.close()},e.showInfoWindow=o.map.showInfoWindow,e.hideInfoWindow=o.map.hideInfoWindow;var m=d.mapId?{id:d.mapId}:0;r.getMap(m).then(function(t){if(d.visible&&d.__open(t,e),d.visibleOnMarker){var n=d.visibleOnMarker;d.__open(t,e,t.markers[n])}})};return{restrict:"E",require:["?^map","?^ngMap"],link:c}};e.$inject=["Attr2MapOptions","$compile","$q","$templateRequest","$timeout","$parse","NgMap"],angular.module("ngMap").directive("infoWindow",e)}(),function(){"use strict";angular.module("ngMap").directive("kmlLayer",["Attr2MapOptions",function(e){var t=e,n=function(e,t){var n=new google.maps.KmlLayer(e);for(var o in t)google.maps.event.addListener(n,o,t[o]);return n};return{restrict:"E",require:["?^map","?^ngMap"],link:function(e,o,i,a){a=a[0]||a[1];var r=t.orgAttributes(o),s=t.filter(i),p=t.getOptions(s,{scope:e}),c=t.getEvents(e,s),u=n(p,c);a.addObject("kmlLayers",u),a.observeAttrSetObj(r,i,u),o.bind("$destro
 y",function(){a.deleteObject("kmlLayers",u)})}}}])}(),function(){"use strict";angular.module("ngMap").directive("mapData",["Attr2MapOptions","NgMap",function(e,t){var n=e;return{restrict:"E",require:["?^map","?^ngMap"],link:function(e,o,i){var a=n.filter(i),r=n.getOptions(a,{scope:e}),s=n.getEvents(e,a,s);t.getMap().then(function(t){for(var n in r){var o=r[n];"function"==typeof e[o]?t.data[n](e[o]):t.data[n](o)}for(var i in s)t.data.addListener(i,s[i])})}}}])}(),function(){"use strict";var e,t,n,o=[],i=[],a=function(n,a,r){var s=r.mapLazyLoadParams||r.mapLazyLoad;if(void 0===window.google||void 0===window.google.maps){i.push({scope:n,element:a,savedHtml:o[i.length]}),window.lazyLoadCallback=function(){e(function(){i.forEach(function(e){e.element.html(e.savedHtml),t(e.element.contents())(e.scope)})},100)};var p=document.createElement("script");p.src=s+(s.indexOf("?")>-1?"&":"?")+"callback=lazyLoadCallback",document.querySelector('script[src="'+p.src+'"]')||document.body.appendChild(p
 )}else a.html(o),t(a.contents())(n)},r=function(e,t){return!t.mapLazyLoad&&void 0,o.push(e.html()),n=t.mapLazyLoad,void 0!==window.google&&void 0!==window.google.maps?!1:(e.html(""),{pre:a})},s=function(n,o){return t=n,e=o,{compile:r}};s.$inject=["$compile","$timeout"],angular.module("ngMap").directive("mapLazyLoad",s)}(),function(){"use strict";angular.module("ngMap").directive("mapType",["$parse","NgMap",function(e,t){return{restrict:"E",require:["?^map","?^ngMap"],link:function(n,o,i,a){a=a[0]||a[1];var r,s=i.name;if(!s)throw"invalid map-type name";if(r=e(i.object)(n),!r)throw"invalid map-type object";t.getMap().then(function(e){e.mapTypes.set(s,r)}),a.addObject("mapTypes",r)}}}])}(),function(){"use strict";var e=function(){return{restrict:"AE",controller:"__MapController",controllerAs:"ngmap"}};angular.module("ngMap").directive("map",[e]),angular.module("ngMap").directive("ngMap",[e])}(),function(){"use strict";angular.module("ngMap").directive("mapsEngineLayer",["Attr2MapOption
 s",function(e){var t=e,n=function(e,t){var n=new google.maps.visualization.MapsEngineLayer(e);for(var o in t)google.maps.event.addListener(n,o,t[o]);return n};return{restrict:"E",require:["?^map","?^ngMap"],link:function(e,o,i,a){a=a[0]||a[1];var r=t.filter(i),s=t.getOptions(r,{scope:e}),p=t.getEvents(e,r,p),c=n(s,p);a.addObject("mapsEngineLayers",c)}}}])}(),function(){"use strict";var e,t,n,o=function(e,t){var o;if(n.defaultOptions.marker)for(var i in n.defaultOptions.marker)"undefined"==typeof e[i]&&(e[i]=n.defaultOptions.marker[i]);e.position instanceof google.maps.LatLng||(e.position=new google.maps.LatLng(0,0)),o=new google.maps.Marker(e),Object.keys(t).length>0;for(var a in t)a&&google.maps.event.addListener(o,a,t[a]);return o},i=function(i,a,r,s){s=s[0]||s[1];var p,c=e.orgAttributes(a),u=e.filter(r),l=e.getOptions(u,i,{scope:i}),g=e.getEvents(i,u);l.position instanceof google.maps.LatLng||(p=l.position);var d=o(l,g);s.addObject("markers",d),p&&n.getGeoLocation(p).then(functio
 n(e){d.setPosition(e),l.centered&&d.map.setCenter(e);var n=r.geoCallback;n&&t(n)(i)}),s.observeAttrSetObj(c,r,d),a.bind("$destroy",function(){s.deleteObject("markers",d)})},a=function(o,a,r){return e=o,t=a,n=r,{restrict:"E",require:["^?map","?^ngMap"],link:i}};a.$inject=["Attr2MapOptions","$parse","NgMap"],angular.module("ngMap").directive("marker",a)}(),function(){"use strict";angular.module("ngMap").directive("overlayMapType",["NgMap",function(e){return{restrict:"E",require:["?^map","?^ngMap"],link:function(t,n,o,i){i=i[0]||i[1];var a=o.initMethod||"insertAt",r=t[o.object];e.getMap().then(function(e){if("insertAt"==a){var t=parseInt(o.index,10);e.overlayMapTypes.insertAt(t,r)}else"push"==a&&e.overlayMapTypes.push(r)}),i.addObject("overlayMapTypes",r)}}}])}(),function(){"use strict";var e=function(e,t){var n=e,o=function(e,o,i,a){if("false"===i.placesAutoComplete)return!1;var r=n.filter(i),s=n.getOptions(r,{scope:e}),p=n.getEvents(e,r),c=new google.maps.places.Autocomplete(o[0],s);
 for(var u in p)google.maps.event.addListener(c,u,p[u]);var l=function(){t(function(){a&&a.$setViewValue(o.val())},100)};google.maps.event.addListener(c,"place_changed",l),o[0].addEventListener("change",l),i.$observe("types",function(e){if(e){var t=n.toOptionValue(e,{key:"types"});c.setTypes(t)}}),i.$observe("componentRestrictions",function(t){t&&c.setComponentRestrictions(e.$eval(t))})};return{restrict:"A",require:"?ngModel",link:o}};e.$inject=["Attr2MapOptions","$timeout"],angular.module("ngMap").directive("placesAutoComplete",e)}(),function(){"use strict";var e=function(e,t){var n,o=e.name;switch(delete e.name,o){case"circle":e.center instanceof google.maps.LatLng||(e.center=new google.maps.LatLng(0,0)),n=new google.maps.Circle(e);break;case"polygon":n=new google.maps.Polygon(e);break;case"polyline":n=new google.maps.Polyline(e);break;case"rectangle":n=new google.maps.Rectangle(e);break;case"groundOverlay":case"image":var i=e.url,a={opacity:e.opacity,clickable:e.clickable,id:e.id}
 ;n=new google.maps.GroundOverlay(i,e.bounds,a)}for(var r in t)t[r]&&google.maps.event.addListener(n,r,t[r]);return n},t=function(t,n,o){var i=t,a=function(t,a,r,s){s=s[0]||s[1];var p,c,u=i.orgAttributes(a),l=i.filter(r),g=i.getOptions(l,{scope:t}),d=i.getEvents(t,l);c=g.name,g.center instanceof google.maps.LatLng||(p=g.center);var m=e(g,d);s.addObject("shapes",m),p&&"circle"==c&&o.getGeoLocation(p).then(function(e){m.setCenter(e),m.centered&&m.map.setCenter(e);var o=r.geoCallback;o&&n(o)(t)}),s.observeAttrSetObj(u,r,m),a.bind("$destroy",function(){s.deleteObject("shapes",m)})};return{restrict:"E",require:["?^map","?^ngMap"],link:a}};t.$inject=["Attr2MapOptions","$parse","NgMap"],angular.module("ngMap").directive("shape",t)}(),function(){"use strict";var e=function(e,t){var n=e,o=function(e,t,n){var o,i;t.container&&(i=document.getElementById(t.container),i=i||document.querySelector(t.container)),i?o=new google.maps.StreetViewPanorama(i,t):(o=e.getStreetView(),o.setOptions(t));for(va
 r a in n)a&&google.maps.event.addListener(o,a,n[a]);return o},i=function(e,i,a){var r=n.filter(a),s=n.getOptions(r,{scope:e}),p=n.getControlOptions(r),c=angular.extend(s,p),u=n.getEvents(e,r);t.getMap().then(function(e){var t=o(e,c,u);e.setStreetView(t),!t.getPosition()&&t.setPosition(e.getCenter()),google.maps.event.addListener(t,"position_changed",function(){t.getPosition()!==e.getCenter()&&e.setCenter(t.getPosition())});var n=google.maps.event.addListener(e,"center_changed",function(){t.setPosition(e.getCenter()),google.maps.event.removeListener(n)})})};return{restrict:"E",require:["?^map","?^ngMap"],link:i}};e.$inject=["Attr2MapOptions","NgMap"],angular.module("ngMap").directive("streetViewPanorama",e)}(),function(){"use strict";angular.module("ngMap").directive("trafficLayer",["Attr2MapOptions",function(e){var t=e,n=function(e,t){var n=new google.maps.TrafficLayer(e);for(var o in t)google.maps.event.addListener(n,o,t[o]);return n};return{restrict:"E",require:["?^map","?^ngMap"]
 ,link:function(e,o,i,a){a=a[0]||a[1];var r=t.orgAttributes(o),s=t.filter(i),p=t.getOptions(s,{scope:e}),c=t.getEvents(e,s),u=n(p,c);a.addObject("trafficLayers",u),a.observeAttrSetObj(r,i,u),o.bind("$destroy",function(){a.deleteObject("trafficLayers",u)})}}}])}(),function(){"use strict";angular.module("ngMap").directive("transitLayer",["Attr2MapOptions",function(e){var t=e,n=function(e,t){var n=new google.maps.TransitLayer(e);for(var o in t)google.maps.event.addListener(n,o,t[o]);return n};return{restrict:"E",require:["?^map","?^ngMap"],link:function(e,o,i,a){a=a[0]||a[1];var r=t.orgAttributes(o),s=t.filter(i),p=t.getOptions(s,{scope:e}),c=t.getEvents(e,s),u=n(p,c);a.addObject("transitLayers",u),a.observeAttrSetObj(r,i,u),o.bind("$destroy",function(){a.deleteObject("transitLayers",u)})}}}])}(),function(){"use strict";var e=/([\:\-\_]+(.))/g,t=/^moz([A-Z])/,n=function(){return function(n){return n.replace(e,function(e,t,n,o){return o?n.toUpperCase():n}).replace(t,"Moz$1")}};angular.mo
 dule("ngMap").filter("camelCase",n)}(),function(){"use strict";var e=function(){return function(e){try{return JSON.parse(e),e}catch(t){return e.replace(/([\$\w]+)\s*:/g,function(e,t){return'"'+t+'":'}).replace(/'([^']+)'/g,function(e,t){return'"'+t+'"'})}}};angular.module("ngMap").filter("jsonize",e)}(),function(){"use strict";var isoDateRE=/^(\d{4}\-\d\d\-\d\d([tT][\d:\.]*)?)([zZ]|([+\-])(\d\d):?(\d\d))?$/,Attr2MapOptions=function($parse,$timeout,$log,NavigatorGeolocation,GeoCoder,camelCaseFilter,jsonizeFilter){var orgAttributes=function(e){e.length>0&&(e=e[0]);for(var t={},n=0;n<e.attributes.length;n++){var o=e.attributes[n];t[o.name]=o.value}return t},getJSON=function(e){var t=/^[\+\-]?[0-9\.]+,[ ]*\ ?[\+\-]?[0-9\.]+$/;return e.match(t)&&(e="["+e+"]"),JSON.parse(jsonizeFilter(e))},getLatLng=function(e){var t=e;return e[0].constructor==Array?t=e.map(function(e){return new google.maps.LatLng(e[0],e[1])}):!isNaN(parseFloat(e[0]))&&isFinite(e[0])&&(t=new google.maps.LatLng(t[0],t[1])
 ),t},toOptionValue=function(input,options){var output;try{output=getNumber(input)}catch(err){try{var output=getJSON(input);if(output instanceof Array)output=output[0].constructor==Object?output:getLatLng(output);else if(output===Object(output)){var newOptions=options;newOptions.doNotConverStringToNumber=!0,output=getOptions(output,newOptions)}}catch(err2){if(input.match(/^[A-Z][a-zA-Z0-9]+\(.*\)$/))try{var exp="new google.maps."+input;output=eval(exp)}catch(e){output=input}else if(input.match(/^([A-Z][a-zA-Z0-9]+)\.([A-Z]+)$/))try{var matches=input.match(/^([A-Z][a-zA-Z0-9]+)\.([A-Z]+)$/);output=google.maps[matches[1]][matches[2]]}catch(e){output=input}else if(input.match(/^[A-Z]+$/))try{var capitalizedKey=options.key.charAt(0).toUpperCase()+options.key.slice(1);options.key.match(/temperatureUnit|windSpeedUnit|labelColor/)?(capitalizedKey=capitalizedKey.replace(/s$/,""),output=google.maps.weather[capitalizedKey][input]):output=google.maps[capitalizedKey][input]}catch(e){output=input
 }else if(input.match(isoDateRE))try{output=new Date(input)}catch(e){output=input}else if(input.match(/^{/)&&options.scope)try{var expr=input.replace(/{{/,"").replace(/}}/g,"");output=options.scope.$eval(expr)}catch(err){output=input}else output=input}}if(("center"==options.key||"center"==options.key)&&output instanceof Array&&(output=new google.maps.LatLng(output[0],output[1])),"bounds"==options.key&&output instanceof Array&&(output=new google.maps.LatLngBounds(output[0],output[1])),"icons"==options.key&&output instanceof Array)for(var i=0;i<output.length;i++){var el=output[i];el.icon.path.match(/^[A-Z_]+$/)&&(el.icon.path=google.maps.SymbolPath[el.icon.path])}if("icon"==options.key&&output instanceof Object){(""+output.path).match(/^[A-Z_]+$/)&&(output.path=google.maps.SymbolPath[output.path]);for(var key in output){var arr=output[key];"anchor"==key||"origin"==key||"labelOrigin"==key?output[key]=new google.maps.Point(arr[0],arr[1]):("size"==key||"scaledSize"==key)&&(output[key]=new
  google.maps.Size(arr[0],arr[1]))}}return output},getAttrsToObserve=function(e){var t=[];if(!e.noWatcher)for(var n in e){var o=e[n];o&&o.match(/\{\{.*\}\}/)&&t.push(camelCaseFilter(n))}return t},filter=function(e){var t={};for(var n in e)n.match(/^\$/)||n.match(/^ng[A-Z]/)||(t[n]=e[n]);return t},getOptions=function(e,t){t=t||{};var n={};for(var o in e)if(e[o]||0===e[o]){if(o.match(/^on[A-Z]/))continue;if(o.match(/ControlOptions$/))continue;n[o]="string"!=typeof e[o]?e[o]:t.doNotConverStringToNumber&&e[o].match(/^[0-9]+$/)?e[o]:toOptionValue(e[o],{key:o,scope:t.scope})}return n},getEvents=function(e,t){var n={},o=function(e){return"_"+e.toLowerCase()},i=function(t){var n=t.match(/([^\(]+)\(([^\)]*)\)/),o=n[1],i=n[2].replace(/event[ ,]*/,""),a=$parse("["+i+"]");return function(t){function n(e,t){return e[t]}var i=a(e),r=o.split(".").reduce(n,e);r&&r.apply(this,[t].concat(i)),$timeout(function(){e.$apply()})}};for(var a in t)if(t[a]){if(!a.match(/^on[A-Z]/))continue;var r=a.replace(/^o
 n/,"");r=r.charAt(0).toLowerCase()+r.slice(1),r=r.replace(/([A-Z])/g,o);var s=t[a];n[r]=new i(s)}return n},getControlOptions=function(e){var t={};if("object"!=typeof e)return!1;for(var n in e)if(e[n]){if(!n.match(/(.*)ControlOptions$/))continue;var o=e[n],i=o.replace(/'/g,'"');i=i.replace(/([^"]+)|("[^"]+")/g,function(e,t,n){return t?t.replace(/([a-zA-Z0-9]+?):/g,'"$1":'):n});try{var a=JSON.parse(i);for(var r in a)if(a[r]){var s=a[r];if("string"==typeof s?s=s.toUpperCase():"mapTypeIds"===r&&(s=s.map(function(e){return e.match(/^[A-Z]+$/)?google.maps.MapTypeId[e.toUpperCase()]:e})),"style"===r){var p=n.charAt(0).toUpperCase()+n.slice(1),c=p.replace(/Options$/,"")+"Style";a[r]=google.maps[c][s]}else a[r]="position"===r?google.maps.ControlPosition[s]:s}t[n]=a}catch(u){}}return t};return{filter:filter,getOptions:getOptions,getEvents:getEvents,getControlOptions:getControlOptions,toOptionValue:toOptionValue,getAttrsToObserve:getAttrsToObserve,orgAttributes:orgAttributes}};Attr2MapOptions.
 $inject=["$parse","$timeout","$log","NavigatorGeolocation","GeoCoder","camelCaseFilter","jsonizeFilter"],angular.module("ngMap").service("Attr2MapOptions",Attr2MapOptions)}(),function(){"use strict";var e,t=function(t){var n=e.defer(),o=new google.maps.Geocoder;return o.geocode(t,function(e,t){t==google.maps.GeocoderStatus.OK?n.resolve(e):n.reject(t)}),n.promise},n=function(n){return e=n,{geocode:t}};n.$inject=["$q"],angular.module("ngMap").service("GeoCoder",n)}(),function(){"use strict";var e,t=function(t){var n=e.defer();return navigator.geolocation?(void 0===t?t={timeout:5e3}:void 0===t.timeout&&(t.timeout=5e3),navigator.geolocation.getCurrentPosition(function(e){n.resolve(e)},function(e){n.reject(e)},t)):n.reject("Browser Geolocation service failed."),n.promise},n=function(n){return e=n,{getCurrentPosition:t}};n.$inject=["$q"],angular.module("ngMap").service("NavigatorGeolocation",n)}(),function(){"use strict";var e,t,n,o=[],i=function(n){var i=t.createElement("div");i.style.wi
 dth="100%",i.style.height="100%",n.appendChild(i);var a=new e.google.maps.Map(i,{});return o.push(a),a},a=function(e,t){for(var n,i=0;i<o.length;i++){var a=o[i];if(a.id==t&&!a.inUse){var r=a.getDiv();e.appendChild(r),n=a;break}}return n},r=function(e){for(var t,n=0;n<o.length;n++){var i=o[n];if(!i.id&&!i.inUse){var a=i.getDiv();e.appendChild(a),t=i;break}}return t},s=function(e){var t=a(e,e.id)||r(e);return t?n(function(){google.maps.event.trigger(t,"idle")},100):t=i(e),t.inUse=!0,t},p=function(e){e.inUse=!1},c=function(){for(var e=0;e<o.length;e++)o[e]=null;o=[]},u=function(i,a,r){return t=i[0],e=a,n=r,{mapInstances:o,resetMapInstances:c,getMapInstance:s,returnMapInstance:p}};u.$inject=["$document","$window","$timeout"],angular.module("ngMap").factory("NgMapPool",u)}(),function(){"use strict";var e,t,n,o,i,a,r,s={},p=function(n,o){var i;return n.currentStyle?i=n.currentStyle[o]:e.getComputedStyle&&(i=t.defaultView.getComputedStyle(n,null).getPropertyValue(o)),i},c=function(e){var t
 =s[e||0];return t.map instanceof google.maps.Map?void 0:(t.initializeMap(),t.map)},u=function(t){function o(n){s[t]?i.resolve(s[t].map):n>a?i.reject("could not find map"):e.setTimeout(function(){o(n+100)},100)}t="object"==typeof t?t.id:t,t=t||0;var i=n.defer(),a=2e3;return o(0),i.promise},l=function(e){if(e.map){var t=Object.keys(s).length;s[e.map.id||t]=e}},g=function(e){var t=Object.keys(s).length-1,n=e.map.id||t;if(e.map){for(var o in e.eventListeners){var i=e.eventListeners[o];google.maps.event.removeListener(i)}e.map.controls&&e.map.controls.forEach(function(e){e.clear()})}e.map.heatmapLayers&&Object.keys(e.map.heatmapLayers).forEach(function(t){e.deleteObject("heatmapLayers",e.map.heatmapLayers[t])}),delete s[n]},d=function(e,t){var i=n.defer();return!e||e.match(/^current/i)?o.getCurrentPosition(t).then(function(e){var t=e.coords.latitude,n=e.coords.longitude,o=new google.maps.LatLng(t,n);i.resolve(o)},function(e){i.reject(e)}):a.geocode({address:e}).then(function(e){i.resolve
 (e[0].geometry.location)},function(e){i.reject(e)}),i.promise},m=function(e,t){return function(n){if(n){var o=r("set-"+e),a=i.toOptionValue(n,{key:e});t[o]&&(e.match(/center|position/)&&"string"==typeof a?d(a).then(function(e){t[o](e)}):t[o](a))}}},f=function(e){var t=e.getAttribute("default-style");"true"==t?(e.style.display="block",e.style.height="300px"):("block"!=p(e,"display")&&(e.style.display="block"),p(e,"height").match(/^(0|auto)/)&&(e.style.height="300px"))};angular.module("ngMap").provider("NgMap",function(){var s={};this.setDefaultOptions=function(e){s=e};var p=function(p,v,y,h,M,b,O){return e=p,t=v[0],n=y,o=h,i=M,a=b,r=O,{defaultOptions:s,addMap:l,deleteMap:g,getMap:u,initMap:c,setStyle:f,getGeoLocation:d,observeAndSet:m}};p.$inject=["$window","$document","$q","NavigatorGeolocation","Attr2MapOptions","GeoCoder","camelCaseFilter"],this.$get=p})}(),function(){"use strict";var e,t=function(t,n){n=n||t.getCenter();var o=e.defer(),i=new google.maps.StreetViewService;return i
 .getPanoramaByLocation(n||t.getCenter,100,function(e,t){t===google.maps.StreetViewStatus.OK?o.resolve(e.location.pano):o.resolve(!1)
+}),o.promise},n=function(e,t){var n=new google.maps.StreetViewPanorama(e.getDiv(),{enableCloseButton:!0});n.setPano(t)},o=function(o){return e=o,{getPanorama:t,setPanorama:n}};o.$inject=["$q"],angular.module("ngMap").service("StreetView",o)}(),"ngMap"});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/assets/js/underscore-min_1.8.3.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/assets/js/underscore-min_1.8.3.js b/traffic_portal/app/src/assets/js/underscore-min_1.8.3.js
new file mode 100644
index 0000000..f01025b
--- /dev/null
+++ b/traffic_portal/app/src/assets/js/underscore-min_1.8.3.js
@@ -0,0 +1,6 @@
+//     Underscore.js 1.8.3
+//     http://underscorejs.org
+//     (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+//     Underscore may be freely distributed under the MIT license.
+(function(){function n(n){function t(t,r,e,u,i,o){for(;i>=0&&o>i;i+=n){var a=u?u[i]:i;e=r(e,t[a],a,t)}return e}return function(r,e,u,i){e=b(e,i,4);var o=!k(r)&&m.keys(r),a=(o||r).length,c=n>0?0:a-1;return arguments.length<3&&(u=r[o?o[c]:c],c+=n),t(r,e,u,o,c,a)}}function t(n){return function(t,r,e){r=x(r,e);for(var u=O(t),i=n>0?0:u-1;i>=0&&u>i;i+=n)if(r(t[i],i,t))return i;return-1}}function r(n,t,r){return function(e,u,i){var o=0,a=O(e);if("number"==typeof i)n>0?o=i>=0?i:Math.max(i+a,o):a=i>=0?Math.min(i+1,a):i+a+1;else if(r&&i&&a)return i=r(e,u),e[i]===u?i:-1;if(u!==u)return i=t(l.call(e,o,a),m.isNaN),i>=0?i+o:-1;for(i=n>0?o:a-1;i>=0&&a>i;i+=n)if(e[i]===u)return i;return-1}}function e(n,t){var r=I.length,e=n.constructor,u=m.isFunction(e)&&e.prototype||a,i="constructor";for(m.has(n,i)&&!m.contains(t,i)&&t.push(i);r--;)i=I[r],i in n&&n[i]!==u[i]&&!m.contains(t,i)&&t.push(i)}var u=this,i=u._,o=Array.prototype,a=Object.prototype,c=Function.prototype,f=o.push,l=o.slice,s=a.toString,p=a.h
 asOwnProperty,h=Array.isArray,v=Object.keys,g=c.bind,y=Object.create,d=function(){},m=function(n){return n instanceof m?n:this instanceof m?void(this._wrapped=n):new m(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=m),exports._=m):u._=m,m.VERSION="1.8.3";var b=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}},x=function(n,t,r){return null==n?m.identity:m.isFunction(n)?b(n,t,r):m.isObject(n)?m.matcher(n):m.property(n)};m.iteratee=function(n,t){return x(n,t,1/0)};var _=function(n,t){return function(r){var e=arguments.length;if(2>e||null==r)return r;for(var u=1;e>u;u++)for(var i=arguments[u],o=n(i),a=o.length,c=0;a>c;c++){var f=o[c];t&&r[f]!==void 0||(r[f]=i[f])}return r}}
 ,j=function(n){if(!m.isObject(n))return{};if(y)return y(n);d.prototype=n;var t=new d;return d.prototype=null,t},w=function(n){return function(t){return null==t?void 0:t[n]}},A=Math.pow(2,53)-1,O=w("length"),k=function(n){var t=O(n);return"number"==typeof t&&t>=0&&A>=t};m.each=m.forEach=function(n,t,r){t=b(t,r);var e,u;if(k(n))for(e=0,u=n.length;u>e;e++)t(n[e],e,n);else{var i=m.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},m.map=m.collect=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=Array(u),o=0;u>o;o++){var a=e?e[o]:o;i[o]=t(n[a],a,n)}return i},m.reduce=m.foldl=m.inject=n(1),m.reduceRight=m.foldr=n(-1),m.find=m.detect=function(n,t,r){var e;return e=k(n)?m.findIndex(n,t,r):m.findKey(n,t,r),e!==void 0&&e!==-1?n[e]:void 0},m.filter=m.select=function(n,t,r){var e=[];return t=x(t,r),m.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e},m.reject=function(n,t,r){return m.filter(n,m.negate(x(t)),r)},m.every=m.all=function(n,t,r){t=x(t,r);for(var e=!k(n)
 &&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(!t(n[o],o,n))return!1}return!0},m.some=m.any=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(t(n[o],o,n))return!0}return!1},m.contains=m.includes=m.include=function(n,t,r,e){return k(n)||(n=m.values(n)),("number"!=typeof r||e)&&(r=0),m.indexOf(n,t,r)>=0},m.invoke=function(n,t){var r=l.call(arguments,2),e=m.isFunction(t);return m.map(n,function(n){var u=e?t:n[t];return null==u?u:u.apply(n,r)})},m.pluck=function(n,t){return m.map(n,m.property(t))},m.where=function(n,t){return m.filter(n,m.matcher(t))},m.findWhere=function(n,t){return m.find(n,m.matcher(t))},m.max=function(n,t,r){var e,u,i=-1/0,o=-1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],e>i&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(u>o||u===-1/0&&i===-1/0)&&(i=n,o=u)});return i},m.min=function(n,t,r){var e,u,i=1/0,o=1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var
  a=0,c=n.length;c>a;a++)e=n[a],i>e&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(o>u||1/0===u&&1/0===i)&&(i=n,o=u)});return i},m.shuffle=function(n){for(var t,r=k(n)?n:m.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=m.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},m.sample=function(n,t,r){return null==t||r?(k(n)||(n=m.values(n)),n[m.random(n.length-1)]):m.shuffle(n).slice(0,Math.max(0,t))},m.sortBy=function(n,t,r){return t=x(t,r),m.pluck(m.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=x(r,e),m.each(t,function(e,i){var o=r(e,i,t);n(u,e,o)}),u}};m.groupBy=F(function(n,t,r){m.has(n,r)?n[r].push(t):n[r]=[t]}),m.indexBy=F(function(n,t,r){n[r]=t}),m.countBy=F(function(n,t,r){m.has(n,r)?n[r]++:n[r]=1}),m.toArray=function(n){return n?m.isArray(n)?l.c
 all(n):k(n)?m.map(n,m.identity):m.values(n):[]},m.size=function(n){return null==n?0:k(n)?n.length:m.keys(n).length},m.partition=function(n,t,r){t=x(t,r);var e=[],u=[];return m.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},m.first=m.head=m.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:m.initial(n,n.length-t)},m.initial=function(n,t,r){return l.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},m.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:m.rest(n,Math.max(0,n.length-t))},m.rest=m.tail=m.drop=function(n,t,r){return l.call(n,null==t||r?1:t)},m.compact=function(n){return m.filter(n,m.identity)};var S=function(n,t,r,e){for(var u=[],i=0,o=e||0,a=O(n);a>o;o++){var c=n[o];if(k(c)&&(m.isArray(c)||m.isArguments(c))){t||(c=S(c,t,r));var f=0,l=c.length;for(u.length+=l;l>f;)u[i++]=c[f++]}else r||(u[i++]=c)}return u};m.flatten=function(n,t){return S(n,t,!1)},m.without=function(n){return m.difference(n,l.call(arguments,1))},m.uniq=m.unique=function(n,t,
 r,e){m.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=x(r,e));for(var u=[],i=[],o=0,a=O(n);a>o;o++){var c=n[o],f=r?r(c,o,n):c;t?(o&&i===f||u.push(c),i=f):r?m.contains(i,f)||(i.push(f),u.push(c)):m.contains(u,c)||u.push(c)}return u},m.union=function(){return m.uniq(S(arguments,!0,!0))},m.intersection=function(n){for(var t=[],r=arguments.length,e=0,u=O(n);u>e;e++){var i=n[e];if(!m.contains(t,i)){for(var o=1;r>o&&m.contains(arguments[o],i);o++);o===r&&t.push(i)}}return t},m.difference=function(n){var t=S(arguments,!0,!0,1);return m.filter(n,function(n){return!m.contains(t,n)})},m.zip=function(){return m.unzip(arguments)},m.unzip=function(n){for(var t=n&&m.max(n,O).length||0,r=Array(t),e=0;t>e;e++)r[e]=m.pluck(n,e);return r},m.object=function(n,t){for(var r={},e=0,u=O(n);u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},m.findIndex=t(1),m.findLastIndex=t(-1),m.sortedIndex=function(n,t,r,e){r=x(r,e,1);for(var u=r(t),i=0,o=O(n);o>i;){var a=Math.floor((i+o)/2);r(n[a])<u?i=a+1:o=a}return i},m.i
 ndexOf=r(1,m.findIndex,m.sortedIndex),m.lastIndexOf=r(-1,m.findLastIndex),m.range=function(n,t,r){null==t&&(t=n||0,n=0),r=r||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=Array(e),i=0;e>i;i++,n+=r)u[i]=n;return u};var E=function(n,t,r,e,u){if(!(e instanceof t))return n.apply(r,u);var i=j(n.prototype),o=n.apply(i,u);return m.isObject(o)?o:i};m.bind=function(n,t){if(g&&n.bind===g)return g.apply(n,l.call(arguments,1));if(!m.isFunction(n))throw new TypeError("Bind must be called on a function");var r=l.call(arguments,2),e=function(){return E(n,e,t,this,r.concat(l.call(arguments)))};return e},m.partial=function(n){var t=l.call(arguments,1),r=function(){for(var e=0,u=t.length,i=Array(u),o=0;u>o;o++)i[o]=t[o]===m?arguments[e++]:t[o];for(;e<arguments.length;)i.push(arguments[e++]);return E(n,r,this,this,i)};return r},m.bindAll=function(n){var t,r,e=arguments.length;if(1>=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=m.bind(n[r],n);return n},m.
 memoize=function(n,t){var r=function(e){var u=r.cache,i=""+(t?t.apply(this,arguments):e);return m.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},m.delay=function(n,t){var r=l.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},m.defer=m.partial(m.delay,m,1),m.throttle=function(n,t,r){var e,u,i,o=null,a=0;r||(r={});var c=function(){a=r.leading===!1?0:m.now(),o=null,i=n.apply(e,u),o||(e=u=null)};return function(){var f=m.now();a||r.leading!==!1||(a=f);var l=t-(f-a);return e=this,u=arguments,0>=l||l>t?(o&&(clearTimeout(o),o=null),a=f,i=n.apply(e,u),o||(e=u=null)):o||r.trailing===!1||(o=setTimeout(c,l)),i}},m.debounce=function(n,t,r){var e,u,i,o,a,c=function(){var f=m.now()-o;t>f&&f>=0?e=setTimeout(c,t-f):(e=null,r||(a=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,o=m.now();var f=r&&!e;return e||(e=setTimeout(c,t)),f&&(a=n.apply(i,u),i=u=null),a}},m.wrap=function(n,t){return m.partial(t,n)},m.negate=function(n){return func
 tion(){return!n.apply(this,arguments)}},m.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},m.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},m.before=function(n,t){var r;return function(){return--n>0&&(r=t.apply(this,arguments)),1>=n&&(t=null),r}},m.once=m.partial(m.before,2);var M=!{toString:null}.propertyIsEnumerable("toString"),I=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];m.keys=function(n){if(!m.isObject(n))return[];if(v)return v(n);var t=[];for(var r in n)m.has(n,r)&&t.push(r);return M&&e(n,t),t},m.allKeys=function(n){if(!m.isObject(n))return[];var t=[];for(var r in n)t.push(r);return M&&e(n,t),t},m.values=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},m.mapObject=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=u.length,o={},a=0;i>a;a++)e=u[a],o[e]=t(n[e],e
 ,n);return o},m.pairs=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},m.invert=function(n){for(var t={},r=m.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},m.functions=m.methods=function(n){var t=[];for(var r in n)m.isFunction(n[r])&&t.push(r);return t.sort()},m.extend=_(m.allKeys),m.extendOwn=m.assign=_(m.keys),m.findKey=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=0,o=u.length;o>i;i++)if(e=u[i],t(n[e],e,n))return e},m.pick=function(n,t,r){var e,u,i={},o=n;if(null==o)return i;m.isFunction(t)?(u=m.allKeys(o),e=b(t,r)):(u=S(arguments,!1,!1,1),e=function(n,t,r){return t in r},o=Object(o));for(var a=0,c=u.length;c>a;a++){var f=u[a],l=o[f];e(l,f,o)&&(i[f]=l)}return i},m.omit=function(n,t,r){if(m.isFunction(t))t=m.negate(t);else{var e=m.map(S(arguments,!1,!1,1),String);t=function(n,t){return!m.contains(e,t)}}return m.pick(n,t,r)},m.defaults=_(m.allKeys,!0),m.create=function(n,t){var r=j(n);return t&&m.extendOwn(r,t),r},m.clone=
 function(n){return m.isObject(n)?m.isArray(n)?n.slice():m.extend({},n):n},m.tap=function(n,t){return t(n),n},m.isMatch=function(n,t){var r=m.keys(t),e=r.length;if(null==n)return!e;for(var u=Object(n),i=0;e>i;i++){var o=r[i];if(t[o]!==u[o]||!(o in u))return!1}return!0};var N=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof m&&(n=n._wrapped),t instanceof m&&(t=t._wrapped);var u=s.call(n);if(u!==s.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}var i="[object Array]"===u;if(!i){if("object"!=typeof n||"object"!=typeof t)return!1;var o=n.constructor,a=t.constructor;if(o!==a&&!(m.isFunction(o)&&o instanceof o&&m.isFunction(a)&&a instanceof a)&&"constructor"in n&&"constructor"in t)return!1}r=r||[],e=e||[];for(var c=r.length;c--;)if(r[c]===n)return e[c]===t;if(r.push(n),e.push(t)
 ,i){if(c=n.length,c!==t.length)return!1;for(;c--;)if(!N(n[c],t[c],r,e))return!1}else{var f,l=m.keys(n);if(c=l.length,m.keys(t).length!==c)return!1;for(;c--;)if(f=l[c],!m.has(t,f)||!N(n[f],t[f],r,e))return!1}return r.pop(),e.pop(),!0};m.isEqual=function(n,t){return N(n,t)},m.isEmpty=function(n){return null==n?!0:k(n)&&(m.isArray(n)||m.isString(n)||m.isArguments(n))?0===n.length:0===m.keys(n).length},m.isElement=function(n){return!(!n||1!==n.nodeType)},m.isArray=h||function(n){return"[object Array]"===s.call(n)},m.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},m.each(["Arguments","Function","String","Number","Date","RegExp","Error"],function(n){m["is"+n]=function(t){return s.call(t)==="[object "+n+"]"}}),m.isArguments(arguments)||(m.isArguments=function(n){return m.has(n,"callee")}),"function"!=typeof/./&&"object"!=typeof Int8Array&&(m.isFunction=function(n){return"function"==typeof n||!1}),m.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))}
 ,m.isNaN=function(n){return m.isNumber(n)&&n!==+n},m.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===s.call(n)},m.isNull=function(n){return null===n},m.isUndefined=function(n){return n===void 0},m.has=function(n,t){return null!=n&&p.call(n,t)},m.noConflict=function(){return u._=i,this},m.identity=function(n){return n},m.constant=function(n){return function(){return n}},m.noop=function(){},m.property=w,m.propertyOf=function(n){return null==n?function(){}:function(t){return n[t]}},m.matcher=m.matches=function(n){return n=m.extendOwn({},n),function(t){return m.isMatch(t,n)}},m.times=function(n,t,r){var e=Array(Math.max(0,n));t=b(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},m.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},m.now=Date.now||function(){return(new Date).getTime()};var B={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},T=m.invert(B),R=function(n){var t=function(t){return n[t]},r="(?:"+m.keys(n)
 .join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};m.escape=R(B),m.unescape=R(T),m.result=function(n,t,r){var e=null==n?void 0:n[t];return e===void 0&&(e=r),m.isFunction(e)?e.call(n):e};var q=0;m.uniqueId=function(n){var t=++q+"";return n?n+t:t},m.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var K=/(.)^/,z={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\u2028|\u2029/g,L=function(n){return"\\"+z[n]};m.template=function(n,t,r){!t&&r&&(t=r),t=m.defaults({},t,m.templateSettings);var e=RegExp([(t.escape||K).source,(t.interpolate||K).source,(t.evaluate||K).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,o,a){return i+=n.slice(u,a).replace(D,L),u=a+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":o&&(i+="';\n"+o+"\n__p+='"),t}),i+="';\n",t.variable||(i
 ="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var o=new Function(t.variable||"obj","_",i)}catch(a){throw a.source=i,a}var c=function(n){return o.call(this,n,m)},f=t.variable||"obj";return c.source="function("+f+"){\n"+i+"}",c},m.chain=function(n){var t=m(n);return t._chain=!0,t};var P=function(n,t){return n._chain?m(t).chain():t};m.mixin=function(n){m.each(m.functions(n),function(t){var r=m[t]=n[t];m.prototype[t]=function(){var n=[this._wrapped];return f.apply(n,arguments),P(this,r.apply(m,n))}})},m.mixin(m),m.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=o[n];m.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],P(this,r)}}),m.each(["concat","join","slice"],function(n){var t=o[n];m.prototype[n]=function(){return P(this,t.apply(this._wrapped,arguments))}}),m.prototype.value=f
 unction(){return this._wrapped},m.prototype.valueOf=m.prototype.toJSON=m.prototype.value,m.prototype.toString=function(){return""+this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return m})}).call(this);
+//# sourceMappingURL=underscore-min.map
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/assets/other/collateral.txt
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/assets/other/collateral.txt b/traffic_portal/app/src/assets/other/collateral.txt
deleted file mode 100644
index f402ca7..0000000
--- a/traffic_portal/app/src/assets/other/collateral.txt
+++ /dev/null
@@ -1 +0,0 @@
-This is a document you want to share with your traffic portal users.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/api/ASNService.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/api/ASNService.js b/traffic_portal/app/src/common/api/ASNService.js
new file mode 100644
index 0000000..9cc604a
--- /dev/null
+++ b/traffic_portal/app/src/common/api/ASNService.js
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var ASNService = function(Restangular, locationUtils, messageModel) {
+
+    this.getASNs = function(queryParams) {
+        return Restangular.all('asns').getList(queryParams);
+    };
+
+    this.getASN = function(id) {
+        return Restangular.one("asns", id).get();
+    };
+
+    this.createASN = function(asn) {
+        return Restangular.service('asns').post(asn)
+            .then(
+                function() {
+                    messageModel.setMessages([ { level: 'success', text: 'ASN created' } ], true);
+                    locationUtils.navigateToPath('/admin/asns');
+                },
+                function(fault) {
+                    messageModel.setMessages(fault.data.alerts, false);
+                }
+            );
+    };
+
+    this.updateASN = function(asn) {
+        return asn.put()
+            .then(
+            function() {
+                messageModel.setMessages([ { level: 'success', text: 'ASN updated' } ], false);
+            },
+            function(fault) {
+                messageModel.setMessages(fault.data.alerts, false);
+            }
+        );
+    };
+
+    this.deleteASN = function(id) {
+        return Restangular.one("asns", id).remove()
+            .then(
+            function() {
+                messageModel.setMessages([ { level: 'success', text: 'ASN deleted' } ], true);
+            },
+            function(fault) {
+                messageModel.setMessages(fault.data.alerts, true);
+            }
+        );
+    };
+
+};
+
+ASNService.$inject = ['Restangular', 'locationUtils', 'messageModel'];
+module.exports = ASNService;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/api/AuthService.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/api/AuthService.js b/traffic_portal/app/src/common/api/AuthService.js
index c3fea82..aa8536e 100644
--- a/traffic_portal/app/src/common/api/AuthService.js
+++ b/traffic_portal/app/src/common/api/AuthService.js
@@ -1,82 +1,71 @@
 /*
-
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
  */
 
-var AuthService = function($http, $state, $location, $q, userModel, deliveryServicesModel, messageModel, ENV) {
+var AuthService = function($rootScope, $http, $state, $location, $q, $state, httpService, userModel, messageModel, ENV) {
 
     this.login = function(username, password) {
         userModel.resetUser();
-        deliveryServicesModel.resetDeliveryServices();
-        var promise = $http.post(
-                ENV.apiEndpoint['1.2'] + "user/login", { u: username, p: password })
-            .success(function(result) {
-                var redirect = decodeURIComponent($location.search().redirect);
-                if (redirect !== 'undefined') {
-                    $location.search('redirect', null); // remove the redirect query param
-                    $location.url(redirect);
-                } else {
-                    $location.url('/dashboard');
+        return httpService.post(ENV.api['root'] + 'user/login', { u: username, p: password })
+            .then(
+                function(result) {
+                    $rootScope.$broadcast('authService::login');
+                    var redirect = decodeURIComponent($location.search().redirect);
+                    if (redirect !== 'undefined') {
+                        $location.search('redirect', null); // remove the redirect query param
+                        $location.url(redirect);
+                    } else {
+                        $location.url('/monitor/dashboard');
+                    }
+                },
+                function(fault) {
+                    // do nothing
                 }
-                return result;
-            })
-            .error(function(fault) {
-                return fault;
-            });
-
-        return promise;
+            );
     };
 
     this.tokenLogin = function(token) {
         userModel.resetUser();
-        deliveryServicesModel.resetDeliveryServices();
-        var deferred = $q.defer();
-        $http.post(
-                ENV.apiEndpoint['1.2'] + "user/login/token", { t: token })
-            .success(function(result) {
-                deferred.resolve(result);
-            })
-            .error(function(fault) {
-                deferred.reject(fault);
-            });
-
-        return deferred.promise;
+        return httpService.post(ENV.api['root'] + 'user/login/token', { t: token });
     };
 
     this.logout = function() {
         userModel.resetUser();
-        deliveryServicesModel.resetDeliveryServices();
-        var promise = $http.post(
-                ENV.apiEndpoint['1.2'] + "user/logout")
-            .success(function(result) {
-                if ($state.current.name == 'trafficPortal.public.home.landing') {
-                    messageModel.setMessages(result.alerts, false);
-                } else {
-                    messageModel.setMessages(result.alerts, true);
-                    $state.go('trafficPortal.public.home.landing');
+        httpService.post(ENV.api['root'] + 'user/logout').
+            then(
+                function(result) {
+                    $rootScope.$broadcast('authService::logout');
+                    if ($state.current.name == 'trafficPortal.public.login') {
+                        messageModel.setMessages(result.alerts, false);
+                    } else {
+                        messageModel.setMessages(result.alerts, true);
+                        $state.go('trafficPortal.public.login');
+                    }
+                    return result;
                 }
-                return result;
-            })
-            .error(function(fault) {
-                return fault;
-            });
+        );
+    };
 
-        return promise;
+    this.resetPassword = function(email) {
+        // Todo: api endpoint not implemented yet
     };
 
 };
 
-AuthService.$inject = ['$http', '$state', '$location', '$q', 'userModel', 'deliveryServicesModel', 'messageModel', 'ENV'];
-module.exports = AuthService;
\ No newline at end of file
+AuthService.$inject = ['$rootScope', '$http', '$state', '$location', '$q', '$state', 'httpService', 'userModel', 'messageModel', 'ENV'];
+module.exports = AuthService;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/api/CDNService.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/api/CDNService.js b/traffic_portal/app/src/common/api/CDNService.js
new file mode 100644
index 0000000..e9221d0
--- /dev/null
+++ b/traffic_portal/app/src/common/api/CDNService.js
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var CDNService = function($http, $q, Restangular, locationUtils, messageModel, ENV) {
+
+    this.getCDNs = function(all) {
+        var request = $q.defer();
+
+        $http.get(ENV.api['root'] + "cdns")
+            .then(
+                function(result) {
+                    var response;
+                    if (all) { // there is a CDN called "ALL" that is not really a CDN but you might want it...
+                        response = result.data.response;
+                    } else {
+                        response = _.filter(result.data.response, function(cdn) {
+                            return cdn.name != 'ALL';
+                        });
+                    }
+                    request.resolve(response);
+                },
+                function(fault) {
+                    request.reject();
+                }
+            );
+
+        return request.promise;
+    };
+
+
+    this.getCDN = function(id) {
+        return Restangular.one("cdns", id).get();
+    };
+
+    this.createCDN = function(cdn) {
+        return Restangular.service('cdns').post(cdn)
+            .then(
+                function() {
+                    messageModel.setMessages([ { level: 'success', text: 'CDN created' } ], true);
+                    locationUtils.navigateToPath('/admin/cdns');
+                },
+                function(fault) {
+                    messageModel.setMessages(fault.data.alerts, false);
+                }
+            );
+    };
+
+    this.updateCDN = function(cdn) {
+        return cdn.put()
+            .then(
+                function() {
+                    messageModel.setMessages([ { level: 'success', text: 'CDN updated' } ], false);
+                },
+                function(fault) {
+                    messageModel.setMessages(fault.data.alerts, false);
+                }
+            );
+    };
+
+    this.deleteCDN = function(id) {
+        return Restangular.one("cdns", id).remove()
+            .then(
+                function() {
+                    messageModel.setMessages([ { level: 'success', text: 'CDN deleted' } ], true);
+                },
+                function(fault) {
+                    messageModel.setMessages(fault.data.alerts, true);
+                }
+            );
+    };
+
+    this.queueServerUpdates = function(id) {
+        return Restangular.one("cdns", id).customPOST( { action: "queue"}, "queue_update" )
+            .then(
+                function() {
+                    messageModel.setMessages([ { level: 'success', text: 'Queued CDN server updates' } ], false);
+                },
+                function(fault) {
+                    messageModel.setMessages(fault.data.alerts, false);
+                }
+            );
+    };
+
+    this.clearServerUpdates = function(id) {
+        return Restangular.one("cdns", id).customPOST( { action: "dequeue"}, "queue_update" )
+            .then(
+                function() {
+                    messageModel.setMessages([ { level: 'success', text: 'Cleared CDN server updates' } ], false);
+                },
+                function(fault) {
+                    messageModel.setMessages(fault.data.alerts, false);
+                }
+            );
+    };
+
+    this.getCapacity = function() {
+        var request = $q.defer();
+
+        $http.get(ENV.api['root'] + "cdns/capacity")
+            .then(
+                function(result) {
+                    request.resolve(result.data.response);
+                },
+                function(fault) {
+                    request.reject();
+                }
+            );
+
+        return request.promise;
+    };
+
+    this.getRoutingMethods = function() {
+        var request = $q.defer();
+
+        $http.get(ENV.api['root'] + "cdns/routing")
+            .then(
+                function(result) {
+                    request.resolve(result.data.response);
+                },
+                function(fault) {
+                    request.reject();
+                }
+            );
+
+        return request.promise;
+    };
+
+    this.getCurrentStats = function() {
+        var request = $q.defer();
+
+        $http.get(ENV.api['root'] + "current_stats")
+            .then(
+                function(result) {
+                    request.resolve(result.data.response);
+                },
+                function(fault) {
+                    request.reject();
+                }
+            );
+
+        return request.promise;
+    };
+
+    this.getCurrentSnapshot = function(cdnName) {
+        var request = $q.defer();
+
+        $http.get(ENV.api['root'] + "cdns/" + cdnName + "/snapshot")
+            .then(
+                function(result) {
+                    request.resolve(result.data.response);
+                },
+                function(fault) {
+                    request.reject();
+                }
+            );
+
+        return request.promise;
+    };
+
+    this.getNewSnapshot = function(cdnName) {
+        var request = $q.defer();
+
+        $http.get(ENV.api['root'] + "cdns/" + cdnName + "/snapshot/new")
+            .then(
+                function(result) {
+                    request.resolve(result.data.response);
+                },
+                function(fault) {
+                    request.reject();
+                }
+            );
+
+        return request.promise;
+    };
+
+    this.snapshot = function(cdn) {
+        var request = $q.defer();
+
+        $http.put(ENV.api['root'] + "cdns/" + cdn.id + "/snapshot")
+            .then(
+                function() {
+                    messageModel.setMessages([ { level: 'success', text: 'Snapshot performed' } ], true);
+                    locationUtils.navigateToPath('/admin/cdns/' + cdn.id);
+
+                },
+                function(fault) {
+                    messageModel.setMessages(fault.data.alerts, false);
+                }
+            );
+
+        return request.promise;
+    };
+
+
+
+
+};
+
+CDNService.$inject = ['$http', '$q', 'Restangular', 'locationUtils', 'messageModel', 'ENV'];
+module.exports = CDNService;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/api/CacheGroupParameterService.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/api/CacheGroupParameterService.js b/traffic_portal/app/src/common/api/CacheGroupParameterService.js
new file mode 100644
index 0000000..ee1da69
--- /dev/null
+++ b/traffic_portal/app/src/common/api/CacheGroupParameterService.js
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var CacheGroupParameterService = function(Restangular, messageModel, httpService, ENV) {
+
+	this.getCacheGroupParameters = function(cachegroupId) {
+		return Restangular.one('cachegroups', cachegroupId).getList('parameters')
+	};
+
+	this.unlinkCacheGroupParameter = function(cgId, paramId) {
+		return httpService.delete(ENV.api['root'] + 'cachegroupparameters/' + cgId + '/' + paramId)
+			.then(
+				function() {
+					messageModel.setMessages([ { level: 'success', text: 'Cachegroup and parameter were unlinked.' } ], false);
+				},
+				function(fault) {
+					messageModel.setMessages(fault.data.alerts, true);
+				}
+			);
+	};
+
+	this.linkCacheGroupParameters = function(cgParamMappings) {
+		return Restangular.service('cachegroupparameters').post(cgParamMappings)
+			.then(
+				function() {
+					messageModel.setMessages([ { level: 'success', text: 'Parameters linked to cache group' } ], false);
+				},
+				function(fault) {
+					messageModel.setMessages(fault.data.alerts, false);
+				}
+			);
+	};
+
+};
+
+CacheGroupParameterService.$inject = ['Restangular', 'messageModel', 'httpService', 'ENV'];
+module.exports = CacheGroupParameterService;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/api/CacheGroupService.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/api/CacheGroupService.js b/traffic_portal/app/src/common/api/CacheGroupService.js
new file mode 100644
index 0000000..cec928e
--- /dev/null
+++ b/traffic_portal/app/src/common/api/CacheGroupService.js
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var CacheGroupService = function($http, $q, Restangular, locationUtils, messageModel, ENV) {
+
+    this.getCacheGroups = function(queryParams) {
+        return Restangular.all('cachegroups').getList(queryParams);
+    };
+
+    this.getCacheGroup = function(id) {
+        return Restangular.one("cachegroups", id).get();
+    };
+
+    this.createCacheGroup = function(cacheGroup) {
+        return Restangular.service('cachegroups').post(cacheGroup)
+            .then(
+                function() {
+                    messageModel.setMessages([ { level: 'success', text: 'CacheGroup created' } ], true);
+                    locationUtils.navigateToPath('/configure/cache-groups');
+                },
+                function(fault) {
+                    messageModel.setMessages(fault.data.alerts, false);
+                }
+            );
+    };
+
+    this.updateCacheGroup = function(cacheGroup) {
+        return cacheGroup.put()
+            .then(
+                function() {
+                    messageModel.setMessages([ { level: 'success', text: 'Cache group updated' } ], false);
+                },
+                function(fault) {
+                    messageModel.setMessages(fault.data.alerts, false);
+                }
+            );
+    };
+
+    this.deleteCacheGroup = function(id) {
+        return Restangular.one("cachegroups", id).remove()
+            .then(
+                function() {
+                    messageModel.setMessages([ { level: 'success', text: 'Cache group deleted' } ], true);
+                },
+                function(fault) {
+                    messageModel.setMessages(fault.data.alerts, true);
+                }
+            );
+    };
+
+    this.queueServerUpdates = function(cgId, cdnId) {
+        return Restangular.one("cachegroups", cgId).customPOST( { action: "queue", cdnId: cdnId }, "queue_update" )
+            .then(
+                function() {
+                    messageModel.setMessages([ { level: 'success', text: 'Queued cache group server updates' } ], false);
+                },
+                function(fault) {
+                    messageModel.setMessages(fault.data.alerts, false);
+                }
+            );
+    };
+
+    this.clearServerUpdates = function(cgId, cdnId) {
+        return Restangular.one("cachegroups", cgId).customPOST( { action: "dequeue", cdnId: cdnId}, "queue_update" )
+            .then(
+                function() {
+                    messageModel.setMessages([ { level: 'success', text: 'Cleared cache group server updates' } ], false);
+                },
+                function(fault) {
+                    messageModel.setMessages(fault.data.alerts, false);
+                }
+            );
+    };
+
+    this.getParameterCacheGroups = function(paramId) {
+        // todo: this needs an api: /parameters/:id/cachegroups
+        return Restangular.one('parameters', paramId).getList('cachegroups');
+    };
+
+    this.getCacheGroupHealth = function() {
+        var deferred = $q.defer();
+
+        $http.get(ENV.api['root'] + "cdns/health")
+            .then(
+                function(result) {
+                    deferred.resolve(result.data.response);
+                },
+                function(fault) {
+                    deferred.reject(fault);
+                }
+            );
+
+        return deferred.promise;
+    };
+
+};
+
+CacheGroupService.$inject = ['$http', '$q', 'Restangular', 'locationUtils', 'messageModel', 'ENV'];
+module.exports = CacheGroupService;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/api/CacheStatsService.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/api/CacheStatsService.js b/traffic_portal/app/src/common/api/CacheStatsService.js
new file mode 100644
index 0000000..3bcd97a
--- /dev/null
+++ b/traffic_portal/app/src/common/api/CacheStatsService.js
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var CacheStatsService = function($http, $q, httpService, ENV) {
+
+	this.getBandwidth = function(cdnName, start, end) {
+		var request = $q.defer();
+
+		var url = ENV.api['root'] + "cache_stats",
+			params = { cdnName: cdnName, metricType: 'bandwidth', startDate: start.seconds(00).format(), endDate: end.seconds(00).format()};
+
+		$http.get(url, { params: params })
+			.then(
+				function(result) {
+					request.resolve(result.data.response);
+				},
+				function(fault) {
+					request.reject();
+				}
+			);
+
+		return request.promise;
+	};
+
+	this.getConnections = function(cdnName, start, end) {
+		var request = $q.defer();
+
+		var url = ENV.api['root'] + "cache_stats",
+			params = { cdnName: cdnName, metricType: 'connections', startDate: start.seconds(00).format(), endDate: end.seconds(00).format()};
+
+		$http.get(url, { params: params })
+			.then(
+				function(result) {
+					request.resolve(result.data.response);
+				},
+				function(fault) {
+					request.reject();
+				}
+			);
+
+		return request.promise;
+	};
+
+};
+
+CacheStatsService.$inject = ['$http', '$q', 'httpService', 'ENV'];
+module.exports = CacheStatsService;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/api/ChangeLogService.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/api/ChangeLogService.js b/traffic_portal/app/src/common/api/ChangeLogService.js
new file mode 100644
index 0000000..133438e
--- /dev/null
+++ b/traffic_portal/app/src/common/api/ChangeLogService.js
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var ChangeLogService = function(Restangular, $rootScope, httpService, ENV) {
+
+	this.getNewLogCount = function() {
+		return httpService.get(ENV.api['root'] + 'logs/newcount', { ignoreLoadingBar: true });
+	};
+
+	this.getChangeLogs = function(queryParams) {
+		// broadcast an event that will zero out the change log count immediately
+		$rootScope.$broadcast('changeLogService::getChangeLogs');
+		return Restangular.all('logs').getList(queryParams);
+	};
+
+};
+
+ChangeLogService.$inject = ['Restangular', '$rootScope', 'httpService', 'ENV'];
+module.exports = ChangeLogService;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/api/DeliveryServiceRegexService.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/api/DeliveryServiceRegexService.js b/traffic_portal/app/src/common/api/DeliveryServiceRegexService.js
new file mode 100644
index 0000000..8276a3c
--- /dev/null
+++ b/traffic_portal/app/src/common/api/DeliveryServiceRegexService.js
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var DeliveryServiceRegexService = function(Restangular, locationUtils, messageModel) {
+
+	this.getDeliveryServiceRegexes = function(dsId) {
+		return Restangular.one('deliveryservices', dsId).getList('regexes');
+	};
+
+	this.getDeliveryServiceRegex = function(dsId, regexId) {
+		return Restangular.one('deliveryservices', dsId).one('regexes', regexId).get();
+	};
+
+	this.createDeliveryServiceRegex = function(dsId, regex) {
+		return Restangular.one('deliveryservices', dsId).all('regexes').post(regex)
+			.then(
+				function() {
+					messageModel.setMessages([ { level: 'success', text: 'Regex created' } ], true);
+					locationUtils.navigateToPath('/configure/delivery-services/' + dsId + '/regexes');
+				},
+				function(fault) {
+					messageModel.setMessages(fault.data.alerts, false);
+				}
+			);
+	};
+
+	this.updateDeliveryServiceRegex = function(regex) {
+		return regex.put()
+			.then(
+				function() {
+					messageModel.setMessages([ { level: 'success', text: 'Regex updated' } ], false);
+				},
+				function(fault) {
+					messageModel.setMessages(fault.data.alerts, false);
+				}
+			);
+	};
+
+	this.deleteDeliveryServiceRegex = function(dsId, regexId) {
+		return Restangular.one('deliveryservices', dsId).one('regexes', regexId).remove()
+			.then(
+				function() {
+					messageModel.setMessages([ { level: 'success', text: 'Regex deleted' } ], true);
+				},
+				function(fault) {
+					messageModel.setMessages(fault.data.alerts, true);
+				}
+			);
+	};
+
+};
+
+DeliveryServiceRegexService.$inject = ['Restangular', 'locationUtils', 'messageModel'];
+module.exports = DeliveryServiceRegexService;