/fix-gmail2.js
http://gmail2-for-opera.googlecode.com/ · JavaScript · 765 lines · 386 code · 64 blank · 315 comment · 99 complexity · 59480364123a69f10a7cbc8a7a0d7728 MD5 · raw file
- // ==UserScript==
- // @name Gmail 2.0 fixes for Opera
- // @author dbloom (special thanks: Ayush, diz^X, fearphage, xErath)
- // @include http://mail.google.com/mail/*ui=2*
- // @include https://mail.google.com/mail/*ui=2*
- // @include http://mail.google.com/mail/*ui=1*
- // @include https://mail.google.com/mail/*ui=1*
- // @include http://mail.google.com/mail/contacts/ui/ContactManager*
- // @include https://mail.google.com/mail/contacts/ui/ContactManager*
- // @include http://mail.google.com/hosted/*.*/*ui=2*
- // @include https://mail.google.com/hosted/*.*/*ui=2*
- // @include http://mail.google.com/hosted/*.*/*ui=1*
- // @include https://mail.google.com/hosted/*.*/*ui=1*
- // @include http://mail.google.com/hosted/*.*/contacts/ui/ContactManager*
- // @include https://mail.google.com/hosted/*.*/contacts/ui/ContactManager*
- // @include http://mail.google.com/a/*.*/*ui=2*
- // @include https://mail.google.com/a/*.*/*ui=2*
- // @include http://mail.google.com/a/*.*/*ui=1*
- // @include https://mail.google.com/a/*.*/*ui=1*
- // @include http://mail.google.com/a/*.*/contacts/ui/ContactManager*
- // @include https://mail.google.com/a/*.*/contacts/ui/ContactManager*
- // @include https://www.google.com/*/ServiceLogin*
- // ==/UserScript==
- // 27-11-2007 5:50PM Added version numbering,
- // fixed overflow issue with chat conversations in Merlin
- // 6:05PM Actually fixed it :P
- // 28-11-2007 10:23AM Applied the getBoundingClientRect patch to all frames.
- // This fixes the "Options" menu on chats...
- // Also fixed rich text editor.
- // 28-11-2007 2:00PM Fixed font size in rich text editor.
- // 5:13PM Fixed a Merlin syntax error caused by regex_cm_1
- // 30-11-2007 4:33PM Fixed bug that broke chat popout windows
- // 01-12-2007 10:38AM Removed fix for top border on messages because
- // Google fixed it. Code cleanup.
- // Reduced UA string for Safari spoofing on contact mgr.
- // 12:57PM Fixed "sign out"/"older version" links
- // 3:33PM Prevent scrolling from canvas frame from scrolling
- // parent frame
- // "Next sender" thing at bottom right corner now updates
- // as you scroll in conversations.
- // 02-12-2007 7:44PM Fixed regression that caused scrolling to not work on
- // "show original", etc view.
- // 9:54PM Detect script source view/etc using document.compatMode,
- // to prevent the script from running when viewing Gmail's
- // JS and such.
- // 04-12-2007 9:43AM Fixed adding labels to messages
- // 10:48AM Fixed UNIX regression caused by previous fix
- // 05-12-2007 7:10AM The UNIX fix didn't actually work. Disabled <wbr>/­
- // in UNIX - unfortunately, this means long words won't
- // wrap :-(
- // 06-12-2007 9:45PM Added "Use new version" checkbox to login page :-)
- // 08-12-2007 11:25AM Fixed "highlight" feature in rich text editor
- // Improved "quote" feature in rich text editor
- // (this fix has a few bugs...:-P)
- // Fixed <wbr> in Kestrel on UNIX (still
- // unfixed in Merlin on UNIX)
- // 14-12-2007 10:13AM Hide getBoundingClientRect on builds where it is broken
- // Code cleanup
- // 16-12-2007 11:15AM Moved to Google Code
- // Minor code cleanup and tweaks
- // 16-12-2007 10:27PM Fixed scroll to top when opening messages
- // Fixed label display next to conversation subject heading
- // 27-12-2007 8:26PM Hello from Oslo :-)
- // Used Greasemonkey API to improve scroll to top behavior
- // Removed getBoundingClientRect fixes because they are no
- // longer needed in the latest snapshots.
- // Fixed bug that left a newline char in <textarea> after
- // sending chat message
- // Contact pictures now appear in contact manager
- // 29-12-2007 9:03PM No more horizontal scrollbar!!!
- // 30-12-2007 10:15AM Disabled getBoundingClientRect in some builds I forgot to
- // disable it for...
- // 25-01-2008 4:10PM Updated to work with some changes Google made to IFRAMEs
- // 26-01-2008 1:40PM Fixed rich text editor, chat (again)
- // 30-01-2008 8:30AM Fixed duplicate label creation (again)
- // 14-02-2008 10:02AM Google has fixed some Opera bugs, script updated
- // accordingly :-D
- // 06-04-2008 7:04PM Fixed disabled horizontal scrollbar
- // Attempted to fix many random, tough to reproduce JS
- // errors by blocking queued mouse events if their target
- // is no longer in the same spot as before (this matches
- // the behavior of other browsers)
- // Optimized scroll to top behavior to prevent extra
- // repaint
- // Simplified selectors used in CSS patches for better
- // performance
- // Enabled "Newer version" link in ?ui=1
- // Temporarily override String.prototype.indexOf to prevent
- // actually needing "&nocheckbrowser" in the URL
- // Track reflow count in the performance debugging log
- // Hide text selections caused by focus
- // Fix names on contact manager - Google fixed their code
- // to make it standards compliant, which worked in Firefox
- // 3 but also revealed a new Opera bug :-P
- // Fixed include's so that the user JS supports Google Apps
- // For Your Domain's Gmail service
- (function gmail2_user_js(opera, doc) {
-
- if (location.search) {
- var s = location.search + '&';
- if (s.indexOf('?ui=1&') + s.indexOf('&ui=1&') > -2) {
- // The best way to get the "Newer version" link is to mask as another
- // browser inside of the JS frame on Gmail 1.0. There is no WebKit or
- // Opera-specific code here, and masking as Gecko would require spoofing
- // navigator.product, and masking as IE7 is risky. So we mask as WebKit.
- if (window.name == 'js')
- navigator.userAgent = 'Mozilla/5.0 (' + navigator.platform + '; U; en-us) AppleWebKit/525.13 (żpera, like KHTML) Version/3.1 Damer/525.13';
- return;
- }
- }
-
- if (top == self) {
- // Make Gmail 2.0 think that "nocheckbrowser" is in location.href
- (function(indexOf) {
- String.prototype.indexOf = function(k) {
- if (k == 'nocheckbrowser' && this == location.href) {
- // Restore the original indexOf for performance reasons
- String.prototype.indexOf = indexOf;
- return 1337;
- }
- return indexOf.apply(this, arguments);
- };
- })(String.prototype.indexOf);
- }
-
- // Make a <style> element to provide CSS fixes as needed
- var headEl = document.getElementsByTagName('head')[0];
- if (headEl) {
- var styleEl = document.createElement('style');
- headEl.appendChild(styleEl);
- headEl = null;
- }
-
- // True if the version number starts with 9.5, or has 2 or more digits before the first decimal point.
- // (parseFloat/etc is risky because Opera versions can have more than 1 decimal point...)
- var isKestrel = (opera.version().indexOf('.') > 1 || opera.version().indexOf('9.5') == 0);
-
- if (location.pathname.indexOf('/ServiceLogin') > -1) {
- document.addEventListener('DOMContentLoaded'
- ,function() {
- var doc = document, continueUri;
-
- // No new UI for Google Reader yet. ;-P
- if (!doc.getElementById('PersistentCookie') || !doc.getElementById('continue') ||
- ((continueUri = doc.getElementById('continue').value).indexOf('/mail') == -1))
- return;
-
- var referenceRow = doc.getElementById('PersistentCookie').parentNode.parentNode
- ,ui2TR = doc.createElement('tr')
- ,tdCheckUi2 = ui2TR.appendChild(doc.createElement('td'));
- tdCheckUi2.setAttribute('align', 'right');
-
- var checkUi2 = tdCheckUi2.appendChild(doc.createElement('input'));
- checkUi2.setAttribute('type', 'checkbox');
- checkUi2.setAttribute('id', 'ui2');
- checkUi2.checked = (doc.cookie.indexOf('ui2=yes') > -1);
-
- var tdLabelUi2 = ui2TR.appendChild(doc.createElement('td'))
- ,labelUi2 = tdLabelUi2.appendChild(doc.createElement('label'));
- labelUi2.setAttribute('class', 'gaia le lbl');
- labelUi2.setAttribute('for', 'ui2');
- labelUi2.appendChild(doc.createTextNode('Use new version'));
-
- referenceRow.parentNode.insertBefore(ui2TR, referenceRow);
-
- checkUi2.form.addEventListener('submit'
- ,function() {
- var newUri;
- if (document.getElementById('ui2').checked) {
- // By 2027, Opera will be leading the browser market so this script will no longer be needed anyway =)
- document.cookie = 'ui2=yes; expires=Wed, Dec 1, 2027 3:25:48 PM; path=/';
- newUri = continueUri.replace('ui=1', 'ui=2');
- if (newUri.indexOf('ui=2') == -1) {
- var splitByHash = newUri.split('#'), base = splitByHash.shift() + (newUri.indexOf('?') > -1 ? '&' : '?') + 'ui=2';
- splitByHash.unshift(base);
- newUri = splitByHash.join('#');
- }
- document.getElementById('continue').value = newUri;
- }
- else {
- // We don't really need this cookie to last because "new version" is not checked by default.
- // So, we let this cookie expire immediately by using a date in the past for expires.
- document.cookie = 'ui2=no; expires=Fri, 27 Jul 2001 02:47:11 UTC; path=/';
- if (continueUri.indexOf('disablechatbrowsercheck') == -1)
- document.getElementById('continue').value += (continueUri.indexOf('?') > -1 ? '&' : '?') + 'disablechatbrowsercheck';
- }
- }
- ,false
- );
- }
- ,false
- );
- return;
- }
-
- // Opera bug: getBoundingClientRect horribly broken in some builds
- switch (navigator.platform.substr(0,3) + opera.buildNumber()) {
- case 'Mac4579': case 'Win9694': case 'Lin1719':
- case 'Mac4591': case 'Win9716': case 'Lin1729':
- delete Element.prototype.getBoundingClientRect;
- }
- // Make sure we aren't in a chat popout window (&view=cw)
- if ((location.search.indexOf('&view=cw') == -1) &&
- (window.top === window.self) ||
- window.frameElement && (window.frameElement.name == 'js')) {
- /*
- // Opera bug: Script in parent frame called from an IFRAME terminates if IFRAME is removed (298005)
- // Workaround: delay removal of IFRAMEs.
- var delay = 10000;
- (function(removeChild) {
- // Removes scripts so that any unexecuted <script>s don't run after
- // the frame they are in is removed.
- var removeScripts = function(doc) {
- var elements, i;
- elements = doc.getElementsByTagName('iframe');
- i = elements.length;
- if (i--) do {
- removeScripts(elements[i].contentDocument);
- } while (i--);
- elements = doc.getElementsByTagName('script');
- i = elements.length;
- if (i--) do {
- removeChild.call(elements[i].parentNode, elements[i]);
- } while (i--);
- };
- Element.prototype.removeChild = function(child) {
- if (!(child instanceof HTMLIFrameElement))
- return removeChild.apply(this, arguments);
- var parent = this;
- var timeout;
- // If the iframe is reused, cancel the timeout!
- var reinsertListener = function() {
- clearTimeout(timeout);
- child.removeEventListener('DOMNodeInsertedIntoDocument', reinsertListener, false);
- };
- child.addEventListener('DOMNodeInsertedIntoDocument', reinsertListener, false);
- timeout = setTimeout(function() {
- if (child && child.parentNode === parent) {
- child.removeEventListener('DOMNodeInsertedIntoDocument', reinsertListener, false);
- removeChild.call(parent, child);
- }
- }, delay);
- removeScripts(child.contentDocument);
- child.contentDocument.close();
- };
- })(Element.prototype.removeChild);*/
-
-
- // Opera bug: XMLHttpRequest not continuously firing onreadystatechange and updating responseText on data receive (299926)
- // Special thanks to diz^X for debugging chat and finding this problem!
- // Workaround: Poll responseText and fire onreadystatechange as needed
- (function(send) {
- // We replace onreadystatechange with an instance of this wrapper.
- // This way, we can use instanceof later to determine if
- // onreadystatechange is already wrapped.
- var WrappedReadyStateChange = function(xhrInstance) {
- var o = xhrInstance.onreadystatechange;
- var rText = '';
- var interval = null;
- return function() {
- if (xhrInstance.readyState == 3 && xhrInstance.status == 200) {
- rText = xhrInstance.responseText;
- o.apply(this, arguments);
- if (interval === null) {
- var t = this, a = arguments;
- interval = setInterval(function() {
- var success;
- try {
- if (xhrInstance.readyState == 3) {
- if (xhrInstance.responseText.length != rText.length) {
- rText = xhrInstance.responseText;
- o.apply(t, a);
- }
- success = true;
- }
- } finally {
- if (!success) {
- clearInterval(interval);
- interval = null;
- rText = '';
- }
- }
- }, 250);
- }
- } else {
- if (interval !== null) {
- clearInterval(interval);
- interval = null;
- }
- rText = '';
- if (xhrInstance.readyState == 4) {
- xhrInstance.onreadystatechange = o;
- }
- o.apply(this, arguments);
- }
- };
- };
- XMLHttpRequest.prototype.send = function() {
- if (this.onreadystatechange && !(this.onreadystatechange instanceof WrappedReadyStateChange)) {
- var o = this.onreadystatechange;
- this.onreadystatechange = new WrappedReadyStateChange(this);
- var success;
- try {
- var retVal = send.apply(this, arguments);
- success = true;
- } finally {
- if (!success) {
- this.onreadystatechange = o;
- }
- }
- return retVal;
- }
- if (interval !== null) clearInterval(interval);
- interval = null;
- rText = '';
- return send.apply(this, arguments);
- };
- })(XMLHttpRequest.prototype.send);
-
- opera.addEventListener('BeforeScript',function (e) {
- if (e.element.src.indexOf('&name=bjs&') > -1) {
- opera.removeEventListener('BeforeScript', arguments.callee, false);
-
- // Opera bug: IFRAME script thread changing top.location.href to a fragment identifier causes a reload (298627)
- // Workaround: change top.location.hash instead
- // (thanks xErath)
- e.element.text = e.element.text.replace(/(\w+)\.href=(\w+)/g,'($1==top.location&&top.location.href.split("#")[0]==$2.split("#")[0])?($1.hash=$2.split("#").pop())&&$1.href:$1.href=$2');
- }
-
- // Google bug: Using ­ in Opera breaks adding labels to messages (301574)
- /*
- var replacement;
- if (isKestrel) {
- replacement = '<wbr style=\\\"white-space: nowrap;\\\" \\\/>';
- } else if (navigator.platform.indexOf('Win') == navigator.platform.indexOf('Mac')) {
- // *nix
- // replacement = '"<wbr style=\\\"position:absolute;width:0px;height:0px;overflow:hidden;content:"\\\\00200B"\\\">"';
- replacement = ''; // TODO: Find a good <wbr> substitute that works on Linux...:-P
- } else {
- // Win/Mac
- replacement = '<wbr style=\\\"content:"\\\\00200B"\\\">';
- }
- e.element.text = e.element.text.replace('­', replacement);
- */
- },false);
-
- // Better fix using GreaseMonkey API is now implemented.
- /*
- opera.addEventListener('BeforeScript',function (e) {
- if (e.element.src.indexOf('&name=js&') + 1) {
- opera.removeEventListener('BeforeScript', arguments.callee, false);
- // Google bug: Browser sniffing makes Gmail not scroll to top automatically (303234)
- // Workaround: Activate the Safari-specific code for this.
- //e.element.text = e.element.text.replace(/if\(([\d\w\s]+)\)\{if\(this\.([\d\w\s]+)\.([\d\w\s]+)\)\{this\.([\d\w\s]+)\.([\d\w\s]+)\.focus\(\);this\.([\d\w\s]+)\.([\d\w\s]+)\.blur\(\)\}\}/i, 'if (!$1){if(this.$2.$3 && "$2"=="$4" && "$4"=="$6" && "$3"=="$5" && "$5"=="$7"){this.$2.$3.focus();this.$2.$3.blur();}}');
- }
- }, false);
- */
- (function(originalEval) {
-
- // Some general-purpose code for unborking things
- function Fixes() {
- this.fixes = [];
- };
- Fixes.prototype.addFix = function(var_args) {
- var fixes = Array.prototype.slice.call(arguments);
- do { this.fixes.push(fixes.pop()) } while (fixes.length);
- };
- Fixes.prototype.unbork = function(str) {
- var fix, fixIndex = this.fixes.length;
- if (!fixIndex--) return str;
- do {
- fix = this.fixes[fixIndex];
- if (fix.canUnbork(str)) {
- str = fix.unbork(str);
- this.fixes.splice(fixIndex, 1);
- return str;
- }
- } while (fixIndex--);
- return str;
- };
- Fixes.prototype.isEmpty = function() {
- return this.fixes.length == 0;
- }
-
- function Fix(substr) {
- this.substr = substr;
- this.unborkers = [];
- };
- Fix.prototype.addUnborker = function(var_args) {
- var unborkers = Array.prototype.slice.call(arguments);
- do { this.unborkers.push(unborkers.pop()) } while (unborkers.length);
- };
- Fix.prototype.canUnbork = function(str) {
- // without "str.indexOf && ", chat breaks because it likes to pass
- // non-string things to eval for some reason.
- return str.indexOf && str.indexOf(this.substr) != -1;
- };
- Fix.prototype.unbork = function(str) {
- var unbork, unborkerIndex = this.unborkers.length;
- if (!unborkerIndex--) return str;
- do {
- unbork = this.unborkers[unborkerIndex];
- str = unbork(str);
- } while (unborkerIndex--);
- return str;
- };
-
- function Unborker(find, replace) {
- return function(txt) {
- return txt.replace(find, replace);
- };
- };
-
- // Override browser blocking on rich text editor
- // The regex matches a string like this:
- // return Cdp&&Cqp(IXa)>=0||Cep&&Cqp(JXa)>=0||Cmp&&Cqp(KXa)>=0}
- // (...which basically tests for Moz 1.8+, IE 6+, or WebKit 520+)
- var fixRichTextBrowserBlocking = new Unborker(/return [\d\w\s]*&&[\d\w\s]*\([\d\w\s]*\)[\s]?>=[\s]?0[\s]?\|\|[\d\w\s]*&&[\d\w\s]*\([\d\w]*\)[\s]?>=[\s]?0[\s]?\|\|[\d\w\s]*&&[\d\w\s]*\([\d\w\s]*\)[\s]?>=0[\s]?}/, '\nreturn true;}\n');
-
- // Google bug: Opera uses IE-compatible font-sizes in quirksmode
- // This workaround creates a rule that has more specificity than
- // the .tr-field rule, instead of using !important, because using
- // !important would override inline font styles on the editable text.
- var fixRichTextFontSize = new Unborker(/\.tr-field\{/, 'html > body.tr-field{font-size:x-small;}.tr-field{');
-
- // Google bug: WTF... getSelection().parentElement()???
- var fixGetSelectionParentElement = new Unborker(/(\w+)[\s]?\.[\s]?getSelection\(\)[\s]?\.[\s]?parentElement\(\)/, '\n($1.getSelection().getRangeAt(0).commonAncestorContainer instanceof $1.getSelection().getRangeAt(0).commonAncestorContainer.defaultView.Text)?($1.getSelection().getRangeAt(0).commonAncestorContainer.parentNode):($1.getSelection().getRangeAt(0).commonAncestorContainer)\n');
- // Google bug: highlight uses IE codepath...hiliteColor, not backColor...
- var fixHilite = new Unborker(/backColor/, 'hiliteColor');
-
- // Google bug: quote uses IE codepath
- // This workaround isn't "quite right" but it is usable...
- var fixQuote = new Unborker(/(\w+)[\s]?\.surroundContents[\s]?\((\w+)\)[\s]?/g, '(($2 instanceof $2.ownerDocument.defaultView.HTMLQuoteElement)?(function(){$2.ownerDocument.execCommand("FormatBlock",null,"address");var addresses=$2.ownerDocument.getElementsByTagName("address");var i = addresses.length;if (i--){$2.innerHTML="";do{$2.innerHTML = (i?"<br>":"") + addresses[i].innerHTML + $2.innerHTML;if (i) addresses[i].parentNode.removeChild(addresses[i]);}while(i--);addresses[0].parentNode.replaceChild($2,addresses[0]);} })():$1.surroundContents($2))');
-
- var cmFix = new Fix('_MD_Before(\'cm\')');
- cmFix.addUnborker(fixRichTextBrowserBlocking, fixRichTextFontSize);
-
- var eFix = new Fix('_MD_Before(\'e\')');
- eFix.addUnborker(fixGetSelectionParentElement, fixHilite, fixQuote);
-
- var evalFixes = new Fixes();
- evalFixes.addFix(cmFix, eFix);
-
- window.eval = function(str) {
- str = evalFixes.unbork(str);
-
- if (evalFixes.isEmpty()) {
- // Just in case window.eval was wrapped again by another user JS...
- if (window.eval === arguments.callee) {
- // Okay, we can safely restore eval.
- window.eval = originalEval;
- }
- }
-
- return originalEval.call(this, str);
- };
- })(window.eval);
-
- // Prevent Gmail from throwing errors on resize --
- // this bug needs further analysis.
- // (The IE codepath delays calls to dispatchEvent("sizechange"),
- // and that workaround seems to work for Opera too.)
- /*opera.addEventListener('BeforeScript', function(e) {
- e.element.text = e.element.text.replace(/;this\.dispatchEvent\((\w+)\)/g, ';($1=="sizechange"?(this.__timeout = setTimeout((function(o){clearTimeout(o.__timeout || 0);return function(){delete o.__timeout;o.dispatchEvent($1)};})(this),50)):(this.dispatchEvent($1)))');
- }, false);*/
-
- // Fix chat - Merlin doesn't support overflow-x, overflow-y
- if (!('overflowX' in document.documentElement.style)) {
- styleEl.text += 'div { overflow: auto !important; } div span { margin-left: 0 !important; }';
- }
- if (window.top === window.self) {
- // === TOP FRAME ===
-
- // Don't break viewing of script source, etc...
- if (document.compatMode == 'BackCompat') {
- return;
- }
-
- // Override browser blocking
- var href = location.href;
- //if (href.indexOf('nocheckbrowser') == -1)
- //location.replace(href.split('#')[0] + (href.indexOf('?') > -1 ? '&' : '?') + 'nocheckbrowser' + location.hash);
-
- // Hook into Gmail 2.0's GreaseMonkey APIs.
- var greaseReady = false;
- var greaseLoaded = false;
- var gmonkey, gmail;
- var initGrease = function(newGmonkey) {
- if (greaseReady || greaseLoaded) return;
- if (!newGmonkey.load) {
- setTimeout(function(){initGrease(newGmonkey)}, 100);
- return;
- }
- greaseReady = true;
- gmonkey = newGmonkey;
- gmonkey.load('1.0', function(GmailAPI) {
- greaseLoaded = true;
- gmail = GmailAPI;
- gmail.registerViewChangeCallback(function(view) {
- // Google bug: Browser sniffing makes Gmail not scroll to top automatically (303234)
- // Scrolling forces a repaint. Use a timeout to coalesce the repaint
- // with the paint for the new view.
- setTimeout(function() {
- var i = document.getElementById("canvas_frame").contentWindow.document.getElementsByTagName('input')[0];
- i.focus();
- i.blur();
- }, 0);
- //document.getElementById("canvas_frame").contentWindow.scrollTo(0,0);
- });
- });
- };
- window.addEventListener('load', function() {
- window.removeEventListener('load', arguments.callee, false);
- initGrease(js.gmonkey);
- }, false);
-
- // Opera bug: onunload should fire on top before firing on iframes (like FF/Safari) (301176)
- // Gmail reloads becuase it expects the other ordering...
- // Workaround: no reload
- //window.location.reload = function() { };
-
- // Google bug: documentElement doesn't fire scroll events in Kestrel...
- if (isKestrel) {
- window.addEventListener('DOMContentLoaded', function() {
- var canvasFrame = document.getElementById('canvas_frame');
- if (!canvasFrame || !canvasFrame.contentDocument) {
- setTimeout(arguments.callee, 10);
- return;
- }
- var contentDocument = canvasFrame.contentDocument;
- (function(addEventListener) {
- contentDocument.documentElement.addEventListener = function(evtType) {
- if (evtType == 'scroll') {
- return contentDocument.addEventListener.apply(contentDocument, arguments);
- }
- return addEventListener.apply(this, arguments);
- };
- })(contentDocument.documentElement.addEventListener);
- window.removeEventListener('DOMContentLoaded', arguments.callee, false);
- }, false);
- }
-
- // Opera bug: preventDefault doesn't work with keydown event. (234302)
- // This causes a line break to be left in the chat <textarea> after sending a message (by pressing [enter])
- // Workaround: clear out the <textarea> when it seems appropriate
- /*
- opera.addEventListener('AfterEvent.keydown', function(e) {
- var target = e.event.target;
- if (e.eventCancelled && target instanceof HTMLTextAreaElement && e.event.keyCode == 13) {
- if (target.value.replace(/\s/g,'') == '') {
- var timeout;
- var targetClear = function() {
- target.value = '';
- target.readOnly = false;
- target.removeEventListener('keyup', targetClear, false);
- clearTimeout(timeout);
- };
- target.readOnly = true;
- timeout = setTimeout(targetClear, 10);
- target.addEventListener('keyup', targetClear, false);
- }
- }
- },false);
- */
- }
- } else {
- // === CHILD FRAMES ===
-
- // Opera bug: Not possible to hide focus rect using CSS (298044)
- // Workaround: disable focus() on DIV :-P
- // (Disabled to test focus functionality once this is fixed...)
- // HTMLDivElement.prototype.focus = function() { };
-
- // Trying to fix scroll jumping when clicking things. Doesn't really work
- // for some reason.
- /*(function(focus) {
- HTMLDivElement.prototype.focus = function() {
- if (!this.ownerDocument || !this.ownerDocument.body)
- return focus.apply(this, arguments);
- var b = this.ownerDocument.body;
- var x = b.scrollLeft, y = b.scrollTop;
- var retVal = focus.apply(this, arguments);
- x -= b.scrollLeft;
- y -= b.scrollTop;
- opera.postError(x + '\n' + y);
- if (x || y || 1) this.ownerDocument.defaultView.scrollBy(x, y);
- return retVal;
- };
- })(HTMLDivElement.prototype.focus);*/
-
- // Make it possible to change links in the rich text editor.
- // Probably a timing issue with ordering of blur and mousedown events
- // across frames (needs analysis...)
- opera.addEventListener('BeforeEventListener.blur', function(e) {
- if (e.event.target instanceof HTMLDocument) {
- e.preventDefault();
- }
- }, false);
- opera.addEventListener('BeforeEventListener.keydown', function(e) {
- if (e.event.ctrlKey && e.event.altKey) {
- e.stopPropagation();
- }
- }, false);
- if (window.frameElement && frameElement.id.indexOf('canvas_frame') > -1) {
- // === CANVAS FRAME ===
-
- // Opera bug: <html> or <body> with overflow:hidden too easy to scroll (300804)
- top.document.body.style.position = 'fixed !important';
-
- if ('overflowY' in document.documentElement.style) {
- // Hide disabled horizontal scrollbar in Kestrel (Opera bug, 292597)
- styleEl.text += 'html { overflow-x: auto !important; overflow-y: auto !important } body { overflow: auto !important }';
- } else {
- // Make scrollbars appear in Merlin
- // This makes a horizontal scrollbar too, but there's not much I can do to prevent it...:-(
- styleEl.text += 'html > body { overflow: auto !important; }';
- }
-
-
- // Opera bug: Fix page width if Gmail is loaded in an inactive tab
- // (TODO: Make some TCs and file this bug :-P)
- // Watch the loadingDiv to know when the canvas is completely rendered.
- var loadingDiv = top.document.getElementById('loading');
- var fixWidthTimeout = 0;
- var fixWidth = function() {
- // Don't fire the resize event if devtools are open,
- // because we don't want the resize event listener to reset
- // the entire DOM whenever the Gmail window is focused, making
- // the devtools DOM tree useless.
- if (!window.__registered) {
- var evt = document.createEvent('HTMLEvents');
- evt.initEvent('resize', true, true);
- window.dispatchEvent(evt);
- }
- };
- var focusListener = function() {
- top.removeEventListener('focus', focusListener, false);
- clearTimeout(fixWidthTimeout);
- fixWidthTimeout = setTimeout(fixWidth, 500);
- };
- var blurListener = function() {
- top.addEventListener('focus', focusListener, false);
- clearTimeout(fixWidthTimeout);
- };
- loadingDiv.addEventListener('DOMAttrModified', function(attrEvent) {
- if (attrEvent.attrName != 'style' || loadingDiv.style.display != 'none')
- return;
- loadingDiv.removeEventListener('DOMAttrModified', arguments.callee, false);
- fixWidth();
- top.addEventListener('focus', focusListener, false);
- top.addEventListener('blur', blurListener, false);
- }, false);
-
- // Google bug: Fix missing top border around messages (298593)
- // FIXED by Google at cl 5964091
- // styleEl.text += 'div[style="height:10px;display:"] { position: relative; }';
-
- // Opera bug: Incorrect documentElement.scrollHeight if height is specified for root element (300393 - Kestrel regression)
- if (isKestrel) {
- styleEl.text += 'html { width: auto !important; height: auto !important; }';
- }
-
- if (isKestrel) {
- // hide annoying focus highlighting
- // this selector is complex, but the browser's selector engine can
- // short circuit it out whenever ::selection doesn't match so
- // it doesn't really slow anything down.
- styleEl.text += 'div[hidefocus]:not([contenteditable]):focus::selection, div[hidefocus]:not([contenteditable]):focus *::selection, div[tabindex]:not([contenteditable]):focus::selection, div[tabindex]:focus:not([contenteditable]) *::selection { background-color: transparent !important }';
- }
-
- // Google bug: Fix label positioning. This is horribly broken on Safari 3
- // and Firefox 3 too...
- // Opera bug: Repainting after hovering over label name is messed up. Caused by repaint bugs with inline-table (304347)
- // (Workaround: add a hidden outline. This workaround only works if scrolled all the way to top for some reason...)
- //styleEl.text += '\/*h1 > span + span { outline: 1px hidden white !important; }*\/ h1 > span + span > table { display: inline-table !important; vertical-align: top !important; }';
- // This selector is faster than the older one:
- styleEl.text += 'h1 table { vertical-align: top !important; display: inline-table !important }';
- } else if (location.pathname.indexOf('/ContactManager') > -1) {
- // === CONTACTS MANAGER ===
-
- // Google bug: Make names in contact manager visible (294911)
- // ...has been fixed by Google, revealing:
- // Opera bug: floats shouldn't provide opportunity for linebreaking
- // (314479)
- styleEl.text += '.checkable-list .row .check { display: inline !important; float: none !important }';
-
- // Opera bug: onload/onerror don't fire on images dynamically added to display:none parent (304786)
- // without this workaround, contact images won't appear in the contact manager.
- styleEl.text += '.contact-pane img[onload][style*="display: none"] { display: inline !important; }';
-
- // horizontal scrolling issue when hovering contact image (needs analysis)
- styleEl.text += '.contact-pane { overflow-x: hidden !important; }';
-
- // Opera bug: repaint issue when hovering contact image (needs analysis)
- styleEl.text += '#contact-picture > div > div { outline: 0px solid white; }';
-
- // Fix contact manager width (needs analysis...)
- // Google's standards-mode WebKit viewport size calculation seems to work fine in Opera 9.5 Quirks Mode. :-P
- navigator.userAgent = 'Opera, spoofing as AppleWebKit/523.12';
- opera.addEventListener('BeforeScript',function () {
- window.opera = undefined;
- }, false);
- document.compatMode = 'CSS1Compat';
- styleEl.text += '#ContactManager { height: 100% !important; overflow: hidden !important; }';
- // Merlin doesn't support overflow-x, overflow-y
- if (!('overflowX' in document.documentElement.style)) {
- styleEl.text += '.checkable-list, .group-list, .contact-pane { overflow: auto !important; }';
- }
- }
- }
- })(window.opera, window.document);
- // Performance profiling: Logs event handler executions that take more than 1 second. (thanks fearphage)
- (function(opera) {
- var re = /(?:After|Before)EventListener/;
- currentEvents = {};
- opera.addEventListener('BeforeEventListener', function(e) {
- if (!re.test(e.event.type)) {
- currentEvents[e.event.type] = {
- date: new Date(),
- reflowCount: opera.reflowCount
- };
- }
- },false);
- var mouseEventFix = function(e) {
- var actualTarget = document.elementFromPoint(e.event.clientX + pageXOffset, e.event.clientY + pageYOffset);
- actualTarget = (actualTarget instanceof Text) ? actualTarget.parentNode : actualTarget;
- if (e.event.target != actualTarget) {
- e.preventDefault();
- }
- };
- opera.addEventListener('BeforeEvent.mousedown', mouseEventFix, false);
- opera.addEventListener('BeforeEvent.mouseup', mouseEventFix, false);
- opera.addEventListener('BeforeEvent.click', mouseEventFix, false);
- // opera.addEventListener('BeforeEventListener.mousemove', mouseEventFix, false);
-
- opera.addEventListener('AfterEventListener', function(e) {
- var type = e.event.type, time;
- // v-- assignment statement!
- if (!re.test(type) && (e2 = currentEvents[type])) {
- time = new Date() - e2.date;
- reflows = opera.reflowCount - e2.reflowCount;
- if (time > 1000 || reflows > 5) {
- opera.postError(type + ': ' + time + 'ms, ' + reflows + ' reflows');
- }
- delete currentEvents[type];
- }
- }, false);
- })(window.opera);
- // For debugging
- if (top.location.search.indexOf('likeGecko') > -1) {
- window.opera = undefined;
- navigator.product = 'Gecko';
- navigator.userAgent = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11';
- }
- if (top.location.search.indexOf('likeNothing') > -1) {
- window.opera = undefined;
- }