diff --git a/build.py b/build.py
index 8cf81b1e..7bd5606b 100755
--- a/build.py
+++ b/build.py
@@ -225,7 +225,7 @@ if buildMobile:
     except:
         pass
     shutil.rmtree("mobile/assets/plugins")
-    shutil.copytree(os.path.join(outDir,"plugins"), "mobile/assets/plugins", ignore=shutil.ignore_patterns('*.meta.js', 'force-https*'))
+    shutil.copytree(os.path.join(outDir,"plugins"), "mobile/assets/plugins", ignore=shutil.ignore_patterns('*.meta.js', 'force-https*', 'privacy-view*'))
 
 
     if buildMobile != 'copyonly':
diff --git a/code/boot.js b/code/boot.js
index 09d56bf3..d38a30b2 100644
--- a/code/boot.js
+++ b/code/boot.js
@@ -139,10 +139,10 @@ window.setupMap = function() {
   //their usage policy has no limits (except required notification above 4000 tiles/sec - we're perhaps at 50 tiles/sec based on CloudMade stats)
   var mqSubdomains = [ 'otile1','otile2', 'otile3', 'otile4' ];
   var mqTileUrlPrefix = window.location.protocol !== 'https:' ? 'http://{s}.mqcdn.com' : 'https://{s}-s.mqcdn.com';
-  var mqMapOpt = {attribution: osmAttribution+', Tiles Courtesy of MapQuest', maxZoom: 18, detectRetena: true, subdomains: mqSubdomains};
+  var mqMapOpt = {attribution: osmAttribution+', Tiles Courtesy of MapQuest', maxZoom: 18, subdomains: mqSubdomains};
   var mqMap = new L.TileLayer(mqTileUrlPrefix+'/tiles/1.0.0/map/{z}/{x}/{y}.jpg',mqMapOpt);
   //MapQuest satellite coverage outside of the US is rather limited - so not really worth having as we have google as an option
-  //var mqSatOpt = {attribution: 'Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency', mazZoom: 18, detectRetena: true, subdomains: mqSubdomains};
+  //var mqSatOpt = {attribution: 'Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency', mazZoom: 18, subdomains: mqSubdomains};
   //var mqSat = new L.TileLayer('http://{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg',mqSatOpt);
 
   var views = [
@@ -203,7 +203,8 @@ window.setupMap = function() {
   // layers. This likely leads to broken layer selection because the
   // views/cookie order does not match the layer chooser order.
   try {
-    map.addLayer(views[readCookie('ingress.intelmap.type')]);
+    convertCookieToLocalStorage('ingress.intelmap.type');
+    map.addLayer(views[localStorage['ingress.intelmap.type']]);
   } catch(e) { map.addLayer(views[0]); }
 
   map.attributionControl.setPrefix('');
@@ -228,7 +229,7 @@ window.setupMap = function() {
 
   map.on('baselayerchange', function () {
     var selInd = $('[name=leaflet-base-layers]:checked').parent().index();
-    writeCookie('ingress.intelmap.type', selInd);
+    localStorage['ingress.intelmap.type']=selInd;
   });
 
   // map update status handling
@@ -405,7 +406,13 @@ function boot() {
   $('#sidebar').show();
 
   if(window.bootPlugins)
-    $.each(window.bootPlugins, function(ind, ref) { ref(); });
+    $.each(window.bootPlugins, function(ind, ref) {
+      try {
+        ref();
+      } catch(err) {
+        console.log("error starting plugin: index "+ind+", error: "+err);
+      }
+    });
 
   window.runOnSmartphonesAfterBoot();
 
@@ -413,6 +420,7 @@ function boot() {
   setTimeout('window.map.invalidateSize(false);', 500);
 
   window.iitcLoaded = true;
+  window.runHooks('iitcLoaded');
 }
 
 // this is the minified load.js script that allows us to easily load
diff --git a/code/geosearch.js b/code/geosearch.js
index 8204b646..7bd7d1ee 100644
--- a/code/geosearch.js
+++ b/code/geosearch.js
@@ -24,6 +24,6 @@ window.setupGeosearch = function() {
     e.preventDefault();
   });
   $('#geosearchwrapper img').click(function(){
-    map.locate({setView : true});;
+    map.locate({setView : true, maxZoom: 13});;
   });
 }
diff --git a/code/hooks.js b/code/hooks.js
index 5b4eb167..35fef550 100644
--- a/code/hooks.js
+++ b/code/hooks.js
@@ -53,14 +53,14 @@
 //              set reached to true.
 // requestFinished: called after each request finished. Argument is
 //              {success: boolean} indicated the request success or fail.
-
+// iitcLoaded: called after IITC and all plugins loaded
 
 
 window._hooks = {}
 window.VALID_HOOKS = ['portalAdded', 'portalDetailsUpdated',
   'publicChatDataAvailable', 'factionChatDataAvailable', 'portalDataLoaded',
   'beforePortalReRender', 'checkRenderLimit', 'requestFinished', 'nicknameClicked',
-  'geoSearch'];
+  'geoSearch', 'iitcLoaded'];
 
 window.runHooks = function(event, data) {
   if(VALID_HOOKS.indexOf(event) === -1) throw('Unknown event type: ' + event);
diff --git a/code/map_data.js b/code/map_data.js
index dd5c2636..2c956a95 100644
--- a/code/map_data.js
+++ b/code/map_data.js
@@ -173,16 +173,17 @@ window.handlePortalsRender = function(portals) {
   // Preserve selectedPortal because it will get lost on re-rendering
   // the portal
   var oldSelectedPortal = selectedPortal;
-
   runHooks('portalDataLoaded', {portals : portals});
   $.each(portals, function(ind, portal) {
     //~ if(selectedPortal === portal[0]) portalUpdateAvailable = true;
-    if(urlPortal && portal[0] === urlPortal) portalInUrlAvailable = true;
     if(urlPortalLL && urlPortalLL[0] === portal[2].locationE6.latE6/1E6 && urlPortalLL[1] === portal[2].locationE6.lngE6/1E6) {
       urlPortal = portal[0];
       portalInUrlAvailable = true;
       urlPortalLL = null;
     }
+    if(window.portals[portal[0]]) {
+      highlightPortal(window.portals[portal[0]]);
+    }
     renderPortal(portal);
   });
 
@@ -285,7 +286,7 @@ window.renderPortal = function(ent) {
   // do nothing if portal did not change
   var layerGroup = portalsLayers[parseInt(portalLevel)];
   var old = findEntityInLeaflet(layerGroup, window.portals, ent[0]);
-  if(old) {
+  if(!changing_highlighters && old) {
     var oo = old.options;
 
     // Default checks to see if a portal needs to be re-rendered
@@ -333,6 +334,7 @@ window.renderPortal = function(ent) {
     clickable: true,
     level: portalLevel,
     team: team,
+    ent: ent,
     details: ent[2],
     guid: ent[0]});
 
@@ -370,7 +372,7 @@ window.renderPortal = function(ent) {
   });
 
   window.renderResonators(ent, null);
-
+  highlightPortal(p);
   window.runHooks('portalAdded', {portal: p});
   p.addTo(layerGroup);
 }
diff --git a/code/panes.js b/code/panes.js
index 8da89133..99ffdc59 100644
--- a/code/panes.js
+++ b/code/panes.js
@@ -1,6 +1,7 @@
 // created to start cleaning up "window" interaction
 //
 window.show = function(id) {
+        window.hideall();
         switch(id) {
                 case 'full':
                         window.chat.show('full');
@@ -19,6 +20,7 @@ window.show = function(id) {
                         break;
                 case 'map':
                         window.smartphone.mapButton.click();
+                        $('#portal_highlight_select').show();
                         break;
                 case 'info':
                         window.smartphone.sideButton.click();
@@ -28,3 +30,8 @@ window.show = function(id) {
                         break;
         }
 }
+
+window.hideall = function() {
+    $('#chatcontrols, #chat, #chatinput, #sidebartoggle, #scrollwrapper, #updatestatus, #portal_highlight_select').hide();
+    $('#map').css('visibility', 'hidden');
+}
diff --git a/code/portal_detail_display.js b/code/portal_detail_display.js
index ed2e3e8d..d1d69220 100644
--- a/code/portal_detail_display.js
+++ b/code/portal_detail_display.js
@@ -49,7 +49,7 @@ window.renderPortalDetails = function(guid) {
   var lng = d.locationE6.lngE6/1E6;
   var perma = '/intel?ll='+lat+','+lng+'&z=17&pll='+lat+','+lng;
   var imgTitle = 'title="'+getPortalDescriptionFromDetails(d)+'\n\nClick to show full image."';
-  var poslinks = 'window.showPortalPosLinks('+lat+','+lng+',\'' + d.portalV2.descriptiveText.TITLE + '\')';
+  var poslinks = 'window.showPortalPosLinks('+lat+','+lng+',\''+escapeJavascriptString(d.portalV2.descriptiveText.TITLE)+'\')';
 
   $('#portaldetails')
     .attr('class', TEAM_TO_CSS[getTeam(d)])
diff --git a/code/portal_highlighter.js b/code/portal_highlighter.js
new file mode 100644
index 00000000..05bfd911
--- /dev/null
+++ b/code/portal_highlighter.js
@@ -0,0 +1,64 @@
+// Portal Highlighter //////////////////////////////////////////////////////////
+// these functions handle portal highlighters
+
+
+window._highlighters = null;
+window._current_highlighter = localStorage.portal_highlighter;
+window.changing_highlighters = false;
+window._no_highlighter = 'No Highlights';
+
+window.addPortalHighlighter = function(name, callback) {
+  if(_highlighters === null) {
+    _highlighters = {};
+  }
+  _highlighters[name] = callback;
+  if(localStorage.portal_highlighter === undefined) {
+    _current_highlighter = name;
+    localStorage.portal_highlighter = name;
+  }
+  portalHighlighterControl();
+}
+
+window.portalHighlighterControl = function() {
+  if(_highlighters !== null) {
+    if($('#portal_highlight_select').length === 0) {
+      $("body").append("");
+    }
+    $("#portal_highlight_select").html('');
+    $("#portal_highlight_select").append($("