<commit_before>var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var randomstring = require("randomstring");
var qrcode = require("qrcode");
var repeat = require("repeat");
var cookieParser = require("cookie-parser");
var Cookies = require("cookies");
var port = 3011;

require('console-stamp')(console, 'HH:MM:ss.l');

/*

 "-sessionId-": {
 lastActivity: -timestamp-,
 host: -socket-,
 remotes: [-socket-],

 }

 */
var sessions = {};

repeat(function () {
    var expiredSessions = [];
    var now = new Date().valueOf();
    Object.keys(sessions).forEach(function (key) {
        var session = sessions[key];
        if (now - session.lastActivity > 3.6e+6) {// 1 hour
            expiredSessions.push(key);
        }
    });

    if (expiredSessions.length > 0) {
        console.info("Cleaning up " + expiredSessions.length + " expired sessions (" + (Object.keys(sessions).length - expiredSessions.length) + " remaining)...");
        expiredSessions.forEach(function (session) {
            delete sessions[session];
        });
    }
}).every(30, "minutes").during(function () {
    return true;
}).start();

app.use(function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    if (req.method === 'OPTIONS') {
        return res.send(200);
    } else {
        return next();
    }
});
app.use(express.static("static"));
app.use(cookieParser());

app.get("/api/session", function (req, res) {// Continue or create session
    var cookies = new Cookies(req, res);

    // First check if there's an old session
    var sessionId = cookies.get("rs-session-id");
    if (!sessionId || !sessions[sessionId]) {
        // Or create a new one
        sessionId = randomstring.generate(10);

        sessions[sessionId] = {
            id: sessionId,
            lastActivity: new Date().valueOf(),
            observer: undefined,
            host: undefined,
            remotes: []
        };

        console.info("New Session: " + sessionId)
    } else {
        sessions[sessionId].lastActivity = new Date().valueOf();
    }

    // Set session cookie
    cookies.set("rs-session-id", sessionId, {
        maxAge: 3.6e+6
    });

    qrcode.toDataURL("https://remote-sli.de/" + sessionId, {margin: 1, scale: 5}, function (err, url) {
        res.send({
            session: sessionId,
            qr: url
        })
    });
});

app.get("*", function (req, res) {
    // redirects
    if ('remote-slide.ga' == req.headers.host || 'www.remote-sli.de' == req.headers.host) {
        res.redirect("https://remote-sli.de" + req.originalUrl);
        return;
    }

    res.sendFile(__dirname + "/views/index.html");
});

function getConnectionInfo(session) {
    return {
        observer: session.observer ? true : false,
        host: session.host ? true : false,
        remotes: session.remotes.length
    };
}

io.on('connection', function (socket) {
    console.log("connection");
    socket.emit("init", {state: "start"});

    socket.on('init', function (msg) {
        console.log(msg)
        var clientType = msg["iAm"];// either 'host', 'remote' or 'observer'
        if ('host' !== clientType && 'remote' !== clientType && 'observer' !== clientType) {
            socket.emit("err", {code: 400, msg: "Invalid client type"});
            return;
        }
        var sessionId = msg["session"];

        var session = sessions[sessionId];
        if (!session) {
            socket.emit("err", {code: 404, msg: "Session not found"});
            socket.emit("init", {state: "not_found"});
            return;
        }

        // Assign session ID
        socket.clientType = clientType;
        socket.sessionId = sessionId;

        if (socket.clientType == 'observer') {
            if (session.observer) {
                session.observer.disconnect();
            }
            session.observer = socket;
        }
        if (socket.clientType == 'host') {
            if (session.host) {
                session.host.disconnect();
            }
            session.host = socket;
            if (session.observer) {
                session.observer.emit("info", {type: "client_connected", clientType: "host", info: getConnectionInfo(session)});
            }
            session.remotes.forEach(function (remote) {
                remote.emit("info", {type: "client_connected", clientType: "host", info: getConnectionInfo(session)});
            });
        }
        if (socket.clientType == 'remote') {
            session.remotes.push(socket);
            if (session.observer) {
                session.observer.emit("info", {type: "client_connected", clientType: "remote", info: getConnectionInfo(session)});
            }
            if (session.host) {
                session.host.emit("info", {type: "client_connected", clientType: "remote", info: getConnectionInfo(session)});
            }
        }

        socket.emit("init", {state: "success", youAre: clientType, info: getConnectionInfo(session)});
        console.log("[+]" + (clientType == 'host' ? "Host" : clientType == 'remote' ? "Remote" : clientType == 'observer' ? "Observer" : "???" ) + " for #" + sessionId + " connected (Host: " + (session.host ? "connected" : "not connected") + ", " + session.remotes.length + " Remotes connected)");
    });

    socket.on("control", function (msg) {
        console.log(msg);
        if (!socket.sessionId || !socket.clientType) {
            socket.emit("err", {code: 400, msg: "Invalid session"});
            return;
        }
        var session = sessions[socket.sessionId];
        if (!session) {
            socket.emit("err", {code: 400, msg: "Invalid session"});
            return;
        }

        // we should only need to listen for control messages by clients
        if (socket.clientType == 'remote') {
            var keyCode = msg["keyCode"];

            if (!keyCode) {
                socket.emit("err", {code: 400, msg: "Missing keyCode"});
                return;
            }

            var keys = {};
            if (msg.keys && msg.keys.ctrl)
                keys.ctrl = true;
            if (msg.keys && msg.keys.shift)
                keys.shift = true;
            if (msg.keys && msg.keys.alt)
                keys.alt = true;

            if (session.host) {
                session.host.emit("control", {keyCode: keyCode, keys: keys});
            } else {
                //TODO: maybe change to err
                socket.emit("info", {code: 200, msg: "Session host not yet connected"});
            }

            session.lastActivity = new Date().valueOf();

            session.remotes.forEach(function (remote) {
                remote.emit("control", {keyCode: keyCode, keys: keys});
            })
        }
    });

    socket.on("_forward", function (data) {
        console.log(data)
        if (!socket.sessionId || !socket.clientType) {
            socket.emit("err", {code: 400, msg: "Invalid session"});
            return;
        }
        var session = sessions[socket.sessionId];
        if (!session) {
            socket.emit("err", {code: 400, msg: "Invalid session"});
            return;
        }

        if (!data.event) {
            socket.emit("err", {code: 400, msg: "Missing 'event' value for forward"});
            return;
        }
        var event = data.event;
        delete data.event;

        if (socket.clientType == 'remote') {
            if (session.host) {
                session.host.emit(event, data);
            } else {
                //TODO: maybe change to err
                socket.emit("info", {code: 200, msg: "Session host not yet connected"});
            }
        } else if (socket.clientType == 'host') {
            if (session.remotes.length > 0) {
                session.remotes.forEach(function (remote) {
                    remote.emit(event, data);
                })
            } else {
                //TODO: maybe change to err
                socket.emit("info", {code: 200, msg: "No session remotes are connected yet"});
            }
        }
    });

    socket.on('latency', function () {
        socket.emit('latency', {t: Date.now()});
    });

    socket.on('disconnect', function () {
        if (socket.sessionId) {
            // Remove the client
            var session = sessions[socket.sessionId];
            if (session) {
                if (socket.clientType == 'observer') {
                    session.observer = undefined;
                    console.log("[-] Observer of #" + socket.sessionId + " disconnected (Observer: " + (session.observer ? "connected" : "not connected") + ", Host: " + (session.host ? "connected" : "not connected") + ", " + session.remotes.length + " Remotes connected)");
                }
                if (socket.clientType == 'host') {
                    session.host = undefined;
                    console.log("[-] Host of #" + socket.sessionId + " disconnected (Observer: " + (session.observer ? "connected" : "not connected") + ", Host: " + (session.host ? "connected" : "not connected") + ", " + session.remotes.length + " Remotes connected)");

                    if (session.observer) {
                        session.observer.emit("info", {type: "client_disconnected", clientType: "host", info: getConnectionInfo(session)});
                    }
                    session.remotes.forEach(function (remote) {
                        remote.emit("info", {type: "client_disconnected", clientType: "host", info: getConnectionInfo(session)});
                    });
                }
                if (socket.clientType == 'remote') {
                    var index = session.remotes.indexOf(socket);
                    if (index !== -1) {
                        session.remotes.splice(index, 1);
                    }
                    console.log("[-] A remote of #" + socket.sessionId + " disconnected (Observer: " + (session.observer ? "connected" : "not connected") + ", Host: " + (session.host ? "connected" : "not connected") + ", " + session.remotes.length + " Remotes connected)")

                    if (session.observer) {
                        session.observer.emit("info", {type: "client_disconnected", clientType: "remote", info: getConnectionInfo(session)});
                    }
                    if (session.host) {
                        session.host.emit("info", {type: "client_disconnected", clientType: "remote", info: getConnectionInfo(session)});
                    }
                    session.remotes.forEach(function (remote) {
                        remote.emit("info", {type: "client_disconnected", clientType: "remote", info: getConnectionInfo(session)});
                    });
                }
            }
        }
    });
});

http.listen(port, function () {
    console.log('listening on *:' + port);
});<commit_msg>track injector type<commit_after>var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var randomstring = require("randomstring");
var qrcode = require("qrcode");
var repeat = require("repeat");
var cookieParser = require("cookie-parser");
var Cookies = require("cookies");
var port = 3011;

require('console-stamp')(console, 'HH:MM:ss.l');

/*

 "-sessionId-": {
 lastActivity: -timestamp-,
 host: -socket-,
 remotes: [-socket-],

 }

 */
var sessions = {};

repeat(function () {
    var expiredSessions = [];
    var now = new Date().valueOf();
    Object.keys(sessions).forEach(function (key) {
        var session = sessions[key];
        if (now - session.lastActivity > 3.6e+6) {// 1 hour
            expiredSessions.push(key);
        }
    });

    if (expiredSessions.length > 0) {
        console.info("Cleaning up " + expiredSessions.length + " expired sessions (" + (Object.keys(sessions).length - expiredSessions.length) + " remaining)...");
        expiredSessions.forEach(function (session) {
            delete sessions[session];
        });
    }
}).every(30, "minutes").during(function () {
    return true;
}).start();

app.use(function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    if (req.method === 'OPTIONS') {
        return res.send(200);
    } else {
        return next();
    }
});
app.use(express.static("static"));
app.use(cookieParser());

app.get("/api/session", function (req, res) {// Continue or create session
    var cookies = new Cookies(req, res);

    // First check if there's an old session
    var sessionId = cookies.get("rs-session-id");
    if (!sessionId || !sessions[sessionId]) {
        // Or create a new one
        sessionId = randomstring.generate(10);

        sessions[sessionId] = {
            id: sessionId,
            lastActivity: new Date().valueOf(),
            observer: undefined,
            host: undefined,
            remotes: []
        };

        console.info("New Session: " + sessionId)
    } else {
        sessions[sessionId].lastActivity = new Date().valueOf();
    }

    // Set session cookie
    cookies.set("rs-session-id", sessionId, {
        maxAge: 3.6e+6
    });

    qrcode.toDataURL("https://remote-sli.de/" + sessionId, {margin: 1, scale: 5}, function (err, url) {
        res.send({
            session: sessionId,
            qr: url
        })
    });
});

app.get("*", function (req, res) {
    // redirects
    if ('remote-slide.ga' == req.headers.host || 'www.remote-sli.de' == req.headers.host) {
        res.redirect("https://remote-sli.de" + req.originalUrl);
        return;
    }

    res.sendFile(__dirname + "/views/index.html");
});

function getConnectionInfo(session) {
    return {
        observer: session.observer ? true : false,
        host: session.host ? true : false,
        remotes: session.remotes.length
    };
}

io.on('connection', function (socket) {
    console.log("connection");
    socket.emit("init", {state: "start"});

    socket.on('init', function (msg) {
        console.log(msg)
        var clientType = msg["iAm"];// either 'host', 'remote' or 'observer'
        if ('host' !== clientType && 'remote' !== clientType && 'observer' !== clientType) {
            socket.emit("err", {code: 400, msg: "Invalid client type"});
            return;
        }
        var sessionId = msg["session"];

        var session = sessions[sessionId];
        if (!session) {
            socket.emit("err", {code: 404, msg: "Session not found"});
            socket.emit("init", {state: "not_found"});
            return;
        }

        // Assign session ID
        socket.clientType = clientType;
        socket.sessionId = sessionId;
        if (msg.injector) {
            session.injectorType = msg.injector;
        }

        if (socket.clientType == 'observer') {
            if (session.observer) {
                session.observer.disconnect();
            }
            session.observer = socket;
        }
        if (socket.clientType == 'host') {
            if (session.host) {
                session.host.disconnect();
            }
            session.host = socket;
            if (session.observer) {
                session.observer.emit("info", {type: "client_connected", clientType: "host", info: getConnectionInfo(session)});
            }
            session.remotes.forEach(function (remote) {
                remote.emit("info", {type: "client_connected", clientType: "host", info: getConnectionInfo(session)});
            });
        }
        if (socket.clientType == 'remote') {
            session.remotes.push(socket);
            if (session.observer) {
                session.observer.emit("info", {type: "client_connected", clientType: "remote", info: getConnectionInfo(session)});
            }
            if (session.host) {
                session.host.emit("info", {type: "client_connected", clientType: "remote", info: getConnectionInfo(session)});
            }
        }

        socket.emit("init", {state: "success", youAre: clientType, info: getConnectionInfo(session)});
        console.log("[+]" + (clientType == 'host' ? "Host" : clientType == 'remote' ? "Remote" : clientType == 'observer' ? "Observer" : "???" ) + " for #" + sessionId + " connected (Host: " + (session.host ? "connected" : "not connected") + ", " + session.remotes.length + " Remotes connected)");
    });

    socket.on("control", function (msg) {
        console.log(msg);
        if (!socket.sessionId || !socket.clientType) {
            socket.emit("err", {code: 400, msg: "Invalid session"});
            return;
        }
        var session = sessions[socket.sessionId];
        if (!session) {
            socket.emit("err", {code: 400, msg: "Invalid session"});
            return;
        }

        // we should only need to listen for control messages by clients
        if (socket.clientType == 'remote') {
            var keyCode = msg["keyCode"];

            if (!keyCode) {
                socket.emit("err", {code: 400, msg: "Missing keyCode"});
                return;
            }

            var keys = {};
            if (msg.keys && msg.keys.ctrl)
                keys.ctrl = true;
            if (msg.keys && msg.keys.shift)
                keys.shift = true;
            if (msg.keys && msg.keys.alt)
                keys.alt = true;

            if (session.host) {
                session.host.emit("control", {keyCode: keyCode, keys: keys});
            } else {
                //TODO: maybe change to err
                socket.emit("info", {code: 200, msg: "Session host not yet connected"});
            }

            session.lastActivity = new Date().valueOf();

            session.remotes.forEach(function (remote) {
                remote.emit("control", {keyCode: keyCode, keys: keys});
            })
        }
    });

    socket.on("_forward", function (data) {
        console.log(data)
        if (!socket.sessionId || !socket.clientType) {
            socket.emit("err", {code: 400, msg: "Invalid session"});
            return;
        }
        var session = sessions[socket.sessionId];
        if (!session) {
            socket.emit("err", {code: 400, msg: "Invalid session"});
            return;
        }

        if (!data.event) {
            socket.emit("err", {code: 400, msg: "Missing 'event' value for forward"});
            return;
        }
        var event = data.event;
        delete data.event;

        if (socket.clientType == 'remote') {
            if (session.host) {
                session.host.emit(event, data);
            } else {
                //TODO: maybe change to err
                socket.emit("info", {code: 200, msg: "Session host not yet connected"});
            }
        } else if (socket.clientType == 'host') {
            if (session.remotes.length > 0) {
                session.remotes.forEach(function (remote) {
                    remote.emit(event, data);
                })
            } else {
                //TODO: maybe change to err
                socket.emit("info", {code: 200, msg: "No session remotes are connected yet"});
            }
        }
    });

    socket.on('latency', function () {
        socket.emit('latency', {t: Date.now()});
    });

    socket.on('disconnect', function () {
        if (socket.sessionId) {
            // Remove the client
            var session = sessions[socket.sessionId];
            if (session) {
                if (socket.clientType == 'observer') {
                    session.observer = undefined;
                    console.log("[-] Observer of #" + socket.sessionId + " disconnected (Observer: " + (session.observer ? "connected" : "not connected") + ", Host: " + (session.host ? "connected" : "not connected") + ", " + session.remotes.length + " Remotes connected)");
                }
                if (socket.clientType == 'host') {
                    session.host = undefined;
                    console.log("[-] Host of #" + socket.sessionId + " disconnected (Observer: " + (session.observer ? "connected" : "not connected") + ", Host: " + (session.host ? "connected" : "not connected") + ", " + session.remotes.length + " Remotes connected)");

                    if (session.observer) {
                        session.observer.emit("info", {type: "client_disconnected", clientType: "host", info: getConnectionInfo(session)});
                    }
                    session.remotes.forEach(function (remote) {
                        remote.emit("info", {type: "client_disconnected", clientType: "host", info: getConnectionInfo(session)});
                    });
                }
                if (socket.clientType == 'remote') {
                    var index = session.remotes.indexOf(socket);
                    if (index !== -1) {
                        session.remotes.splice(index, 1);
                    }
                    console.log("[-] A remote of #" + socket.sessionId + " disconnected (Observer: " + (session.observer ? "connected" : "not connected") + ", Host: " + (session.host ? "connected" : "not connected") + ", " + session.remotes.length + " Remotes connected)")

                    if (session.observer) {
                        session.observer.emit("info", {type: "client_disconnected", clientType: "remote", info: getConnectionInfo(session)});
                    }
                    if (session.host) {
                        session.host.emit("info", {type: "client_disconnected", clientType: "remote", info: getConnectionInfo(session)});
                    }
                    session.remotes.forEach(function (remote) {
                        remote.emit("info", {type: "client_disconnected", clientType: "remote", info: getConnectionInfo(session)});
                    });
                }
            }
        }
    });
});

http.listen(port, function () {
    console.log('listening on *:' + port);
});<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2010 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2010 by Kris Maglione <maglione.k at Gmail>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
"use strict";

/** @scope modules */

/**
 * @instance browser
 */
const Browser = Module("browser", {
}, {
    climbUrlPath: function (count) {
        try {
            var url = util.newURI(buffer.URL).QueryInterface(Ci.nsIURL);
        }
        catch (e) {}
        dactyl.assert(url);

        while (count-- && url.path != "/")
            url.path = url.path.replace(/[^\/]+\/?$/, "")

        dactyl.assert(url.spec != buffer.URL);
        dactyl.open(url.spec);
    },

    incrementURL: function (count) {
        let matches = buffer.URL.match(/(.*?)(\d+)(\D*)$/);
        dactyl.assert(matches);

        let [, pre, number, post] = matches;
        let newNumber = parseInt(number, 10) + count;
        let newNumberStr = String(newNumber > 0 ? newNumber : 0);
        if (number.match(/^0/)) { // add 0009<C-a> should become 0010
            while (newNumberStr.length < number.length)
                newNumberStr = "0" + newNumberStr;
        }

        dactyl.open(pre + newNumberStr + post);
    }
}, {
    options: function () {
        options.add(["encoding", "enc"],
            "Sets the current buffer's character encoding",
            "string", "UTF-8",
            {
                scope: Option.SCOPE_LOCAL,
                getter: function () config.browser.docShell.QueryInterface(Ci.nsIDocCharset).charset,
                setter: function (val) {
                    if (options["encoding"] == val)
                        return val;

                    // Stolen from browser.jar/content/browser/browser.js, more or less.
                    try {
                        config.browser.docShell.QueryInterface(Ci.nsIDocCharset).charset = val;
                        PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, val);
                        getWebNavigation().reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
                    }
                    catch (e) { dactyl.reportError(e); }
                    return null;
                },
                completer: function (context) completion.charset(context)
            });
    },

    mappings: function () {
        mappings.add([modes.NORMAL],
            ["y"], "Yank current location to the clipboard",
            function () { dactyl.clipboardWrite(buffer.URL, true); });

        // opening websites
        mappings.add([modes.NORMAL],
            ["o"], "Open one or more URLs",
            function () { commandline.open(":", "open ", modes.EX); });

        mappings.add([modes.NORMAL], ["O"],
            "Open one or more URLs, based on current location",
            function () { commandline.open(":", "open " + buffer.URL, modes.EX); });

        mappings.add([modes.NORMAL], ["t"],
            "Open one or more URLs in a new tab",
            function () { commandline.open(":", "tabopen ", modes.EX); });

        mappings.add([modes.NORMAL], ["T"],
            "Open one or more URLs in a new tab, based on current location",
            function () { commandline.open(":", "tabopen " + buffer.URL, modes.EX); });

        mappings.add([modes.NORMAL], ["w"],
            "Open one or more URLs in a new window",
            function () { commandline.open(":", "winopen ", modes.EX); });

        mappings.add([modes.NORMAL], ["W"],
            "Open one or more URLs in a new window, based on current location",
            function () { commandline.open(":", "winopen " + buffer.URL, modes.EX); });

        mappings.add([modes.NORMAL],
            ["<C-a>"], "Increment last number in URL",
            function (count) { Browser.incrementURL(Math.max(count, 1)); },
            { count: true });

        mappings.add([modes.NORMAL],
            ["<C-x>"], "Decrement last number in URL",
            function (count) { Browser.incrementURL(-Math.max(count, 1)); },
            { count: true });

        mappings.add([modes.NORMAL], ["~"],
            "Open home directory",
            function () { dactyl.open("~"); });

        mappings.add([modes.NORMAL], ["gh"],
            "Open homepage",
            function () { BrowserHome(); });

        mappings.add([modes.NORMAL], ["gH"],
            "Open homepage in a new tab",
            function () {
                let homepages = gHomeButton.getHomePage();
                dactyl.open(homepages, { from: "homepage", where: dactyl.NEW_TAB });
            });

        mappings.add([modes.NORMAL], ["gu"],
            "Go to parent directory",
            function (count) { Browser.climbUrlPath(Math.max(count, 1)); },
            { count: true });

        mappings.add([modes.NORMAL], ["gU"],
            "Go to the root of the website",
            function () { Browser.climbUrlPath(-1); });

        mappings.add([modes.NORMAL], ["<C-l>"],
            "Redraw the screen",
            function () { ex.redraw(); });
    },

    commands: function () {
        commands.add(["downl[oads]", "dl"],
            "Show progress of current downloads",
            function () {
                dactyl.open("chrome://mozapps/content/downloads/downloads.xul",
                    { from: "downloads"});
            },
            { argCount: "0" });

        commands.add(["o[pen]"],
            "Open one or more URLs in the current tab",
            function (args) { dactyl.open(args[0] || "about:blank"); },
            {
                completer: function (context) completion.url(context),
                domains: function (args) array.compact(dactyl.parseURLs(args[0] || "").map(
                    function (url) util.getHost(url))),
                literal: 0,
                privateData: true
            });

        commands.add(["redr[aw]"],
            "Redraw the screen",
            function () {
                window.QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIDOMWindowUtils).redraw();
                statusline.updateUrl();
                commandline.clear();
            },
            { argCount: "0" });
    }
});

// vim: set fdm=marker sw=4 ts=4 et:
<commit_msg>Small change to Browser.climbUrlPath.<commit_after>// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2010 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2010 by Kris Maglione <maglione.k at Gmail>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
"use strict";

/** @scope modules */

/**
 * @instance browser
 */
const Browser = Module("browser", {
}, {
    climbUrlPath: function (count) {
        let url = util.newURI(buffer.URL);
        dactyl.assert(url instanceof Ci.nsIURL);

        while (count-- && url.path != "/")
            url.path = url.path.replace(/[^\/]+\/?$/, "")

        dactyl.assert(url.spec != buffer.URL);
        dactyl.open(url.spec);
    },

    incrementURL: function (count) {
        let matches = buffer.URL.match(/(.*?)(\d+)(\D*)$/);
        dactyl.assert(matches);

        let [, pre, number, post] = matches;
        let newNumber = parseInt(number, 10) + count;
        let newNumberStr = String(newNumber > 0 ? newNumber : 0);
        if (number.match(/^0/)) { // add 0009<C-a> should become 0010
            while (newNumberStr.length < number.length)
                newNumberStr = "0" + newNumberStr;
        }

        dactyl.open(pre + newNumberStr + post);
    }
}, {
    options: function () {
        options.add(["encoding", "enc"],
            "Sets the current buffer's character encoding",
            "string", "UTF-8",
            {
                scope: Option.SCOPE_LOCAL,
                getter: function () config.browser.docShell.QueryInterface(Ci.nsIDocCharset).charset,
                setter: function (val) {
                    if (options["encoding"] == val)
                        return val;

                    // Stolen from browser.jar/content/browser/browser.js, more or less.
                    try {
                        config.browser.docShell.QueryInterface(Ci.nsIDocCharset).charset = val;
                        PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, val);
                        getWebNavigation().reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
                    }
                    catch (e) { dactyl.reportError(e); }
                    return null;
                },
                completer: function (context) completion.charset(context)
            });
    },

    mappings: function () {
        mappings.add([modes.NORMAL],
            ["y"], "Yank current location to the clipboard",
            function () { dactyl.clipboardWrite(buffer.URL, true); });

        // opening websites
        mappings.add([modes.NORMAL],
            ["o"], "Open one or more URLs",
            function () { commandline.open(":", "open ", modes.EX); });

        mappings.add([modes.NORMAL], ["O"],
            "Open one or more URLs, based on current location",
            function () { commandline.open(":", "open " + buffer.URL, modes.EX); });

        mappings.add([modes.NORMAL], ["t"],
            "Open one or more URLs in a new tab",
            function () { commandline.open(":", "tabopen ", modes.EX); });

        mappings.add([modes.NORMAL], ["T"],
            "Open one or more URLs in a new tab, based on current location",
            function () { commandline.open(":", "tabopen " + buffer.URL, modes.EX); });

        mappings.add([modes.NORMAL], ["w"],
            "Open one or more URLs in a new window",
            function () { commandline.open(":", "winopen ", modes.EX); });

        mappings.add([modes.NORMAL], ["W"],
            "Open one or more URLs in a new window, based on current location",
            function () { commandline.open(":", "winopen " + buffer.URL, modes.EX); });

        mappings.add([modes.NORMAL],
            ["<C-a>"], "Increment last number in URL",
            function (count) { Browser.incrementURL(Math.max(count, 1)); },
            { count: true });

        mappings.add([modes.NORMAL],
            ["<C-x>"], "Decrement last number in URL",
            function (count) { Browser.incrementURL(-Math.max(count, 1)); },
            { count: true });

        mappings.add([modes.NORMAL], ["~"],
            "Open home directory",
            function () { dactyl.open("~"); });

        mappings.add([modes.NORMAL], ["gh"],
            "Open homepage",
            function () { BrowserHome(); });

        mappings.add([modes.NORMAL], ["gH"],
            "Open homepage in a new tab",
            function () {
                let homepages = gHomeButton.getHomePage();
                dactyl.open(homepages, { from: "homepage", where: dactyl.NEW_TAB });
            });

        mappings.add([modes.NORMAL], ["gu"],
            "Go to parent directory",
            function (count) { Browser.climbUrlPath(Math.max(count, 1)); },
            { count: true });

        mappings.add([modes.NORMAL], ["gU"],
            "Go to the root of the website",
            function () { Browser.climbUrlPath(-1); });

        mappings.add([modes.NORMAL], ["<C-l>"],
            "Redraw the screen",
            function () { ex.redraw(); });
    },

    commands: function () {
        commands.add(["downl[oads]", "dl"],
            "Show progress of current downloads",
            function () {
                dactyl.open("chrome://mozapps/content/downloads/downloads.xul",
                    { from: "downloads"});
            },
            { argCount: "0" });

        commands.add(["o[pen]"],
            "Open one or more URLs in the current tab",
            function (args) { dactyl.open(args[0] || "about:blank"); },
            {
                completer: function (context) completion.url(context),
                domains: function (args) array.compact(dactyl.parseURLs(args[0] || "").map(
                    function (url) util.getHost(url))),
                literal: 0,
                privateData: true
            });

        commands.add(["redr[aw]"],
            "Redraw the screen",
            function () {
                window.QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIDOMWindowUtils).redraw();
                statusline.updateUrl();
                commandline.clear();
            },
            { argCount: "0" });
    }
});

// vim: set fdm=marker sw=4 ts=4 et:
<|endoftext|>


<commit_before>import { create_svg_url, normalize_svg } from './utils/svg.js';
import { generate_svg } from './generator/svg.js';

import { cell_id, is_letter, add_alias, unique_id, lerp } from './utils/index.js';
import { lazy, clamp, sequence, get_value } from './utils/index.js';
import { by_unit, by_charcode } from './utils/transform.js';
import { last } from './utils/list.js';

import calc from './calc.js';
import { memo } from './utils/memo.js';
import { expand } from './utils/expand.js';
import Stack from './utils/stack.js';
import Noise from './utils/noise.js';

import { shapes, create_shape_points } from './generator/shapes.js';
import parse_value_group from './parser/parse-value-group.js';
import parse_shape_commands from './parser/parse-shape-commands.js';
import parse_svg from './parser/parse-svg.js';
import parse_svg_path from './parser/parse-svg-path.js';

import * as Uniforms from './uniforms.js';

function make_sequence(c) {
  return lazy((n, ...actions) => {
    if (!actions || !n) return '';
    let count = get_value(n());
    let evaluated = calc(count);
    if (evaluated === 0) {
      evaluated = count;
    }
    return sequence(
      evaluated,
      (...args) => {
        return actions.map(action => {
          return get_value(action(...args))
        }).join(',');
      }
    ).join(c);
  });
}

function push_stack(context, name, value) {
  if (!context[name]) context[name] = new Stack();
  context[name].push(value);
  return value;
}

function flip_value(num) {
  return -1 * num;
}

function map2d(value, min, max, amp = 1) {
  let dimention = 2;
  let v = Math.sqrt(dimention / 4) * amp;
  let [ma, mb] = [-v, v];
  return lerp((value - ma) / (mb - ma), min * amp, max * amp);
}

const Expose = add_alias({

  i({ count }) {
    return _ => count;
  },

  y({ y }) {
    return _ => y;
  },

  x({ x }) {
    return _ => x;
  },

  z({ z }) {
    return _ => z;
  },

  I({ grid }) {
    return _ => grid.count;
  },

  Y({ grid }) {
    return _ => grid.y;
  },

  X({ grid }) {
    return _ => grid.x;
  },

  Z({ grid }) {
    return _ => grid.z;
  },

  id({ x, y, z }) {
    return _ => cell_id(x, y, z);
  },

  n({ extra }) {
    let lastExtra = last(extra);
    return n => lastExtra ? (lastExtra[0] + (Number(n) || 0)) : '@n';
  },

  nx({ extra }) {
    let lastExtra = last(extra);
    return n => lastExtra ? (lastExtra[1] + (Number(n) || 0)) : '@nx';
  },

  ny({ extra }) {
    let lastExtra = last(extra);
    return n => lastExtra ? (lastExtra[2] + (Number(n) || 0)) : '@ny';
  },

  N({ extra }) {
    let lastExtra = last(extra);
    return n => lastExtra ? (lastExtra[3] + (Number(n) || 0)) : '@N';
  },

  m: make_sequence(','),

  M: make_sequence(' '),

  µ: make_sequence(''),

  p({ context, pick }) {
    return expand((...args) => {
      if (!args.length) {
        args = context.last_pick_args || [];
      }
      let picked = pick(args);
      context.last_pick_args = args;
      return push_stack(context, 'last_pick', picked);
    });
  },

  P({ context, pick, position }) {
    let counter = 'P-counter' + position;
    return expand((...args) => {
      let normal = true;
      if (!args.length) {
        args = context.last_pick_args || [];
        normal = false;
      }
      let stack = context.last_pick;
      let last = stack ? stack.last(1) : '';
      if (normal) {
        if (!context[counter]) {
          context[counter] = {};
        }
        last = context[counter].last_pick;
      }
      if (args.length > 1) {
        let i = args.findIndex(n => n === last);
        if (i !== -1) {
          args.splice(i, 1);
        }
      }
      let picked = pick(args);
      context.last_pick_args = args;
      if (normal) {
        context[counter].last_pick = picked;
      }
      return push_stack(context, 'last_pick', picked);
    });
  },

  pn({ context, extra, position }) {
    let counter = 'pn-counter' + position;
    let lastExtra = last(extra);
    return expand((...args) => {
      if (!context[counter]) context[counter] = 0;
      context[counter] += 1;
      let max = args.length;
      let [idx = context[counter]] = lastExtra || [];
      let pos = (idx - 1) % max;
      let value = args[pos];
      return push_stack(context, 'last_pick', value);
    });
  },

  pd({ context, extra, position, shuffle }) {
    let counter = 'pd-counter' + position;
    let values = 'pd-values' + position;
    let lastExtra = last(extra);
    return expand((...args) => {
      if (!context[counter]) context[counter] = 0;
      context[counter] += 1;
      if (!context[values]) {
        context[values] = shuffle(args || []);
      }
      let max = args.length;
      let [idx = context[counter]] = lastExtra || [];
      let pos = (idx - 1) % max;
      let value = context[values][pos];
      return push_stack(context, 'last_pick', value);
    });
  },

  lp({ context }) {
    return (n = 1) => {
      let stack = context.last_pick;
      return stack ? stack.last(n) : '';
    };
  },

  r({ context, rand }) {
    return (...args) => {
      let transform = args.every(is_letter)
        ? by_charcode
        : by_unit;
      let value = transform(rand)(...args);
      return push_stack(context, 'last_rand', value);
    };
  },

  rn({ x, y, context, position, grid, extra, shuffle }) {
    let counter = 'noise-2d' + position;
    let [ni, nx, ny, nm, NX, NY] = last(extra) || [];
    let isSeqContext = (ni && nm);
    return (...args) => {
      let [start, end = start, freq = 1, amp = 1] = args;
      if (args.length == 1) {
        [start, end] = [0, start];
      }
      if (!context[counter]) {
        context[counter] = new Noise(shuffle);
      }
      freq = clamp(freq, 0, Infinity);
      amp = clamp(amp, 0, Infinity);
      let transform = [start, end].every(is_letter) ? by_charcode : by_unit;
      let t = isSeqContext
        ? context[counter].noise((nx - 1)/NX * freq, (ny - 1)/NY * freq, 0)
        : context[counter].noise((x - 1)/grid.x * freq, (y - 1)/grid.y * freq, 0);
      let fn = transform((start, end) => map2d(t * amp, start, end, amp));
      let value = fn(start, end);
      return push_stack(context, 'last_rand', value);
    };
  },

  lr({ context }) {
    return (n = 1) => {
      let stack = context.last_rand;
      return stack ? stack.last(n) : '';
    };
  },

  noise({ context, grid, position, shuffle, ...rest }) {
    let vars = {
      i: rest.count, I: grid.count,
      x: rest.x, X: grid.x,
      y: rest.y, Y: grid.y,
      z: rest.z, Z: grid.z,
    };
    return (x, y, z = 0) => {
      let counter = 'raw-noise-2d' + position;
      if (!context[counter]) {
        context[counter] = new Noise(shuffle);
      }
      return context[counter].noise(
        calc(x, vars),
        calc(y, vars),
        calc(z, vars)
      );
    };
  },

  stripe() {
    return (...input) => {
      let colors = input.map(get_value);
      let max = colors.length;
      let default_count = 0;
      let custom_sizes = [];
      let prev;
      if (!max) {
        return '';
      }
      colors.forEach(step => {
        let [_, size] = parse_value_group(step);
        if (size !== undefined) custom_sizes.push(size);
        else default_count += 1;
      });
      let default_size = custom_sizes.length
        ? `(100% - ${custom_sizes.join(' - ')}) / ${default_count}`
        : `100% / ${max}`
      return colors.map((step, i) => {
        if (custom_sizes.length) {
          let [color, size] = parse_value_group(step);
          let prefix = prev ? (prev + ' + ') : '';
          prev = prefix + (size !== undefined ? size : default_size);
          return `${color} 0 calc(${ prev })`
        }
        return `${step} 0 ${100 / max * (i + 1)}%`
      })
      .join(',');
    }
  },

  calc() {
    return value => calc(get_value(value));
  },

  hex() {
    return value => parseInt(get_value(value)).toString(16);
  },

  svg: lazy((...args) => {
    let value = args.map(input => get_value(input())).join(',');
    if (!value.startsWith('<')) {
      let parsed = parse_svg(value);
      value = generate_svg(parsed);
    }
    let svg = normalize_svg(value);
    return create_svg_url(svg);
  }),

  filter: lazy((...args) => {
    let value = args.map(input => get_value(input())).join(',');
    let id = unique_id('filter-');
    if (!value.startsWith('<')) {
      let parsed = parse_svg(value, {
        type: 'block',
        name: 'filter'
      });
      value = generate_svg(parsed);
    }
    let svg = normalize_svg(value).replace(
      /<filter([\s>])/,
      `<filter id="${ id }"$1`
    );
    return create_svg_url(svg, id);
  }),

  var() {
    return value => `var(${ get_value(value) })`;
  },

  ut() {
    return value => `var(--${ Uniforms.uniform_time.name })`;
  },

  uw() {
    return value => `var(--${ Uniforms.uniform_width.name })`;
  },

  uh() {
    return value => `var(--${ Uniforms.uniform_height.name })`;
  },

  ux() {
    return value => `var(--${ Uniforms.uniform_mousex.name })`;
  },

  uy() {
    return value => `var(--${ Uniforms.uniform_mousey.name })`;
  },

  plot({ count, context, extra, position, grid }) {
    let key = 'offset-points' + position;
    let lastExtra = last(extra);
    return commands => {
      let [idx = count, _, __, max = grid.count] = lastExtra || [];
      if (!context[key]) {
        let config = parse_shape_commands(commands);
        delete config['fill'];
        delete config['fill-rule'];
        delete config['frame'];
        config.points = max;
        context[key] = create_shape_points(config, {min: 1, max: 65536});
      }
      return context[key][idx - 1];
    };
  },

  shape() {
    return memo('shape-function', (type = '', ...args) => {
      type = String(type).trim();
      let points = [];
      if (type.length) {
        if (typeof shapes[type] === 'function') {
          points = shapes[type](args);
        } else {
          let commands = type;
          let rest = args.join(',');
          if (rest.length) {
            commands = type + ',' + rest;
          }
          let config = parse_shape_commands(commands);
          points = create_shape_points(config, {min: 3, max: 3600});
        }
      }
      return `polygon(${points.join(',')})`;
    });
  },

  doodle() {
    return value => value;
  },

  shaders() {
    return value => value;
  },

  canvas() {
    return value => value;
  },

  pattern() {
    return value => value;
  },

  invert() {
    return commands => {
      let parsed = parse_svg_path(commands);
      if (!parsed.valid) return commands;
      return parsed.commands.map(({ name, value }) => {
        switch (name) {
          case 'v': return 'h' + value.join(' ');
          case 'V': return 'H' + value.join(' ');
          case 'h': return 'v' + value.join(' ');
          case 'H': return 'V' + value.join(' ');
          default:  return name + value.join(' ');
        }
      }).join(' ');
    };
  },

  flipH() {
    return commands => {
      let parsed = parse_svg_path(commands);
      if (!parsed.valid) return commands;
      return parsed.commands.map(({ name, value }) => {
        switch (name) {
          case 'h':
          case 'H': return name + value.map(flip_value).join(' ');
          default:  return name + value.join(' ');
        }
      }).join(' ');
    };
  },

  flipV() {
    return commands => {
      let parsed = parse_svg_path(commands);
      if (!parsed.valid) return commands;
      return parsed.commands.map(({ name, value }) => {
        switch (name) {
          case 'v':
          case 'V': return name + value.map(flip_value).join(' ');
          default:  return name + value.join(' ');
        }
      }).join(' ');
    };
  },

  flip(...args) {
    let flipH = Expose.flipH(...args);
    let flipV = Expose.flipV(...args);
    return commands => {
      return flipV(flipH(commands));
    }
  },

  reverse(...args) {
    return commands => {
      let parsed = parse_svg_path(commands);
      if (!parsed.valid) return commands;
      return parsed.commands.reverse().map(({ name, value }) => {
        return name + value.join(' ');
      }).join(' ');
    }
  },

}, {

  'index': 'i',
  'col': 'x',
  'row': 'y',
  'depth': 'z',
  'rand': 'r',
  'pick': 'p',

  // error prone
  'stripes': 'stripe',
  'strip':   'stripe',
  'patern':  'pattern',
  'flipv': 'flipV',
  'fliph': 'flipH',

  // legacy names, keep them before 1.0
  't': 'ut',
  'svg-filter': 'filter',
  'last-rand': 'lr',
  'last-pick': 'lp',
  'multiple': 'm',
  'multi': 'm',
  'rep': 'µ',
  'repeat': 'µ',
  'ms': 'M',
  's':  'I',
  'size': 'I',
  'sx': 'X',
  'size-x': 'X',
  'size-col': 'X',
  'max-col': 'X',
  'sy': 'Y',
  'size-y': 'Y',
  'size-row': 'Y',
  'max-row': 'Y',
  'sz': 'Z',
  'size-z': 'Z',
  'size-depth': 'Z',
  'pick-by-turn': 'pn',
  'pick-n': 'pn',
  'pick-d': 'pd',
  'offset': 'plot',
  'Offset': 'Plot',
  'point': 'plot',
  'Point': 'Plot',
  'paint': 'canvas',
});

export default Expose;
<commit_msg>Fix context value by adding signature for each sequence<commit_after>import { create_svg_url, normalize_svg } from './utils/svg.js';
import { generate_svg } from './generator/svg.js';

import { cell_id, is_letter, add_alias, unique_id, lerp } from './utils/index.js';
import { lazy, clamp, sequence, get_value } from './utils/index.js';
import { by_unit, by_charcode } from './utils/transform.js';
import { last } from './utils/list.js';

import calc from './calc.js';
import { memo } from './utils/memo.js';
import { expand } from './utils/expand.js';
import Stack from './utils/stack.js';
import Noise from './utils/noise.js';

import { shapes, create_shape_points } from './generator/shapes.js';
import parse_value_group from './parser/parse-value-group.js';
import parse_shape_commands from './parser/parse-shape-commands.js';
import parse_svg from './parser/parse-svg.js';
import parse_svg_path from './parser/parse-svg-path.js';

import * as Uniforms from './uniforms.js';

function make_sequence(c) {
  return lazy((n, ...actions) => {
    if (!actions || !n) return '';
    let count = get_value(n());
    let evaluated = calc(count);
    if (evaluated === 0) {
      evaluated = count;
    }
    let signature = Math.random();
    return sequence(
      evaluated,
      (...args) => {
        return actions.map(action => {
          return get_value(action(...args, signature))
        }).join(',');
      }
    ).join(c);
  });
}

function push_stack(context, name, value) {
  if (!context[name]) context[name] = new Stack();
  context[name].push(value);
  return value;
}

function flip_value(num) {
  return -1 * num;
}

function map2d(value, min, max, amp = 1) {
  let dimention = 2;
  let v = Math.sqrt(dimention / 4) * amp;
  let [ma, mb] = [-v, v];
  return lerp((value - ma) / (mb - ma), min * amp, max * amp);
}

const Expose = add_alias({

  i({ count }) {
    return _ => count;
  },

  y({ y }) {
    return _ => y;
  },

  x({ x }) {
    return _ => x;
  },

  z({ z }) {
    return _ => z;
  },

  I({ grid }) {
    return _ => grid.count;
  },

  Y({ grid }) {
    return _ => grid.y;
  },

  X({ grid }) {
    return _ => grid.x;
  },

  Z({ grid }) {
    return _ => grid.z;
  },

  id({ x, y, z }) {
    return _ => cell_id(x, y, z);
  },

  n({ extra }) {
    let lastExtra = last(extra);
    return n => lastExtra ? (lastExtra[0] + (Number(n) || 0)) : '@n';
  },

  nx({ extra }) {
    let lastExtra = last(extra);
    return n => lastExtra ? (lastExtra[1] + (Number(n) || 0)) : '@nx';
  },

  ny({ extra }) {
    let lastExtra = last(extra);
    return n => lastExtra ? (lastExtra[2] + (Number(n) || 0)) : '@ny';
  },

  N({ extra }) {
    let lastExtra = last(extra);
    return n => lastExtra ? (lastExtra[3] + (Number(n) || 0)) : '@N';
  },

  m: make_sequence(','),

  M: make_sequence(' '),

  µ: make_sequence(''),

  p({ context, pick }) {
    return expand((...args) => {
      if (!args.length) {
        args = context.last_pick_args || [];
      }
      let picked = pick(args);
      context.last_pick_args = args;
      return push_stack(context, 'last_pick', picked);
    });
  },

  P({ context, pick, position }) {
    let counter = 'P-counter' + position;
    return expand((...args) => {
      let normal = true;
      if (!args.length) {
        args = context.last_pick_args || [];
        normal = false;
      }
      let stack = context.last_pick;
      let last = stack ? stack.last(1) : '';
      if (normal) {
        if (!context[counter]) {
          context[counter] = {};
        }
        last = context[counter].last_pick;
      }
      if (args.length > 1) {
        let i = args.findIndex(n => n === last);
        if (i !== -1) {
          args.splice(i, 1);
        }
      }
      let picked = pick(args);
      context.last_pick_args = args;
      if (normal) {
        context[counter].last_pick = picked;
      }
      return push_stack(context, 'last_pick', picked);
    });
  },

  pn({ context, extra, position }) {
    let lastExtra = last(extra);
    let sig = lastExtra ? last(lastExtra) : '';
    let counter = 'pn-counter' + position + sig;
    return expand((...args) => {
      if (!context[counter]) context[counter] = 0;
      context[counter] += 1;
      let max = args.length;
      let [idx = context[counter]] = lastExtra || [];
      let pos = (idx - 1) % max;
      let value = args[pos];
      return push_stack(context, 'last_pick', value);
    });
  },

  pd({ context, extra, position, shuffle }) {
    let lastExtra = last(extra);
    let sig = lastExtra ? last(lastExtra) : '';
    let counter = 'pd-counter' + position  + sig;
    let values = 'pd-values' + position + sig;;
    return expand((...args) => {
      if (!context[counter]) context[counter] = 0;
      context[counter] += 1;
      if (!context[values]) {
        context[values] = shuffle(args || []);
      }
      let max = args.length;
      let [idx = context[counter]] = lastExtra || [];
      let pos = (idx - 1) % max;
      let value = context[values][pos];
      return push_stack(context, 'last_pick', value);
    });
  },

  lp({ context }) {
    return (n = 1) => {
      let stack = context.last_pick;
      return stack ? stack.last(n) : '';
    };
  },

  r({ context, rand }) {
    return (...args) => {
      let transform = args.every(is_letter)
        ? by_charcode
        : by_unit;
      let value = transform(rand)(...args);
      return push_stack(context, 'last_rand', value);
    };
  },

  rn({ x, y, context, position, grid, extra, shuffle }) {
    let counter = 'noise-2d' + position;
    let [ni, nx, ny, nm, NX, NY] = last(extra) || [];
    let isSeqContext = (ni && nm);
    return (...args) => {
      let [start, end = start, freq = 1, amp = 1] = args;
      if (args.length == 1) {
        [start, end] = [0, start];
      }
      if (!context[counter]) {
        context[counter] = new Noise(shuffle);
      }
      freq = clamp(freq, 0, Infinity);
      amp = clamp(amp, 0, Infinity);
      let transform = [start, end].every(is_letter) ? by_charcode : by_unit;
      let t = isSeqContext
        ? context[counter].noise((nx - 1)/NX * freq, (ny - 1)/NY * freq, 0)
        : context[counter].noise((x - 1)/grid.x * freq, (y - 1)/grid.y * freq, 0);
      let fn = transform((start, end) => map2d(t * amp, start, end, amp));
      let value = fn(start, end);
      return push_stack(context, 'last_rand', value);
    };
  },

  lr({ context }) {
    return (n = 1) => {
      let stack = context.last_rand;
      return stack ? stack.last(n) : '';
    };
  },

  noise({ context, grid, position, shuffle, ...rest }) {
    let vars = {
      i: rest.count, I: grid.count,
      x: rest.x, X: grid.x,
      y: rest.y, Y: grid.y,
      z: rest.z, Z: grid.z,
    };
    return (x, y, z = 0) => {
      let counter = 'raw-noise-2d' + position;
      if (!context[counter]) {
        context[counter] = new Noise(shuffle);
      }
      return context[counter].noise(
        calc(x, vars),
        calc(y, vars),
        calc(z, vars)
      );
    };
  },

  stripe() {
    return (...input) => {
      let colors = input.map(get_value);
      let max = colors.length;
      let default_count = 0;
      let custom_sizes = [];
      let prev;
      if (!max) {
        return '';
      }
      colors.forEach(step => {
        let [_, size] = parse_value_group(step);
        if (size !== undefined) custom_sizes.push(size);
        else default_count += 1;
      });
      let default_size = custom_sizes.length
        ? `(100% - ${custom_sizes.join(' - ')}) / ${default_count}`
        : `100% / ${max}`
      return colors.map((step, i) => {
        if (custom_sizes.length) {
          let [color, size] = parse_value_group(step);
          let prefix = prev ? (prev + ' + ') : '';
          prev = prefix + (size !== undefined ? size : default_size);
          return `${color} 0 calc(${ prev })`
        }
        return `${step} 0 ${100 / max * (i + 1)}%`
      })
      .join(',');
    }
  },

  calc() {
    return value => calc(get_value(value));
  },

  hex() {
    return value => parseInt(get_value(value)).toString(16);
  },

  svg: lazy((...args) => {
    let value = args.map(input => get_value(input())).join(',');
    if (!value.startsWith('<')) {
      let parsed = parse_svg(value);
      value = generate_svg(parsed);
    }
    let svg = normalize_svg(value);
    return create_svg_url(svg);
  }),

  filter: lazy((...args) => {
    let value = args.map(input => get_value(input())).join(',');
    let id = unique_id('filter-');
    if (!value.startsWith('<')) {
      let parsed = parse_svg(value, {
        type: 'block',
        name: 'filter'
      });
      value = generate_svg(parsed);
    }
    let svg = normalize_svg(value).replace(
      /<filter([\s>])/,
      `<filter id="${ id }"$1`
    );
    return create_svg_url(svg, id);
  }),

  var() {
    return value => `var(${ get_value(value) })`;
  },

  ut() {
    return value => `var(--${ Uniforms.uniform_time.name })`;
  },

  uw() {
    return value => `var(--${ Uniforms.uniform_width.name })`;
  },

  uh() {
    return value => `var(--${ Uniforms.uniform_height.name })`;
  },

  ux() {
    return value => `var(--${ Uniforms.uniform_mousex.name })`;
  },

  uy() {
    return value => `var(--${ Uniforms.uniform_mousey.name })`;
  },

  plot({ count, context, extra, position, grid }) {
    let key = 'offset-points' + position;
    let lastExtra = last(extra);
    return commands => {
      let [idx = count, _, __, max = grid.count] = lastExtra || [];
      if (!context[key]) {
        let config = parse_shape_commands(commands);
        delete config['fill'];
        delete config['fill-rule'];
        delete config['frame'];
        config.points = max;
        context[key] = create_shape_points(config, {min: 1, max: 65536});
      }
      return context[key][idx - 1];
    };
  },

  shape() {
    return memo('shape-function', (type = '', ...args) => {
      type = String(type).trim();
      let points = [];
      if (type.length) {
        if (typeof shapes[type] === 'function') {
          points = shapes[type](args);
        } else {
          let commands = type;
          let rest = args.join(',');
          if (rest.length) {
            commands = type + ',' + rest;
          }
          let config = parse_shape_commands(commands);
          points = create_shape_points(config, {min: 3, max: 3600});
        }
      }
      return `polygon(${points.join(',')})`;
    });
  },

  doodle() {
    return value => value;
  },

  shaders() {
    return value => value;
  },

  canvas() {
    return value => value;
  },

  pattern() {
    return value => value;
  },

  invert() {
    return commands => {
      let parsed = parse_svg_path(commands);
      if (!parsed.valid) return commands;
      return parsed.commands.map(({ name, value }) => {
        switch (name) {
          case 'v': return 'h' + value.join(' ');
          case 'V': return 'H' + value.join(' ');
          case 'h': return 'v' + value.join(' ');
          case 'H': return 'V' + value.join(' ');
          default:  return name + value.join(' ');
        }
      }).join(' ');
    };
  },

  flipH() {
    return commands => {
      let parsed = parse_svg_path(commands);
      if (!parsed.valid) return commands;
      return parsed.commands.map(({ name, value }) => {
        switch (name) {
          case 'h':
          case 'H': return name + value.map(flip_value).join(' ');
          default:  return name + value.join(' ');
        }
      }).join(' ');
    };
  },

  flipV() {
    return commands => {
      let parsed = parse_svg_path(commands);
      if (!parsed.valid) return commands;
      return parsed.commands.map(({ name, value }) => {
        switch (name) {
          case 'v':
          case 'V': return name + value.map(flip_value).join(' ');
          default:  return name + value.join(' ');
        }
      }).join(' ');
    };
  },

  flip(...args) {
    let flipH = Expose.flipH(...args);
    let flipV = Expose.flipV(...args);
    return commands => {
      return flipV(flipH(commands));
    }
  },

  reverse(...args) {
    return commands => {
      let parsed = parse_svg_path(commands);
      if (!parsed.valid) return commands;
      return parsed.commands.reverse().map(({ name, value }) => {
        return name + value.join(' ');
      }).join(' ');
    }
  },

}, {

  'index': 'i',
  'col': 'x',
  'row': 'y',
  'depth': 'z',
  'rand': 'r',
  'pick': 'p',

  // error prone
  'stripes': 'stripe',
  'strip':   'stripe',
  'patern':  'pattern',
  'flipv': 'flipV',
  'fliph': 'flipH',

  // legacy names, keep them before 1.0
  't': 'ut',
  'svg-filter': 'filter',
  'last-rand': 'lr',
  'last-pick': 'lp',
  'multiple': 'm',
  'multi': 'm',
  'rep': 'µ',
  'repeat': 'µ',
  'ms': 'M',
  's':  'I',
  'size': 'I',
  'sx': 'X',
  'size-x': 'X',
  'size-col': 'X',
  'max-col': 'X',
  'sy': 'Y',
  'size-y': 'Y',
  'size-row': 'Y',
  'max-row': 'Y',
  'sz': 'Z',
  'size-z': 'Z',
  'size-depth': 'Z',
  'pick-by-turn': 'pn',
  'pick-n': 'pn',
  'pick-d': 'pd',
  'offset': 'plot',
  'Offset': 'Plot',
  'point': 'plot',
  'Point': 'Plot',
  'paint': 'canvas',
});

export default Expose;
<|endoftext|>


<commit_before>var createQueue = require('queue-async')
  , backoff = require('backoff')
  , es = require('event-stream')
  , crypto = require('crypto')
  , xtend = require('xtend')
  , mime = require('mime')
  , once = require('once')
  , knox = require('knox')
  , Intimidate = require('intimidate')
  , url = require('url')
  , fs = require('fs')

// Define some content type mappings
mime.define({
  'application/json': ['map']
});

module.exports = s3syncer

function s3syncer(db, options) {
  if (!options) {
    options = db || {}
    db = false
  }

  options.concurrency = options.concurrency || 16
  options.headers = options.headers || {}
  options.cacheSrc = options.cacheSrc || __dirname + '/.sync'
  options.cacheDest = options.cacheDest || '/.sync'
  options.retries = options.retries || 7;
  options.maxRetries = 20;
  options.backoffInterval = 20;

  // var client = knox.createClient(options)
  var client = new Intimidate(options)
    , queue = createQueue(options.concurrency)
    , region = options.region === 'us-standard' ? false : options.region
    , secure = options.secure || !('secure' in options)
    , subdomain = region ? 's3-' + region : 's3'
    , protocol = secure ? 'https' : 'http'
    , prefix = options.prefix || ''
    , hashkey = options.hashKey || function(details) {
      return details.fullPath
    }

  var stream = es.map(function(data, next) {
    queue.defer(function(details, done) {
      details.fullPath = details.fullPath || details.src
      details.path = details.path || details.dest
      syncFile(details, function(err) {
        return err ? next(err) : done(), next(null, details)
      })
    }, data)
  })

  stream.getCache = getCache
  stream.putCache = putCache

  function syncFile(details, next) {
    var absolute = details.fullPath
      , relative = prefix + (details.path.charAt(0) === '/'
        ? details.path.slice(1)
        : details.path)

    var destination =
          protocol + '://'
        + subdomain
        + '.amazonaws.com/'
        + options.bucket
        + '/' + relative

    details.url = destination

    return checkForUpload(next)

    // hashFile(absolute, destination, function(err, md5) {
    //   if (err) return next(err)
    //   details.md5 = md5
    //   details.url = destination
    //   details.fresh = false
    //   details.cached = false

    //   if (!db) return checkForUpload(next)

    //   var key = 'md5:' + hashkey(details)

    //   db.get(key, function(err, result) {
    //     if (!err && result === md5) {
    //       details.cached = true
    //       return next(null, details)
    //     }
    //     checkForUpload(function(err) {
    //       if (err) return next(err)
    //       db.put(key, md5, next)
    //     })
    //   })
    // })

    function checkForUpload(next) {
      return uploadFile(details, next)
      // client.headFile(relative, function(err, res) {
      //   if (err) return next(err)
      //   if (
      //     res.statusCode === 404 || (
      //     res.headers['x-amz-meta-syncfilehash'] &&
      //     res.headers['x-amz-meta-syncfilehash'] !== details.md5
      //   )) return uploadFile(details, next)
      //   if (res.statusCode >= 300) return next(new Error('Bad status code: ' + res.statusCode))
      //   return next(null, details)
      // })
    }
  }

  function uploadFile(details, next) {
    var absolute = details.fullPath
      , relative = prefix + details.path
      , lasterr
      , off = backoff.fibonacci({
        initialDelay: 500
      })

    details.fresh = true

    off.failAfter(options.retries)
    off.on('fail', function() {
      next(lasterr || new Error('unknown error'))
    }).on('ready', function() {
      // var headers = xtend({
      //     'x-amz-acl': 'public-read'
      //   , 'x-amz-meta-syncfilehash': details.md5
      //   , 'Content-Type': mime.lookup(absolute)
      // }, options.headers)

      // client.putFile(absolute, relative, headers, function(err, res) {
      //   if (!err) {
      //     if (res.statusCode < 300) return next(null, details)
      //     err = new Error('Bad status code: ' + res.statusCode)
      //   }

      //   lasterr = err
      //   stream.emit('fail', err)
      //   off.backoff()
      // })

      client.upload(absolute, relative, function(err, res) {
        if (!err) {
          if (res.statusCode < 300) return next(null, details)
          err = new Error('Bad status code: ' + res.statusCode)
        }
        lasterr = err
        stream.emit('fail', err)
        off.backoff()
      })

    }).backoff()
  }

  function getCache(callback) {
    callback = once(callback)

    client.getFile(options.cacheDest, function(err, res) {
      if (err) return callback(err)
      if (res.statusCode === 404) return callback(null)

      es.pipeline(
          res
        , es.split()
        , es.parse()
        , db.createWriteStream()
      ).once('close', callback)
       .once('error', callback)
    })
  }

  function putCache(callback) {
    callback = once(callback)

    db.createReadStream()
      .pipe(es.stringify())
      .pipe(fs.createWriteStream(options.cacheSrc))
      .once('error', callback)
      .once('close', function() {
        client.putFile(options.cacheSrc, options.cacheDest, function(err) {
          if (err) return callback(err)
          fs.unlink(options.cacheSrc, callback)
        })
     })
  }

  function hashFile(filename, destination, callback) {
    var hash = crypto.createHash('md5')
      , done = false

    hash.update(JSON.stringify([
        options.headers
      , destination
    ]))

    fs.createReadStream(filename).on('data', function(d) {
      hash.update(d)
    }).once('error', function(err) {
      if (!done) callback(err)
      done = true
    }).once('close', function() {
      if (!done) callback(null, hash.digest('hex'))
      done = true
    })
  }

  return stream
}
<commit_msg>removed the mime changes<commit_after>var createQueue = require('queue-async')
  , backoff = require('backoff')
  , es = require('event-stream')
  , crypto = require('crypto')
  , xtend = require('xtend')
  , mime = require('mime')
  , once = require('once')
  , knox = require('knox')
  , Intimidate = require('intimidate')
  , url = require('url')
  , fs = require('fs')

module.exports = s3syncer

function s3syncer(db, options) {
  if (!options) {
    options = db || {}
    db = false
  }

  options.concurrency = options.concurrency || 16
  options.headers = options.headers || {}
  options.cacheSrc = options.cacheSrc || __dirname + '/.sync'
  options.cacheDest = options.cacheDest || '/.sync'
  options.retries = options.retries || 7;
  options.maxRetries = 20;
  options.backoffInterval = 20;

  // var client = knox.createClient(options)
  var client = new Intimidate(options)
    , queue = createQueue(options.concurrency)
    , region = options.region === 'us-standard' ? false : options.region
    , secure = options.secure || !('secure' in options)
    , subdomain = region ? 's3-' + region : 's3'
    , protocol = secure ? 'https' : 'http'
    , prefix = options.prefix || ''
    , hashkey = options.hashKey || function(details) {
      return details.fullPath
    }

  var stream = es.map(function(data, next) {
    queue.defer(function(details, done) {
      details.fullPath = details.fullPath || details.src
      details.path = details.path || details.dest
      syncFile(details, function(err) {
        return err ? next(err) : done(), next(null, details)
      })
    }, data)
  })

  stream.getCache = getCache
  stream.putCache = putCache

  function syncFile(details, next) {
    var absolute = details.fullPath
      , relative = prefix + (details.path.charAt(0) === '/'
        ? details.path.slice(1)
        : details.path)

    var destination =
          protocol + '://'
        + subdomain
        + '.amazonaws.com/'
        + options.bucket
        + '/' + relative

    details.url = destination

    return checkForUpload(next)

    // hashFile(absolute, destination, function(err, md5) {
    //   if (err) return next(err)
    //   details.md5 = md5
    //   details.url = destination
    //   details.fresh = false
    //   details.cached = false

    //   if (!db) return checkForUpload(next)

    //   var key = 'md5:' + hashkey(details)

    //   db.get(key, function(err, result) {
    //     if (!err && result === md5) {
    //       details.cached = true
    //       return next(null, details)
    //     }
    //     checkForUpload(function(err) {
    //       if (err) return next(err)
    //       db.put(key, md5, next)
    //     })
    //   })
    // })

    function checkForUpload(next) {
      return uploadFile(details, next)
      // client.headFile(relative, function(err, res) {
      //   if (err) return next(err)
      //   if (
      //     res.statusCode === 404 || (
      //     res.headers['x-amz-meta-syncfilehash'] &&
      //     res.headers['x-amz-meta-syncfilehash'] !== details.md5
      //   )) return uploadFile(details, next)
      //   if (res.statusCode >= 300) return next(new Error('Bad status code: ' + res.statusCode))
      //   return next(null, details)
      // })
    }
  }

  function uploadFile(details, next) {
    var absolute = details.fullPath
      , relative = prefix + details.path
      , lasterr
      , off = backoff.fibonacci({
        initialDelay: 500
      })

    details.fresh = true

    off.failAfter(options.retries)
    off.on('fail', function() {
      next(lasterr || new Error('unknown error'))
    }).on('ready', function() {
      // var headers = xtend({
      //     'x-amz-acl': 'public-read'
      //   , 'x-amz-meta-syncfilehash': details.md5
      //   , 'Content-Type': mime.lookup(absolute)
      // }, options.headers)

      // client.putFile(absolute, relative, headers, function(err, res) {
      //   if (!err) {
      //     if (res.statusCode < 300) return next(null, details)
      //     err = new Error('Bad status code: ' + res.statusCode)
      //   }

      //   lasterr = err
      //   stream.emit('fail', err)
      //   off.backoff()
      // })

      client.upload(absolute, relative, function(err, res) {
        if (!err) {
          if (res.statusCode < 300) return next(null, details)
          err = new Error('Bad status code: ' + res.statusCode)
        }
        lasterr = err
        stream.emit('fail', err)
        off.backoff()
      })

    }).backoff()
  }

  function getCache(callback) {
    callback = once(callback)

    client.getFile(options.cacheDest, function(err, res) {
      if (err) return callback(err)
      if (res.statusCode === 404) return callback(null)

      es.pipeline(
          res
        , es.split()
        , es.parse()
        , db.createWriteStream()
      ).once('close', callback)
       .once('error', callback)
    })
  }

  function putCache(callback) {
    callback = once(callback)

    db.createReadStream()
      .pipe(es.stringify())
      .pipe(fs.createWriteStream(options.cacheSrc))
      .once('error', callback)
      .once('close', function() {
        client.putFile(options.cacheSrc, options.cacheDest, function(err) {
          if (err) return callback(err)
          fs.unlink(options.cacheSrc, callback)
        })
     })
  }

  function hashFile(filename, destination, callback) {
    var hash = crypto.createHash('md5')
      , done = false

    hash.update(JSON.stringify([
        options.headers
      , destination
    ]))

    fs.createReadStream(filename).on('data', function(d) {
      hash.update(d)
    }).once('error', function(err) {
      if (!done) callback(err)
      done = true
    }).once('close', function() {
      if (!done) callback(null, hash.digest('hex'))
      done = true
    })
  }

  return stream
}
<|endoftext|>


<commit_before>/***** BEGIN LICENSE BLOCK ***** {{{
Version: MPL 1.1/GPL 2.0/LGPL 2.1

The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.

Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>

Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/

/** @scope modules */

/**
 * @instance browser
 */
function Browser() //{{{
{
    ////////////////////////////////////////////////////////////////////////////////
    ////////////////////// PRIVATE SECTION /////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    // TODO: support 'nrformats'? -> probably not worth it --mst
    function incrementURL(count)
    {
        let matches = buffer.URL.match(/(.*?)(\d+)(\D*)$/);
        if (!matches)
            return void liberator.beep();

        let [, pre, number, post] = matches;
        let newNumber = parseInt(number, 10) + count;
        let newNumberStr = String(newNumber > 0 ? newNumber : 0);
        if (number.match(/^0/)) // add 0009<C-a> should become 0010
        {
            while (newNumberStr.length < number.length)
                newNumberStr = "0" + newNumberStr;
        }

        liberator.open(pre + newNumberStr + post);
    }

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// OPTIONS /////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    options.add(["enc[oding]"],
        "Sets the current buffer's character encoding",
        "string", "UTF-8",
        {
            scope: options.OPTION_SCOPE_LOCAL,
            getter: function () getBrowser().docShell.QueryInterface(Ci.nsIDocCharset).charset,
            setter: function (val)
            {
                if (options["encoding"] == val)
                    return val;

                // Stolen from browser.jar/content/browser/browser.js, more or less.
                try
                {
                    getBrowser().docShell.QueryInterface(Ci.nsIDocCharset).charset = val;
                    PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, val);
                    getWebNavigation().reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
                }
                catch (e) { liberator.reportError(e); }
            },
            completer: function (context) completion.charset(context),
            validator: Option.validateCompleter
        });

    // only available in FF 3.5
    services.add("privateBrowsing", "@mozilla.org/privatebrowsing;1", Ci.nsIPrivateBrowsingService);
    if (services.get("privateBrowsing"))
    {
        options.add(["private", "pornmode"],
            "Set the 'private browsing' option",
            "boolean", false,
            {
                setter: function (value) services.get("privateBrowsing").privateBrowsingEnabled = value,
                getter: function () services.get("privateBrowsing").privateBrowsingEnabled
            });
        let services = modules.services; // Storage objects are global to all windows, 'modules' isn't.
        storage.newObject("private-mode", function () {
            ({
                init: function ()
                {
                    services.get("observer").addObserver(this, "private-browsing", false);
                    services.get("observer").addObserver(this, "quit-application", false);
                    this.private = services.get("privateBrowsing").privateBrowsingEnabled;
                },
                observe: function (subject, topic, data)
                {
                    if (topic == "private-browsing")
                    {
                        if (data == "enter")
                            storage.privateMode = true;
                        else if (data == "exit")
                            storage.privateMode = false;
                        storage.fireEvent("private-mode", "change", storage.privateMode);
                    }
                    else if (topic == "quit-application")
                    {
                        services.get("observer").removeObserver(this, "quit-application");
                        services.get("observer").removeObserver(this, "private-browsing");
                    }
                }
            }).init();
        }, false);
        storage.addObserver("private-mode",
            function (key, event, value) {
                autocommands.trigger("PrivateMode", { state: value });
            }, window);
    }

    options.add(["urlseparator"],
        "Set the separator regex used to separate multiple URL args",
        "string", ",\\s");

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// MAPPINGS ////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    mappings.add([modes.NORMAL],
        ["y"], "Yank current location to the clipboard",
        function () { util.copyToClipboard(buffer.URL, true); });

    // opening websites
    mappings.add([modes.NORMAL],
        ["o"], "Open one or more URLs",
        function () { commandline.open(":", "open ", modes.EX); });

    mappings.add([modes.NORMAL], ["O"],
        "Open one or more URLs, based on current location",
        function () { commandline.open(":", "open " + buffer.URL, modes.EX); });

    mappings.add([modes.NORMAL], ["t"],
        "Open one or more URLs in a new tab",
        function () { commandline.open(":", "tabopen ", modes.EX); });

    mappings.add([modes.NORMAL], ["T"],
        "Open one or more URLs in a new tab, based on current location",
        function () { commandline.open(":", "tabopen " + buffer.URL, modes.EX); });

    mappings.add([modes.NORMAL], ["w"],
        "Open one or more URLs in a new window",
        function () { commandline.open(":", "winopen ", modes.EX); });

    mappings.add([modes.NORMAL], ["W"],
        "Open one or more URLs in a new window, based on current location",
        function () { commandline.open(":", "winopen " + buffer.URL, modes.EX); });

    mappings.add([modes.NORMAL],
        ["<C-a>"], "Increment last number in URL",
        function (count) { incrementURL(Math.max(count, 1)); },
        { count: true });

    mappings.add([modes.NORMAL],
        ["<C-x>"], "Decrement last number in URL",
        function (count) { incrementURL(-Math.max(count, 1)); },
        { count: true });

    mappings.add([modes.NORMAL], ["~"],
        "Open home directory",
        function () { liberator.open("~"); });

    mappings.add([modes.NORMAL], ["gh"],
        "Open homepage",
        function () { BrowserHome(); });

    mappings.add([modes.NORMAL], ["gH"],
        "Open homepage in a new tab",
        function ()
        {
            let homepages = gHomeButton.getHomePage();
            liberator.open(homepages, /\bhomepage\b/.test(options["activate"]) ?
                    liberator.NEW_TAB : liberator.NEW_BACKGROUND_TAB);
        });

    mappings.add([modes.NORMAL], ["gu"],
        "Go to parent directory",
        function (count)
        {
            function isDirectory(url)
            {
                if (/^file:\/|^\//.test(url))
                {
                    let file = io.getFile(url);
                    return file.exists() && file.isDirectory();
                }
                else
                {
                    // for all other locations just check if the URL ends with /
                    return /\/$/.test(url);
                }
            }

            if (count < 1)
                count = 1;

            // XXX
            let url = buffer.URL;
            for (let i = 0; i < count; i++)
            {
                if (isDirectory(url))
                    url = url.replace(/^(.*?:)(.*?)([^\/]+\/*)$/, "$1$2/");
                else
                    url = url.replace(/^(.*?:)(.*?)(\/+[^\/]+)$/, "$1$2/");
            }
            url = url.replace(/^(.*:\/+.*?)\/+$/, "$1/"); // get rid of more than 1 / at the end

            if (url == buffer.URL)
                liberator.beep();
            else
                liberator.open(url);
        },
        { count: true });

    mappings.add([modes.NORMAL], ["gU"],
        "Go to the root of the website",
        function ()
        {
            let uri = content.document.location;
            if (/(about|mailto):/.test(uri.protocol)) // exclude these special protocols for now
                return void liberator.beep();
            liberator.open(uri.protocol + "//" + (uri.host || "") + "/");
        });

    mappings.add([modes.NORMAL], ["<C-l>"],
        "Redraw the screen",
        function () { commands.get("redraw").execute("", false); });

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// COMMANDS ////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    commands.add(["downl[oads]", "dl"],
        "Show progress of current downloads",
        function ()
        {
            liberator.open("chrome://mozapps/content/downloads/downloads.xul",
                options.get("newtab").has("all", "downloads")
                    ? liberator.NEW_TAB : liberator.CURRENT_TAB);
        },
        { argCount: "0" });

    commands.add(["o[pen]", "e[dit]"],
        "Open one or more URLs in the current tab",
        function (args)
        {
            args = args.string;

            if (args)
                liberator.open(args);
            else
                liberator.open("about:blank");
        },
        {
            completer: function (context) completion.url(context),
            literal: 0,
            privateData: true,
        });

    commands.add(["redr[aw]"],
        "Redraw the screen",
        function ()
        {
            let wu = window.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
            wu.redraw();
            modes.show();
        },
        { argCount: "0" });

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// PUBLIC SECTION //////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    return {
        // TODO: extract browser-specific functionality from liberator
    };
    //}}}
}; //}}}

// vim: set fdm=marker sw=4 ts=4 et:
<commit_msg>Fix 'encoding' names.<commit_after>/***** BEGIN LICENSE BLOCK ***** {{{
Version: MPL 1.1/GPL 2.0/LGPL 2.1

The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.

Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>

Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/

/** @scope modules */

/**
 * @instance browser
 */
function Browser() //{{{
{
    ////////////////////////////////////////////////////////////////////////////////
    ////////////////////// PRIVATE SECTION /////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    // TODO: support 'nrformats'? -> probably not worth it --mst
    function incrementURL(count)
    {
        let matches = buffer.URL.match(/(.*?)(\d+)(\D*)$/);
        if (!matches)
            return void liberator.beep();

        let [, pre, number, post] = matches;
        let newNumber = parseInt(number, 10) + count;
        let newNumberStr = String(newNumber > 0 ? newNumber : 0);
        if (number.match(/^0/)) // add 0009<C-a> should become 0010
        {
            while (newNumberStr.length < number.length)
                newNumberStr = "0" + newNumberStr;
        }

        liberator.open(pre + newNumberStr + post);
    }

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// OPTIONS /////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    options.add(["encoding", "enc"],
        "Sets the current buffer's character encoding",
        "string", "UTF-8",
        {
            scope: options.OPTION_SCOPE_LOCAL,
            getter: function () getBrowser().docShell.QueryInterface(Ci.nsIDocCharset).charset,
            setter: function (val)
            {
                if (options["encoding"] == val)
                    return val;

                // Stolen from browser.jar/content/browser/browser.js, more or less.
                try
                {
                    getBrowser().docShell.QueryInterface(Ci.nsIDocCharset).charset = val;
                    PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, val);
                    getWebNavigation().reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
                }
                catch (e) { liberator.reportError(e); }
            },
            completer: function (context) completion.charset(context),
            validator: Option.validateCompleter
        });

    // only available in FF 3.5
    services.add("privateBrowsing", "@mozilla.org/privatebrowsing;1", Ci.nsIPrivateBrowsingService);
    if (services.get("privateBrowsing"))
    {
        options.add(["private", "pornmode"],
            "Set the 'private browsing' option",
            "boolean", false,
            {
                setter: function (value) services.get("privateBrowsing").privateBrowsingEnabled = value,
                getter: function () services.get("privateBrowsing").privateBrowsingEnabled
            });
        let services = modules.services; // Storage objects are global to all windows, 'modules' isn't.
        storage.newObject("private-mode", function () {
            ({
                init: function ()
                {
                    services.get("observer").addObserver(this, "private-browsing", false);
                    services.get("observer").addObserver(this, "quit-application", false);
                    this.private = services.get("privateBrowsing").privateBrowsingEnabled;
                },
                observe: function (subject, topic, data)
                {
                    if (topic == "private-browsing")
                    {
                        if (data == "enter")
                            storage.privateMode = true;
                        else if (data == "exit")
                            storage.privateMode = false;
                        storage.fireEvent("private-mode", "change", storage.privateMode);
                    }
                    else if (topic == "quit-application")
                    {
                        services.get("observer").removeObserver(this, "quit-application");
                        services.get("observer").removeObserver(this, "private-browsing");
                    }
                }
            }).init();
        }, false);
        storage.addObserver("private-mode",
            function (key, event, value) {
                autocommands.trigger("PrivateMode", { state: value });
            }, window);
    }

    options.add(["urlseparator"],
        "Set the separator regex used to separate multiple URL args",
        "string", ",\\s");

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// MAPPINGS ////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    mappings.add([modes.NORMAL],
        ["y"], "Yank current location to the clipboard",
        function () { util.copyToClipboard(buffer.URL, true); });

    // opening websites
    mappings.add([modes.NORMAL],
        ["o"], "Open one or more URLs",
        function () { commandline.open(":", "open ", modes.EX); });

    mappings.add([modes.NORMAL], ["O"],
        "Open one or more URLs, based on current location",
        function () { commandline.open(":", "open " + buffer.URL, modes.EX); });

    mappings.add([modes.NORMAL], ["t"],
        "Open one or more URLs in a new tab",
        function () { commandline.open(":", "tabopen ", modes.EX); });

    mappings.add([modes.NORMAL], ["T"],
        "Open one or more URLs in a new tab, based on current location",
        function () { commandline.open(":", "tabopen " + buffer.URL, modes.EX); });

    mappings.add([modes.NORMAL], ["w"],
        "Open one or more URLs in a new window",
        function () { commandline.open(":", "winopen ", modes.EX); });

    mappings.add([modes.NORMAL], ["W"],
        "Open one or more URLs in a new window, based on current location",
        function () { commandline.open(":", "winopen " + buffer.URL, modes.EX); });

    mappings.add([modes.NORMAL],
        ["<C-a>"], "Increment last number in URL",
        function (count) { incrementURL(Math.max(count, 1)); },
        { count: true });

    mappings.add([modes.NORMAL],
        ["<C-x>"], "Decrement last number in URL",
        function (count) { incrementURL(-Math.max(count, 1)); },
        { count: true });

    mappings.add([modes.NORMAL], ["~"],
        "Open home directory",
        function () { liberator.open("~"); });

    mappings.add([modes.NORMAL], ["gh"],
        "Open homepage",
        function () { BrowserHome(); });

    mappings.add([modes.NORMAL], ["gH"],
        "Open homepage in a new tab",
        function ()
        {
            let homepages = gHomeButton.getHomePage();
            liberator.open(homepages, /\bhomepage\b/.test(options["activate"]) ?
                    liberator.NEW_TAB : liberator.NEW_BACKGROUND_TAB);
        });

    mappings.add([modes.NORMAL], ["gu"],
        "Go to parent directory",
        function (count)
        {
            function isDirectory(url)
            {
                if (/^file:\/|^\//.test(url))
                {
                    let file = io.getFile(url);
                    return file.exists() && file.isDirectory();
                }
                else
                {
                    // for all other locations just check if the URL ends with /
                    return /\/$/.test(url);
                }
            }

            if (count < 1)
                count = 1;

            // XXX
            let url = buffer.URL;
            for (let i = 0; i < count; i++)
            {
                if (isDirectory(url))
                    url = url.replace(/^(.*?:)(.*?)([^\/]+\/*)$/, "$1$2/");
                else
                    url = url.replace(/^(.*?:)(.*?)(\/+[^\/]+)$/, "$1$2/");
            }
            url = url.replace(/^(.*:\/+.*?)\/+$/, "$1/"); // get rid of more than 1 / at the end

            if (url == buffer.URL)
                liberator.beep();
            else
                liberator.open(url);
        },
        { count: true });

    mappings.add([modes.NORMAL], ["gU"],
        "Go to the root of the website",
        function ()
        {
            let uri = content.document.location;
            if (/(about|mailto):/.test(uri.protocol)) // exclude these special protocols for now
                return void liberator.beep();
            liberator.open(uri.protocol + "//" + (uri.host || "") + "/");
        });

    mappings.add([modes.NORMAL], ["<C-l>"],
        "Redraw the screen",
        function () { commands.get("redraw").execute("", false); });

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// COMMANDS ////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    commands.add(["downl[oads]", "dl"],
        "Show progress of current downloads",
        function ()
        {
            liberator.open("chrome://mozapps/content/downloads/downloads.xul",
                options.get("newtab").has("all", "downloads")
                    ? liberator.NEW_TAB : liberator.CURRENT_TAB);
        },
        { argCount: "0" });

    commands.add(["o[pen]", "e[dit]"],
        "Open one or more URLs in the current tab",
        function (args)
        {
            args = args.string;

            if (args)
                liberator.open(args);
            else
                liberator.open("about:blank");
        },
        {
            completer: function (context) completion.url(context),
            literal: 0,
            privateData: true,
        });

    commands.add(["redr[aw]"],
        "Redraw the screen",
        function ()
        {
            let wu = window.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
            wu.redraw();
            modes.show();
        },
        { argCount: "0" });

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// PUBLIC SECTION //////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    return {
        // TODO: extract browser-specific functionality from liberator
    };
    //}}}
}; //}}}

// vim: set fdm=marker sw=4 ts=4 et:
<|endoftext|>


<commit_before>var path = require('path');
var koala = require('./lib');
var spdy = require('spdy');
var keys = require('spdy-keys');
var fs = require('fs');

var PORT = process.env.PORT || 5000;
var ENV = process.env.NODE_ENV || 'development';

var app = koala({
    fileServer: {
        root: __dirname + '/dist/',
        index: true,
        maxAge: '1 year'
    }
});

//app.get('/', function *(next) {
//    yield* this.fileServer.send('index.html');
//    if (this.isSpdy) {
//        yield* this.fileServer.push('css/global.css');
//        yield* this.fileServer.push('js/circles.js');
//    }
//});

//var server = spdy.createServer({
//    key: fs.readFileSync('server.key'),
//    cert: fs.readFileSync('server.crt'),
//    ca: fs.readFileSync('server.csr')
//}, app.callback());

//var server = spdy.createServer(keys, app.callback());
//
//server.listen(PORT);

app.listen(PORT);

console.log('Listening on port: ' + PORT);
console.log('Running app in environment: ' + ENV);
<commit_msg>Temporarily remove spdy features<commit_after>var path = require('path');
var koala = require('./lib');
var koala = require('./lib');

var fs = require('fs');

var PORT = process.env.PORT || 5000;
var ENV = process.env.NODE_ENV || 'development';

var app = koala({
    fileServer: {
        root: __dirname + '/dist/',
        index: true,
        maxAge: '1 year'
    }
});

app.listen(PORT);

console.log('Listening on port: ' + PORT);
console.log('Running app in environment: ' + ENV);
<|endoftext|>