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