Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: lib/aardvark.js

Issue 5671525202001920: Issue 227 - Element hiding selection by mouse wheel broken (Closed)
Patch Set: Created May 1, 2014, 11:31 a.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/aardvark.js
===================================================================
--- a/lib/aardvark.js
+++ b/lib/aardvark.js
@@ -33,69 +33,69 @@ let Aardvark = exports.Aardvark =
boxElem: null,
paintNode: null,
prevPos: null,
start: function(wrapper)
{
if (!this.canSelect(wrapper.browser))
return;
-
+
if (this.browser)
this.quit();
-
+
this.window = wrapper.window;
this.browser = wrapper.browser;
E = function(id) wrapper.E(id);
-
+
this.browser.addEventListener("click", this.onMouseClick, true);
this.browser.addEventListener("DOMMouseScroll", this.onMouseScroll, true);
this.browser.addEventListener("keypress", this.onKeyPress, true);
this.browser.addEventListener("mousemove", this.onMouseMove, true);
this.browser.addEventListener("select", this.quit, false);
this.browser.contentWindow.addEventListener("pagehide", this.onPageHide, true);
-
+
this.browser.contentWindow.focus();
-
+
let doc = this.browser.contentDocument;
this.boxElem = doc.importNode(E("ehh-elementmarker").firstElementChild.cloneNode(true), true);
-
+
this.initHelpBox();
-
+
if (Prefs.showhelp)
this.showMenu();
-
+
// Make sure to select some element immeditely (whichever is in the center of the browser window)
let [wndWidth, wndHeight] = this.getWindowSize(doc.defaultView);
this.isUserSelected = false;
this.onMouseMove({clientX: wndWidth / 2, clientY: wndHeight / 2, screenX: -1, screenY: -1, target: null});
},
canSelect: function(browser)
{
if (!browser || !browser.contentWindow ||
!(browser.contentDocument instanceof Ci.nsIDOMHTMLDocument))
{
return false;
}
-
+
let location = browser.contentWindow.location;
if (location.href == "about:blank")
return false;
-
+
if (!Prefs.acceptlocalfiles &&
location.hostname == "" &&
location.protocol != "mailbox:" &&
location.protocol != "imap:" &&
location.protocol != "news:" &&
location.protocol != "snews:")
{
return false;
}
-
+
return true;
},
doCommand: function(command, event)
{
if (this[command](this.selectedElem))
{
this.showCommandLabel(this.commands[command + "_key"], this.commands[command + "_altkey"], this.commands[command + "_label"]);
@@ -105,65 +105,65 @@ let Aardvark = exports.Aardvark =
if (event)
event.preventDefault();
},
showCommandLabel: function(key, alternativeKey, label)
{
if (this.commandLabelTimer)
this.commandLabelTimer.cancel();
-
+
E("ehh-commandlabel-key").textContent = key.toUpperCase();
E("ehh-commandlabel-alternativeKey").textContent = alternativeKey.toUpperCase();
E("ehh-commandlabel-label").setAttribute("value", label);
-
+
var commandLabel = E("ehh-commandlabel");
commandLabel.showPopup(this.window.document.documentElement, this.mouseX, this.mouseY, "tooltip", "topleft", "topleft");
-
+
this.commandLabelTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.commandLabelTimer.initWithCallback(function()
{
commandLabel.hidePopup();
Aardvark.commandLabelTimer = null;
}, 400, Ci.nsITimer.TYPE_ONE_SHOT);
},
initHelpBox: function()
{
var helpBoxRows = E("ehh-helpbox-rows");
if (helpBoxRows.firstElementChild)
return;
-
+
// Help box hasn't been filled yet, need to do it now
var stringService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
var strings = stringService.createBundle("chrome://elemhidehelper/locale/global.properties");
-
+
for (var i = 0; i < this.commands.length; i++)
{
var command = this.commands[i];
var key = strings.GetStringFromName("command." + command + ".key");
var alternativeKey = strings.GetStringFromName("command." + command + ".alternativeKey");
var label = strings.GetStringFromName("command." + command + ".label");
this.commands[command + "_key"] = key.toLowerCase();
this.commands[command + "_altkey"] = alternativeKey.toLowerCase();
this.commands[command + "_label"] = label;
-
+
var row = this.window.document.createElement("row");
helpBoxRows.appendChild(row);
-
+
var element = this.window.document.createElement("description");
element.textContent = key.toUpperCase();
element.className = "key";
row.appendChild(element);
-
+
var element = this.window.document.createElement("description");
element.textContent = alternativeKey.toUpperCase();
element.className = "key";
row.appendChild(element);
-
+
element = this.window.document.createElement("description");
element.setAttribute("value", label);
element.className = "label";
row.appendChild(element);
}
},
hideTooltips: function()
@@ -176,89 +176,88 @@ let Aardvark = exports.Aardvark =
tooltip.hidePopup();
}
},
onMouseClick: function(event)
{
if (event.button != 0 || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey)
return;
-
+
this.doCommand("select", event);
},
onMouseScroll: function(event)
{
if (!event.shiftKey || event.altKey || event.ctrlKey || event.metaKey)
return;
-
+
if ("axis" in event && event.axis != event.VERTICAL_AXIS)
return;
-
- for (let i = 0; i < Math.abs(event.detail); i++)
- this.doCommand(event.detail > 0 ? "wider" : "narrower", event);
+
+ this.doCommand(event.detail > 0 ? "wider" : "narrower", event);
},
onKeyPress: function(event)
{
if (event.altKey || event.ctrlKey || event.metaKey)
return;
-
+
var command = null;
if (event.keyCode == event.DOM_VK_ESCAPE)
command = "quit";
else if (event.keyCode == event.DOM_VK_RETURN)
command = "select";
else if (event.charCode)
{
var key = String.fromCharCode(event.charCode).toLowerCase();
var commands = this.commands;
for (var i = 0; i < commands.length; i++)
if (commands[commands[i] + "_key"] == key || commands[commands[i] + "_altkey"] == key)
command = commands[i];
}
-
+
if (command)
this.doCommand(command, event);
},
onPageHide: function(event)
{
this.doCommand("quit", null);
},
onMouseMove: function(event)
{
this.mouseX = event.screenX;
this.mouseY = event.screenY;
-
+
this.hideSelection();
-
+
let x = event.clientX;
let y = event.clientY;
-
+
// We might have coordinates relative to a frame, recalculate relative to top window
let node = event.target;
while (node && node.ownerDocument && node.ownerDocument.defaultView && node.ownerDocument.defaultView.frameElement)
{
node = node.ownerDocument.defaultView.frameElement;
let rect = node.getBoundingClientRect();
x += rect.left;
y += rect.top;
}
-
+
let elem = this.browser.contentDocument.elementFromPoint(x, y);
while (elem && "contentDocument" in elem && this.canSelect(elem))
{
let rect = elem.getBoundingClientRect();
x -= rect.left;
y -= rect.top;
elem = elem.contentDocument.elementFromPoint(x, y);
}
-
+
if (elem)
{
if (!this.lockedAnchor)
this.setAnchorElement(elem);
else
{
this.lockedAnchor = elem;
this.selectElement(this.selectedElem);
@@ -278,32 +277,32 @@ let Aardvark = exports.Aardvark =
this.selectElement(this.selectedElem);
}
}
},
setAnchorElement: function(anchor)
{
this.anchorElem = anchor;
-
+
let newSelection = anchor;
if (this.isUserSelected)
{
// User chose an element via wider/narrower commands, keep the selection if
// out new anchor is still a child of that element
let e = newSelection;
while (e && e != this.selectedElem)
e = this.getParentElement(e);
-
+
if (e)
newSelection = this.selectedElem;
else
this.isUserSelected = false;
}
-
+
this.selectElement(newSelection);
},
appendDescription: function(node, value, className)
{
var descr = this.window.document.createElement("description");
descr.setAttribute("value", value);
if (className)
@@ -320,50 +319,50 @@ let Aardvark = exports.Aardvark =
let tagName = elem.tagName.toLowerCase();
let addition = "";
if (elem.id != "")
addition += ", id: " + elem.id;
if (elem.className != "")
addition += ", class: " + elem.className;
if (elem.style.cssText != "")
addition += ", style: " + elem.style.cssText;
-
+
return [tagName, addition];
},
selectElement: function(elem)
{
this.selectedElem = elem;
this.prevSelectionUpdate = Date.now();
-
+
let border = this.boxElem.getElementsByClassName("ehh-border")[0];
let label = this.boxElem.getElementsByClassName("ehh-label")[0];
let labelTag = this.boxElem.getElementsByClassName("ehh-labelTag")[0];
let labelAddition = this.boxElem.getElementsByClassName("ehh-labelAddition")[0];
if (this.boxElem.parentNode)
this.boxElem.parentNode.removeChild(this.boxElem);
-
+
let doc = this.browser.contentDocument;
let [wndWidth, wndHeight] = this.getWindowSize(doc.defaultView);
let pos = this.getElementPosition(elem);
this.boxElem.style.left = Math.min(pos.left - 1, wndWidth - 2) + "px";
this.boxElem.style.top = Math.min(pos.top - 1, wndHeight - 2) + "px";
border.style.width = Math.max(pos.right - pos.left - 2, 0) + "px";
border.style.height = Math.max(pos.bottom - pos.top - 2, 0) + "px";
-
+
[labelTag.textContent, labelAddition.textContent] = this.getElementLabel(elem);
-
+
// If there is not enough space to show the label move it up a little
if (pos.bottom < wndHeight - 25)
label.className = "ehh-label";
else
label.className = "ehh-label onTop";
-
+
doc.documentElement.appendChild(this.boxElem);
this.paintNode = doc.defaultView;
if (this.paintNode)
{
this.prevPos = pos;
this.paintNode.addEventListener("MozAfterPaint", this.onAfterPaint, false);
}
@@ -391,56 +390,56 @@ let Aardvark = exports.Aardvark =
function intersectRect(rect, wnd)
{
let [wndWidth, wndHeight] = this.getWindowSize(wnd);
rect.left = Math.max(rect.left, 0);
rect.top = Math.max(rect.top, 0);
rect.right = Math.min(rect.right, wndWidth);
rect.bottom = Math.min(rect.bottom, wndHeight);
}
-
+
let rect = element.getBoundingClientRect();
let wnd = element.ownerDocument.defaultView;
-
+
rect = {left: rect.left, top: rect.top,
right: rect.right, bottom: rect.bottom};
while (true)
{
intersectRect.call(this, rect, wnd);
-
+
if (!wnd.frameElement)
break;
-
+
// Recalculate coordinates to be relative to frame's parent window
let frameElement = wnd.frameElement;
wnd = frameElement.ownerDocument.defaultView;
-
+
let frameRect = frameElement.getBoundingClientRect();
let frameStyle = wnd.getComputedStyle(frameElement, null);
let relLeft = frameRect.left + parseFloat(frameStyle.borderLeftWidth) + parseFloat(frameStyle.paddingLeft);
let relTop = frameRect.top + parseFloat(frameStyle.borderTopWidth) + parseFloat(frameStyle.paddingTop);
-
+
rect.left += relLeft;
rect.right += relLeft;
rect.top += relTop;
rect.bottom += relTop;
}
-
+
return rect;
},
getParentElement: function(elem)
{
let result = elem.parentNode;
if (result && result.nodeType == Ci.nsIDOMElement.DOCUMENT_NODE && result.defaultView && result.defaultView.frameElement)
result = result.defaultView.frameElement;
-
+
if (result && result.nodeType != Ci.nsIDOMElement.ELEMENT_NODE)
return null;
-
+
return result;
},
/***************************
* Commands implementation *
***************************/
commands: [
@@ -454,21 +453,21 @@ let Aardvark = exports.Aardvark =
"viewSourceWindow",
"showMenu"
],
wider: function(elem)
{
if (!elem)
return false;
-
+
let newElem = this.getParentElement(elem);
if (!newElem)
return false;
-
+
this.isUserSelected = true;
this.selectElement(newElem);
return true;
},
narrower: function(elem)
{
if (elem)
@@ -477,61 +476,61 @@ let Aardvark = exports.Aardvark =
// We need to select the element just before the selected one.
let e = this.anchorElem;
let newElem = null;
while (e && e != elem)
{
newElem = e;
e = this.getParentElement(e);
}
-
+
if (!e || !newElem)
return false;
-
+
this.isUserSelected = true;
this.selectElement(newElem);
return true;
}
return false;
},
lock: function(elem)
{
if (!elem)
return false;
-
+
if (this.lockedAnchor)
{
this.setAnchorElement(this.lockedAnchor);
this.lockedAnchor = null;
}
else
this.lockedAnchor = this.anchorElem;
-
+
return true;
},
-
+
quit: function()
{
if (!this.browser)
return false;
-
+
if ("blinkTimer" in this)
this.stopBlinking();
-
+
if (this.commandLabelTimer)
this.commandLabelTimer.cancel();
if (this.viewSourceTimer)
this.viewSourceTimer.cancel();
this.commandLabelTimer = null;
this.viewSourceTimer = null;
-
+
this.hideSelection();
this.hideTooltips();
-
+
this.browser.removeEventListener("click", this.onMouseClick, true);
this.browser.removeEventListener("DOMMouseScroll", this.onMouseScroll, true);
this.browser.removeEventListener("keypress", this.onKeyPress, true);
this.browser.removeEventListener("mousemove", this.onMouseMove, true);
this.browser.removeEventListener("select", this.quit, false);
this.browser.contentWindow.removeEventListener("pagehide", this.onPageHide, true);
this.anchorElem = null;
@@ -544,189 +543,189 @@ let Aardvark = exports.Aardvark =
E = function(id) null;
return false;
},
select: function(elem)
{
if (!elem)
return false;
-
+
this.window.openDialog("chrome://elemhidehelper/content/composer.xul", "_blank",
"chrome,centerscreen,resizable,dialog=no", elem);
this.quit();
return false;
},
blinkElement: function(elem)
{
if (!elem)
return false;
-
+
if ("blinkTimer" in this)
this.stopBlinking();
-
+
let counter = 0;
this.blinkElem = elem;
this.blinkOrigValue = elem.style.visibility;
this.blinkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.blinkTimer.initWithCallback(function()
{
counter++;
elem.style.visibility = (counter % 2 == 0 ? "visible" : "hidden");
if (counter == 6)
Aardvark.stopBlinking();
}, 250, Ci.nsITimer.TYPE_REPEATING_SLACK);
-
+
return true;
},
stopBlinking: function()
{
this.blinkTimer.cancel();
this.blinkElem.style.visibility = this.blinkOrigValue;
-
+
delete this.blinkElem;
delete this.blinkOrigValue;
delete this.blinkTimer;
},
viewSource: function(elem)
{
if (!elem)
return false;
-
+
var sourceBox = E("ehh-viewsource");
if (sourceBox.state == "open" && this.commentElem == elem)
{
sourceBox.hidePopup();
return true;
}
sourceBox.hidePopup();
-
+
while (sourceBox.firstElementChild)
sourceBox.removeChild(sourceBox.firstElementChild);
this.getOuterHtmlFormatted(elem, sourceBox);
this.commentElem = elem;
-
+
let anchor = this.window.document.documentElement;
let x = this.mouseX;
let y = this.mouseY;
this.viewSourceTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.viewSourceTimer.initWithCallback(function()
{
sourceBox.showPopup(anchor, x, y, "tooltip", "topleft", "topleft");
Aardvark.viewSourceTimer = null;
}, 500, Ci.nsITimer.TYPE_ONE_SHOT);
return true;
},
viewSourceWindow: function(elem)
{
if (!elem)
return false;
-
+
var range = elem.ownerDocument.createRange();
range.selectNodeContents(elem);
var selection = {rangeCount: 1, getRangeAt: function() {return range}};
-
+
this.window.openDialog("chrome://global/content/viewPartialSource.xul", "_blank", "scrollbars,resizable,chrome,dialog=no",
null, null, selection, "selection");
return true;
},
getOuterHtmlFormatted: function(node, container)
{
var type = null;
switch (node.nodeType)
{
case node.ELEMENT_NODE:
var box = this.window.document.createElement("vbox");
box.className = "elementBox";
-
+
var startTag = this.window.document.createElement("hbox");
startTag.className = "elementStartTag";
if (!node.firstElementChild)
startTag.className += " elementEndTag";
-
+
this.appendDescription(startTag, "<", null);
this.appendDescription(startTag, node.tagName, "tagName");
-
+
for (var i = 0; i < node.attributes.length; i++)
{
var attr = node.attributes[i];
this.appendDescription(startTag, attr.name, "attrName");
if (attr.value != "")
{
this.appendDescription(startTag, "=", null);
this.appendDescription(startTag, '"' + attr.value.replace(/"/, "&quot;") + '"', "attrValue");
}
}
-
+
this.appendDescription(startTag, node.firstElementChild ? ">" : " />", null);
box.appendChild(startTag);
-
+
if (node.firstElementChild)
{
for (var child = node.firstElementChild; child; child = child.nextElementSibling)
this.getOuterHtmlFormatted(child, box);
-
+
var endTag = this.window.document.createElement("hbox");
endTag.className = "elementEndTag";
this.appendDescription(endTag, "<", null);
this.appendDescription(endTag, "/" + node.tagName, "tagName");
this.appendDescription(endTag, ">", null);
box.appendChild(endTag);
}
container.appendChild(box);
return;
-
+
case node.TEXT_NODE:
type = "text";
break;
case node.CDATA_SECTION_NODE:
type = "cdata";
break;
case node.COMMENT_NODE:
type = "comment";
break;
default:
return;
}
-
+
var text = node.nodeValue.replace(/\r/g, '').replace(/^\s+/, '').replace(/\s+$/, '');
if (text == "")
return;
-
+
if (type != "cdata")
{
text = text.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
}
text = text.replace(/\t/g, " ");
if (type == "cdata")
text = "<![CDATA[" + text + "]]>";
else if (type == "comment")
text = "<!--" + text + "-->";
-
+
var lines = text.split("\n");
for (var i = 0; i < lines.length; i++)
this.appendDescription(container, lines[i].replace(/^\s+/, '').replace(/\s+$/, ''), type);
},
showMenu: function()
{
var helpBox = E("ehh-helpbox");
if (helpBox.state == "open")
{
helpBox.hidePopup();
return true;
}
-
+
// Show help box
helpBox.showPopup(this.browser, -1, -1, "tooltip", "topleft", "topleft");
return true;
}
}
// Makes sure event handlers like Aardvark.onKeyPress always have the correct
// this pointer set.
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld