mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-19 11:27:59 +01:00
1311 lines
42 KiB
JavaScript
Executable File
1311 lines
42 KiB
JavaScript
Executable File
/*
|
||
Aardvark is BSD licensed. Use as you wish. Gimme credit if you wanna.
|
||
|
||
Copyright (c) 2005-2008, Rob Brown
|
||
|
||
This work ‘as-is’ we provide.
|
||
No warranty, express or implied.
|
||
We’ve done our best,
|
||
to debug and test.
|
||
Liability for damages denied.
|
||
|
||
Permission is granted hereby,
|
||
to copy, share, and modify.
|
||
Use as is fit,
|
||
free or for profit.
|
||
On this notice these rights rely.
|
||
*/
|
||
|
||
/*
|
||
* Copyright © 2011 Kris Maglione
|
||
* Original portions available under the terms of the MIT license.
|
||
*/
|
||
|
||
"use strict";
|
||
var INFO =
|
||
["plugin", { name: "aardvark",
|
||
version: "0.3",
|
||
href: "http://dactyl.sf.net/pentadactyl/plugins#aardvark-plugin",
|
||
summary: "Aardvark page editor",
|
||
xmlns: "dactyl" },
|
||
["author", { email: "maglione.k@gmail.com" },
|
||
"Kris Maglione"],
|
||
["author", {}, "Rob Brown"],
|
||
["license", {},
|
||
"Some unnamed BSD variant"],
|
||
["project", { name: "Pentadactyl", "min-version": "1.0" }],
|
||
["p", {},
|
||
"This plugin is a Pentadactyl port of the Aardvark Firefox add-on ",
|
||
"and bookmarklet. The code is moderately horrendous, but it ",
|
||
"generally works."],
|
||
["item", {},
|
||
["tags", {}, ":aardvark"],
|
||
["spec", {}, ["ex", {}, ":aardvark"]],
|
||
["description", { short: "true" },
|
||
["p", {}, "Start Aardvark mode."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_b"],
|
||
["spec", {}, "b"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Decolorize element."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_c"],
|
||
["spec", {}, "c"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Colorize element."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_d"],
|
||
["spec", {}, "d"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Remove width-specifying styles."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_h"],
|
||
["spec", {}, "h"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Show a list of available keys."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_i"],
|
||
["spec", {}, "i"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Isolate element."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_j"],
|
||
["spec", {}, "j"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Convert element to JavaScript source."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_k"],
|
||
["spec", {}, "k"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Kill an element using the R.I.P. add-on."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_n"],
|
||
["spec", {}, "n"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Select a lower element."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_p"],
|
||
["spec", {}, "p"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Paste the last yanked element."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_r"],
|
||
["spec", {}, "r"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Remove element."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_s"],
|
||
["spec", {}, "s"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Select the given element or the contents of the frontmost display box."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_t"],
|
||
["spec", {}, "t"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Thunk the element in a global variable."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_u"],
|
||
["spec", {}, "u"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Undo the last operation."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_v"],
|
||
["spec", {}, "v"],
|
||
["description", { short: "true" },
|
||
["p", {}, "View element source."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_w"],
|
||
["spec", {}, "w"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Select a higher element."]]],
|
||
|
||
["item", {},
|
||
["tags", {}, "A_x"],
|
||
["spec", {}, "x"],
|
||
["description", { short: "true" },
|
||
["p", {}, "Show the element's XPath."]]]];
|
||
|
||
highlight.loadCSS(String.raw`
|
||
AardvarkDBox;;* {
|
||
padding: 0;
|
||
border: 1px solid #000;
|
||
background-color: #888;
|
||
color: black;
|
||
font-family: arial;
|
||
font-size: 13px;
|
||
}
|
||
AardvarkDragger;;* {
|
||
font-size: 12px;
|
||
text-align: left;
|
||
color: #000;
|
||
padding: 2px;
|
||
height: 14px;
|
||
margin: 0;
|
||
cursor: move;
|
||
background-color: #d8d7dc;
|
||
}
|
||
AardvarkClose;;* {
|
||
vertical-align: middle;
|
||
width: 17px;
|
||
height: 17px;
|
||
margin: -2px 4px 0 0;
|
||
cursor: pointer;
|
||
}
|
||
AardvarkSelect;;* {
|
||
float: right;
|
||
font-size: 11px;
|
||
color: #008;
|
||
margin: 0;
|
||
padding: 0;
|
||
cursor: pointer;
|
||
}
|
||
AardvarkInner;;* {
|
||
float: left;
|
||
margin: 0;
|
||
border: 0;
|
||
padding: 4px;
|
||
font-size: 13px;
|
||
color: #000;
|
||
background: #fff;
|
||
}
|
||
|
||
Aardvark;;* {
|
||
border-color: black;
|
||
border-width: 1px 2px 2px 1px;
|
||
border-style: solid;
|
||
font-family: arial;
|
||
text-align: left;
|
||
color: #000;
|
||
font-size: 12px;
|
||
position: absolute;
|
||
padding: 2px 5px;
|
||
}
|
||
AardvarkLabel;;*;Aardvark {
|
||
border-top-width: 0;
|
||
border-bottom-left-radius: 6px;
|
||
border-bottom-right-radius: 6px;
|
||
background-color: #fff0cc;
|
||
z-index: 5005;
|
||
}
|
||
AardvarkKeybox;;*;Aardvark {
|
||
background-color: #dfd;
|
||
z-index: 100006;
|
||
}
|
||
AardvarkBorder;;* {
|
||
position: absolute;
|
||
z-index: 5001;
|
||
border-color: #f00;
|
||
border-style: solid;
|
||
}
|
||
|
||
AardvarkIsolated;;* {
|
||
float: none;
|
||
position: static;
|
||
padding: 5px;
|
||
margin: 5px;
|
||
}
|
||
AardvarkIsolatedBody;;* {
|
||
width: 100%;
|
||
background: none;
|
||
background-color: white;
|
||
background-image: none;
|
||
text-align: left;
|
||
}
|
||
|
||
AardvarkBW;;* {
|
||
color: #000 !important;
|
||
background-color: #fff !important;
|
||
font-family: arial !important;
|
||
font-size: 13px !important;
|
||
text-align: left !important;
|
||
background-image: none !important;
|
||
}
|
||
AardvarkBW:-moz-any-link;;* {
|
||
text-decoration: underline !important;
|
||
}
|
||
AardvarkDewidthified;;* {
|
||
width: auto !important;
|
||
}
|
||
`);
|
||
|
||
group.styles.add("aardvark", "*", String.raw`
|
||
[dactyl|highlight~=AardvarkDBox] div.vsblock {
|
||
border: 1px solid #ccc;
|
||
border-right: 0;
|
||
margin: -1px 0 -1px 1em;
|
||
padding: 0;
|
||
}
|
||
[dactyl|highlight~=AardvarkDBox] div.vsline {
|
||
border-right: 0;
|
||
margin: 0 0 0 .6em;
|
||
text-indent: -.6em;
|
||
padding: 0;
|
||
}
|
||
[dactyl|highlight~=AardvarkDBox] div.vsindent {
|
||
border-right: 0;
|
||
margin: 0 0 0 1.6em;
|
||
text-indent: -.6em;
|
||
padding: 0;
|
||
}
|
||
[dactyl|highlight~=AardvarkDBox] span.tag {
|
||
color: #c00;
|
||
font-weight:bold;
|
||
}
|
||
[dactyl|highlight~=AardvarkDBox] span.pname {
|
||
color: #080;
|
||
font-weight: bold;
|
||
}
|
||
[dactyl|highlight~=AardvarkDBox] span.pval {
|
||
color:#00a;
|
||
font-weight: bold;
|
||
}
|
||
[dactyl|highlight~=AardvarkDBox] span.aname {
|
||
color: #050;
|
||
font-style: italic;
|
||
font-weight: normal;
|
||
}
|
||
[dactyl|highlight~=AardvarkDBox] span.aval {
|
||
color:#007;
|
||
font-style: italic;
|
||
font-weight: normal;
|
||
}
|
||
|
||
[dactyl|highlight~=AardvarkDBox] a,
|
||
[dactyl|highlight~=AardvarkDBox] a:visited {
|
||
color: #007;
|
||
text-decoration: underline;
|
||
}
|
||
[dactyl|highlight~=AardvarkDBox] a:hover {
|
||
color: #00f;
|
||
}
|
||
`);
|
||
|
||
let images = {
|
||
close: `data:image/gif;base64,
|
||
R0lGODlhEQARAMMPANuywOyNo+wnUfEENPN1kfFLbre0u/78/P7S28ZWcIdsdaJIXolVY7IyTpKS
|
||
mP///yH5BAEAAA8ALAAAAAARABEAAARw8MkJQAAz5yCGHwKhTcVnglhWDqsnlIIxBV5wEC6CdMnU
|
||
FYcgQYAIIjwywIcQbB52HsVDuXRCPQsD1eXEfbJbovP2Ycg64idTGJV4bdB1qeHweYbLlUImAXRO
|
||
ZXUaCYANCoIjBgoLCwyHfCMTBpOREQA7
|
||
`,
|
||
};
|
||
|
||
for (let k in images)
|
||
images[k] = String(images[k].replace(/\s+/g, ""));
|
||
|
||
function nodeName(node) {
|
||
if (node.nodeName == node.localName.toUpperCase())
|
||
return node.localName;
|
||
return node.nodeName;
|
||
}
|
||
|
||
var AardvarkDBox = Class("AardvarkDBox", {
|
||
init: function (aardvark, params) {
|
||
if (!("dragger" in params))
|
||
params.dragger = true;
|
||
|
||
this.aardvark = aardvark;
|
||
|
||
this.bgColor = params.bgColor || "";
|
||
this.position = params.position;
|
||
this.id = aardvark.dBoxId++;
|
||
|
||
var dims = aardvark.getWindowDimensions();
|
||
dims.width -= 15;
|
||
dims.height -= 15;
|
||
this.dims = dims;
|
||
|
||
let style = "position: absolute; z-index: 100000;" +
|
||
"top: " + dims.scrollY + "px;" +
|
||
"left: " + dims.scrollX + "px;" +
|
||
"max-width: " + (dims.width - 20) + "px;" +
|
||
"max-height: " + (dims.height - 20) + "px;"
|
||
|
||
let outerDiv =
|
||
["div", { key: "outerContainer",
|
||
highlight: "AardvarkDBox",
|
||
style: style,
|
||
id: "aardvarkdbox-" + aardvark.dBoxId },
|
||
!params.dragger ? [] :
|
||
["div", { command: "drag", key: "dragBar",
|
||
highlight: "AardvarkDragger" },
|
||
["img", { src: images.close,
|
||
command: "kill",
|
||
highlight: "AardvarkClose",
|
||
alt: "close" }],
|
||
!params.selectLink ? [] :
|
||
["a", { highlight: "AardvarkSelect", command: "select" },
|
||
"select all"],
|
||
params.title || ""],
|
||
["div", { key: "innerContainer", highlight: "AardvarkInner",
|
||
style: "overflow: " + (params.hideScrollbar ? "hidden" : "auto") },
|
||
content || ""]];
|
||
|
||
outerDiv = DOM.fromJSON(outerDiv, aardvark.doc, this);
|
||
outerDiv.isAardvark = true;
|
||
outerDiv.dbox = this;
|
||
DOM(outerDiv).mousedown(this.closure.onMouseDown)
|
||
.appendTo(aardvark.container);
|
||
|
||
if (params.data)
|
||
DOM(this.innerContainer).empty().append(params.data);
|
||
|
||
aardvark.dBoxId++;
|
||
return this;
|
||
},
|
||
|
||
onMouseDown: function onMouseDown(event) {
|
||
let command = DOM(event.target).attr("command");
|
||
if (command in this && event.button == 0) {
|
||
this[command](event);
|
||
event.preventDefault();
|
||
}
|
||
},
|
||
|
||
kill: function kill() {
|
||
if (!this.outerContainer.parentNode)
|
||
return false;
|
||
DOM(this.outerContainer).remove();
|
||
return true;
|
||
},
|
||
|
||
select: function select() {
|
||
this.aardvark.highlightText(this.innerContainer);
|
||
},
|
||
|
||
drag: function drag() {
|
||
let elem = this.outerContainer;
|
||
this.aardvark.dragElement = elem;
|
||
this.aardvark.dragStartPos = this.aardvark.getPos(elem);
|
||
this.aardvark.dragClickX = this.aardvark.mousePosX;
|
||
this.aardvark.dragClickY = this.aardvark.mousePosY;
|
||
},
|
||
|
||
show: function show() {
|
||
var dims = this.dims;
|
||
var draggerHeight = 1;
|
||
if (this.dragBar) {
|
||
draggerHeight = 18;
|
||
}
|
||
var w = this.innerContainer.offsetWidth;
|
||
if (!this.innerContainer.style.width || this.innerContainer.style.width != "")
|
||
w += 25;
|
||
|
||
if (this.dragBar)
|
||
w = Math.max(w, this.dragBar.offsetWidth + 12);
|
||
w = Math.min(w, dims.width - 20);
|
||
|
||
this.outerContainer.style.width = w + "px";
|
||
this.innerContainer.style.width = w + "px";
|
||
|
||
var diff;
|
||
if ((diff = this.innerContainer.offsetWidth - w) > 0)
|
||
this.innerContainer.style.width = (w - diff) + "px";
|
||
|
||
var h = Math.min(dims.height - 25,
|
||
this.innerContainer.offsetHeight);
|
||
|
||
this.outerContainer.style.height = (h + draggerHeight) + "px";
|
||
this.innerContainer.style.height = h + "px";
|
||
if ((diff = this.innerContainer.offsetHeight - h) > 0)
|
||
this.innerContainer.style.height = (h - diff) + "px";
|
||
|
||
this.innerContainer.style.backgroundColor = this.bgColor;
|
||
|
||
if (this.position) {
|
||
var { x, y } = this.position;
|
||
}
|
||
else {
|
||
x = dims.width / 2 - w / 2;
|
||
y = dims.height / 2 - h / 2;
|
||
}
|
||
x += dims.scrollX;
|
||
y += dims.scrollY;
|
||
|
||
this.aardvark.moveElem(this.outerContainer, x, y);
|
||
}
|
||
});
|
||
|
||
var Aardvark = Class("Aardvark", {
|
||
init: function init(window) {
|
||
this.window = window;
|
||
this.doc = window.document;
|
||
|
||
this.top = DOM(["div", { highlight: "hints" }], this.doc)
|
||
.appendTo(this.doc.body);
|
||
this.top.style.height;
|
||
this.top[0].isAardvark = true;
|
||
|
||
this.container = this.top.findAnon("anonid", "hints") || this.container;
|
||
|
||
modes.push(modes.AARDVARK, null, this.closure);
|
||
|
||
this.undoStack = [];
|
||
|
||
this.makeElems();
|
||
this.selectedElem = null;
|
||
},
|
||
|
||
get mappingSelf() { return this; },
|
||
|
||
enter: function enter() {
|
||
group.events.listen(this.doc, this, "events");
|
||
},
|
||
|
||
leave: function leave(stack) {
|
||
group.events.unlisten(this.doc, this, "events");
|
||
if (!stack.push)
|
||
this.top.remove();
|
||
},
|
||
|
||
closePanels: function closePanels() {
|
||
let panels = this.container.children
|
||
.filter((elem) => DOM(elem).highlight.has("AardvarkDBox"));
|
||
panels.remove();
|
||
return panels.length;
|
||
},
|
||
|
||
rip: function rip(elem) {
|
||
if (window.RemoveItPermanently)
|
||
RemoveItPermanently.doRipNode(elem);
|
||
else {
|
||
var dbox = AardvarkDBox(this, { data: this.strings.ripHelp });
|
||
dbox.show();
|
||
}
|
||
return true;
|
||
},
|
||
|
||
wider: function wider(elem) {
|
||
if (elem && elem.parentNode) {
|
||
var newElem = this.findValidElement(elem.parentNode);
|
||
if (!newElem)
|
||
return false;
|
||
|
||
if (this.widerStack &&
|
||
this.widerStack.length > 0 &&
|
||
this.widerStack[this.widerStack.length - 1] == elem) {
|
||
this.widerStack.push(newElem);
|
||
}
|
||
else {
|
||
this.widerStack = [elem, newElem];
|
||
}
|
||
this.selectedElem = newElem;
|
||
this.showBoxAndLabel(newElem, this.makeElementLabelString(newElem));
|
||
this.didWider = true;
|
||
return true;
|
||
}
|
||
return false;
|
||
},
|
||
|
||
narrower: function narrower(elem) {
|
||
if (elem) {
|
||
if (this.widerStack &&
|
||
this.widerStack.length > 1 &&
|
||
this.widerStack[this.widerStack.length - 1] == elem) {
|
||
|
||
this.widerStack.pop();
|
||
let newElem = this.widerStack[this.widerStack.length - 1];
|
||
this.selectedElem = newElem;
|
||
this.showBoxAndLabel(newElem, this.makeElementLabelString(newElem));
|
||
this.didWider = true;
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
},
|
||
|
||
select: function select(elem) {
|
||
let panels = this.container.children
|
||
.filter(elem => elem.dbox);
|
||
if (panels.length)
|
||
panels[panels.length - 1].dbox.select();
|
||
else if (elem)
|
||
this.highlightText(elem);
|
||
},
|
||
|
||
viewSource: function viewSource(elem) {
|
||
var dbox = AardvarkDBox(this, {
|
||
selectLink: true,
|
||
title: this.strings.viewHtmlSource,
|
||
data: this.getOuterHtmlFormatted(elem, 0)
|
||
});
|
||
dbox.show();
|
||
return true;
|
||
},
|
||
|
||
colorize: function colorize(elem) {
|
||
let val = () => Math.round(Math.random() * 16).toString(16);
|
||
|
||
this.undoStack.push({
|
||
styles: {
|
||
backgroundColor: elem.style.backgroundColor,
|
||
backgroundImage: elem.style.backgroundImage
|
||
},
|
||
undo: function undo() { DOM(elem).css(this.styles); }
|
||
});
|
||
|
||
DOM(elem).css({
|
||
backgroundColor: "#" + val() + val() + val(),
|
||
backgroundImage: ""
|
||
});
|
||
return true;
|
||
},
|
||
|
||
removeElement: function removeElement(elem) {
|
||
if (elem.parentNode != null) {
|
||
DOM(elem).hide();
|
||
this.undoStack.push({
|
||
mode: "R",
|
||
elem: elem,
|
||
undo: function () { DOM(this.elem).show() }
|
||
});
|
||
this.clearBox();
|
||
return true;
|
||
}
|
||
return false;
|
||
},
|
||
|
||
paste: function paste(target) {
|
||
if (target.parentNode != null) {
|
||
if (this.undoStack.length && this.undoStack.slice(-1)[0].mode == "R") {
|
||
let src = this.undoStack.pop().elem;
|
||
|
||
if (src.localName == "tr" && target.localName != "tr") {
|
||
var t = DOM(["table", {}, ["tbody"]], this.doc)[0];
|
||
t.firstChild.appendChild(src);
|
||
DOM(src).show();
|
||
src = t;
|
||
}
|
||
else if (src.localName == "td" && target.localName != "td")
|
||
src = DOM(["div"], this.doc).append(DOM(src).contents()).append(src)[0];
|
||
else {
|
||
src.parentNode.removeChild(src);
|
||
DOM(src).show();
|
||
}
|
||
|
||
if (target.localName == "td" && src.localName != "td")
|
||
target.insertBefore(src, target.firstChild);
|
||
else if (target.localName == "tr" && src.localName != "tr")
|
||
target.insertBefore(src, target.firstChild.firstChild);
|
||
else
|
||
target.parentNode.insertBefore(src, target);
|
||
|
||
this.clearBox();
|
||
}
|
||
}
|
||
return true;
|
||
},
|
||
|
||
global: function xpath(target) {
|
||
commandline.input("Variable name: ", function (res) {
|
||
if (res) {
|
||
userContext[res] = target;
|
||
DOM(target.ownerDocument.defaultView).unload(function () {
|
||
if (userContext[res] == target)
|
||
delete userContext[res];
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
xpath: function xpath(target) {
|
||
AardvarkDBox(this, {
|
||
selectLink: true,
|
||
title: this.strings.viewXPath,
|
||
data: DOM(target).xpath
|
||
}).show();
|
||
},
|
||
|
||
isolateElement: function isolateElement(elem) {
|
||
let { body } = this.doc;
|
||
if (elem.parentNode != null) {
|
||
this.clearBox();
|
||
var clone = elem.cloneNode(true);
|
||
DOM(clone).highlight.add("AardvarkIsolated");
|
||
|
||
if (clone.localName == "tr" || clone.localName == "td") {
|
||
if (clone.localName == "td")
|
||
clone = DOM(["tr"], this.doc).append(clone)[0];
|
||
|
||
t = DOM(["table", {}, ["tbody"]], this.doc)[0];
|
||
t.firstChild.appendChild(clone);
|
||
clone = t;
|
||
}
|
||
|
||
var undoData = Array.filter(this.doc.body.childNodes,
|
||
e => !e.isAardvark);
|
||
DOM(undoData).remove();
|
||
undoData.mode = "I";
|
||
undoData.isolated = DOM(body).highlight.has("AardvarkIsolatedBody");
|
||
undoData.undo = function () {
|
||
DOM(body.childNodes).filter(e => !e.isAardvark)
|
||
.remove();
|
||
|
||
DOM(body).highlight.toggle("AardvarkIsolatedBody", this.isolated)
|
||
.prepend(this);
|
||
};
|
||
this.undoStack.push(undoData);
|
||
|
||
DOM(this.doc.body).highlight.add("AardvarkIsolatedBody")
|
||
.append(clone);
|
||
|
||
this.window.scroll(0, 0);
|
||
}
|
||
return true;
|
||
},
|
||
|
||
_undo: function _undo(group) {
|
||
return function undo() {
|
||
(function rec(node) {
|
||
DOM(node).highlight.remove(group)
|
||
.children.each(rec);
|
||
})(this.elem);
|
||
};
|
||
},
|
||
|
||
deWidthify: function deWidthify(node) {
|
||
if (node instanceof Element) {
|
||
if (node.localName != "img") {
|
||
DOM(node).highlight.add("AardvarkDewidthified")
|
||
.children.each(this.closure.deWidthify);
|
||
|
||
if (arguments.length == 1) {
|
||
this.clearBox();
|
||
this.undoStack.push({ elem: node, undo: this._undo("AardvarkDewidthified") });
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
},
|
||
|
||
blackOnWhite: function blackOnWhite(node) {
|
||
if (node instanceof Element) {
|
||
if (node.localName != "img") {
|
||
DOM(node).highlight.add("AardvarkBW")
|
||
.children.each(this.closure.blackOnWhite);
|
||
|
||
if (arguments.length == 1)
|
||
this.undoStack.push({ elem: node, undo: this._undo("AardvarkBW") });
|
||
}
|
||
}
|
||
return true;
|
||
},
|
||
|
||
getOuterHtmlFormatted: function getOuterHtmlFormatted(node, indent) {
|
||
var res = [];
|
||
switch (node.nodeType) {
|
||
case Node.ELEMENT_NODE:
|
||
if (node.style.display == "none")
|
||
break;
|
||
|
||
var isLeaf = node.childNodes.length == 0
|
||
&& this.leafElems[node.localName];
|
||
var isTbody = node.localName == "tbody" && node.attributes.length == 0;
|
||
if (isTbody) {
|
||
for (let node of node.childNodes)
|
||
res.push(this.getOuterHtmlFormatted(node, indent));
|
||
}
|
||
else {
|
||
let inner = ["", "<",
|
||
["span", { class: "tag" }, nodeName(node)]];
|
||
Array.forEach(node.attributes, function (attr) {
|
||
if (attr.value != null) {
|
||
let value = ["span", { class: "pval" }, attr.value];
|
||
if (attr.localName == "style")
|
||
value = template.map(Styles.propertyIter(value),
|
||
({ name, value }) =>
|
||
[["span", { class: "aname" }, name],
|
||
": ",
|
||
["span", { class: "aval" }, value],
|
||
";"],
|
||
" ");
|
||
|
||
inner.push(" ",
|
||
["span", { class: "pname" }, nodeName(attr)],
|
||
'="',
|
||
value,
|
||
'"');
|
||
}
|
||
}, this)
|
||
|
||
if (isLeaf)
|
||
res.push(["div", { class: "vsindent" },
|
||
inner,
|
||
"/>"]);
|
||
else {
|
||
inner = [["div", { class: "vsline" },
|
||
inner]];
|
||
|
||
for (let node of node.childNodes)
|
||
inner.push(this.getOuterHtmlFormatted(node, indent + 1));
|
||
|
||
res.push(["div", { class: "vsline" },
|
||
"</",
|
||
["span", { class: "tag" },
|
||
nodeName(node)],
|
||
">"]);
|
||
|
||
if (indent > 0)
|
||
res.push(["div", { class: "vsblock" }, inner]);
|
||
else
|
||
res.push(inner);
|
||
}
|
||
}
|
||
break;
|
||
case Node.TEXT_NODE:
|
||
var v = DOM.escapeHTML(node.nodeValue).trim();
|
||
if (v)
|
||
res.push(["div", { class: "vsindent" },
|
||
v]);
|
||
break;
|
||
case Node.CDATA_SECTION_NODE:
|
||
res.push(["div", { class: "vsindent" },
|
||
"<![CDATA[" + node.nodeValue + "]]>"]);
|
||
break;
|
||
case Node.ENTITY_REFERENCE_NODE:
|
||
res.push(["", "&", nodeName(node)], ["br"]);
|
||
break;
|
||
case Node.COMMENT_NODE:
|
||
res.push(["div", { class: "vsindent" },
|
||
"<!--" + node.nodeValue + "-->"]);
|
||
break;
|
||
}
|
||
return res;
|
||
},
|
||
|
||
camelCaseProps: {
|
||
},
|
||
|
||
domJavascript: function domJavascript(node, indent) {
|
||
let FmtString = (str, color) => ["", '"',
|
||
["span", { style: "color:" + (color || "#00b") + "; font-weight: bold" },
|
||
util.escapeString(str, "")],
|
||
'"'];
|
||
|
||
let self = this;
|
||
var indentStr = "";
|
||
for (var c = 0; c < indent; c++)
|
||
indentStr += " ";
|
||
|
||
switch (node.nodeType) {
|
||
case Node.ELEMENT_NODE:
|
||
if (node.style.display == "none")
|
||
break;
|
||
|
||
var isLeaf = node.childNodes.length == 0
|
||
&& this.leafElems[node.localName];
|
||
var children = [];
|
||
var numChildren = 0;
|
||
if (!isLeaf) {
|
||
numChildren = node.childNodes.length;
|
||
children = template.map(node.childNodes,
|
||
node => ["", indentStr, " ",
|
||
self.domJavascript(node, indent + 1)],
|
||
["", ",", ["br"]]);
|
||
}
|
||
|
||
var properties = [];
|
||
var styles = [];
|
||
Array.forEach(node.attributes, function ({ nodeName, value }) {
|
||
if (value != null) {
|
||
switch (nodeName) {
|
||
case "style":
|
||
for (let { name, value } of Styles.propertyIter(value))
|
||
styles.push(["", FmtString(util.camelCase(name), "#060"), ":",
|
||
FmtString(value.trim(), "#008")]);
|
||
break;
|
||
default:
|
||
nodeName = this.camelCaseProps[n] || nodeName;
|
||
|
||
properties.push(["", FmtString(nodeName, "#080"), ":",
|
||
FmtString(value, "#00b")]);
|
||
break;
|
||
}
|
||
}
|
||
}, this);
|
||
|
||
if (styles.length) {
|
||
styles = template.map(styles, util.identity, ", ");
|
||
properties.unshift(["", FmtString("style", "080"), ":",
|
||
"{", styles, "}"]);
|
||
}
|
||
|
||
let numProps = properties.length;
|
||
properties = template.map(properties, util.identity, ", ");
|
||
properties = ["", "{", properties, "}"];
|
||
|
||
let xml = ["", "[", FmtString(nodeName(node), "red")];
|
||
if (numChildren) {
|
||
if (numProps)
|
||
return ["", xml, ", ", properties, ",\u000a;", children, "]"];
|
||
else
|
||
return ["", xml, ",'\u000a", children, "]"];
|
||
}
|
||
else if (numProps)
|
||
return ["", xml, ", ", properties, "]"];
|
||
else
|
||
return ["", xml, "]"];
|
||
break;
|
||
case Node.TEXT_NODE:
|
||
var n = node.nodeValue;
|
||
if (node.nodeValue != "")
|
||
n = util.escapeString(n, "");
|
||
|
||
n = n.trim();
|
||
if (n.length > 0)
|
||
return ["", '"', ["b", {}, n], '"'];
|
||
break;
|
||
case Node.CDATA_SECTION_NODE:
|
||
case Node.ENTITY_REFERENCE_NODE:
|
||
case Node.COMMENT_NODE:
|
||
break;
|
||
}
|
||
return null;
|
||
},
|
||
|
||
makeJavascript: function makeJavascript(elem) {
|
||
var dbox = AardvarkDBox(this, {
|
||
selectLink: true,
|
||
title: this.strings.javascriptDomCode,
|
||
data: [
|
||
["pre", { style: "margin:0; width: 97%" },
|
||
this.domJavascript(elem, 0)],
|
||
["br"]
|
||
]
|
||
});
|
||
dbox.show();
|
||
return true;
|
||
},
|
||
|
||
undo: function undo() {
|
||
if (!this.undoStack.length)
|
||
return false;
|
||
|
||
this.clearBox();
|
||
this.undoStack.pop().undo();
|
||
return true;
|
||
},
|
||
|
||
showMenu: function showMenu() {
|
||
if (this.helpBox && this.helpBox.kill()) {
|
||
delete this.helpBox;
|
||
return;
|
||
}
|
||
|
||
var s = ["table", { style: "margin:5px 10px 0 10px;" }];
|
||
for (let map of mappings.iterate(modes.AARDVARK))
|
||
if (map.name != "<Esc>")
|
||
s.push(["tr", {},
|
||
["td", { style: "padding: 3px 7px; border: 1px solid black; font-family: courier; font-weight: bold; background-color: #fff" },
|
||
map.name],
|
||
["td", { style: "padding: 3px 7px; font-size: .9em; text-align: left;" },
|
||
map.description]
|
||
]);
|
||
|
||
var dbox = AardvarkDBox(this, {
|
||
bgColor: "#fff2db",
|
||
selectLink: true,
|
||
position: { x: 20, y: 20 },
|
||
title: this.strings.aardvarkKeystrokes,
|
||
data: s
|
||
});
|
||
dbox.show();
|
||
this.helpBox = dbox;
|
||
return true;
|
||
},
|
||
|
||
highlightText: function highlightText(elem) {
|
||
let s = this.window.getSelection();
|
||
s.removeAllRanges();
|
||
s.addRange(RangeFind.nodeRange(elem));
|
||
dactyl.clipboardWrite(DOM.stringify(s), false, "selection");
|
||
},
|
||
|
||
strings: {
|
||
aardvarkKeystrokes: 'Aardvark keystrokes',
|
||
blackOnWhite: 'black on white',
|
||
deWidthify: 'de-widthify',
|
||
description: 'Utility for cleaning up a page prior to printing, and for analyzing a page.',
|
||
help: 'toggle &help',
|
||
javascriptDomCode: 'Javascript DOM code',
|
||
ripHelp: ["center", {},
|
||
"If you install the excellent ",
|
||
"", ["a", { href: "http://addons.mozilla.org/addon/521/", target: "_blank" }, "R.I.P."], ["br"],
|
||
"(Remove It Permanently), the K command will", ["br"],
|
||
"permanently remove items from a page."],
|
||
undo: 'undo',
|
||
viewHtmlSource: 'View HTML source',
|
||
viewSource: 'view source',
|
||
viewXPath: 'View XPath'
|
||
},
|
||
|
||
//-------------------------------------------------
|
||
// create the box and tag etc (done once and saved)
|
||
makeElems: function makeElems() {
|
||
this.borderElems = {};
|
||
|
||
for (let side of ["top", "bottom", "left", "right"]) {
|
||
this.borderElems[side] = DOM(["div", { style: "display: none; border-width: 0; border-" + side + "-width: 2px;",
|
||
highlight: "AardvarkBorder" }],
|
||
this.doc).appendTo(this.container);
|
||
}
|
||
|
||
this.labelElem = DOM(["div", { style: "display: none;", highlight: "AardvarkLabel" }],
|
||
this.doc).appendTo(this.container)[0];
|
||
|
||
this.keyboxElem = DOM(["div", { style: "display: none;", highlight: "AardvarkKeybox" }],
|
||
this.doc).appendTo(this.container);
|
||
},
|
||
|
||
//-------------------------------------------------
|
||
// show the red box around the element, and display
|
||
// the string in the little tag
|
||
showBoxAndLabel: function showBoxAndLabel(elem, label) {
|
||
var pos = this.getPos(elem);
|
||
var dims = this.getWindowDimensions();
|
||
var y = pos.y;
|
||
|
||
let width = parseFloat(this.borderElems.left.style.borderLeftWidth);
|
||
DOM(values(this.borderElems).map(e => e[0])).css({
|
||
left: pos.x - width + "px",
|
||
top: pos.y - width + "px",
|
||
width: elem.offsetWidth + 2 * width + "px",
|
||
height: elem.offsetHeight + 2 * width + "px"
|
||
}).show();
|
||
|
||
this.borderElems.left.css({
|
||
width: 0
|
||
});
|
||
this.borderElems.right.css({
|
||
width: 0,
|
||
left: (pos.x + elem.offsetWidth) + "px"
|
||
});
|
||
this.borderElems.top.css({
|
||
height: 0
|
||
});
|
||
this.borderElems.bottom.css({
|
||
height: 0,
|
||
top: (pos.y + elem.offsetHeight) + "px"
|
||
});
|
||
|
||
y += elem.offsetHeight + 2;
|
||
DOM(this.labelElem).empty().append(label).show();
|
||
|
||
this.labelElem.style.borderTopWidth = "";
|
||
this.labelElem.style.borderTopLeftRadius = "";
|
||
this.labelElem.style.borderTopRightRadius = "";
|
||
if ((y + this.labelElem.offsetHeight) >= dims.scrollY + dims.height) {
|
||
this.labelElem.style.cssText += "border-top-width: 1px !important; \
|
||
border-top-left-radius: 6px !important; \
|
||
border-top-right-radius: 6px !important;";
|
||
y = (dims.scrollY + dims.height) - this.labelElem.offsetHeight;
|
||
}
|
||
else if (this.labelElem.offsetWidth > elem.offsetWidth) {
|
||
this.labelElem.style.cssText += "border-top-width: 1px !important; \
|
||
border-top-right-radius: 6px !important;";
|
||
}
|
||
this.moveElem(this.labelElem, pos.x + 2, y);
|
||
},
|
||
|
||
//-------------------------------------------------
|
||
// remove the red box and tag
|
||
clearBox: function clearBox() {
|
||
this.selectedElem = null;
|
||
if (this.borderElems != null) {
|
||
for (let elem of values(this.borderElems))
|
||
elem.hide();
|
||
DOM(this.labelElem).hide();
|
||
}
|
||
},
|
||
|
||
//-------------------------------------------------
|
||
hideKeybox: function hideKeybox() {
|
||
this.keyboxElem.hide();
|
||
this.keyboxTimeout = null;
|
||
},
|
||
|
||
//-------------------------------------------------
|
||
showKeybox: function showKeybox(key){
|
||
if (this.keyboxElem == null)
|
||
return;
|
||
|
||
this.keyboxElem.empty().append(
|
||
template.highlightRegexp(key.aardvarkSpec || key.description, /&(.)/g,
|
||
(m, m1) => ["b", { style: "font-size: 2em" }, m1]));
|
||
|
||
var dims = this.getWindowDimensions();
|
||
var y = Math.max(0, dims.scrollY + this.mousePosY + 10);
|
||
if (y > (dims.scrollY + dims.height) - 30)
|
||
y = (dims.scrollY + dims.height) - 60;
|
||
|
||
var x = Math.max(0, this.mousePosX + 10);
|
||
if (x > (dims.scrollX + dims.width) - 60)
|
||
x = (dims.scrollX + dims.width) - 100;
|
||
|
||
this.moveElem(this.keyboxElem, x, y);
|
||
|
||
this.keyboxElem.show();
|
||
if (this.keyboxTimeout)
|
||
this.keyboxTimeout.cancel();
|
||
this.keyboxTimeout = this.timeout(this.hideKeybox, 400);
|
||
},
|
||
|
||
//-------------------------------------------------
|
||
makeElementLabelString: function makeElementLabelString(elem) {
|
||
var s = [["b", { style: "color:#000" }, nodeName(elem)]];
|
||
if (elem.id != "")
|
||
s.push(["b", {}, "#"], ["i", {}, elem.id]);
|
||
|
||
if (elem.className != "")
|
||
s.push(template.map(Array.slice(elem.classList),
|
||
clas => [["b", {}, "."], ["i", {}, clas]]));
|
||
return s;
|
||
},
|
||
|
||
events: {
|
||
mouseup: function onMouseUp(evt) {
|
||
if (this.dragElement) {
|
||
delete this.dragElement;
|
||
delete this.dragClickX;
|
||
delete this.dragClickY;
|
||
delete this.dragStartPos;
|
||
}
|
||
return false;
|
||
},
|
||
|
||
// the following three functions are the main event handlers
|
||
//-------------------------------------------------
|
||
mousemove: function onMouseMove(evt) {
|
||
if (this.mousePosX == evt.clientX &&
|
||
this.mousePosY == evt.clientY) {
|
||
this.moved = false;
|
||
return;
|
||
}
|
||
this.mousePosX = evt.clientX;
|
||
this.mousePosY = evt.clientY;
|
||
if (this.dragElement) {
|
||
this.moveElem(this.dragElement,
|
||
(this.mousePosX - this.dragClickX) + this.dragStartPos.x,
|
||
(this.mousePosY - this.dragClickY) + this.dragStartPos.y);
|
||
this.moved = false;
|
||
}
|
||
else {
|
||
this.moved = true;
|
||
}
|
||
evt.preventDefault();
|
||
},
|
||
|
||
//-------------------------------------------------
|
||
mouseover: function onMouseOver(evt) {
|
||
if (!this.moved)
|
||
return;
|
||
|
||
this.choose(evt.target);
|
||
},
|
||
|
||
click: function click(evt) {
|
||
this.choose(evt.target);
|
||
}
|
||
},
|
||
|
||
choose: function choose(elem) {
|
||
if (elem == this.labelElem || ~values(this.borderElems).indexOf(elem))
|
||
this.clearBox();
|
||
|
||
if (elem.isAardvark || DOM(elem).ancestors.some(e => e.isAardvark))
|
||
return;
|
||
|
||
if (elem == null) {
|
||
this.clearBox();
|
||
return;
|
||
}
|
||
elem = this.findValidElement(elem);
|
||
if (elem == null) {
|
||
this.clearBox();
|
||
return;
|
||
}
|
||
this.didWider = false;
|
||
if (elem != this.selectedElem) {
|
||
this.widerStack = null;
|
||
this.selectedElem = elem;
|
||
this.showBoxAndLabel(elem, this.makeElementLabelString(elem));
|
||
this.innerItem = null;
|
||
this.moved = false;
|
||
}
|
||
},
|
||
|
||
//-------------------------------------------------
|
||
// given an element, walk upwards to find the first
|
||
// valid selectable element
|
||
findValidElement: function findValidElement(elem) {
|
||
for (; elem; elem = elem.parentNode) {
|
||
if (hasOwnProperty(this.alwaysValidElements, elem.localName))
|
||
break;
|
||
|
||
let { display } = DOM(elem).style;
|
||
if (hasOwnProperty(this.validIfBlockElements, elem.localName) && display == "block")
|
||
break;
|
||
if (hasOwnProperty(this.validIfNotInlineElements, elem.localName) && display != "inline")
|
||
break;
|
||
}
|
||
return elem;
|
||
},
|
||
|
||
//-----------------------------------------------------
|
||
getPos: function getPos(elem) {
|
||
let body = this.doc.body || this.doc.documentElement;
|
||
let style = DOM(body).style;
|
||
|
||
if (~["absolute", "fixed", "relative"].indexOf(style.position)) {
|
||
let rect = body.getClientRects()[0];
|
||
var offsets = [-rect.left, -rect.top];
|
||
}
|
||
else
|
||
var offsets = [this.doc.defaultView.scrollX, this.doc.defaultView.scrollY];
|
||
|
||
let rect = DOM(elem).rect;
|
||
var pos = {
|
||
x: rect.left + offsets[0],
|
||
y: rect.top + offsets[1]
|
||
};
|
||
return pos;
|
||
},
|
||
|
||
//-----------------------------------------------------
|
||
// move a div (or whatever) to an x y location
|
||
moveElem: function moveElem(elem, x, y) {
|
||
DOM(elem).css({ left: x + "px", top: y + "px" });
|
||
},
|
||
|
||
//-------------------------------------------------
|
||
getWindowDimensions: function getWindowDimensions() {
|
||
var out = {};
|
||
out.scrollX = this.window.pageXOffset;
|
||
out.scrollY = this.window.pageYOffset;
|
||
if (this.doc.compatMode == "BackCompat") {
|
||
out.width = this.doc.body.clientWidth;
|
||
out.height = this.doc.body.clientHeight;
|
||
}
|
||
else {
|
||
out.width = this.doc.documentElement.clientWidth;
|
||
out.height = this.doc.documentElement.clientHeight;
|
||
}
|
||
return out;
|
||
},
|
||
|
||
dBoxId: 0,
|
||
|
||
alwaysValidElements: Set(["applet", "blockquote", "div", "form",
|
||
"h1", "h2", "h3", "iframe", "img", "object",
|
||
"p", "table", "td", "th", "tr"]),
|
||
|
||
validIfBlockElements: Set(["a", "span"]),
|
||
|
||
validIfNotInlineElements: Set(["code", "li", "ol", "pre", "ul"]),
|
||
|
||
leafElems: Set(["area", "base", "basefont", "br", "col", "frame", "hr",
|
||
"img", "input", "isindex", "link", "meta", "param"])
|
||
});
|
||
|
||
modes.addMode("AARDVARK", {
|
||
char: "A",
|
||
description: "Aardvark page editing mode",
|
||
bases: [modes.COMMAND],
|
||
ownsBuffer: true
|
||
});
|
||
|
||
let aardvark = Aardvark.prototype;
|
||
aardvark.commands = {};
|
||
|
||
// 0: name (member of aardvark.strings, or literal string)
|
||
// 1: function
|
||
// 2: needs element
|
||
// 3: 'true' for extension only
|
||
var commands = [
|
||
["wider", "wider", true],
|
||
["narrower", "narrower", true],
|
||
["undo", "undo", false],
|
||
["remove", "removeElement", true],
|
||
["kill", "rip", true],
|
||
["isolate", "isolateElement", true],
|
||
["blackOnWhite", "blackOnWhite", true],
|
||
["deWidthify", "deWidthify", true],
|
||
["colorize", "colorize", true],
|
||
["viewSource", "viewSource", true],
|
||
["javascript", "makeJavascript", true],
|
||
["paste", "paste", true],
|
||
["select", "select", false],
|
||
["thunk", "global", true],
|
||
["xpath", "xpath", true],
|
||
["help", "showMenu", false],
|
||
];
|
||
|
||
commands.forEach(function([fullname, func, needsElement]) {
|
||
if (aardvark.strings[fullname])
|
||
fullname = aardvark.strings[fullname];
|
||
|
||
let name = fullname.replace("&", "");
|
||
if (!/&/.test(fullname))
|
||
fullname = "&" + fullname;
|
||
let key = /&(.)/.exec(fullname)[1];
|
||
|
||
aardvark.commands[key] = {
|
||
name: name,
|
||
fullName: fullname,
|
||
func: func,
|
||
needsElement: needsElement,
|
||
};
|
||
|
||
group.mappings.add([modes.AARDVARK],
|
||
[key],
|
||
name,
|
||
function({ self }) {
|
||
if (needsElement) {
|
||
if (self.selectedElem && self[func](self.selectedElem) == true)
|
||
self.showKeybox(this);
|
||
}
|
||
else {
|
||
if (self[func](self.selectedElem) == true)
|
||
self.showKeybox(this);
|
||
}
|
||
}, {
|
||
aardvarkSpec: fullname
|
||
});
|
||
});
|
||
|
||
group.mappings.add([modes.AARDVARK],
|
||
["<Esc>", "<C-]>"],
|
||
"Close open panels or leave Aardvark mode",
|
||
function ({ self }) {
|
||
if (!self.closePanels())
|
||
modes.remove(modes.AARDVARK);
|
||
});
|
||
|
||
group.commands.add(["aardvark"],
|
||
"Aardvark page editor",
|
||
function (args) {
|
||
dactyl.assert(!modes.have(modes.AARDVARK))
|
||
Aardvark(buffer.focusedFrame);
|
||
},
|
||
{ argCount: 0 },
|
||
true);
|
||
|
||
function onUnload() {
|
||
modes.removeMode(modes.AARDVARK);
|
||
}
|
||
|