script.js revision 33578eaf140b8eeb5dad6e0c015b2e204054a86b
1f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang/* 2f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Copyright (C) 2012 Google Inc. 3f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Licensed to The Android Open Source Project. 4f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 5f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Licensed under the Apache License, Version 2.0 (the "License"); 6f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * you may not use this file except in compliance with the License. 7f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * You may obtain a copy of the License at 8f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 9f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * http://www.apache.org/licenses/LICENSE-2.0 10f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 11f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Unless required by applicable law or agreed to in writing, software 12f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * distributed under the License is distributed on an "AS IS" BASIS, 13f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * See the License for the specific language governing permissions and 15f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * limitations under the License. 16f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang */ 17f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 183233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huangvar BLOCKED_SRC_ATTR = "blocked-src"; 19f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 2063b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang// the set of Elements currently scheduled for processing in handleAllImageLoads 2163b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang// this is an Array, but we treat it like a Set and only insert unique items 2263b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huangvar gImageLoadElements = []; 2363b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang 2402f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huangvar gScaleInfo; 2502f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 26f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang/** 273a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang * Only revert transforms that do an imperfect job of shrinking content if they fail 283a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang * to shrink by this much. Expressed as a ratio of: 293a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang * (original width difference : width difference after transforms); 303a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang */ 310180f27c0998623b702274048b49cd4bec536cf1Andy HuangTRANSFORM_MINIMUM_EFFECTIVE_RATIO = 0.7; 323a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang 333a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang/** 34f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Returns the page offset of an element. 35f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 36f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * @param {Element} element The element to return the page offset for. 37f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * @return {left: number, top: number} A tuple including a left and top value representing 38f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * the page offset of the element. 39f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang */ 40f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangfunction getTotalOffset(el) { 41f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang var result = { 42f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang left: 0, 43f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang top: 0 44f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang }; 45f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang var parent = el; 46f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 47f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang while (parent) { 48f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang result.left += parent.offsetLeft; 49f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang result.top += parent.offsetTop; 50f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang parent = parent.offsetParent; 51f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 52f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 53f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang return result; 54f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang} 55f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 56ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang/** 57ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang * Walks up the DOM starting at a given element, and returns an element that has the 58ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang * specified class name or null. 59ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang */ 60ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huangfunction up(el, className) { 61ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang var parent = el; 62ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang while (parent) { 63ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang if (parent.classList && parent.classList.contains(className)) { 64ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang break; 65ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang } 66ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang parent = parent.parentNode; 67ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang } 68ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang return parent || null; 69ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang} 70ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang 7102f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huangfunction getCachedValue(div, property, attrName) { 7202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var value; 7302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang if (div.hasAttribute(attrName)) { 7402f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang value = div.getAttribute(attrName); 7502f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang } else { 7602f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang value = div[property]; 7702f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang div.setAttribute(attrName, value); 7802f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang } 7902f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang return value; 8002f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang} 8102f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 82df0d91c5fa064e93b647fd62fe2446791664003fAndy Huangfunction onToggleClick(e) { 83df0d91c5fa064e93b647fd62fe2446791664003fAndy Huang toggleQuotedText(e.target); 84df0d91c5fa064e93b647fd62fe2446791664003fAndy Huang measurePositions(); 85df0d91c5fa064e93b647fd62fe2446791664003fAndy Huang} 86df0d91c5fa064e93b647fd62fe2446791664003fAndy Huang 87df0d91c5fa064e93b647fd62fe2446791664003fAndy Huangfunction toggleQuotedText(toggleElement) { 88f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang var elidedTextElement = toggleElement.nextSibling; 89f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang var isHidden = getComputedStyle(elidedTextElement).display == 'none'; 90f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang toggleElement.innerHTML = isHidden ? MSG_HIDE_ELIDED : MSG_SHOW_ELIDED; 91f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang elidedTextElement.style.display = isHidden ? 'block' : 'none'; 92ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang 93ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang // Revealing the elided text should normalize it to fit-width to prevent 94ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang // this message from blowing out the conversation width. 95ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang if (isHidden) { 96ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang normalizeElementWidths([elidedTextElement]); 97ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang } 98f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang} 99f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 100014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huangfunction collapseAllQuotedText() { 101df0d91c5fa064e93b647fd62fe2446791664003fAndy Huang processQuotedText(document.documentElement, false /* showElided */); 102014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang} 103014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 104df0d91c5fa064e93b647fd62fe2446791664003fAndy Huangfunction processQuotedText(elt, showElided) { 105f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang var i; 106014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang var elements = elt.getElementsByClassName("elided-text"); 107f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang var elidedElement, toggleElement; 108f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang for (i = 0; i < elements.length; i++) { 109f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang elidedElement = elements[i]; 110f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang toggleElement = document.createElement("div"); 11182119afe5d596c69109ad9ce80e025dbec9ca4d1Andy Huang toggleElement.className = "mail-elided-text"; 112f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang toggleElement.innerHTML = MSG_SHOW_ELIDED; 113df0d91c5fa064e93b647fd62fe2446791664003fAndy Huang toggleElement.onclick = onToggleClick; 114df0d91c5fa064e93b647fd62fe2446791664003fAndy Huang elidedElement.style.display = 'none'; 115f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang elidedElement.parentNode.insertBefore(toggleElement, elidedElement); 116df0d91c5fa064e93b647fd62fe2446791664003fAndy Huang if (showElided) { 117df0d91c5fa064e93b647fd62fe2446791664003fAndy Huang toggleQuotedText(toggleElement); 118df0d91c5fa064e93b647fd62fe2446791664003fAndy Huang } 119f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 120f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang} 121f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 1224cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huangfunction isConversationEmpty(bodyDivs) { 1234cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang var i, len; 1244cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang var msgBody; 1254cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang var text; 1264cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang 1274cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang // Check if given divs are empty (in appearance), and disable zoom if so. 1284cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang for (i = 0, len = bodyDivs.length; i < len; i++) { 1294cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang msgBody = bodyDivs[i]; 1304cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang // use 'textContent' to exclude markup when determining whether bodies are empty 1314cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang // (fall back to more expensive 'innerText' if 'textContent' isn't implemented) 1324cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang text = msgBody.textContent || msgBody.innerText; 1334cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang if (text.trim().length > 0) { 1344cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang return false; 1354cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang } 1364cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang } 1374cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang return true; 1384cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang} 1394cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang 140ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huangfunction normalizeAllMessageWidths() { 1414cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang var expandedBodyDivs; 1424cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang var metaViewport; 1434cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang var contentValues; 1444cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang var isEmpty; 1454cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang 1464cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang expandedBodyDivs = document.querySelectorAll(".expanded > .mail-message-content"); 1474cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang 1484cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang isEmpty = isConversationEmpty(expandedBodyDivs); 1494cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang 1504cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang normalizeElementWidths(expandedBodyDivs); 1514cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang 1524cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang // assemble a working <meta> viewport "content" value from the base value in the 1534cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang // document, plus any dynamically determined options 1544cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang metaViewport = document.getElementById("meta-viewport"); 1554cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang contentValues = [metaViewport.getAttribute("content")]; 1564cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang if (isEmpty) { 1574cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang contentValues.push(metaViewport.getAttribute("data-zoom-off")); 1584cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang } else { 1594cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang contentValues.push(metaViewport.getAttribute("data-zoom-on")); 1604cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang } 1614cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang metaViewport.setAttribute("content", contentValues.join(",")); 162ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang} 163ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang 164ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang/* 165ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang * Normalizes the width of all elements supplied to the document body's overall width. 166ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang * Narrower elements are zoomed in, and wider elements are zoomed out. 167ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang * This method is idempotent. 168ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang */ 169ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huangfunction normalizeElementWidths(elements) { 170f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang var i; 171ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang var el; 172adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang var documentWidth; 173ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang var newZoom, oldZoom; 174256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang 175adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang documentWidth = document.body.offsetWidth; 176adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang 177f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang for (i = 0; i < elements.length; i++) { 178ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang el = elements[i]; 179ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang oldZoom = el.style.zoom; 180ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang // reset any existing normalization 181ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang if (oldZoom) { 182ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang el.style.zoom = 1; 183ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang } 184ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang newZoom = documentWidth / el.scrollWidth; 1850180f27c0998623b702274048b49cd4bec536cf1Andy Huang transformContent(el, documentWidth, el.scrollWidth); 1865ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang newZoom = documentWidth / el.scrollWidth; 1875ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang if (NORMALIZE_MESSAGE_WIDTHS) { 1885ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang el.style.zoom = newZoom; 1895ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang } 1905ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang } 1915ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang} 1925ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang 1930180f27c0998623b702274048b49cd4bec536cf1Andy Huangfunction transformContent(el, docWidth, elWidth) { 1945ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang var nodes; 1955ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang var i, len; 1960180f27c0998623b702274048b49cd4bec536cf1Andy Huang var index; 1973a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang var newWidth = elWidth; 1985ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang var wStr; 1993a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang var touched; 2000180f27c0998623b702274048b49cd4bec536cf1Andy Huang // the format of entries in this array is: 2010180f27c0998623b702274048b49cd4bec536cf1Andy Huang // entry := [ undoFunction, undoFunctionThis, undoFunctionParamArray ] 2023a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang var actionLog = []; 2033a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang var node; 2043a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang var start; 2055ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang if (elWidth <= docWidth) { 2065ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang return; 2075ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang } 2083a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang 2093a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang start = Date.now(); 21033578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang // Try munging all divs or textareas with inline styles where the width 2113a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang // is wider than docWidth, and change it to be a max-width. 2123a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang touched = false; 21333578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang nodes = ENABLE_MUNGE_TABLES ? el.querySelectorAll("div[style], textarea[style]") : []; 21433578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang touched = transformBlockElements(nodes, docWidth, actionLog); 2155ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang if (touched) { 2165ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang newWidth = el.scrollWidth; 2173a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang console.log("ran div-width munger on el=" + el + " oldW=" + elWidth + " newW=" + newWidth 2183a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang + " docW=" + docWidth); 2193a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang if (newWidth <= docWidth) { 2203a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang console.log("munger succeeded, elapsed time=" + (Date.now() - start)); 2213a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang return; 2223a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang } 2233a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang } 2243a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang 2250180f27c0998623b702274048b49cd4bec536cf1Andy Huang // OK, that wasn't enough. Find images with widths and override their widths. 2260180f27c0998623b702274048b49cd4bec536cf1Andy Huang nodes = ENABLE_MUNGE_IMAGES ? el.querySelectorAll("img") : []; 2270180f27c0998623b702274048b49cd4bec536cf1Andy Huang touched = transformImages(nodes, docWidth, actionLog); 2280180f27c0998623b702274048b49cd4bec536cf1Andy Huang if (touched) { 2290180f27c0998623b702274048b49cd4bec536cf1Andy Huang newWidth = el.scrollWidth; 2300180f27c0998623b702274048b49cd4bec536cf1Andy Huang console.log("ran img munger on el=" + el + " oldW=" + elWidth + " newW=" + newWidth 2310180f27c0998623b702274048b49cd4bec536cf1Andy Huang + " docW=" + docWidth); 2320180f27c0998623b702274048b49cd4bec536cf1Andy Huang if (newWidth <= docWidth) { 2330180f27c0998623b702274048b49cd4bec536cf1Andy Huang console.log("munger succeeded, elapsed time=" + (Date.now() - start)); 2340180f27c0998623b702274048b49cd4bec536cf1Andy Huang return; 2350180f27c0998623b702274048b49cd4bec536cf1Andy Huang } 2360180f27c0998623b702274048b49cd4bec536cf1Andy Huang } 2370180f27c0998623b702274048b49cd4bec536cf1Andy Huang 2383a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang // OK, that wasn't enough. Find tables with widths and override their widths. 2390180f27c0998623b702274048b49cd4bec536cf1Andy Huang nodes = ENABLE_MUNGE_TABLES ? el.querySelectorAll("table") : []; 2400180f27c0998623b702274048b49cd4bec536cf1Andy Huang touched = addClassToElements(nodes, shouldMungeTable, "munged", 2413a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang actionLog); 2423a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang if (touched) { 2433a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang newWidth = el.scrollWidth; 2445ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang console.log("ran table munger on el=" + el + " oldW=" + elWidth + " newW=" + newWidth 2455ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang + " docW=" + docWidth); 2465ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang if (newWidth <= docWidth) { 2473a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang console.log("munger succeeded, elapsed time=" + (Date.now() - start)); 2485ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang return; 2495ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang } 250f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 2515ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang 2523a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang // OK, that wasn't enough. Try munging all <td> to override any width and nowrap set. 2530180f27c0998623b702274048b49cd4bec536cf1Andy Huang nodes = ENABLE_MUNGE_TABLES ? el.querySelectorAll("td") : []; 2540180f27c0998623b702274048b49cd4bec536cf1Andy Huang touched = addClassToElements(nodes, null /* mungeAll */, "munged", 2553a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang actionLog); 2563a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang if (touched) { 2573a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang newWidth = el.scrollWidth; 2583a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang console.log("ran td munger on el=" + el + " oldW=" + elWidth + " newW=" + newWidth 2593a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang + " docW=" + docWidth); 2603a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang if (newWidth <= docWidth) { 2613a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang console.log("munger succeeded, elapsed time=" + (Date.now() - start)); 2623a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang return; 2635ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang } 2645ea5a8330532a75c83cbb30993a14ee9b821fa2aAndy Huang } 2653a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang 2663a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang // If the transformations shrank the width significantly enough, leave them in place. 2673a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang // We figure that in those cases, the benefits outweight the risk of rendering artifacts. 2683a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang if ((elWidth - newWidth) / (elWidth - docWidth) > TRANSFORM_MINIMUM_EFFECTIVE_RATIO) { 2693a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang console.log("transform(s) deemed effective enough. elapsed time=" 2703a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang + (Date.now() - start)); 2713a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang return; 2723a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang } 2733a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang 2743a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang // reverse all changes if the width is STILL not narrow enough 2753a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang // (except the width->maxWidth change, which is not particularly destructive) 2763a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang for (i = 0, len = actionLog.length; i < len; i++) { 2770180f27c0998623b702274048b49cd4bec536cf1Andy Huang actionLog[i][0].apply(actionLog[i][1], actionLog[i][2]); 2783a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang } 2793a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang if (actionLog.length > 0) { 2803a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang console.log("all mungers failed, changes reversed. elapsed time=" + (Date.now() - start)); 2813a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang } 2823a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang} 2833a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang 2843a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huangfunction addClassToElements(nodes, conditionFn, classToAdd, actionLog) { 2853a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang var i, len; 2863a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang var node; 2873a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang var added = false; 2883a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang for (i = 0, len = nodes.length; i < len; i++) { 2893a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang node = nodes[i]; 2903a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang if (!conditionFn || conditionFn(node)) { 2914821bbb4aeaf4393e45575e0e43c7086521039adAndy Huang if (node.classList.contains(classToAdd)) { 2924821bbb4aeaf4393e45575e0e43c7086521039adAndy Huang continue; 2934821bbb4aeaf4393e45575e0e43c7086521039adAndy Huang } 2943a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang node.classList.add(classToAdd); 2953a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang added = true; 2960180f27c0998623b702274048b49cd4bec536cf1Andy Huang actionLog.push([node.classList.remove, node.classList, [classToAdd]]); 2973a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang } 2983a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang } 2993a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang return added; 3003a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang} 3013a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang 30233578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huangfunction transformBlockElements(nodes, docWidth, actionLog) { 30333578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang var i, len; 30433578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang var node; 30533578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang var wStr; 30633578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang var index; 30733578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang var touched = false; 30833578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang 30933578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang for (i = 0, len = nodes.length; i < len; i++) { 31033578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang node = nodes[i]; 31133578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang wStr = node.style.width || node.style.minWidth; 31233578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang index = wStr ? wStr.indexOf("px") : -1; 31333578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang if (index >= 0 && wStr.slice(0, index) > docWidth) { 31433578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang saveStyleProperty(node, "width", actionLog); 31533578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang saveStyleProperty(node, "minWidth", actionLog); 31633578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang saveStyleProperty(node, "maxWidth", actionLog); 31733578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang node.style.width = "100%"; 31833578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang node.style.minWidth = ""; 31933578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang node.style.maxWidth = wStr; 32033578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang touched = true; 32133578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang } 32233578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang } 32333578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang return touched; 32433578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang} 32533578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang 3260180f27c0998623b702274048b49cd4bec536cf1Andy Huangfunction transformImages(nodes, docWidth, actionLog) { 3270180f27c0998623b702274048b49cd4bec536cf1Andy Huang var i, len; 3280180f27c0998623b702274048b49cd4bec536cf1Andy Huang var node; 3290180f27c0998623b702274048b49cd4bec536cf1Andy Huang var w, h; 3300180f27c0998623b702274048b49cd4bec536cf1Andy Huang var touched = false; 3310180f27c0998623b702274048b49cd4bec536cf1Andy Huang 3320180f27c0998623b702274048b49cd4bec536cf1Andy Huang for (i = 0, len = nodes.length; i < len; i++) { 3330180f27c0998623b702274048b49cd4bec536cf1Andy Huang node = nodes[i]; 3340180f27c0998623b702274048b49cd4bec536cf1Andy Huang w = node.offsetWidth; 3350180f27c0998623b702274048b49cd4bec536cf1Andy Huang h = node.offsetHeight; 3360180f27c0998623b702274048b49cd4bec536cf1Andy Huang // shrink w/h proportionally if the img is wider than available width 3370180f27c0998623b702274048b49cd4bec536cf1Andy Huang if (w > docWidth) { 33833578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang saveStyleProperty(node, "maxWidth", actionLog); 33933578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang saveStyleProperty(node, "width", actionLog); 34033578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang saveStyleProperty(node, "height", actionLog); 3410180f27c0998623b702274048b49cd4bec536cf1Andy Huang node.style.maxWidth = docWidth + "px"; 3420180f27c0998623b702274048b49cd4bec536cf1Andy Huang node.style.width = "100%"; 3430180f27c0998623b702274048b49cd4bec536cf1Andy Huang node.style.height = "auto"; 3440180f27c0998623b702274048b49cd4bec536cf1Andy Huang touched = true; 3450180f27c0998623b702274048b49cd4bec536cf1Andy Huang } 3460180f27c0998623b702274048b49cd4bec536cf1Andy Huang } 3470180f27c0998623b702274048b49cd4bec536cf1Andy Huang return touched; 3480180f27c0998623b702274048b49cd4bec536cf1Andy Huang} 3490180f27c0998623b702274048b49cd4bec536cf1Andy Huang 35033578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huangfunction saveStyleProperty(node, property, actionLog) { 35133578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang var savedName = "data-" + property; 35233578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang node.setAttribute(savedName, node.style[property]); 35333578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang actionLog.push([undoSetProperty, node, [property, savedName]]); 35433578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang} 35533578eaf140b8eeb5dad6e0c015b2e204054a86bAndy Huang 3560180f27c0998623b702274048b49cd4bec536cf1Andy Huangfunction undoSetProperty(property, savedProperty) { 3570180f27c0998623b702274048b49cd4bec536cf1Andy Huang this.style[property] = savedProperty ? this.getAttribute(savedProperty) : ""; 3580180f27c0998623b702274048b49cd4bec536cf1Andy Huang} 3590180f27c0998623b702274048b49cd4bec536cf1Andy Huang 3603a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huangfunction shouldMungeTable(table) { 3613a398b245930ce60ceb51fc605ab0f20c8df1f62Andy Huang return table.hasAttribute("width") || table.style.width; 362f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang} 363f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 364f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huangfunction hideAllUnsafeImages() { 365f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang hideUnsafeImages(document.getElementsByClassName("mail-message-content")); 366f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang} 367f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang 368f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huangfunction hideUnsafeImages(msgContentDivs) { 369f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang var i, msgContentCount; 3703233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang var j, imgCount; 371f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang var msgContentDiv, image; 3723233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang var images; 3733233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang var showImages; 374f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang for (i = 0, msgContentCount = msgContentDivs.length; i < msgContentCount; i++) { 375f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang msgContentDiv = msgContentDivs[i]; 376f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang showImages = msgContentDiv.classList.contains("mail-show-images"); 3773233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 378f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang images = msgContentDiv.getElementsByTagName("img"); 3793233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang for (j = 0, imgCount = images.length; j < imgCount; j++) { 3803233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang image = images[j]; 38141dca185f7683b36bdafd9520c0648c897a95834Paul Westbrook rewriteRelativeImageSrc(image); 3823233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang attachImageLoadListener(image); 3833233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang // TODO: handle inline image attachments for all supported protocols 3843233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang if (!showImages) { 3853233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang blockImage(image); 3863233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang } 3873233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang } 3883233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang } 3893233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang} 3903233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 39141dca185f7683b36bdafd9520c0648c897a95834Paul Westbrook/** 39241dca185f7683b36bdafd9520c0648c897a95834Paul Westbrook * Changes relative paths to absolute path by pre-pending the account uri 39341dca185f7683b36bdafd9520c0648c897a95834Paul Westbrook * @param {Element} imgElement Image for which the src path will be updated. 39441dca185f7683b36bdafd9520c0648c897a95834Paul Westbrook */ 39541dca185f7683b36bdafd9520c0648c897a95834Paul Westbrookfunction rewriteRelativeImageSrc(imgElement) { 39641dca185f7683b36bdafd9520c0648c897a95834Paul Westbrook var src = imgElement.src; 397cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook 398cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook // DOC_BASE_URI will always be a unique x-thread:// uri for this particular conversation 399cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook if (src.indexOf(DOC_BASE_URI) == 0 && (DOC_BASE_URI != CONVERSATION_BASE_URI)) { 400cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook // The conversation specifies a different base uri than the document 401cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook src = CONVERSATION_BASE_URI + src.substring(DOC_BASE_URI.length); 402cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook imgElement.src = src; 40341dca185f7683b36bdafd9520c0648c897a95834Paul Westbrook } 40441dca185f7683b36bdafd9520c0648c897a95834Paul Westbrook}; 40541dca185f7683b36bdafd9520c0648c897a95834Paul Westbrook 40641dca185f7683b36bdafd9520c0648c897a95834Paul Westbrook 4073233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huangfunction attachImageLoadListener(imageElement) { 4083233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang // Reset the src attribute to the empty string because onload will only fire if the src 4093233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang // attribute is set after the onload listener. 4103233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang var originalSrc = imageElement.src; 4113233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang imageElement.src = ''; 412ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang imageElement.onload = imageOnLoad; 4133233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang imageElement.src = originalSrc; 4143233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang} 4153233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 41663b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang/** 41763b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang * Handle an onload event for an <img> tag. 41863b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang * The image could be within an elided-text block, or at the top level of a message. 41963b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang * When a new image loads, its new bounds may affect message or elided-text geometry, 42063b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang * so we need to inspect and adjust the enclosing element's zoom level where necessary. 42163b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang * 42263b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang * Because this method can be called really often, and zoom-level adjustment is slow, 42363b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang * we collect the elements to be processed and do them all later in a single deferred pass. 42463b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang */ 425ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huangfunction imageOnLoad(e) { 426ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang // normalize the quoted text parent if we're in a quoted text block, or else 427ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang // normalize the parent message content element 428ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang var parent = up(e.target, "elided-text") || up(e.target, "mail-message-content"); 42963b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang if (!parent) { 43063b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang // sanity check. shouldn't really happen. 43163b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang return; 43263b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang } 43363b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang 43463b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang // if there was no previous work, schedule a new deferred job 43563b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang if (gImageLoadElements.length == 0) { 43663b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang window.setTimeout(handleAllImageOnLoads, 0); 43763b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang } 43863b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang 43963b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang // enqueue the work if it wasn't already enqueued 44063b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang if (gImageLoadElements.indexOf(parent) == -1) { 44163b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang gImageLoadElements.push(parent); 442ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang } 44363b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang} 44463b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang 44563b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang// handle all deferred work from image onload events 44663b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huangfunction handleAllImageOnLoads() { 44763b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang normalizeElementWidths(gImageLoadElements); 448ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang measurePositions(); 44963b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang // clear the queue so the next onload event starts a new job 45063b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang gImageLoadElements = []; 451ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang} 452ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang 4533233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huangfunction blockImage(imageElement) { 4543233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang var src = imageElement.src; 45541dca185f7683b36bdafd9520c0648c897a95834Paul Westbrook if (src.indexOf("http://") == 0 || src.indexOf("https://") == 0 || 45641dca185f7683b36bdafd9520c0648c897a95834Paul Westbrook src.indexOf("content://") == 0) { 4573233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang imageElement.setAttribute(BLOCKED_SRC_ATTR, src); 4583233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang imageElement.src = "data:"; 4593233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang } 4603233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang} 4613233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 46223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huangfunction setWideViewport() { 46323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang var metaViewport = document.getElementById('meta-viewport'); 46423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang metaViewport.setAttribute('content', 'width=' + WIDE_VIEWPORT_WIDTH); 46523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang} 46623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang 467e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huangfunction restoreScrollPosition() { 468e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang var scrollYPercent = window.mail.getScrollYPercent(); 469e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang if (scrollYPercent && document.body.offsetHeight > window.innerHeight) { 470e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang document.body.scrollTop = Math.floor(scrollYPercent * document.body.offsetHeight); 471e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang } 472e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang} 473e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang 474bffc312614505b923f0054fde20d015bd21112cfAndy Huangfunction onContentReady(event) { 475bffc312614505b923f0054fde20d015bd21112cfAndy Huang window.mail.onContentReady(); 476bffc312614505b923f0054fde20d015bd21112cfAndy Huang} 477bffc312614505b923f0054fde20d015bd21112cfAndy Huang 478bffc312614505b923f0054fde20d015bd21112cfAndy Huangfunction setupContentReady() { 479bffc312614505b923f0054fde20d015bd21112cfAndy Huang var signalDiv; 480bffc312614505b923f0054fde20d015bd21112cfAndy Huang 481bffc312614505b923f0054fde20d015bd21112cfAndy Huang // PAGE READINESS SIGNAL FOR JELLYBEAN AND NEWER 482bffc312614505b923f0054fde20d015bd21112cfAndy Huang // Notify the app on 'webkitAnimationStart' of a simple dummy element with a simple no-op 483bffc312614505b923f0054fde20d015bd21112cfAndy Huang // animation that immediately runs on page load. The app uses this as a signal that the 484bffc312614505b923f0054fde20d015bd21112cfAndy Huang // content is loaded and ready to draw, since WebView delays firing this event until the 485bffc312614505b923f0054fde20d015bd21112cfAndy Huang // layers are composited and everything is ready to draw. 486bffc312614505b923f0054fde20d015bd21112cfAndy Huang // 487bffc312614505b923f0054fde20d015bd21112cfAndy Huang // This code is conditionally enabled on JB+ by setting the ENABLE_CONTENT_READY flag. 488bffc312614505b923f0054fde20d015bd21112cfAndy Huang if (ENABLE_CONTENT_READY) { 489bffc312614505b923f0054fde20d015bd21112cfAndy Huang signalDiv = document.getElementById("initial-load-signal"); 490bffc312614505b923f0054fde20d015bd21112cfAndy Huang signalDiv.addEventListener("webkitAnimationStart", onContentReady, false); 491bffc312614505b923f0054fde20d015bd21112cfAndy Huang } 492bffc312614505b923f0054fde20d015bd21112cfAndy Huang} 493bffc312614505b923f0054fde20d015bd21112cfAndy Huang 49423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang// BEGIN Java->JavaScript handlers 495f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangfunction measurePositions() { 496adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang var overlayTops, overlayBottoms; 497f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang var i; 498f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang var len; 499f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 500adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang var expandedBody, headerSpacer; 501adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang var prevBodyBottom = 0; 502adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang var expandedBodyDivs = document.querySelectorAll(".expanded > .mail-message-content"); 503adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang 504adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang // N.B. offsetTop and offsetHeight of an element with the "zoom:" style applied cannot be 505adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang // trusted. 506f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 507adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang overlayTops = new Array(expandedBodyDivs.length + 1); 508adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang overlayBottoms = new Array(expandedBodyDivs.length + 1); 509adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang for (i = 0, len = expandedBodyDivs.length; i < len; i++) { 510adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang expandedBody = expandedBodyDivs[i]; 511adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang headerSpacer = expandedBody.previousElementSibling; 512f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang // addJavascriptInterface handler only supports string arrays 513adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang overlayTops[i] = "" + prevBodyBottom; 514adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang overlayBottoms[i] = "" + (getTotalOffset(headerSpacer).top + headerSpacer.offsetHeight); 515adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang prevBodyBottom = getTotalOffset(expandedBody.nextElementSibling).top; 516f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 517adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang // add an extra one to mark the top/bottom of the last message footer spacer 518adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang overlayTops[i] = "" + prevBodyBottom; 5197bdc3750454efe59617b7df945eadd7e59bee954Andy Huang overlayBottoms[i] = "" + document.body.offsetHeight; 520f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 521adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang window.mail.onWebContentGeometryChange(overlayTops, overlayBottoms); 522f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang} 523f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 524eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedyfunction unblockImages(messageDomIds) { 525eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy var i, j, images, imgCount, image, blockedSrc; 526eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy for (j = 0, len = messageDomIds.length; j < len; j++) { 527eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy var messageDomId = messageDomIds[j]; 528eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy var msg = document.getElementById(messageDomId); 529eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy if (!msg) { 530eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy console.log("can't unblock, no matching message for id: " + messageDomId); 531eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy continue; 532eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy } 533eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy images = msg.getElementsByTagName("img"); 534eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy for (i = 0, imgCount = images.length; i < imgCount; i++) { 535eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy image = images[i]; 536eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy blockedSrc = image.getAttribute(BLOCKED_SRC_ATTR); 537eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy if (blockedSrc) { 538eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy image.src = blockedSrc; 539eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy image.removeAttribute(BLOCKED_SRC_ATTR); 540eb9a4bdc53269ee05fe11870b9ebf03f18196585Scott Kennedy } 5413233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang } 5423233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang } 5433233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang} 544c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang 545ab2d998506c83e82ddae25b6ba1419414e1e8122Mark Weifunction setConversationHeaderSpacerHeight(spacerHeight) { 546ab2d998506c83e82ddae25b6ba1419414e1e8122Mark Wei var spacer = document.getElementById("conversation-header"); 547ab2d998506c83e82ddae25b6ba1419414e1e8122Mark Wei if (!spacer) { 548ab2d998506c83e82ddae25b6ba1419414e1e8122Mark Wei console.log("can't set spacer for conversation header"); 549ab2d998506c83e82ddae25b6ba1419414e1e8122Mark Wei return; 550ab2d998506c83e82ddae25b6ba1419414e1e8122Mark Wei } 551ab2d998506c83e82ddae25b6ba1419414e1e8122Mark Wei spacer.style.height = spacerHeight + "px"; 552ab2d998506c83e82ddae25b6ba1419414e1e8122Mark Wei measurePositions(); 553ab2d998506c83e82ddae25b6ba1419414e1e8122Mark Wei} 554ab2d998506c83e82ddae25b6ba1419414e1e8122Mark Wei 555c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huangfunction setMessageHeaderSpacerHeight(messageDomId, spacerHeight) { 556c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang var spacer = document.querySelector("#" + messageDomId + " > .mail-message-header"); 557c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang if (!spacer) { 558c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang console.log("can't set spacer for message with id: " + messageDomId); 559c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang return; 560c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang } 561c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang spacer.style.height = spacerHeight + "px"; 562c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang measurePositions(); 563c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang} 564c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang 565c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huangfunction setMessageBodyVisible(messageDomId, isVisible, spacerHeight) { 566c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang var i, len; 567c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang var visibility = isVisible ? "block" : "none"; 568c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang var messageDiv = document.querySelector("#" + messageDomId); 569c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang var collapsibleDivs = document.querySelectorAll("#" + messageDomId + " > .collapsible"); 570c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang if (!messageDiv || collapsibleDivs.length == 0) { 571c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang console.log("can't set body visibility for message with id: " + messageDomId); 572c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang return; 573c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang } 574c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang messageDiv.classList.toggle("expanded"); 575c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang for (i = 0, len = collapsibleDivs.length; i < len; i++) { 576c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang collapsibleDivs[i].style.display = visibility; 577c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang } 578ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang 579ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang // revealing new content should trigger width normalization, since the initial render 580ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang // skips collapsed and super-collapsed messages 581ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang if (isVisible) { 582ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang normalizeElementWidths(messageDiv.getElementsByClassName("mail-message-content")); 583ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang } 584ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy Huang 585c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang setMessageHeaderSpacerHeight(messageDomId, spacerHeight); 586c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang} 58746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 58846dfba6160b55a582b344328067e3dafeb881dd9Andy Huangfunction replaceSuperCollapsedBlock(startIndex) { 589f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang var parent, block, msg; 59046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 59146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang block = document.querySelector(".mail-super-collapsed-block[index='" + startIndex + "']"); 59246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang if (!block) { 59346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang console.log("can't expand super collapsed block at index: " + startIndex); 59446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return; 59546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 59646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang parent = block.parentNode; 59746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang block.innerHTML = window.mail.getTempMessageBodies(); 59846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 599f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang // process the new block contents in one go before we pluck them out of the common ancestor 600f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang processQuotedText(block, false /* showElided */); 601f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang hideUnsafeImages(block.getElementsByClassName("mail-message-content")); 602f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang 603f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang msg = block.firstChild; 604f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang while (msg) { 605f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang parent.insertBefore(msg, block); 606f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang msg = block.firstChild; 60746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 60846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang parent.removeChild(block); 60946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang measurePositions(); 61046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang} 6113bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp 612014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huangfunction replaceMessageBodies(messageIds) { 613014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang var i; 614014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang var id; 615014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang var msgContentDiv; 616014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 617014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang for (i = 0, len = messageIds.length; i < len; i++) { 618014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang id = messageIds[i]; 619014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang msgContentDiv = document.querySelector("#" + id + " > .mail-message-content"); 620014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang msgContentDiv.innerHTML = window.mail.getMessageBody(id); 621df0d91c5fa064e93b647fd62fe2446791664003fAndy Huang processQuotedText(msgContentDiv, true /* showElided */); 622f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang hideUnsafeImages([msgContentDiv]); 623014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } 62491d782abc8015bd651fb5d0252b4d1ef369ec57bAndy Huang measurePositions(); 625014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang} 626014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 62706c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang// handle the special case of adding a single new message at the end of a conversation 62806c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huangfunction appendMessageHtml() { 62906c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang var msg = document.createElement("div"); 63006c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang msg.innerHTML = window.mail.getTempMessageBodies(); 63106c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang msg = msg.children[0]; // toss the outer div, it was just to render innerHTML into 63206c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang document.body.appendChild(msg); 63306c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang processQuotedText(msg, true /* showElided */); 634f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy Huang hideUnsafeImages(msg.getElementsByClassName("mail-message-content")); 63591d782abc8015bd651fb5d0252b4d1ef369ec57bAndy Huang measurePositions(); 63606c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang} 63706c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang 63802f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huangfunction onScaleBegin(screenX, screenY) { 63902f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang// console.log("JS got scaleBegin x/y=" + screenX + "/" + screenY); 64002f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var focusX = screenX + document.body.scrollLeft; 64102f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var focusY = screenY + document.body.scrollTop; 64202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var i, len; 64302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var msgDivs = document.getElementsByClassName("mail-message"); 64402f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var msgDiv, msgBodyDiv; 64502f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var msgTop, msgDivTop, nextMsgTop; 64602f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var initialH; 64702f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var initialScale; 64802f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var scaledOriginX, scaledOriginY; 64902f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var translateX, translateY; 65002f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var origin; 65102f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 65202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang gScaleInfo = undefined; 65302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 65402f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang for (i = 0, len = msgDivs.length; i < len; i++) { 65502f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang msgDiv = msgDivs[i]; 65602f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang msgTop = nextMsgTop ? nextMsgTop : getTotalOffset(msgDiv).top; 65702f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang nextMsgTop = (i < len-1) ? getTotalOffset(msgDivs[i+1]).top : document.body.offsetHeight; 65802f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang if (focusY >= msgTop && focusY < nextMsgTop) { 65902f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang msgBodyDiv = msgDiv.children[1]; 66002f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang initialScale = msgBodyDiv.getAttribute("data-initial-scale") || 1.0; 66102f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 66202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang msgDivTop = getTotalOffset(msgBodyDiv).top; 66302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 664ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang // TODO: correct only for no initial translation 665ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang // FIXME: wrong for initialScale > 1.0 666ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang scaledOriginX = focusX / initialScale; 667ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang scaledOriginY = (focusY - msgDivTop) / initialScale; 66802f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 669ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang // TODO: is this still needed? 670ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang translateX = 0; 671ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang translateY = 0; 67202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 67302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang gScaleInfo = { 67402f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang div: msgBodyDiv, 67502f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang initialScale: initialScale, 676ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang initialScreenX: screenX, 677ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang initialScreenY: screenY, 678ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang originX: scaledOriginX, 679ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang originY: scaledOriginY, 68002f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang translateX: translateX, 68102f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang translateY: translateY, 68202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang initialH: getCachedValue(msgBodyDiv, "offsetHeight", "data-initial-height"), 68302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang minScale: Math.min(document.body.offsetWidth / msgBodyDiv.scrollWidth, 1.0), 684ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang currScale: initialScale, 685ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang currTranslateX: 0, 686ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang currTranslateY: 0 68702f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang }; 68802f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 68902f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang origin = scaledOriginX + "px " + scaledOriginY + "px"; 69002f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang msgBodyDiv.classList.add("zooming-focused"); 69102f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang msgBodyDiv.style.webkitTransformOrigin = origin; 69202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang msgBodyDiv.style.webkitTransform = "scale3d(" + initialScale + "," + initialScale 69302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang + ",1) translate3d(" + translateX + "px," + translateY + "px,0)"; 69402f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang// console.log("scaleBegin, h=" + gScaleInfo.initialH + " origin='" + origin + "'"); 69502f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang break; 69602f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang } 69702f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang } 69802f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang} 69902f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 70002f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huangfunction onScaleEnd(screenX, screenY) { 70102f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var msgBodyDiv; 70202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var scale; 70302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var h; 70402f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang if (!gScaleInfo) { 70502f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang return; 70602f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang } 70702f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 70802f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang// console.log("JS got scaleEnd x/y=" + screenX + "/" + screenY); 70902f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang msgBodyDiv = gScaleInfo.div; 71002f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang scale = gScaleInfo.currScale; 71102f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang msgBodyDiv.style.webkitTransformOrigin = "0 0"; 71202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang // clear any translate 71302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang // switching to a 2D transform here re-renders the fonts more clearly, but introduces 71402f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang // texture upload lag to any subsequent scale operation 715ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang // TODO: conditionalize this based on device GPU performance and/or body size/complexity? 716ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang if (true) { 717ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang msgBodyDiv.style.webkitTransform = "scale(" + gScaleInfo.currScale + ")"; 718ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang } else { 719ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang msgBodyDiv.style.webkitTransform = "scale3d(" + scale + "," + scale + ",1)"; 720ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang } 72102f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang h = gScaleInfo.initialH * scale; 72202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang// console.log("onScaleEnd set h=" + h); 72302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang msgBodyDiv.style.height = h + "px"; 724ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang 725ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang // Use saved translateX/Y rather than calculating from screenX/Y because screenX/Y values 726ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang // from onScaleEnd only track focus of remaining pointers, which is not useful and leads 727ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang // to a perceived jump. 728ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang var deltaScrollX = (scale - 1) * gScaleInfo.originX - gScaleInfo.currTranslateX; 729ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang var deltaScrollY = (scale - 1) * gScaleInfo.originY - gScaleInfo.currTranslateY; 730ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang// console.log("JS adjusting scroll by x/y=" + deltaScrollX + "/" + deltaScrollY); 731ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang 73202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang msgBodyDiv.classList.remove("zooming-focused"); 73302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang msgBodyDiv.setAttribute("data-initial-scale", scale); 7344c54e3627861351c6c5f74dd5132da7d0321377aAndy Huang 7354c54e3627861351c6c5f74dd5132da7d0321377aAndy Huang // TODO: is there a better way to make this more reliable? 7364c54e3627861351c6c5f74dd5132da7d0321377aAndy Huang window.setTimeout(function() { 7374c54e3627861351c6c5f74dd5132da7d0321377aAndy Huang window.scrollBy(deltaScrollX, deltaScrollY); 7384c54e3627861351c6c5f74dd5132da7d0321377aAndy Huang }, 10); 73902f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang} 74002f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 74102f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huangfunction onScale(relativeScale, screenX, screenY) { 74202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var scale; 74302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var translateX, translateY; 74402f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang var transform; 74502f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 74602f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang if (!gScaleInfo) { 74702f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang return; 74802f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang } 74902f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 75002f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang scale = Math.max(gScaleInfo.initialScale * relativeScale, gScaleInfo.minScale); 75102f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang if (scale > 4.0) { 75202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang scale = 4.0; 75302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang } 754ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang translateX = screenX - gScaleInfo.initialScreenX; 755ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang translateY = screenY - gScaleInfo.initialScreenY; 756ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang // TODO: clamp translation to prevent going beyond body edges 75702f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang gScaleInfo.currScale = scale; 758ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang gScaleInfo.currTranslateX = translateX; 759ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang gScaleInfo.currTranslateY = translateY; 76002f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang transform = "translate3d(" + translateX + "px," + translateY + "px,0) scale3d(" 76102f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang + scale + "," + scale + ",1) translate3d(" + gScaleInfo.translateX + "px," 76202f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang + gScaleInfo.translateY + "px,0)"; 76302f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang gScaleInfo.div.style.webkitTransform = transform; 764ce8565b3605c2ce47c75e259f387cd4955f017daAndy Huang// console.log("JS got scale=" + scale + " x/y=" + screenX + "/" + screenY 76502f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang// + " transform='" + transform + "'"); 76602f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang} 76702f9d18a54072db8d86c524f9c09e508092ddd7cAndy Huang 7683233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang// END Java->JavaScript handlers 7693233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 7704cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang// Do this first to ensure that the readiness signal comes through, 7714cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang// even if a stray exception later occurs. 7724cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy HuangsetupContentReady(); 7734cfe22a8b0fe9fb98df9c82303bacc5c1a79f19bAndy Huang 774014ea4c15d147794789b9c5bf4e243fa08781ad9Andy HuangcollapseAllQuotedText(); 775f500db85dd13394dd6bdcfa38824b2ca6ada77e8Andy HuanghideAllUnsafeImages(); 776ffc725fe4b4fa1024feb22b1d1f76c24febadcdbAndy HuangnormalizeAllMessageWidths(); 77723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang//setWideViewport(); 778e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy HuangrestoreScrollPosition(); 779f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy HuangmeasurePositions(); 780f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 781