]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/content/mow.js
Imported Upstream version 1.1+hg7904
[dactyl.git] / common / content / mow.js
index 5c7a28c698e9c1a09cc859f194261cde1382f06d..e2ad98f5d459f512954d8da5ee283f9509e991da 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2011 by Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -21,11 +21,9 @@ var MOW = Module("mow", {
             if (modes.have(modes.OUTPUT_MULTILINE)) {
                 this.resize(true);
 
-                if (options["more"] && this.isScrollable(1)) {
+                if (options["more"] && this.canScroll(1))
                     // start the last executed command's output at the top of the screen
-                    let elements = this.document.getElementsByClassName("ex-command-output");
-                    elements[elements.length - 1].scrollIntoView(true);
-                }
+                    DOM(this.document.body.lastElementChild).scrollIntoView(true);
                 else
                     this.body.scrollTop = this.body.scrollHeight;
 
@@ -37,53 +35,43 @@ var MOW = Module("mow", {
         events.listen(window, this, "windowEvents");
 
         modules.mow = this;
-        let fontSize = util.computedStyle(document.documentElement).fontSize;
+        let fontSize = DOM(document.documentElement).style.fontSize;
         styles.system.add("font-size", "dactyl://content/buffer.xhtml",
                           "body { font-size: " + fontSize + "; } \
                            html|html > xul|scrollbar { visibility: collapse !important; }",
                           true);
 
-        XML.ignoreWhitespace = true;
-        util.overlayWindow(window, {
+        overlay.overlayWindow(window, {
             objects: {
                 eventTarget: this
             },
-            append: <e4x xmlns={XUL} xmlns:dactyl={NS}>
-                <window id={document.documentElement.id}>
-                    <popupset>
-                        <menupopup id="dactyl-contextmenu" highlight="Events" events="contextEvents">
-                            <menuitem id="dactyl-context-copylink"
-                                      label={_("mow.contextMenu.copyLink")} dactyl:group="link"
-                                      oncommand="goDoCommand('cmd_copyLink');"/>
-                            <menuitem id="dactyl-context-copypath"
-                                      label={_("mow.contextMenu.copyPath")} dactyl:group="link path"
-                                      oncommand="dactyl.clipboardWrite(document.popupNode.getAttribute('path'));"/>
-                            <menuitem id="dactyl-context-copy"
-                                      label={_("mow.contextMenu.copy")} dactyl:group="selection"
-                                      command="cmd_copy"/>
-                            <menuitem id="dactyl-context-selectall"
-                                      label={_("mow.contextMenu.selectAll")}
-                                      command="cmd_selectAll"/>
-                        </menupopup>
-                    </popupset>
-                </window>
-                <vbox id={config.commandContainer}>
-                    <vbox class="dactyl-container" id="dactyl-multiline-output-container" hidden="false" collapsed="true">
-                        <iframe id="dactyl-multiline-output" src="dactyl://content/buffer.xhtml"
-                                flex="1" hidden="false" collapsed="false" contextmenu="dactyl-contextmenu"
-                                highlight="Events" />
-                    </vbox>
-                </vbox>
-            </e4x>
+            append: [
+                ["window", { id: document.documentElement.id, xmlns: "xul" },
+                    ["popupset", {},
+                        ["menupopup", { id: "dactyl-contextmenu", highlight: "Events", events: "contextEvents" },
+                            ["menuitem", { id: "dactyl-context-copylink", label: _("mow.contextMenu.copyLink"),
+                                           "dactyl:group": "link",      oncommand: "goDoCommand('cmd_copyLink');" }],
+                            ["menuitem", { id: "dactyl-context-copypath", label: _("mow.contextMenu.copyPath"),
+                                           "dactyl:group": "link path", oncommand: "dactyl.clipboardWrite(document.popupNode.getAttribute('path'));" }],
+                            ["menuitem", { id: "dactyl-context-copy", label: _("mow.contextMenu.copy"),
+                                           "dactyl:group": "selection", command: "cmd_copy" }],
+                            ["menuitem", { id: "dactyl-context-selectall", label: _("mow.contextMenu.selectAll"),
+                                           command: "cmd_selectAll" }]]]],
+
+                ["vbox", { id: config.ids.commandContainer, xmlns: "xul" },
+                    ["vbox", { class: "dactyl-container", id: "dactyl-multiline-output-container", hidden: "false", collapsed: "true" },
+                        ["iframe", { id: "dactyl-multiline-output", src: "dactyl://content/buffer.xhtml",
+                                     flex: "1", hidden: "false", collapsed: "false",
+                                     contextmenu: "dactyl-contextmenu", highlight: "Events" }]]]]
         });
     },
 
     __noSuchMethod__: function (meth, args) Buffer[meth].apply(Buffer, [this.body].concat(args)),
 
     get widget() this.widgets.multilineOutput,
-    widgets: Class.memoize(function widgets() commandline.widgets),
+    widgets: Class.Memoize(function widgets() commandline.widgets),
 
-    body: Class.memoize(function body() this.widget.contentDocument.documentElement),
+    body: Class.Memoize(function body() this.widget.contentDocument.documentElement),
     get document() this.widget.contentDocument,
     get window() this.widget.contentWindow,
 
@@ -94,7 +82,7 @@ var MOW = Module("mow", {
      * @param {string} highlightGroup
      */
     echo: function echo(data, highlightGroup, silent) {
-        let body = this.document.body;
+        let body = DOM(this.document.body);
 
         this.widgets.message = null;
         if (!commandline.commandVisible)
@@ -102,31 +90,30 @@ var MOW = Module("mow", {
 
         if (modes.main != modes.OUTPUT_MULTILINE) {
             modes.push(modes.OUTPUT_MULTILINE, null, {
-                onKeyPress: this.closure.onKeyPress,
-                leave: this.closure(function leave(stack) {
+                onKeyPress: this.bound.onKeyPress,
+
+                leave: stack => {
                     if (stack.pop)
                         for (let message in values(this.messages))
                             if (message.leave)
                                 message.leave(stack);
-                })
+                },
+
+                window: this.window
             });
             this.messages = [];
         }
 
-        // If it's already XML, assume it knows what it's doing.
-        // Otherwise, white space is significant.
-        // The problem elsewhere is that E4X tends to insert new lines
-        // after interpolated data.
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
+        highlightGroup = "CommandOutput " + (highlightGroup || "");
 
-        if (isObject(data) && !isinstance(data, _)) {
+        if (isObject(data) && !isinstance(data, _) && !DOM.isJSONXML(data)) {
             this.lastOutput = null;
 
-            var output = util.xmlToDom(<div class="ex-command-output" style="white-space: nowrap" highlight={highlightGroup}/>,
-                                       this.document);
+            var output = DOM(["div", { style: "white-space: nowrap", highlight: highlightGroup }],
+                             this.document);
             data.document = this.document;
             try {
-                output.appendChild(data.message);
+                output.append(data.message);
             }
             catch (e) {
                 util.reportError(e);
@@ -135,24 +122,25 @@ var MOW = Module("mow", {
             this.messages.push(data);
         }
         else {
-            let style = isString(data) ? "pre" : "nowrap";
-            this.lastOutput = <div class="ex-command-output" style={"white-space: " + style} highlight={highlightGroup}>{data}</div>;
+            let style = isString(data) ? "pre-wrap" : "nowrap";
+            this.lastOutput = ["div", { style: "white-space: " + style, highlight: highlightGroup },
+                               data];
 
-            var output = util.xmlToDom(this.lastOutput, this.document);
+            var output = DOM(this.lastOutput, this.document);
         }
 
         // FIXME: need to make sure an open MOW is closed when commands
         //        that don't generate output are executed
         if (!this.visible) {
             this.body.scrollTop = 0;
-            body.textContent = "";
+            body.empty();
         }
 
-        body.appendChild(output);
+        body.append(output);
 
         let str = typeof data !== "xml" && data.message || data;
         if (!silent)
-            dactyl.triggerObserver("echoMultiline", data, highlightGroup, output);
+            dactyl.triggerObserver("echoMultiline", data, highlightGroup, output[0]);
 
         this._timer.tell();
         if (!this.visible)
@@ -161,7 +149,7 @@ var MOW = Module("mow", {
 
     events: {
         click: function onClick(event) {
-            if (event.getPreventDefault())
+            if (event.defaultPrevented)
                 return;
 
             const openLink = function openLink(where) {
@@ -169,8 +157,8 @@ var MOW = Module("mow", {
                 dactyl.open(event.target.href, where);
             };
 
-            if (event.target instanceof HTMLAnchorElement)
-                switch (events.toString(event)) {
+            if (event.target instanceof Ci.nsIDOMHTMLAnchorElement)
+                switch (DOM.Event.stringify(event)) {
                 case "<LeftMouse>":
                     openLink(dactyl.CURRENT_TAB);
                     break;
@@ -204,14 +192,14 @@ var MOW = Module("mow", {
         popupshowing: function onPopupShowing(event) {
             let menu = commandline.widgets.contextMenu;
             let enabled = {
-                link: window.document.popupNode instanceof HTMLAnchorElement,
+                link: window.document.popupNode instanceof Ci.nsIDOMHTMLAnchorElement,
                 path: window.document.popupNode.hasAttribute("path"),
                 selection: !window.document.commandDispatcher.focusedWindow.getSelection().isCollapsed
             };
 
             for (let node in array.iterValues(menu.children)) {
                 let group = node.getAttributeNS(NS, "group");
-                node.hidden = group && !group.split(/\s+/).every(function (g) enabled[g]);
+                node.hidden = group && !group.split(/\s+/).every(g => enabled[g]);
             }
         }
     },
@@ -219,7 +207,7 @@ var MOW = Module("mow", {
     onKeyPress: function onKeyPress(eventList) {
         const KILL = false, PASS = true;
 
-        if (options["more"] && mow.isScrollable(1))
+        if (options["more"] && mow.canScroll(1))
             this.updateMorePrompt(false, true);
         else {
             modes.pop();
@@ -241,16 +229,21 @@ var MOW = Module("mow", {
 
         let doc = this.widget.contentDocument;
 
-        let availableHeight = config.outputHeight;
+        let trim = this.spaceNeeded;
+        let availableHeight = config.outputHeight - trim;
         if (this.visible)
             availableHeight += parseFloat(this.widgets.mowContainer.height || 0);
         availableHeight -= extra || 0;
 
         doc.body.style.minWidth = this.widgets.commandbar.commandline.scrollWidth + "px";
-        this.widgets.mowContainer.height = Math.min(doc.body.clientHeight, availableHeight) + "px";
-        this.timeout(function ()
-            this.widgets.mowContainer.height = Math.min(doc.body.clientHeight, availableHeight) + "px",
-            0);
+
+        function adjust() {
+            let wantedHeight = doc.body.clientHeight;
+            this.widgets.mowContainer.height = Math.min(wantedHeight, availableHeight) + "px",
+            this.wantedHeight = Math.max(0, wantedHeight - availableHeight);
+        }
+        adjust.call(this);
+        this.timeout(adjust);
 
         doc.body.style.minWidth = "";
 
@@ -258,9 +251,10 @@ var MOW = Module("mow", {
     },
 
     get spaceNeeded() {
-        let rect = this.widgets.commandbar.commandline.getBoundingClientRect();
-        let offset = rect.bottom - window.innerHeight;
-        return Math.max(0, offset);
+        if (DOM("#dactyl-bell", document).isVisible)
+            return 0;
+        return Math.max(0, DOM("#" + config.ids.commandContainer, document).rect.bottom
+                            - window.innerHeight);
     },
 
     /**
@@ -279,7 +273,7 @@ var MOW = Module("mow", {
 
         if (showHelp)
             this.widgets.message = ["MoreMsg", _("mow.moreHelp")];
-        else if (force || (options["more"] && Buffer.isScrollable(elem, 1)))
+        else if (force || (options["more"] && Buffer.canScroll(elem, 1)))
             this.widgets.message = ["MoreMsg", _("mow.more")];
         else
             this.widgets.message = ["Question", _("mow.continue")];
@@ -294,7 +288,7 @@ var MOW = Module("mow", {
             if (!value && elem && elem.contentWindow == document.commandDispatcher.focusedWindow) {
 
                 let focused = content.document.activeElement;
-                if (Events.isInputElement(focused))
+                if (focused && Events.isInputElement(focused))
                     focused.blur();
 
                 document.commandDispatcher.focusedWindow = content;
@@ -303,6 +297,12 @@ var MOW = Module("mow", {
     })
 }, {
 }, {
+    modes: function initModes() {
+        modes.addMode("OUTPUT_MULTILINE", {
+            description: "Active when the multi-line output buffer is open",
+            bases: [modes.NORMAL]
+        });
+    },
     mappings: function initMappings() {
         const PASS = true;
         const DROP = false;
@@ -315,6 +315,11 @@ var MOW = Module("mow", {
                 mow.echo(mow.lastOutput, "Normal");
             });
 
+        mappings.add([modes.OUTPUT_MULTILINE],
+            ["<Esc>", "<C-[>"],
+            "Return to the previous mode",
+            function () { modes.pop(null, { fromEscape: true }); });
+
         let bind = function bind(keys, description, action, test, default_) {
             mappings.add([modes.OUTPUT_MULTILINE],
                 keys, description,
@@ -341,36 +346,36 @@ var MOW = Module("mow", {
 
         bind(["j", "<C-e>", "<Down>"], "Scroll down one line",
              function ({ count }) { mow.scrollVertical("lines", 1 * (count || 1)); },
-             function () mow.isScrollable(1), BEEP);
+             () => mow.canScroll(1), BEEP);
 
         bind(["k", "<C-y>", "<Up>"], "Scroll up one line",
              function ({ count }) { mow.scrollVertical("lines", -1 * (count || 1)); },
-             function () mow.isScrollable(-1), BEEP);
+             () => mow.canScroll(-1), BEEP);
 
         bind(["<C-j>", "<C-m>", "<Return>"], "Scroll down one line, exit on last line",
              function ({ count }) { mow.scrollVertical("lines", 1 * (count || 1)); },
-             function () mow.isScrollable(1), DROP);
+             () => mow.canScroll(1), DROP);
 
         // half page down
         bind(["<C-d>"], "Scroll down half a page",
              function ({ count }) { mow.scrollVertical("pages", .5 * (count || 1)); },
-             function () mow.isScrollable(1), BEEP);
+             () => mow.canScroll(1), BEEP);
 
         bind(["<C-f>", "<PageDown>"], "Scroll down one page",
              function ({ count }) { mow.scrollVertical("pages", 1 * (count || 1)); },
-             function () mow.isScrollable(1), BEEP);
+             () => mow.canScroll(1), BEEP);
 
         bind(["<Space>"], "Scroll down one page",
              function ({ count }) { mow.scrollVertical("pages", 1 * (count || 1)); },
-             function () mow.isScrollable(1), DROP);
+             () => mow.canScroll(1), DROP);
 
         bind(["<C-u>"], "Scroll up half a page",
              function ({ count }) { mow.scrollVertical("pages", -.5 * (count || 1)); },
-             function () mow.isScrollable(-1), BEEP);
+             () => mow.canScroll(-1), BEEP);
 
         bind(["<C-b>", "<PageUp>"], "Scroll up half a page",
              function ({ count }) { mow.scrollVertical("pages", -1 * (count || 1)); },
-             function () mow.isScrollable(-1), BEEP);
+             () => mow.canScroll(-1), BEEP);
 
         bind(["gg"], "Scroll to the beginning of output",
              function () { mow.scrollToPercent(null, 0); });
@@ -380,12 +385,12 @@ var MOW = Module("mow", {
 
         // copy text to clipboard
         bind(["<C-y>"], "Yank selection to clipboard",
-             function () { dactyl.clipboardWrite(buffer.getCurrentWord(mow.window)); });
+             function () { dactyl.clipboardWrite(Buffer.currentWord(mow.window)); });
 
         // close the window
         bind(["q"], "Close the output window",
              function () {},
-             function () false, DROP);
+             () => false, DROP);
     },
     options: function initOptions() {
         options.add(["more"],
@@ -394,4 +399,4 @@ var MOW = Module("mow", {
     }
 });
 
-// vim: set fdm=marker sw=4 ts=4 et:
+// vim: set fdm=marker sw=4 sts=4 ts=8 et: