diff --git a/app/assets/javascripts/discourse/lib/Markdown.Editor.js b/app/assets/javascripts/discourse/lib/Markdown.Editor.js index 1880beec587..8a267d2f34d 100644 --- a/app/assets/javascripts/discourse/lib/Markdown.Editor.js +++ b/app/assets/javascripts/discourse/lib/Markdown.Editor.js @@ -40,6 +40,11 @@ nav = window.navigator, SETTINGS = { lineLength: 72 }, + // Used to work around some browser bugs where we can't use feature testing. + uaSniffed = { + isIE: /msie/.test(nav.userAgent.toLowerCase()), + isIE_5or6: /msie 6/.test(nav.userAgent.toLowerCase()) || /msie 5/.test(nav.userAgent.toLowerCase()) + }; var defaultsStrings = { bold: "Strong Ctrl+B", @@ -330,21 +335,43 @@ // Returns true if the DOM element is visible, false if it's hidden. // Checks if display is anything other than none. util.isVisible = function (elem) { - return window.getComputedStyle(elem, null).getPropertyValue("display") !== "none"; + + if (window.getComputedStyle) { + // Most browsers + return window.getComputedStyle(elem, null).getPropertyValue("display") !== "none"; + } + else if (elem.currentStyle) { + // IE + return elem.currentStyle["display"] !== "none"; + } }; // Adds a listener callback to a DOM element which is fired on a specified // event. util.addEvent = function (elem, event, listener) { - elem.addEventListener(event, listener, false); + if (elem.attachEvent) { + // IE only. The "on" is mandatory. + elem.attachEvent("on" + event, listener); + } + else { + // Other browsers. + elem.addEventListener(event, listener, false); + } }; // Removes a listener callback from a DOM element which is fired on a specified // event. util.removeEvent = function (elem, event, listener) { - elem.removeEventListener(event, listener, false); + if (elem.detachEvent) { + // IE only. The "on" is mandatory. + elem.detachEvent("on" + event, listener); + } + else { + // Other browsers. + elem.removeEventListener(event, listener, false); + } }; // Converts \r\n and \r to \n. @@ -427,8 +454,21 @@ scrollHeight = doc.body.offsetHeight; } - innerWidth = self.innerWidth; - innerHeight = self.innerHeight; + if (self.innerHeight) { + // Non-IE browser + innerWidth = self.innerWidth; + innerHeight = self.innerHeight; + } + else if (doc.documentElement && doc.documentElement.clientHeight) { + // Some versions of IE (IE 6 w/ a DOCTYPE declaration) + innerWidth = doc.documentElement.clientWidth; + innerHeight = doc.documentElement.clientHeight; + } + else if (doc.body) { + // Other versions of IE + innerWidth = doc.body.clientWidth; + innerHeight = doc.body.clientHeight; + } var maxWidth = Math.max(scrollWidth, innerWidth); var maxHeight = Math.max(scrollHeight, innerHeight); @@ -456,7 +496,7 @@ } } - if (mode != "moving") { + if (!uaSniffed.isIE || mode != "moving") { timer = setTimeout(refreshState, 1); } else { @@ -559,7 +599,9 @@ if ((event.ctrlKey || event.metaKey) && !event.altKey) { - var keyCodeChar = String.fromCharCode(event.charCode); + // IE and Opera do not support charCode. + var keyCode = event.charCode || event.keyCode; + var keyCodeChar = String.fromCharCode(keyCode); switch (keyCodeChar.toLowerCase()) { @@ -639,7 +681,7 @@ }); var handlePaste = function () { - if (inputStateObj && inputStateObj.text != panels.input.value) { + if (uaSniffed.isIE || (inputStateObj && inputStateObj.text != panels.input.value)) { if (timer == undefined) { mode = "paste"; saveState(); @@ -1001,6 +1043,24 @@ var isFirstTimeFilled = true; + // IE doesn't let you use innerHTML if the element is contained somewhere in a table + // (which is the case for inline editing) -- in that case, detach the element, set the + // value, and reattach. Yes, that *is* ridiculous. + var ieSafePreviewSet = function (previewText) { + var ieSafeSet = function(panel, text) { + var parent = panel.parentNode; + var sibling = panel.nextSibling; + parent.removeChild(panel); + panel.innerHTML = text; + if (!sibling) + parent.appendChild(panel); + else + parent.insertBefore(panel, sibling); + }; + + ieSafeSet(panels.preview, previewText); + } + var nonSuckyBrowserPreviewSet = function (previewText) { panels.preview.innerHTML = previewText; } @@ -1012,8 +1072,13 @@ if (previewSetter) return previewSetter(previewText); - nonSuckyBrowserPreviewSet(previewText); - previewSetter = nonSuckyBrowserPreviewSet; + try { + nonSuckyBrowserPreviewSet(previewText); + previewSetter = nonSuckyBrowserPreviewSet; + } catch (e) { + previewSetter = ieSafePreviewSet; + previewSetter(previewText); + } }; var pushPreviewHtml = function (previewText) { @@ -1032,7 +1097,14 @@ var fullTop = position.getTop(panels.input) - getDocScrollTop(); - window.scrollBy(0, fullTop - emptyTop); + if (uaSniffed.isIE) { + setTimeout(function () { + window.scrollBy(0, fullTop - emptyTop); + }, 0); + } + else { + window.scrollBy(0, fullTop - emptyTop); + } }; var init = function () { @@ -1063,12 +1135,24 @@ style.zIndex = "2000"; - style.opacity = "0.5"; + if (uaSniffed.isIE) { + style.filter = "alpha(opacity=50)"; + } + else { + style.opacity = "0.5"; + } var pageSize = position.getPageSize(); style.height = pageSize[1] + "px"; - style.left = "0"; - style.width = "100%"; + + if (uaSniffed.isIE) { + style.left = doc.documentElement.scrollLeft; + style.width = doc.documentElement.clientWidth; + } + else { + style.left = "0"; + style.width = "100%"; + } doc.body.appendChild(background); return background; @@ -1195,6 +1279,11 @@ dialog.style.top = "50%"; dialog.style.left = "50%"; dialog.style.display = "block"; + if (uaSniffed.isIE_5or6) { + dialog.style.position = "absolute"; + dialog.style.top = doc.documentElement.scrollTop + 200 + "px"; + dialog.style.left = "50%"; + } doc.body.appendChild(dialog); // This has to be done AFTER adding the dialog to the form if you @@ -1299,6 +1388,16 @@ } }); + // special handler because IE clears the context of the textbox on ESC + if (uaSniffed.isIE) { + util.addEvent(inputBox, "keydown", function (key) { + var code = key.keyCode; + if (code === 27) { + return false; + } + }); + } + // Perform the button's action. function doClick(button) { @@ -1366,6 +1465,19 @@ if (isEnabled) { button.disabled = false + // IE tries to select the background image "button" text (it's + // implemented in a list item) so we have to cache the selection + // on mousedown. + if (uaSniffed.isIE) { + button.onmousedown = function () { + if (doc.activeElement && doc.activeElement !== panels.input) { // we're not even in the input box, so there's no selection + return; + } + panels.ieCachedRange = document.selection.createRange(); + panels.ieCachedScrollTop = panels.input.scrollTop; + }; + } + if (!button.isHelp) { button.onclick = function () { if (this.onmouseout) {