diff --git a/browser/base/resources/content/browser.js b/browser/base/resources/content/browser.js index 2e250a0817ea..5622e3ea75bc 100644 --- a/browser/base/resources/content/browser.js +++ b/browser/base/resources/content/browser.js @@ -134,6 +134,178 @@ function removePrefListener(observer) } } +function ZoomManager() { + // factorAnchor starts on factorOther + this.factorOther = parseInt(gNavigatorBundle.getString("valueOther")); + this.factorAnchor = this.factorOther; +} + +ZoomManager.prototype = { + instance : null, + + getInstance : function() { + if (!ZoomManager.prototype.instance) + ZoomManager.prototype.instance = new ZoomManager(); + + return ZoomManager.prototype.instance; + }, + + MIN : 1, + MAX : 2000, + + zoomFactorsString : "", // cache + zoomFactors : null, + + factorOther : 300, + factorAnchor : 300, + steps : 0, + + get textZoom() { + var currentZoom; + try { + currentZoom = Math.round(getMarkupDocumentViewer().textZoom * 100); + if (this.indexOf(currentZoom) == -1) { + if (currentZoom != this.factorOther) { + this.factorOther = currentZoom; + this.factorAnchor = this.factorOther; + } + } + } catch (e) { + currentZoom = 100; + } + return currentZoom; + }, + + set textZoom(aZoom) { + if (aZoom < this.MIN || aZoom > this.MAX) + throw Components.results.NS_ERROR_INVALID_ARG; + + getMarkupDocumentViewer().textZoom = aZoom / 100; + }, + + enlarge : function() { + this.jump(1); + }, + + reduce : function() { + this.jump(-1); + }, + + reset : function() { + this.textZoom = 100; + }, + + getZoomFactors : function() { + this.ensureZoomFactors(); + + return this.zoomFactors; + }, + + indexOf : function(aZoom) { + this.ensureZoomFactors(); + + var index = -1; + if (this.isZoomInRange(aZoom)) { + index = this.zoomFactors.length - 1; + while (index >= 0 && this.zoomFactors[index] != aZoom) + --index; + } + + return index; + }, + + /***** internal helper functions below here *****/ + + ensureZoomFactors : function() { + var zoomFactorsString = gNavigatorBundle.getString("values"); + if (this.zoomFactorsString != zoomFactorsString) { + this.zoomFactorsString = zoomFactorsString; + this.zoomFactors = zoomFactorsString.split(","); + for (var i = 0; i= 0 && aLevel < this.zoomFactors.length); + }, + + isZoomInRange : function(aZoom) { + return (aZoom >= this.zoomFactors[0] && aZoom <= this.zoomFactors[this.zoomFactors.length - 1]); + }, + + jump : function(aDirection) { + if (aDirection != -1 && aDirection != 1) + throw Components.results.NS_ERROR_INVALID_ARG; + + this.ensureZoomFactors(); + + var currentZoom = this.textZoom; + var insertIndex = -1; + var stepFactor = parseFloat(gNavigatorBundle.getString("stepFactor")); + + // temporarily add factorOther to list + if (this.isZoomInRange(this.factorOther)) { + insertIndex = 0; + while (this.zoomFactors[insertIndex] < this.factorOther) + ++insertIndex; + + if (this.zoomFactors[insertIndex] != this.factorOther) + this.zoomFactors.splice(insertIndex, 0, this.factorOther); + } + + var factor; + var done = false; + + if (this.isZoomInRange(currentZoom)) { + var index = this.indexOf(currentZoom); + if (aDirection == -1 && index == 0 || + aDirection == 1 && index == this.zoomFactors.length - 1) { + this.steps = 0; + this.factorAnchor = this.zoomFactors[index]; + } else { + factor = this.zoomFactors[index + aDirection]; + done = true; + } + } + + if (!done) { + this.steps += aDirection; + factor = this.factorAnchor * Math.pow(stepFactor, this.steps); + if (factor < this.MIN || factor > this.MAX) { + this.steps -= aDirection; + factor = this.factorAnchor * Math.pow(stepFactor, this.steps); + } + factor = Math.round(factor); + if (this.isZoomInRange(factor)) + factor = this.snap(factor); + else + this.factorOther = factor; + } + + if (insertIndex != -1) + this.zoomFactors.splice(insertIndex, 1); + + this.textZoom = factor; + }, + + snap : function(aZoom) { + if (this.isZoomInRange(aZoom)) { + var level = 0; + while (this.zoomFactors[level + 1] < aZoom) + ++level; + + // if aZoom closer to [level + 1] than [level], snap to [level + 1] + if ((this.zoomFactors[level + 1] - aZoom) < (aZoom - this.zoomFactors[level])) + ++level; + + aZoom = this.zoomFactors[level]; + } + + return aZoom; + } +} + /** * We can avoid adding multiple load event listeners and save some time by adding * one listener that calls all real handlers. @@ -276,6 +448,8 @@ function Startup() gBrowser = document.getElementById("content"); gURLBar = document.getElementById("urlbar"); + + registerZoomManager(); SetPageProxyState("invalid", null); @@ -1567,3 +1741,87 @@ function displayPageInfo() "dialog=no", null, "securityTab"); } +/** Document Zoom Management Code + * + * To use this, you'll need to have a + * and a getMarkupDocumentViewer() function which returns a + * nsIMarkupDocumentViewer. + * + **/ + +function registerZoomManager() +{ + var textZoomMenu = document.getElementById("menu_textZoom"); + var zoom = ZoomManager.prototype.getInstance(); + + var parentMenu = textZoomMenu.parentNode; + parentMenu.addEventListener("popupshowing", updateViewMenu, false); + + var insertBefore = document.getElementById("menu_textZoomInsertBefore"); + var popup = insertBefore.parentNode; + var accessKeys = gNavigatorBundle.getString("accessKeys").split(","); + var zoomFactors = zoom.getZoomFactors(); + for (var i = 0; i < zoomFactors.length; ++i) { + var menuItem = document.createElement("menuitem"); + menuItem.setAttribute("type", "radio"); + menuItem.setAttribute("name", "textZoom"); + + var label; + if (zoomFactors[i] == 100) + label = gNavigatorBundle.getString("labelOriginal"); + else + label = gNavigatorBundle.getString("label"); + + menuItem.setAttribute("label", label.replace(/%zoom%/, zoomFactors[i])); + menuItem.setAttribute("accesskey", accessKeys[i]); + menuItem.setAttribute("oncommand", "ZoomManager.prototype.getInstance().textZoom = this.value;"); + menuItem.setAttribute("value", zoomFactors[i]); + popup.insertBefore(menuItem, insertBefore); + } +} + +function updateViewMenu() +{ + var zoom = ZoomManager.prototype.getInstance(); + + var textZoomMenu = document.getElementById("menu_textZoom"); + var menuLabel = gNavigatorBundle.getString("menuLabel").replace(/%zoom%/, zoom.textZoom); + textZoomMenu.setAttribute("label", menuLabel); +} + +function updateTextZoomMenu() +{ + var zoom = ZoomManager.prototype.getInstance(); + + var currentZoom = zoom.textZoom; + + var textZoomOther = document.getElementById("menu_textZoomOther"); + var label = gNavigatorBundle.getString("labelOther"); + textZoomOther.setAttribute("label", label.replace(/%zoom%/, zoom.factorOther)); + textZoomOther.setAttribute("value", zoom.factorOther); + + var popup = document.getElementById("menu_textZoomPopup"); + var item = popup.firstChild; + while (item) { + if (item.getAttribute("name") == "textZoom") { + if (item.getAttribute("value") == currentZoom) + item.setAttribute("checked","true"); + else + item.removeAttribute("checked"); + } + item = item.nextSibling; + } +} + +function setTextZoomOther() +{ + var zoom = ZoomManager.prototype.getInstance(); + + // open dialog and ask for new value + var o = {value: zoom.factorOther, zoomMin: zoom.MIN, zoomMax: zoom.MAX}; + window.openDialog("chrome://communicator/content/askViewZoom.xul", "AskViewZoom", "chrome,modal,titlebar", o); + if (o.zoomOK) + zoom.textZoom = o.value; +} + + diff --git a/browser/base/resources/content/navigatorOverlay.xul b/browser/base/resources/content/navigatorOverlay.xul index de0eaf87abde..e7a894dfc219 100644 --- a/browser/base/resources/content/navigatorOverlay.xul +++ b/browser/base/resources/content/navigatorOverlay.xul @@ -25,7 +25,6 @@ - @@ -41,8 +40,6 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - @@ -84,7 +81,11 @@ - + + + + + @@ -135,7 +136,9 @@ - + + + @@ -213,7 +216,15 @@ - + + + + + + + + + diff --git a/browser/base/resources/locale/navigator.dtd b/browser/base/resources/locale/navigator.dtd index 9b537e0f7c21..0b56739519ab 100644 --- a/browser/base/resources/locale/navigator.dtd +++ b/browser/base/resources/locale/navigator.dtd @@ -158,4 +158,18 @@ + + + + + + + + + + + + + + diff --git a/browser/base/resources/locale/navigator.properties b/browser/base/resources/locale/navigator.properties index 3ed2d5827bd3..158f1aeddfac 100644 --- a/browser/base/resources/locale/navigator.properties +++ b/browser/base/resources/locale/navigator.properties @@ -59,4 +59,30 @@ showskinsdescription=true # "Seach " + search_engine_name + " for " + user_input # e.g. "Search Google for abc" # DO NOT change the %S order when translate, the first %S must be the search engine name. -searchFor=Search %S for "%S" \ No newline at end of file +searchFor=Search %S for "%S" + +# font size submenu +# +# don't translate %zoom% + +menuLabel=Text Zoom (%zoom% %) +label=%zoom% % +labelOriginal=%zoom% % (Original Size) +labelOther=Other (%zoom% %) ... + +# {values} must be greater than 0, include 100 and be in natural order +# {accessKeys} correspond to {values}, where "z" matches the z in +# "Original size" in {labelOriginal} + +values=50,75,90,100,120,150,200 +accessKeys=5,7,9,z,1,0,2 + +# {valueOther} must be greater than the largest in values + +valueOther=300 + +# {stepFactor} is the factor with which the zoom changes when you're +# below the lowest or above the highest value in {values} + +stepFactor=1.5 +