// Copyright 2008 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Generic helpers /** * Create a new XMLHttpRequest in a cross-browser-compatible way. * @return XMLHttpRequest object */ function M_getXMLHttpRequest() { try { return new XMLHttpRequest(); } catch (e) { } try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { } try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { } return null; } /** * Finds the element's parent in the DOM tree. * @param {Element} element The element whose parent we want to find * @return The parent element of the given element */ function M_getParent(element) { if (element.parentNode) { return element.parentNode; } else if (element.parentElement) { // IE compatibility. Why follow standards when you can make up your own? return element.parentElement; } return null; } /** * Finds the event's target in a way that works on all browsers. * @param {Event} e The event object whose target we want to find * @return The element receiving the event */ function M_getEventTarget(e) { var src = e.srcElement ? e.srcElement : e.target; return src; } /** * Function to determine if we are in a KHTML-based browser(Konq/Safari). * @return Boolean of whether we are in a KHTML browser */ function M_isKHTML() { var agt = navigator.userAgent.toLowerCase(); return (agt.indexOf("safari") != -1) || (agt.indexOf("khtml") != -1); } /** * Function to determine if we are running in an IE browser. * @return Boolean of whether we are running in IE */ function M_isIE() { return (navigator.userAgent.toLowerCase().indexOf("msie") != -1) && !window.opera; } /** * Function to determine if we are in a WebKit-based browser (Chrome/Safari). * @return Boolean of whether we are in a WebKit browser */ function M_isWebKit() { return navigator.userAgent.toLowerCase().indexOf("webkit") != -1; } /** * Stop the event bubbling in a browser-independent way. Sometimes required * when it is not easy to return true when an event is handled. * @param {Window} win The window in which this event is happening * @param {Event} e The event that we want to cancel */ function M_stopBubble(win, e) { if (!e) { e = win.event; } e.cancelBubble = true; if (e.stopPropagation) { e.stopPropagation(); } } /** * Return distance in pixels from the top of the document to the given element. * @param {Element} element The element whose offset we want to find * @return Integer value of the height of the element from the top */ function M_getPageOffsetTop(element) { var y = element.offsetTop; if (element.offsetParent != null) { y += M_getPageOffsetTop(element.offsetParent); } return y; } function M_editPatchsetTitle(issue, patchset, xsrf_token, original_patchset_title, patch_count) { var new_patchset_title = prompt( 'Please enter the new title of Patch Set ' + patch_count, original_patchset_title); if (new_patchset_title == null) { return false; } else if (new_patchset_title == original_patchset_title) { // Do not make an HTTP req if the new specified title is exactly the same. return false; } //Build POST data for request. var data = []; data.push('xsrf_token=' + xsrf_token); data.push('patchset_title=' + new_patchset_title); var httpreq = M_getXMLHttpRequest(); if (!httpreq) { return true; } httpreq.onreadystatechange = function() { if (httpreq.readyState == 4) { if (httpreq.status == 200) { window.location.reload(); } } } httpreq.open( 'POST', base_url + issue + "/patchset/" + patchset + '/edit_patchset_title', true); httpreq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); httpreq.send(data.join("&")); return true; } /** * Return distance in pixels of the given element from the left of the document. * @param {Element} element The element whose offset we want to find * @return Integer value of the horizontal position of the element */ function M_getPageOffsetLeft(element) { var x = element.offsetLeft; if (element.offsetParent != null) { x += M_getPageOffsetLeft(element.offsetParent); } return x; } /** * Find the height of the window viewport. * @param {Window} win The window whose viewport we would like to measure * @return Integer value of the height of the given window */ function M_getWindowHeight(win) { return M_getWindowPropertyByBrowser_(win, M_getWindowHeightGetters_); } /** * Find the vertical scroll position of the given window. * @param {Window} win The window whose scroll position we want to find * @return Integer value of the scroll position of the given window */ function M_getScrollTop(win) { return M_getWindowPropertyByBrowser_(win, M_getScrollTopGetters_); } /** * Scroll the target element into view at 1/3rd of the window height only if * the scrolling direction matches the direction that was asked for. * @param {Window} win The window in which the element resides * @param {Element} element The element that we want to bring into view * @param {Integer} direction Positive for scroll down, negative for scroll up */ function M_scrollIntoView(win, element, direction) { var elTop = M_getPageOffsetTop(element); var winHeight = M_getWindowHeight(win); var targetScroll = elTop - winHeight / 3; var scrollTop = M_getScrollTop(win); if ((direction > 0 && scrollTop < targetScroll) || (direction < 0 && scrollTop > targetScroll)) { win.scrollTo(M_getPageOffsetLeft(element), targetScroll); } } /** * Returns whether the element is visible. * @param {Window} win The window that the element resides in * @param {Element} element The element whose visibility we want to determine * @return Boolean of whether the element is visible in the window or not */ function M_isElementVisible(win, element) { var elTop = M_getPageOffsetTop(element); var winHeight = M_getWindowHeight(win); var winTop = M_getScrollTop(win); if (elTop < winTop || elTop > winTop + winHeight) { return false; } return true; } // Cross-browser compatibility quirks and methodology borrowed from // common.js var M_getWindowHeightGetters_ = { ieQuirks_: function(win) { return win.document.body.clientHeight; }, ieStandards_: function(win) { return win.document.documentElement.clientHeight; }, dom_: function(win) { return win.innerHeight; } }; var M_getScrollTopGetters_ = { ieQuirks_: function(win) { return win.document.body.scrollTop; }, ieStandards_: function(win) { return win.document.documentElement.scrollTop; }, dom_: function(win) { return win.pageYOffset; } }; /** * Slightly modified from common.js: Konqueror has the CSS1Compat property * but requires the standard DOM functionlity, not the IE one. */ function M_getWindowPropertyByBrowser_(win, getters) { try { if (!M_isKHTML() && "compatMode" in win.document && win.document.compatMode == "CSS1Compat") { return getters.ieStandards_(win); } else if (M_isIE()) { return getters.ieQuirks_(win); } } catch (e) { // Ignore for now and fall back to DOM method } return getters.dom_(win); } // Global search box magic (global.html) /** * Handle the onblur action of the search box, replacing it with greyed out * instruction text when it is empty. * @param {Element} element The search box element */ function M_onSearchBlur(element) { var defaultMsg = "Enter a changelist#, user, or group"; if (element.value.length == 0 || element.value == defaultMsg) { element.style.color = "gray"; element.value = defaultMsg; } else { element.style.color = ""; } } /** * Handle the onfocus action of the search box, emptying it out if no new text * was entered. * @param {Element} element The search box element */ function M_onSearchFocus(element) { if (element.style.color == "gray") { element.style.color = ""; element.value = ""; } } // Inline diffs (changelist.html) /** * Creates an iframe to load the diff in the background and when that's done, * calls a function to transfer the contents of the iframe into the current DOM. * @param {Integer} suffix The number associated with that diff * @param {String} url The URL that the diff should be fetched from * @return false (for event bubbling purposes) */ function M_showInlineDiff(suffix, url) { var hide = document.getElementById("hide-" + suffix); var show = document.getElementById("show-" + suffix); var frameDiv = document.getElementById("frameDiv-" + suffix); var dumpDiv = document.getElementById("dumpDiv-" + suffix); var diffTR = document.getElementById("diffTR-" + suffix); var hideAll = document.getElementById("hide-alldiffs"); var showAll = document.getElementById("show-alldiffs"); /* Twiddle the "show/hide all diffs" link */ if (hide.style.display != "") { M_CL_hiddenInlineDiffCount -= 1; if (M_CL_hiddenInlineDiffCount == M_CL_maxHiddenInlineDiffCount) { showAll.style.display = "inline"; hideAll.style.display = "none"; } else { showAll.style.display = "none"; hideAll.style.display = "inline"; } } hide.style.display = ""; show.style.display = "none"; dumpDiv.style.display = "block"; // XXX why not ""? diffTR.style.display = ""; if (!frameDiv.innerHTML) { if (M_isKHTML()) { frameDiv.style.display = "block"; // XXX why not ""? } frameDiv.innerHTML = ""; } return false; } /** * Hides the diff that was retrieved with M_showInlineDiff. * @param {Integer} suffix The number associated with the diff we want to hide */ function M_hideInlineDiff(suffix) { var hide = document.getElementById("hide-" + suffix); var show = document.getElementById("show-" + suffix); var dumpDiv = document.getElementById("dumpDiv-" + suffix); var diffTR = document.getElementById("diffTR-" + suffix); var hideAll = document.getElementById("hide-alldiffs"); var showAll = document.getElementById("show-alldiffs"); /* Twiddle the "show/hide all diffs" link */ if (hide.style.display != "none") { M_CL_hiddenInlineDiffCount += 1; if (M_CL_hiddenInlineDiffCount == M_CL_maxHiddenInlineDiffCount) { showAll.style.display = "inline"; hideAll.style.display = "none"; } else { showAll.style.display = "none"; hideAll.style.display = "inline"; } } hide.style.display = "none"; show.style.display = "inline"; diffTR.style.display = "none"; dumpDiv.style.display = "none"; return false; } /** * Dumps the content of the given iframe into the appropriate div in order * for the diff to be displayed. * @param {Element} iframe The IFRAME that contains the diff data * @param {Integer} suffix The number associated with the diff */ function M_dumpInlineDiffContent(iframe, suffix) { var dumpDiv = document.getElementById("dumpDiv-" + suffix); dumpDiv.style.display = "block"; // XXX why not ""? dumpDiv.innerHTML = iframe.contentWindow.document.body.innerHTML; // TODO: The following should work on all browsers instead of the // innerHTML hack above. At this point I don't remember what the exact // problem was, but it didn't work for some reason. // dumpDiv.appendChild(iframe.contentWindow.document.body); if (M_isKHTML()) { var frameDiv = document.getElementById("frameDiv-" + suffix); frameDiv.style.display = "none"; } } /** * Goes through all the diffs and triggers the onclick action on them which * should start the mechanism for displaying them. * @param {Integer} num The number of diffs to display (0-indexed) */ function M_showAllDiffs(num) { for (var i = 0; i < num; i++) { var link = document.getElementById('show-' + i); // Since the user may not have JS, the template only shows the diff inline // for the onclick action, not the href. In order to activate it, we must // call the link's onclick action. if (link.className.indexOf("reverted") == -1) { link.onclick(); } } } /** * Goes through all the diffs and hides them by triggering the hide link. * @param {Integer} num The number of diffs to hide (0-indexed) */ function M_hideAllDiffs(num) { for (var i = 0; i < num; i++) { var link = document.getElementById('hide-' + i); // If the user tries to hide, that means they have JS, which in turn means // that we can just set href in the href of the hide link. link.onclick(); } } // Inline comment submission forms (changelist.html, file.html) /** * Changes the elements display style to "" which renders it visible. * @param {String|Element} elt The id of the element or the element itself */ function M_showElement(elt) { if (typeof elt == "string") { elt = document.getElementById(elt); } if (elt) elt.style.display = ""; } /** * Changes the elements display style to "none" which renders it invisible. * @param {String|Element} elt The id of the element or the element itself */ function M_hideElement(elt) { if (typeof elt == "string") { elt = document.getElementById(elt); } if (elt) elt.style.display = "none"; } /** * Toggle the visibility of a section. The little indicator triangle will also * be toggled. * @param {String} id The id of the target element */ function M_toggleSection(id) { var sectionStyle = document.getElementById(id).style; var pointerStyle = document.getElementById(id + "-pointer").style; if (sectionStyle.display == "none") { sectionStyle.display = ""; pointerStyle.backgroundImage = "url('" + media_url + "opentriangle.gif')"; } else { sectionStyle.display = "none"; pointerStyle.backgroundImage = "url('" + media_url + "closedtriangle.gif')"; } } /** * Callback for XMLHttpRequest. */ function M_PatchSetFetched() { if (http_request.readyState != 4) return; var section = document.getElementById(http_request.div_id); if (http_request.status == 200) { section.innerHTML = http_request.responseText; /* initialize dashboardState again to update cached patch rows */ if (dashboardState) dashboardState.initialize(); } else { section.innerHTML = '
Could not load the patchset (' + http_request.status + ').
'; } } /** * Toggle the visibility of a patchset, and fetches it if necessary. * @param {String} issue The issue key * @param {String} id The patchset key */ function M_toggleSectionForPS(issue, patchset) { var id = 'ps-' + patchset; M_toggleSection(id); var section = document.getElementById(id); if (section.innerHTML.search("