Bug 1163265 - Update Pocket code to latest version (May 8th code drop) r=jaws, a=dolske
authorJustin Dolske <dolske@mozilla.com>
Sun, 10 May 2015 17:19:17 -0700
changeset 260450 86e98ffc152b
parent 260449 e7c47480555d
child 260451 f4179577249b
push id784
push userjdolske@mozilla.com
push date2015-05-11 00:19 +0000
treeherdermozilla-release@32b69592b334 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws, dolske
bugs1163265
milestone38.0
Bug 1163265 - Update Pocket code to latest version (May 8th code drop) r=jaws, a=dolske
browser/components/pocket/main.js
browser/components/pocket/panels/css/saved.css
browser/components/pocket/panels/css/signup.css
browser/components/pocket/panels/js/dictionary.js
browser/components/pocket/panels/js/saved.js
browser/components/pocket/panels/js/signup.js
--- a/browser/components/pocket/main.js
+++ b/browser/components/pocket/main.js
@@ -35,39 +35,40 @@
  *
  */
 
 // TODO : Get the toolbar icons from Firefox's build (Nikki needs to give us a red saved icon)
 // TODO : [needs clarificaiton from Fx] Firefox's plan was to hide Pocket from context menus until the user logs in. Now that it's an extension I'm wondering if we still need to do this.
 // TODO : [needs clarificaiton from Fx] Reader mode (might be a something they need to do since it's in html, need to investigate their code)
 // TODO : [needs clarificaiton from Fx] Move prefs within pktApi.s to sqlite or a local file so it's not editable (and is safer)
 // TODO : [nice to have] - Immediately save, buffer the actions in a local queue and send (so it works offline, works like our native extensions)
-// TODO : Remove console.log entries
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm");
 
 var pktUI = (function() {
 
 	// -- Initialization (on startup and new windows) -- //
 	var inited = false;
 	var _currentPanelDidShow;
     var _currentPanelDidHide;
 	var _isHidden = false;
 	var _notificationTimeout;
-    
+
     // Init panel id at 0. The first actual panel id will have the number 1 so
     // in case at some point any panel has the id 0 we know there is something
     // wrong
     var _panelId = 0;
 
-    var prefBranch = Services.prefs.getBranch("browser.pocket.settings.");
+	var prefBranch = Services.prefs.getBranch("browser.pocket.settings.");
 
-    var savePanelWidth = 350;
-    var savePanelHeights = {collapsed: 153, expanded: 272};
+	var overflowMenuWidth = 230;
+	var overflowMenuHeight = 475;
+	var savePanelWidth = 350;
+	var savePanelHeights = {collapsed: 153, expanded: 272};
 
 	/**
      * Initalizes Pocket UI and panels
      */
 	function onLoad() {
 		
 		if (inited)
 			return;
@@ -111,23 +112,16 @@ var pktUI = (function() {
 		
 		// Hide the extension based on certain criteria
 		hideIntegrationIfNeeded();
 		
 		inited = true;
 	}
 	
 	/**
-     * Called when window/chrome is unloaded
-     */
-	function onUnload() {
-	
-	}
-	
-	/**
 	 * Mark all Pocket integration chrome elements as hidden if certain criteria apply (ex: legacy Pocket extension users or unsupported languages)
 	 */
 	function hideIntegrationIfNeeded() {
 		
 		var hideIntegration = false;
 		
 		// Check if the user had the legacy extension the last time we looked
 		if (prefBranch.getBoolPref('hasLegacyExtension')) {
@@ -161,17 +155,19 @@ var pktUI = (function() {
 	
 	
 	// -- Event Handling -- //
     
     /**
      * Event handler when Pocket toolbar button is pressed
      */
     function pocketButtonOnCommand(event) {
-        tryToSaveCurrentPage();
+    
+    	tryToSaveCurrentPage();
+    
     }
     
     function pocketPanelDidShow(event) {
     	if (_currentPanelDidShow) {
     		_currentPanelDidShow(event);
         }
     	
     }
@@ -287,54 +283,75 @@ var pktUI = (function() {
     /**
      * Show the sign-up panel
      */
     function showSignUp() {
         getFirefoxAccountSignedInUser(function(userdata)
         {
             var fxasignedin = (typeof userdata == 'object' && userdata !== null) ? '1' : '0';
             var startheight = 490;
-            if (pktApi.getSignupAB() == 'storyboard')
+            var inOverflowMenu = isInOverflowMenu();
+            
+            if (inOverflowMenu) 
+            {
+            	startheight = overflowMenuHeight;
+            }
+            else if (pktApi.getSignupAB() == 'storyboard')
             {
                 startheight = 460;
                 if (fxasignedin == '1')
                 {
                     startheight = 406;
                 }
             }
             else
             {
                 if (fxasignedin == '1')
                 {
                     startheight = 436;
                 }
             }
-           var panelId = showPanel("chrome://browser/content/pocket/panels/signup.html?pockethost=" + Services.prefs.getCharPref("browser.pocket.site") + "&fxasignedin=" + fxasignedin + "&variant=" + pktApi.getSignupAB(), {
-               onShow: function() {
-                },
-               onHide: panelDidHide,
-               width: 300,
-               height: startheight
-           });
-        });
+            var variant;
+            if (inOverflowMenu)
+            {
+                variant = 'overflow';
+            }
+            else
+            {
+                variant = pktApi.getSignupAB();
+            }
+            var panelId = showPanel("chrome://browser/content/pocket/panels/signup.html?pockethost=" + Services.prefs.getCharPref("browser.pocket.site") + "&fxasignedin=" + fxasignedin + "&variant=" + variant + '&inoverflowmenu=' + inOverflowMenu + "&locale=" + getUILocale(), {
+            		onShow: function() {
+                    },
+        			onHide: panelDidHide,
+            		width: inOverflowMenu ? overflowMenuWidth : 300,
+            		height: startheight
+            	});
+            });
     }
 
     /**
      * Show the logged-out state / sign-up panel
      */
     function saveAndShowConfirmation(url, title) {
 
         // Validate input parameter
         if (typeof url !== 'undefined' && url.startsWith("about:reader?url=")) {
             url = ReaderMode.getOriginalUrl(url);
         }
 
         var isValidURL = (typeof url !== 'undefined' && (url.startsWith("http") || url.startsWith('https')));
 
-        var panelId = showPanel("chrome://browser/content/pocket/panels/saved.html?pockethost=" + Services.prefs.getCharPref("browser.pocket.site") + "&premiumStatus=" + (pktApi.isPremiumUser() ? '1' : '0'), {
+        var inOverflowMenu = isInOverflowMenu();
+        var startheight = pktApi.isPremiumUser() && isValidURL ? savePanelHeights.expanded : savePanelHeights.collapsed;
+        if (inOverflowMenu) {
+        	startheight = overflowMenuHeight;
+        }
+
+    	var panelId = showPanel("chrome://browser/content/pocket/panels/saved.html?pockethost=" + Services.prefs.getCharPref("browser.pocket.site") + "&premiumStatus=" + (pktApi.isPremiumUser() ? '1' : '0') + '&inoverflowmenu='+inOverflowMenu + "&locale=" + getUILocale(), {
     		onShow: function() {
                 var saveLinkMessageId = 'saveLink';
 
                 // Send error message for invalid url
                 if (!isValidURL) {
                     // TODO: Pass key for localized error in error object
                     var error = {
                         message: 'Only links can be saved',
@@ -345,17 +362,18 @@ var pktUI = (function() {
                 }
 
                 // Check online state
                 if (!navigator.onLine) {
                     // TODO: Pass key for localized error in error object
                     var error = {
                         message: 'You must be connected to the Internet in order to save to Pocket. Please connect to the Internet and try again.'
                     };
-                    pktUIMessaging.sendErrorMessageToPanel(panelId, saveLinkMessageId, error);                    return;
+                    pktUIMessaging.sendErrorMessageToPanel(panelId, saveLinkMessageId, error);
+                    return;
                 }
 
                 // Add url
                 var options = {
                     success: function(data, request) {
                         var item = data.item;
                         var successResponse = {
                             status: "success",
@@ -370,39 +388,40 @@ var pktUI = (function() {
                             return;
                         }
 
                         // If there is no error message in the error use a
                         // complete catch-all
                         var errorMessage = error.message || "There was an error when trying to save to Pocket.";
                         var panelError = { message: errorMessage}
 
-                         // Send error message to panel
+                        // Send error message to panel
                         pktUIMessaging.sendErrorMessageToPanel(panelId, saveLinkMessageId, panelError);
                     }
                 }
 
                 // Add title if given
                 if (typeof title !== "undefined") {
                     options.title = title;
                 }
 
                 // Send the link
 				pktApi.addLink(url, options);
 			},
 			onHide: panelDidHide,
-            width: savePanelWidth,
-            height: pktApi.isPremiumUser() && isValidURL ? savePanelHeights.expanded : savePanelHeights.collapsed
+    		width: inOverflowMenu ? overflowMenuWidth : savePanelWidth,
+    		height: startheight
     	});
     }
 
     /**
      * Open a generic panel
      */
     function showPanel(url, options) {
+
         // Add new panel id
         _panelId += 1;
         url += ("&panelId=" + _panelId);
 
         // We don't have to hide and show the panel again if it's already shown
         // as if the user tries to click again on the toolbar button the overlay
         // will close instead of the button will be clicked
     	var iframe = getPanelFrame();
@@ -418,21 +437,20 @@ var pktUI = (function() {
     	// panel.setAttribute('consumeoutsideclicks', false);
     	//
 
     	// For some reason setting onpopupshown and onpopuphidden on the panel directly didn't work, so
     	// do it this hacky way for now
     	_currentPanelDidShow = options.onShow;
     	_currentPanelDidHide = options.onHide;
 
-        resizePanel({
-            width: options.width,
-            height: options.height
-        });
-
+    	resizePanel({
+    		width: options.width,
+    		height: options.height
+    	});
         return _panelId;
     }
 
     /**
      * Resize the panel
      * options = {
      * 	width: ,
      *	height: ,
@@ -447,39 +465,39 @@ var pktUI = (function() {
     	// TODO : Animate the change if given options.animate = true
     	getPanel().sizeTo(options.width, options.height);
     }
 
     /**
      * Called when the signup and saved panel was hidden
      */
     function panelDidHide() {
+        
     }
 
     /**
      * Register all of the messages needed for the panels
      */
     function registerEventMessages() {
     	var iframe = getPanelFrame();
 
     	// Only register the messages once
         var didInitAttributeKey = 'did_init';
         var didInitMessageListener = iframe.getAttribute(didInitAttributeKey);
-        if (typeof didInitMessageListener !== "undefined" && didInitMessageListener == 1) {
+    	if (typeof didInitMessageListener !== "undefined" && didInitMessageListener == 1) {
             return;
         }
-
     	iframe.setAttribute(didInitAttributeKey, 1);
 
 		// When the panel is displayed it generated an event called
 		// "show": we will listen for that event and when it happens,
 		// send our own "show" event to the panel's script, so the
 		// script can prepare the panel for display.
-		var _showMessageId = "show";
-        pktUIMessaging.addMessageListener(_showMessageId, function(panelId, data) {
+        var _showMessageId = "show";
+		pktUIMessaging.addMessageListener(_showMessageId, function(panelId, data) {
 			// Let panel know that it is ready
 			pktUIMessaging.sendMessageToPanel(panelId, _showMessageId);
 		});
 
         // Open a new tab with a given url and activate if
         var _openTabWithUrlMessageId = "openTabWithUrl";
         pktUIMessaging.addMessageListener(_openTabWithUrlMessageId, function(panelId, data) {
 
@@ -490,159 +508,178 @@ var pktUI = (function() {
             }
 
             var url = data.url;
             openTabWithUrl(url, activate);
             pktUIMessaging.sendResponseMessageToPanel(panelId, _openTabWithUrlMessageId, url);
         });
 
 		// Close the panel
-		var _closeMessageId = "close";
-       pktUIMessaging.addMessageListener(_closeMessageId, function(panelId, data) {
+        var _closeMessageId = "close";
+		pktUIMessaging.addMessageListener(_closeMessageId, function(panelId, data) {
 			getPanel().hidePopup();
 		});
 
 		// Send the current url to the panel
-		var _getCurrentURLMessageId = "getCurrentURL";
-       pktUIMessaging.addMessageListener(_getCurrentURLMessageId, function(panelId, data) {
+        var _getCurrentURLMessageId = "getCurrentURL";
+		pktUIMessaging.addMessageListener(_getCurrentURLMessageId, function(panelId, data) {
             pktUIMessaging.sendResponseMessageToPanel(panelId, _getCurrentURLMessageId, getCurrentUrl());
 		});
 
         var _resizePanelMessageId = "resizePanel";
-        pktUIMessaging.addMessageListener(_resizePanelMessageId, function(panelId, data) {
-           resizePanel(data);
+		pktUIMessaging.addMessageListener(_resizePanelMessageId, function(panelId, data) {
+			resizePanel(data);
         });
 
 		// Callback post initialization to tell background script that panel is "ready" for communication.
-       pktUIMessaging.addMessageListener("listenerReady", function(panelId, data) {
+		pktUIMessaging.addMessageListener("listenerReady", function(panelId, data) {
 
-       });
+		});
 
-       pktUIMessaging.addMessageListener("collapseSavePanel", function(panelId, data) {
-           if (!pktApi.isPremiumUser())
-               resizePanel({width:savePanelWidth, height:savePanelHeights.collapsed});
+		pktUIMessaging.addMessageListener("collapseSavePanel", function(panelId, data) {
+			if (!pktApi.isPremiumUser() && !isInOverflowMenu())
+				resizePanel({width:savePanelWidth, height:savePanelHeights.collapsed});
 		});
 
 		pktUIMessaging.addMessageListener("expandSavePanel", function(panelId, data) {
-           resizePanel({width:savePanelWidth, height:savePanelHeights.expanded});
+			if (!isInOverflowMenu())
+				resizePanel({width:savePanelWidth, height:savePanelHeights.expanded});
 		});
 
 		// Ask for recently accessed/used tags for auto complete
 		var _getTagsMessageId = "getTags";
         pktUIMessaging.addMessageListener(_getTagsMessageId, function(panelId, data) {
 			pktApi.getTags(function(tags, usedTags) {
                 pktUIMessaging.sendResponseMessageToPanel(panelId, _getTagsMessageId, {
                     tags: tags,
                     usedTags: usedTags
                 });
 			});
 		});
 
 		// Ask for suggested tags based on passed url
-		var _getSuggestedTagsMessageId = "getSuggestedTags";
-       pktUIMessaging.addMessageListener(_getSuggestedTagsMessageId, function(panelId, data) {
-           pktApi.getSuggestedTagsForURL(data.url, {
+        var _getSuggestedTagsMessageId = "getSuggestedTags";
+		pktUIMessaging.addMessageListener(_getSuggestedTagsMessageId, function(panelId, data) {
+			pktApi.getSuggestedTagsForURL(data.url, {
 				success: function(data, response) {
 					var suggestedTags = data.suggested_tags;
 					var successResponse = {
 						status: "success",
 						value: {
-							suggestedTags : suggestedTags
+							suggestedTags: suggestedTags
 						}
 					}
-					pktUIMessaging.sendResponseMessageToPanel(panelId, _getSuggestedTagsMessageId, successResponse);
+                    pktUIMessaging.sendResponseMessageToPanel(panelId, _getSuggestedTagsMessageId, successResponse);
 				},
 				error: function(error, response) {
-					pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _getSuggestedTagsMessageId, error);
+                    pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _getSuggestedTagsMessageId, error);
 				}
 			})
 		});
 
 		// Pass url and array list of tags, add to existing save item accordingly
-		var _addTagsMessageId = "addTags";
-       pktUIMessaging.addMessageListener(_addTagsMessageId, function(panelId, data) {
-           pktApi.addTagsToURL(data.url, data.tags, {
+        var _addTagsMessageId = "addTags";
+		pktUIMessaging.addMessageListener(_addTagsMessageId, function(panelId, data) {
+			pktApi.addTagsToURL(data.url, data.tags, {
 				success: function(data, response) {
 				    var successResponse = {status: "success"};
                     pktUIMessaging.sendResponseMessageToPanel(panelId, _addTagsMessageId, successResponse);
 				},
 				error: function(error, response) {
-				  pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _addTagsMessageId, error);
+                    pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _addTagsMessageId, error);
 				}
 			});
 		});
 
 		// Based on clicking "remove page" CTA, and passed unique item id, remove the item
-		var _deleteItemMessageId = "deleteItem";
-       pktUIMessaging.addMessageListener(_deleteItemMessageId, function(panelId, data) {
-           pktApi.deleteItem(data.itemId, {
+        var _deleteItemMessageId = "deleteItem";
+		pktUIMessaging.addMessageListener(_deleteItemMessageId, function(panelId, data) {
+			pktApi.deleteItem(data.itemId, {
 				success: function(data, response) {
 				    var successResponse = {status: "success"};
                     pktUIMessaging.sendResponseMessageToPanel(panelId, _deleteItemMessageId, successResponse);
 				},
 				error: function(error, response) {
-					pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _deleteItemMessageId, error);
+				    pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _deleteItemMessageId, error);
 				}
 			})
 		});
 	}
-	
+
 	// -- Browser Navigation -- //
-	
+
 	/**
      * Open a new tab with a given url and notify the iframe panel that it was opened
      */
 
 	function openTabWithUrl(url, activate) {
         var tab = gBrowser.addTab(url);
         if (activate) {
             gBrowser.selectedTab = tab;
         }
 	}
-    
-    
+
+
     // -- Helper Functions -- //
-    
+
     function getCurrentUrl() {
     	return getBrowser().currentURI.spec;
     }
-    
+
     function getCurrentTitle() {
         return getBrowser().contentTitle;
     }
-    
+
     function getPanel() {
         var frame = getPanelFrame();
         var panel = frame;
         while (panel && panel.localName != "panel") {
             panel = panel.parentNode;
         }
     	return panel;
     }
-    
+
     function getPanelFrame() {
     	return document.getElementById('pocket-panel-iframe');
     }
     
+    function isInOverflowMenu() {
+        var frame = getPanelFrame();
+        var view = frame;
+        while (view && view.localName != "panelview") {
+            view = view.parentNode;
+        }
+
+        var isSubview = (view && view.getAttribute("current") == "true");
+        return isSubview;
+    }
+
     function hasLegacyExtension() {
     	return !!document.getElementById('RIL_urlbar_add');
     }
-    
+
     function isHidden() {
     	return _isHidden;
     }
-    
+
     function getFirefoxAccountSignedInUser(callback) {
-       fxAccounts.getSignedInUser().then(userData => {
-           callback(userData);
-       }).then(null, error => {
-           callback();
-       });
+	    fxAccounts.getSignedInUser().then(userData => {
+    		callback(userData);
+    	}).then(null, error => {
+      		callback();
+	    });
     }
     
+    function getUILocale() {
+    	var locale = Cc["@mozilla.org/chrome/chrome-registry;1"].
+             getService(Ci.nsIXULChromeRegistry).
+             getSelectedLocale("browser");
+        return locale;
+    }
+
     /**
      * Toolbar animations
      */
     
     function showPocketAnimation() {
     	
     	// Borrowed from bookmark star animation:
     	// https://dxr.mozilla.org/mozilla-central/source/browser/base/content/browser-places.js#1568
@@ -702,18 +739,18 @@ var pktUI = (function() {
 	      notifier.style.transform = starIconTransform;
 	      dropmarkerNotifier.style.transform = dropmarkerTransform;
 	
 	      let dropmarkerAnimationNode = dropmarkerNotifier.firstChild;
 	      dropmarkerAnimationNode.style.MozImageRegion = dropmarkerStyle.MozImageRegion;
 	      dropmarkerAnimationNode.style.listStyleImage = dropmarkerStyle.listStyleImage;
 	    }
 	
-	    let isInOverflowPanel = button.getAttribute("overflowedItem") == "true";
-	    if (!isInOverflowPanel) {
+	    let isInOverflowMenu = button.getAttribute("overflowedItem") == "true";
+	    if (!isInOverflowMenu) {
 	      notifier.setAttribute("notification", "finish");
 	      button.setAttribute("notification", "finish");
 	      dropmarkerNotifier.setAttribute("notification", "finish");
 	    }
 	
 	    _notificationTimeout = setTimeout( () => {
 	      notifier.removeAttribute("notification");
 	      dropmarkerNotifier.removeAttribute("notification");
@@ -735,19 +772,17 @@ var pktUI = (function() {
     	pocketPanelDidShow: pocketPanelDidShow,
     	pocketPanelDidHide: pocketPanelDidHide,
 
         pocketContextSaveLinkOnCommand,
         pocketContextSavePageOnCommand,
 
         pocketBookmarkBarOpenPocketCommand,
 
-    	tryToSaveUrl: tryToSaveUrl,
-    	
-		isHidden
+    	tryToSaveUrl: tryToSaveUrl
     };
 }());
 
 // -- Communication to Background -- //
 // https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Interaction_between_privileged_and_non-privileged_pages
 var pktUIMessaging = (function() {
 
     /**
--- a/browser/components/pocket/panels/css/saved.css
+++ b/browser/components/pocket/panels/css/saved.css
@@ -6,16 +6,17 @@
  *  Contents:
  *  Global
  *  Loading spinner
  *  Core detail
  *  Tag entry
  *  Recent/suggested tags
  *  Premium upsell
  *  Token input/autocomplete
+ *  Overflow mode
  */
 
 /*=Global
 --------------------------------------------------------------------------------------- */
 .pkt_ext_containersaved {
     background-color: #fbfbfb;
     border-radius: 4px;
     display: block;
@@ -271,17 +272,16 @@
 .pkt_ext_containersaved .pkt_ext_item_actions .pkt_ext_actions_separator {
     border-left: 2px solid #777;
     height: 0.75em;
     margin-top: 0.3em;
     padding: 0;
     width: 10px;
 }
 .pkt_ext_containersaved .pkt_ext_item_actions a {
-    -webkit-font-feature-settings: normal;
     background: transparent;
     color: #0095dd;
     display: block;
     font-feature-settings: normal;
     font-size: 12px;
     font-weight: normal;
     letter-spacing: normal;
     line-height: inherit;
@@ -451,16 +451,17 @@
 }
 
 /*=Recent/suggested tags
 --------------------------------------------------------------------------------------- */
 .pkt_ext_containersaved .pkt_ext_suggestedtag_detail,
 .pkt_ext_containersaved .pkt_ext_suggestedtag_detailshown {
     border-top: 1px solid #c1c1c1;
     bottom: 0;
+    box-sizing: border-box;
     background: #ebebeb;
     clear: both;
     left: 0;
     opacity: 0;
     min-height: 110px;
     position: fixed;
     visibility: hidden;
     width: 100%;
@@ -506,17 +507,18 @@
     display: block;
     margin: 0;
     height: 2em; 
     overflow: hidden;
     padding: 2px 0 0 0;
 }
 .pkt_ext_containersaved .pkt_ext_suggestedtag_detail ul {
     height: auto;
-    margin: 0 2em 0 0;
+    margin: 0;
+    max-height: 4em;
     padding-top: 6px;
 }
 .pkt_ext_containersaved .pkt_ext_recenttag_detail li,
 .pkt_ext_containersaved .pkt_ext_suggestedtag_detail li {
     background: none;
     float: left;
     height: inherit;
     line-height: 1.5; 
@@ -743,8 +745,70 @@
     }
 }
 .pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled .token-input-selected-token {
     background-color: #f7f7f7;
 }
 .pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled .token-input-selected-token span {
     color: #bbb;
 }
+
+/*=Overflow mode
+--------------------------------------------------------------------------------------- */
+.pkt_ext_saved_overflow .pkt_ext_logo {
+    float: none;
+    margin: 0.5em auto 0;
+}
+.pkt_ext_saved_overflow .pkt_ext_initload {
+    top: -8px;
+}
+.pkt_ext_saved_overflow .pkt_ext_loadingspinner {
+    top: 10em;
+}
+.pkt_ext_saved_overflow .pkt_ext_topdetail {
+    float: none;
+    margin: 0 auto;
+    padding: 0 1em;
+}
+.pkt_ext_saved_overflow h2 {
+    margin-bottom: 0.5em;
+    margin-top: 0;
+    text-align: center;
+}
+.pkt_ext_saved_overflow .pkt_ext_item_actions ul {
+    display: inline-block;
+    width: auto;
+}
+.pkt_ext_saved_overflow .pkt_ext_item_actions li {
+    float: none;
+    padding-left: 1em;
+    padding-right: 1em;
+    text-align: center;
+}
+.pkt_ext_saved_overflow .pkt_ext_item_actions .pkt_ext_removeitem,
+.pkt_ext_saved_overflow .pkt_ext_item_actions .pkt_ext_openpocket {
+    float: none;
+    text-align: center;
+    padding-left: 0;
+    padding-right: 0;
+}
+.pkt_ext_saved_overflow .pkt_ext_item_actions .pkt_ext_actions_separator {
+    display: none;
+}
+.pkt_ext_saved_overflow .pkt_ext_tag_detail {
+    margin-top: 0;
+}
+.pkt_ext_saved_overflow .pkt_ext_suggestedtag_detail,
+.pkt_ext_saved_overflow .pkt_ext_suggestedtag_detailshown {
+    top: 14.75em;
+}
+.pkt_ext_saved_overflow .pkt_ext_edit_msg {
+    top: 16em;
+}
+.pkt_ext_container_finalerrorstate.pkt_ext_saved_overflow .pkt_ext_errordetail {
+    box-sizing: border-box;
+    left: 0;
+    padding-left: 1em;
+    padding-right: 1em;
+    text-align: center;
+    top: 8.3em;
+    width: 100%;
+}
--- a/browser/components/pocket/panels/css/signup.css
+++ b/browser/components/pocket/panels/css/signup.css
@@ -3,17 +3,17 @@
  *  Description:
  *  With base elements out of the way, this sets all custom styling for the extension.
  *
  *  Contents:
  *  Global
  *  Core detail
  *  Core detail - storyboard
  *  Buttons
- *  Responsive
+ *  Overflow mode
  */
 
 /*=Global
 --------------------------------------------------------------------------------------- */
 .pkt_ext_containersignup {
     background-color: #ebebeb;
     color: #333;
     display: block;
@@ -121,17 +121,17 @@
     height: 18px;
     margin-top: -9px;
     right: -15px;
     position: absolute;
     text-indent: -9999px;
     width: 18px;
     top: 50%;
 }
-@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) {
+@media (min-resolution: 1.1dppx) {
     .pkt_ext_containersignup .ff_signuphelp {
         background-image: url(../img/signup_help@2x.png);
         background-size: 18px 18px;
     }
 }
 .pkt_ext_containersignup .alreadyhave {
     font-size: 12px;
     max-width: 320px;
@@ -300,8 +300,31 @@
 }
 .pkt_ext_containersignup .signup-btn-firefox .text {
     color: #fff;
 }
 .pkt_ext_containersignup .btn-disabled .text {
     color: #ccc;
     color: rgba(255,255,255,0.6);
 }
+
+/*=Overflow mode
+--------------------------------------------------------------------------------------- */
+.pkt_ext_signup_overflow .pkt_ext_tagline {
+    margin-bottom: 1em;
+    padding: 0 1em;
+}
+.pkt_ext_signup_overflow .pkt_ext_introimg {
+    background-size: 200px 98px;
+    height: 98px;
+    width: 200px;
+}
+.pkt_ext_signup_overflow .signup-btn-firefox,
+.pkt_ext_signup_overflow .signup-btn-email {
+    font-size: 14px;
+    min-width: 12.6em;
+    padding-left: 0.75em;
+    padding-right: 0.75em;
+}
+.pkt_ext_signup_overflow .signup-btn-firefox .text {
+    padding-left: 0;
+    padding-right: 0;
+}
\ No newline at end of file
--- a/browser/components/pocket/panels/js/dictionary.js
+++ b/browser/components/pocket/panels/js/dictionary.js
@@ -1,10 +1,10 @@
 Translations = {};
-Translations.en = 
+Translations.en =
 {
 	addtags: "Add Tags",
 	alreadyhaveacct: "Already a Pocket user?",
 	errorgeneric: "There was an error when trying to save to Pocket.",
 	learnmore: "Learn More",
 	loginnow: "Log in",
 	maxtaglength: "Tags are limited to 25 characters",
 	mustbeconnected: "You must be connected to the Internet in order to save to Pocket. Please check your connection and try again.",
@@ -20,11 +20,131 @@ Translations.en =
 	signupemail: "Sign up with email",
 	signuptosave: "Sign up for Pocket. It’s free.",
 	suggestedtags: "Suggested Tags",
 	tagline: "Save articles and videos from Firefox to view in Pocket on any device, any time.",
 	taglinestory_one: "Click the Pocket Button to save any article, video or page from Firefox.",
 	taglinestory_two: "View in Pocket on any device, any time.",
 	tagssaved: "Tags Added",
 	signinfirefox: "Sign in with Firefox",
-	signupfirefox: "Sign up with Firefox",
+  signupfirefox: "Sign up with Firefox",
 	viewlist: "View List"
-}
\ No newline at end of file
+};
+
+Translations.de =
+{
+  addtags: "Tags hinzufügen",
+  alreadyhaveacct: "Sind Sie bereits Pocket-Nutzer?",
+  continueff: "Mit Firefox fortfahren",
+  error1: "Wir konnten keine vorgeschlagenen Tags für dieses Element finden.",
+  error2: "Tags dürfen höchsten 25 Zeichen lang sein.",
+  error3: "Bitte melden Sie sich bei Pocket an und versuchen Sie es erneut.",
+  learnmore: "Mehr erfahren",
+  loginnow: "Anmelden",
+  onlylinkssaved: "Es können nur Links gespeichert werden",
+  pagenotsaved: "Seite nicht gespeichert",
+  pageremoved: "Seite entfernt",
+  pagesaved: "Bei Pocket gespeichert",
+  processingremove: "Seite wird entfernt…",
+  processingtags: "Tags werden hinzugefügt…",
+  removepage: "Seite entfernen",
+  save: "Speichern",
+  signupemail: "Mit E-Mail registrieren",
+  signuptosave: "Registrieren Sie sich bei Pocket. Das ist kostenlos.",
+  suggestedtags: "Vorgeschlagene Tags",
+  tagline: "Speichern Sie Artikel und Videos aus Firefox bei Pocket, um sie jederzeit und auf jedem Gerät ansehen zu können.",
+  taglinestory_one: "Klicken Sie auf die Pocket-Schaltfläche, um beliebige Artikel, Videos und Seiten aus Firefox zu speichern.",
+  taglinestory_two: "Lesen Sie diese mit Pocket, jederzeit und auf jedem Gerät.",
+  tagssaved: "Tags hinzugefügt",
+  signinfirefox: "Mit Firefox anmelden",
+  signupfirefox: "Mit Firefox registrieren",
+  viewlist: "Liste anzeigen"
+};
+
+Translations.es =
+{
+  addtags: "Añadir etiquetas",
+  alreadyhaveacct: "¿Ya tiene cuenta Pocket?",
+  continueff: "Continuar con Firefox",
+  error1: "No se han encontrado etiquetas sugeridas para este elemento.",
+  error2: "Las etiquetas están limitadas a 25 caracteres.",
+  error3: "Inicie sesión en Pocket y vuelva a intentarlo.",
+  learnmore: "Saber más",
+  loginnow: "Iniciar sesión",
+  onlylinkssaved: "Solo se pueden guardar enlaces",
+  pagenotsaved: "Página no guardada",
+  pageremoved: "Página eliminada",
+  pagesaved: "Guardada en Pocket",
+  processingremove: "Eliminando página…",
+  processingtags: "Añadiendo etiquetas…",
+  removepage: "Eliminar página",
+  save: "Guardar",
+  signupemail: "Regístrese con su correo.",
+  signuptosave: "Regístrese en Pocket. Es gratis.",
+  suggestedtags: "Etiquetas sugeridas",
+  tagline: "Guarde artículos y vídeos desde Firefox en Pocket para verlos en cualquier dispositivo y en cualquier momento.",
+  taglinestory_one: "Pulse el botón Pocket para guardar cualquier artículo, vídeo o página desde Firefox.",
+  taglinestory_two: "Véalo en Pocket en cualquier dispositivo y en cualquier momento.",
+  tagssaved: "Etiquetas añadidas",
+  signinfirefox: "Inicie sesión con Firefox",
+  signupfirefox: "Regístrese con Firefox",
+  viewlist: "Ver lista"
+};
+
+Translations.ja =
+{
+  addtags: "タグを追加",
+  alreadyhaveacct: "アカウントをお持ちですか?",
+  continueff: "Firefox で続行",
+  error1: "この項目に合うタグが見つかりません。",
+  error2: "タグは 25 文字までです。",
+  error3: "Pocket にログインしてやり直してください。",
+  learnmore: "詳細",
+  loginnow: "ログイン",
+  onlylinkssaved: "リンクのみ保存できます",
+  pagenotsaved: "ページを保存できませんでした",
+  pageremoved: "ページを削除しました",
+  pagesaved: "Pocket に保存しました",
+  processingremove: "ページを削除中...",
+  processingtags: "タグを追加中...",
+  removepage: "ページを削除",
+  save: "保存",
+  signupemail: "メールでアカウント登録",
+  signuptosave: "Pocket にアカウント登録してください。無料です。",
+  suggestedtags: "タグ候補",
+  tagline: "Pocket でいつでもどこでも見れるよう、Firefox から記事や動画を保存できます。",
+  taglinestory_one: "Firefox から記事や動画やページを保存するには、Pocket ボタンをクリックしてください。",
+  taglinestory_two: "Pocket でいつでもどこでも見れます。",
+  tagssaved: "タグを追加しました",
+  signinfirefox: "Firefox でログイン",
+  signupfirefox: "Firefox でアカウント登録",
+  viewlist: "マイリストを表示"
+};
+
+Translations.ru =
+{
+  addtags: "Добавить теги",
+  alreadyhaveacct: "Уже используете Pocket?",
+  continueff: "Продолжить через Firefox",
+  error1: "Мы не смогли найти для этого элемента рекомендуемые теги.",
+  error2: "Длина тега не должна превышать 25 символов.",
+  error3: "Пожалуйста, войдите в Pocket, и попробуйте ещё раз.",
+  learnmore: "Узнайте больше",
+  loginnow: "Войдите",
+  onlylinkssaved: "Можно сохранять только ссылки",
+  pagenotsaved: "Страница не сохранена",
+  pageremoved: "Страница удалена",
+  pagesaved: "Сохранено в Pocket",
+  processingremove: "Удаление страницы...",
+  processingtags: "Добавление тегов...",
+  removepage: "Удалить страницу",
+  save: "Сохранить",
+  signupemail: "Регистрация по эл. почте",
+  signuptosave: "Зарегистрируйтесь в Pocket. Это бесплатно.",
+  suggestedtags: "Рекомендуемые теги",
+  tagline: "Сохраняйте статьи и видео из Firefox для просмотра в Pocket на любом устройстве, в любой момент.",
+  taglinestory_one: "Щёлкните по кнопке Pocket, чтобы сохранить любую статью, видео или страницу из Firefox.",
+  taglinestory_two: "Просматривайте их в Pocket на любом устройстве, в любой момент.",
+  tagssaved: "Теги добавлены",
+  signinfirefox: "Войти через Firefox",
+  signupfirefox: "Регистрация через Firefox",
+  viewlist: "Просмотреть список"
+};
\ No newline at end of file
--- a/browser/components/pocket/panels/js/saved.js
+++ b/browser/components/pocket/panels/js/saved.js
@@ -3,26 +3,27 @@ PKT_SAVED_OVERLAY is the view itself and
 It does not contain any logic for saving or communication with the extension or server.
 */
 var PKT_SAVED_OVERLAY = function (options) 
 {
     var myself = this;
     this.inited = false;
     this.active = false;
     this.wrapper = null;
+    this.pockethost = "getpocket.com";
     this.savedItemId = 0;
     this.savedUrl = '';
     this.premiumStatus = false;
     this.panelId = 0;
     this.preventCloseTimerCancel = false;
     this.closeValid = true;
     this.mouseInside = false;
     this.autocloseTimer = null;
+    this.inoverflowmenu = false;
     this.dictJSON = {};
-    // TODO: allow the timer to be editable?
     this.autocloseTiming = 3500;
     this.autocloseTimingFinalState = 2000;
     this.mouseInside = false;
     this.userTags = [];
     this.cxt_suggested_available = 0;
     this.cxt_entered = 0;
     this.cxt_suggested = 0;
     this.cxt_removed = 0;
@@ -50,16 +51,17 @@ var PKT_SAVED_OVERLAY = function (option
     this.fillSuggestedTags = function()
     {
         if (!$('.pkt_ext_suggestedtag_detail').length) 
         {
             myself.suggestedTagsLoaded = true;
             myself.startCloseTimer();
             return;
         }
+
         thePKT_SAVED.sendMessage("getSuggestedTags",
         {
             url: myself.savedUrl || window.location.toString()
         }, function(resp) 
         {
             $('.pkt_ext_suggestedtag_detail').removeClass('pkt_ext_suggestedtag_detail_loading');
             if (resp.status == 'success') 
             {
@@ -247,20 +249,20 @@ var PKT_SAVED_OVERLAY = function (option
             },
             onDelete: function() {
                 myself.checkValidTagSubmit();
                 changestamp = Date.now();
                 myself.showActiveTags();
                 myself.checkPlaceholderStatus();
             },
             onShowDropdown: function() {
-               thePKT_SAVED.sendMessage("expandSavePanel");
+            	thePKT_SAVED.sendMessage("expandSavePanel");
             },
             onHideDropdown: function() {
-               thePKT_SAVED.sendMessage("collapseSavePanel");
+            	thePKT_SAVED.sendMessage("collapseSavePanel");
             }
         });
         $('body').on('keydown',function(e) {
             var key = e.keyCode || e.which;
             if (key == 8) {
                 var selected = $('.token-input-selected-token');
                 if (selected.length) {
                     e.preventDefault();
@@ -304,16 +306,17 @@ var PKT_SAVED_OVERLAY = function (option
             $('.token-input-token').each(function()
             {
                 var text = $.trim($(this).find('p').text());
                 if (text.length)
                 {
                     originaltags.push(text);
                 }
             });
+
             thePKT_SAVED.sendMessage("addTags",
             {
                 url: myself.savedUrl || window.location.toString(),
                 tags: originaltags   
             }, function(resp)
             {
                 if (resp.status == 'success') 
                 {
@@ -331,16 +334,17 @@ var PKT_SAVED_OVERLAY = function (option
             if ($(this).parents('.pkt_ext_item_actions_disabled').length) {
                 e.preventDefault();
                 return;
             }
             if ($(this).hasClass('pkt_ext_removeitem')) {
                 e.preventDefault();
                 myself.disableInput();
                 $('.pkt_ext_containersaved').find('.pkt_ext_detail h2').text(myself.dictJSON.processingremove);
+
                 thePKT_SAVED.sendMessage("deleteItem",
                 {
                     itemId: myself.savedItemId
                 },function(resp) {
                     if (resp.status == 'success') {
                         myself.showStateFinalMsg(myself.dictJSON.pageremoved);
                     }
                     else if (resp.status == 'error') {
@@ -452,17 +456,17 @@ var PKT_SAVED_OVERLAY = function (option
         this.wrapper.find('.pkt_ext_detail h2').text(headline);
         this.wrapper.find('.pkt_ext_detail h3').text(detail);
         this.wrapper.addClass('pkt_ext_container_detailactive pkt_ext_container_finalstate pkt_ext_container_finalerrorstate');
         this.preventCloseTimerCancel = true;
         this.startCloseTimer(myself.autocloseTimingFinalState);
     }
     this.getTranslations = function()
     {
-        var language = window.navigator.language.toLowerCase();
+        var language = this.locale || '';
         this.dictJSON = {};
 
         var dictsuffix = 'en-US';
 
         if (language.indexOf('en') == 0)
         {
             dictsuffix = 'en';
         }
@@ -526,21 +530,25 @@ var PKT_SAVED_OVERLAY = function (option
         {
             dictsuffix = 'ko';
         }
         else if (language.indexOf('pl') == 0)
         {
             dictsuffix = 'pl';
         }
 
-        // TODO: when we add all dictionaries, modify this, but for now hard code to English
-        dictsuffix = 'en';
-
         this.dictJSON = Translations[dictsuffix];
-        
+        if (typeof this.dictJSON !== 'object')
+        {
+            this.dictJSON = Translations['en'];
+        }
+        if (typeof this.dictJSON !== 'object')
+        {
+            this.dictJSON = {};
+        }
     };
 };
 
 PKT_SAVED_OVERLAY.prototype = {
     create : function() 
     {
         if (this.active)
         {
@@ -549,16 +557,22 @@ PKT_SAVED_OVERLAY.prototype = {
         this.active = true;
 
         // set translations
         this.getTranslations();
 
         // set host
         this.dictJSON.pockethost = this.pockethost;
 
+        // extra modifier class for collapsed state
+        if (this.inoverflowmenu)
+        {
+            $('body').addClass('pkt_ext_saved_overflow');
+        }
+
         // Create actual content
         $('body').append(Handlebars.templates.saved_shell(this.dictJSON));
 
         // Add in premium content (if applicable based on premium status)
         this.createPremiumFunctionality();
 
         // Initialize functionality for overlay
         this.wrapper = $('.pkt_ext_containersaved');
@@ -607,16 +621,26 @@ PKT_SAVED.prototype = {
         {
             myself.overlay.premiumStatus = (url[1] == '1');
         }
         var host = window.location.href.match(/pockethost=([\w|\.]*)&?/);
         if (host && host.length > 1)
         {
             myself.overlay.pockethost = host[1];
         }
+        var inoverflowmenu = window.location.href.match(/inoverflowmenu=([\w|\.]*)&?/);
+        if (inoverflowmenu && inoverflowmenu.length > 1)
+        {
+            myself.overlay.inoverflowmenu = (inoverflowmenu[1] == 'true');
+        }
+        var locale = window.location.href.match(/locale=([\w|\.]*)&?/);
+        if (locale && locale.length > 1)
+        {
+            myself.overlay.locale = locale[1].toLowerCase();
+        }
 
         myself.overlay.panelId = pktPanelMessaging.panelIdFromURL(window.location.href);
 
         myself.overlay.create();
 
         // tell back end we're ready
         thePKT_SAVED.sendMessage("show");
 
@@ -633,17 +657,17 @@ PKT_SAVED.prototype = {
                     else
                     {
                         myself.overlay.showStateError(myself.overlay.dictJSON.pagenotsaved,resp.error.message);
                     }
                 }       
                 else
                 {
                     myself.overlay.showStateError(myself.overlay.dictJSON.pagenotsaved,myself.overlay.dictJSON.errorgeneric);
-                }
+                }         
                 return;
             }
 
             myself.overlay.showStateSaved(resp);
         });
 
     }
 }
--- a/browser/components/pocket/panels/js/signup.js
+++ b/browser/components/pocket/panels/js/signup.js
@@ -1,30 +1,28 @@
 /*
 PKT_SIGNUP_OVERLAY is the view itself and contains all of the methods to manipute the overlay and messaging.
 It does not contain any logic for saving or communication with the extension or server.
 */
 var PKT_SIGNUP_OVERLAY = function (options) 
 {
     var myself = this;
-    this.baseHost = "getpocket.com";
-
     this.inited = false;
     this.active = false;
     this.delayedStateSaved = false;
     this.wrapper = null;
     this.variant = window.___PKT__SIGNUP_VARIANT;
     this.tagline = window.___PKT__SIGNUP_TAGLINE || '';
     this.preventCloseTimerCancel = false;
-    // TODO: populate this with actual translations
     this.translations = {};
     this.closeValid = true;
     this.mouseInside = false;
     this.autocloseTimer = null;
     this.variant = "";
+    this.inoverflowmenu = false;
     this.pockethost = "getpocket.com";
     this.fxasignedin = false;
     this.panelId = 0;
     this.dictJSON = {};
     this.initCloseTabEvents = function() {
         $('.btn,.pkt_ext_learnmore,.alreadyhave > a').click(function(e)
         {
             e.preventDefault();
@@ -55,17 +53,17 @@ var PKT_SIGNUP_OVERLAY = function (optio
         {
             return String(s).replace(/[&<>"']/g, function (str) {
                 return sanitizeMap[str];
             });
         }
     };
     this.getTranslations = function()
     {
-        var language = window.navigator.language.toLowerCase();
+        var language = this.locale || '';
         this.dictJSON = {};
 
         var dictsuffix = 'en-US';
 
         if (language.indexOf('en') == 0)
         {
             dictsuffix = 'en';
         }
@@ -129,21 +127,25 @@ var PKT_SIGNUP_OVERLAY = function (optio
         {
             dictsuffix = 'ko';
         }
         else if (language.indexOf('pl') == 0)
         {
             dictsuffix = 'pl';
         }
 
-        // TODO: when we add all dictionaries, modify this, but for now hard code to English
-        dictsuffix = 'en';
-
         this.dictJSON = Translations[dictsuffix];
-        
+        if (typeof this.dictJSON !== 'object')
+        {
+            this.dictJSON = Translations['en'];
+        }
+        if (typeof this.dictJSON !== 'object')
+        {
+            this.dictJSON = {};
+        }
     };
 };
 
 PKT_SIGNUP_OVERLAY.prototype = {
     create : function() 
     {
         var myself = this;
 
@@ -157,41 +159,58 @@ PKT_SIGNUP_OVERLAY.prototype = {
         {
             this.fxasignedin = (fxasignedin[1] == '1');
         }
         var host = window.location.href.match(/pockethost=([\w|\.]*)&?/);
         if (host && host.length > 1)
         {
             this.pockethost = host[1];
         }
+        var inoverflowmenu = window.location.href.match(/inoverflowmenu=([\w|\.]*)&?/);
+        if (inoverflowmenu && inoverflowmenu.length > 1)
+        {
+            this.inoverflowmenu = (inoverflowmenu[1] == 'true');
+        }
+        var locale = window.location.href.match(/locale=([\w|\.]*)&?/);
+        if (locale && locale.length > 1)
+        {
+           this.locale = locale[1].toLowerCase();
+        }
 
         this.panelId = pktPanelMessaging.panelIdFromURL(window.location.href);
 
         if (this.active)
         {
             return;
         }
         this.active = true;
 
         // set translations
         this.getTranslations();
         this.dictJSON.fxasignedin = this.fxasignedin ? 1 : 0;
         this.dictJSON.variant = (this.variant ? this.variant : 'undefined');
         this.dictJSON.pockethost = this.pockethost;
 
+        // extra modifier class for collapsed state
+        if (this.inoverflowmenu)
+        {
+            $('body').addClass('pkt_ext_signup_overflow');
+        }
+
         // Create actual content
         if (this.variant == 'storyboard')
         {
             $('body').append(Handlebars.templates.signupstoryboard_shell(this.dictJSON));
         }
         else
         {
             $('body').append(Handlebars.templates.signup_shell(this.dictJSON));
         }
 
+
         // tell background we're ready
         thePKT_SIGNUP.sendMessage("show");
 
         // close events
         this.initCloseTabEvents();
     }
 };