script.js revision 014ea4c15d147794789b9c5bf4e243fa08781ad9
1/* 2 * Copyright (C) 2012 Google Inc. 3 * Licensed to The Android Open Source Project. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18var BLOCKED_SRC_ATTR = "blocked-src"; 19 20/** 21 * Returns the page offset of an element. 22 * 23 * @param {Element} element The element to return the page offset for. 24 * @return {left: number, top: number} A tuple including a left and top value representing 25 * the page offset of the element. 26 */ 27function getTotalOffset(el) { 28 var result = { 29 left: 0, 30 top: 0 31 }; 32 var parent = el; 33 34 while (parent) { 35 result.left += parent.offsetLeft; 36 result.top += parent.offsetTop; 37 parent = parent.offsetParent; 38 } 39 40 return result; 41} 42 43function toggleQuotedText(e) { 44 var toggleElement = e.target; 45 var elidedTextElement = toggleElement.nextSibling; 46 var isHidden = getComputedStyle(elidedTextElement).display == 'none'; 47 toggleElement.innerHTML = isHidden ? MSG_HIDE_ELIDED : MSG_SHOW_ELIDED; 48 elidedTextElement.style.display = isHidden ? 'block' : 'none'; 49 measurePositions(); 50} 51 52function collapseAllQuotedText() { 53 collapseQuotedText(document.documentElement); 54} 55 56function collapseQuotedText(elt) { 57 var i; 58 var elements = elt.getElementsByClassName("elided-text"); 59 var elidedElement, toggleElement; 60 for (i = 0; i < elements.length; i++) { 61 elidedElement = elements[i]; 62 toggleElement = document.createElement("div"); 63 toggleElement.className = "mail-elided-text"; 64 toggleElement.innerHTML = MSG_SHOW_ELIDED; 65 toggleElement.onclick = toggleQuotedText; 66 elidedElement.parentNode.insertBefore(toggleElement, elidedElement); 67 } 68} 69 70function normalizeMessageWidths() { 71 var i; 72 var elements = document.getElementsByClassName("mail-message-content"); 73 var messageElement; 74 var documentWidth = document.body.offsetWidth; 75 76 for (i = 0; i < elements.length; i++) { 77 messageElement = elements[i]; 78 messageElement.style.zoom = documentWidth / messageElement.scrollWidth; 79 } 80} 81 82function hideUnsafeImages() { 83 var i, bodyCount; 84 var j, imgCount; 85 var body, image; 86 var images; 87 var showImages; 88 var bodies = document.getElementsByClassName("mail-message-content"); 89 for (i = 0, bodyCount = bodies.length; i < bodyCount; i++) { 90 body = bodies[i]; 91 showImages = body.classList.contains("mail-show-images"); 92 93 images = body.getElementsByTagName("img"); 94 for (j = 0, imgCount = images.length; j < imgCount; j++) { 95 image = images[j]; 96 rewriteRelativeImageSrc(image); 97 attachImageLoadListener(image); 98 // TODO: handle inline image attachments for all supported protocols 99 if (!showImages) { 100 blockImage(image); 101 } 102 } 103 } 104} 105 106/** 107 * Changes relative paths to absolute path by pre-pending the account uri 108 * @param {Element} imgElement Image for which the src path will be updated. 109 */ 110function rewriteRelativeImageSrc(imgElement) { 111 var src = imgElement.src; 112 113 // DOC_BASE_URI will always be a unique x-thread:// uri for this particular conversation 114 if (src.indexOf(DOC_BASE_URI) == 0 && (DOC_BASE_URI != CONVERSATION_BASE_URI)) { 115 // The conversation specifies a different base uri than the document 116 src = CONVERSATION_BASE_URI + src.substring(DOC_BASE_URI.length); 117 imgElement.src = src; 118 } 119}; 120 121 122function attachImageLoadListener(imageElement) { 123 // Reset the src attribute to the empty string because onload will only fire if the src 124 // attribute is set after the onload listener. 125 var originalSrc = imageElement.src; 126 imageElement.src = ''; 127 imageElement.onload = measurePositions; 128 imageElement.src = originalSrc; 129} 130 131function blockImage(imageElement) { 132 var src = imageElement.src; 133 if (src.indexOf("http://") == 0 || src.indexOf("https://") == 0 || 134 src.indexOf("content://") == 0) { 135 imageElement.setAttribute(BLOCKED_SRC_ATTR, src); 136 imageElement.src = "data:"; 137 } 138} 139 140function setWideViewport() { 141 var metaViewport = document.getElementById('meta-viewport'); 142 metaViewport.setAttribute('content', 'width=' + WIDE_VIEWPORT_WIDTH); 143} 144 145// BEGIN Java->JavaScript handlers 146function measurePositions() { 147 var overlayBottoms; 148 var h; 149 var i; 150 var len; 151 152 var expandedSpacerDivs = document.querySelectorAll(".expanded > .spacer"); 153 154 overlayBottoms = new Array(expandedSpacerDivs.length + 1); 155 for (i = 0, len = expandedSpacerDivs.length; i < len; i++) { 156 h = expandedSpacerDivs[i].offsetHeight; 157 // addJavascriptInterface handler only supports string arrays 158 overlayBottoms[i] = "" + (getTotalOffset(expandedSpacerDivs[i]).top + h); 159 } 160 // add an extra one to mark the bottom of the last message 161 overlayBottoms[i] = "" + document.body.offsetHeight; 162 163 window.mail.onWebContentGeometryChange(overlayBottoms); 164} 165 166function unblockImages(messageDomId) { 167 var i, images, imgCount, image, blockedSrc; 168 var msg = document.getElementById(messageDomId); 169 if (!msg) { 170 console.log("can't unblock, no matching message for id: " + messageDomId); 171 return; 172 } 173 images = msg.getElementsByTagName("img"); 174 for (i = 0, imgCount = images.length; i < imgCount; i++) { 175 image = images[i]; 176 blockedSrc = image.getAttribute(BLOCKED_SRC_ATTR); 177 if (blockedSrc) { 178 image.src = blockedSrc; 179 image.removeAttribute(BLOCKED_SRC_ATTR); 180 } 181 } 182} 183 184function setMessageHeaderSpacerHeight(messageDomId, spacerHeight) { 185 var spacer = document.querySelector("#" + messageDomId + " > .mail-message-header"); 186 if (!spacer) { 187 console.log("can't set spacer for message with id: " + messageDomId); 188 return; 189 } 190 spacer.style.height = spacerHeight + "px"; 191 measurePositions(); 192} 193 194function setMessageBodyVisible(messageDomId, isVisible, spacerHeight) { 195 var i, len; 196 var visibility = isVisible ? "block" : "none"; 197 var messageDiv = document.querySelector("#" + messageDomId); 198 var collapsibleDivs = document.querySelectorAll("#" + messageDomId + " > .collapsible"); 199 if (!messageDiv || collapsibleDivs.length == 0) { 200 console.log("can't set body visibility for message with id: " + messageDomId); 201 return; 202 } 203 messageDiv.classList.toggle("expanded"); 204 for (i = 0, len = collapsibleDivs.length; i < len; i++) { 205 collapsibleDivs[i].style.display = visibility; 206 } 207 setMessageHeaderSpacerHeight(messageDomId, spacerHeight); 208} 209 210function replaceSuperCollapsedBlock(startIndex) { 211 var parent, block, header; 212 213 block = document.querySelector(".mail-super-collapsed-block[index='" + startIndex + "']"); 214 if (!block) { 215 console.log("can't expand super collapsed block at index: " + startIndex); 216 return; 217 } 218 parent = block.parentNode; 219 block.innerHTML = window.mail.getTempMessageBodies(); 220 221 header = block.firstChild; 222 while (header) { 223 parent.insertBefore(header, block); 224 header = block.firstChild; 225 } 226 parent.removeChild(block); 227 measurePositions(); 228} 229 230function onContentReady(event) { 231 window.mail.onContentReady(); 232} 233 234function replaceMessageBodies(messageIds) { 235 var i; 236 var id; 237 var msgContentDiv; 238 239 for (i = 0, len = messageIds.length; i < len; i++) { 240 id = messageIds[i]; 241 msgContentDiv = document.querySelector("#" + id + " > .mail-message-content"); 242 msgContentDiv.innerHTML = window.mail.getMessageBody(id); 243 collapseQuotedText(msgContentDiv); 244 } 245} 246 247// END Java->JavaScript handlers 248 249window.onload = function() { 250 // PAGE READINESS SIGNAL FOR JELLYBEAN AND NEWER 251 // Notify the app on 'webkitAnimationStart' of a simple dummy element with a simple no-op 252 // animation that immediately runs on page load. The app uses this as a signal that the 253 // content is loaded and ready to draw, since WebView delays firing this event until the 254 // layers are composited and everything is ready to draw. 255 // 256 // This code is conditionally enabled on JB+ by setting the 'initial-load' CSS class on this 257 // dummy element. 258 document.getElementById("initial-load-signal") 259 .addEventListener("webkitAnimationStart", onContentReady, false); 260 document.getElementById("initial-load-signal").style.webkitAnimationName = 'initial-load'; 261}; 262 263collapseAllQuotedText(); 264hideUnsafeImages(); 265normalizeMessageWidths(); 266//setWideViewport(); 267measurePositions(); 268 269