<commit_before>/*
 *
LispyScript - Javascript using tree syntax!
This is the compiler written in javascipt
 *
*/
define(function(require, exports, module) {

var _ = require('underscore');
var Module = module.constructor;

String.prototype.repeat = function(num) {
    return new Array(num + 1).join(this);
};

exports.version = "0.1.6";
var _LS = {},
    banner = "// Generated by LispyScript v" + this.version + "\n",
    isWhitespace = /\s/,
    isFunction = /^function/,
    validSymbol = /^[a-zA-Z_$]([\-\.\[]{0,1}[0-9a-zA-Z_$]+\]{0,1})*\?{0,1}$/,
    validName = /[a-zA-Z_$]/,
    noReturn = /^var|set|throw\b/,
    indent = -4,
    keywords = {},
    macros = {},
    templates = {};
templates["function"] = _.template("function(<%= params %>) {\n<%= expressions %><%= indent %>}");
templates["try"] = _.template("(function () {\n<%= indent %><%= indent %>try {\n<%= indent %><%= trypart %>\n<%= indent %><%= indent %>} catch (e) {\n<%= indent %><%= indent %><%= indent %>return (<%= catchpart %>)(e);\n<%= indent %><%= indent %>}\n<%= indent %>})()");
templates["if"] = _.template("<%= condition %> ?\n<%= indent %><%= trueexpr %> :\n<%= indent %><%= falseexpr %>");
templates["operator"] = _.template("(<%= loperand %> <%= operator %> <%= roperand %>)");
templates["str"] = _.template("[<%= elems %>].join('')");

var parse = function(code, filename) {
    code = "(" + code + ")";
    var length = code.length,
        pos = 1,
        lineno = 1;

    var parser = function() {
        var tree = [],
            token = "",
            special = "",
            isString = false,
            isSingleString = false,
            isJSArray = 0,
            isJSObject = 0,
            isListComplete = false,
            isComment = false,
            isRegex = false;
        tree._line = lineno;
        tree._filename = filename;
        var handleToken = function() {
            if (token) {
                if (special) {
                    tree.push([ special, token ]);
                    special = "";
                }
                else
                    tree.push(token);
                token = "";
            }
        };
        while (pos < length) {
            var c = code.charAt(pos);
            pos++;
            if (c == "\n") {
                lineno++;
                if (isComment) isComment = false;
            }
            if (isComment) continue;
            if (isRegex && c !== "/") {
                token += c;
                continue;
            }
            if (c == '"') {
                if (isString && token[token.length - 1] === "\\") {
                    token += c;
                    continue;
                }
                isString = !isString;
                token += c;
                continue;
            }
            if (isString) {
                if (c === "\n")
                    token += "\\n";
                else
                    token += c;
                continue;
            }
            if (isSingleString) {
                token += c;
                continue;
            }
            if (c == '[') {
                isJSArray++;
                token += c;
                continue;
            }
            if (c == ']') {
                if (isJSArray === 0) throw handleError(4, tree._line, tree._filename);
                isJSArray--;
                token += c;
                continue;
            }
            if (isJSArray) {
                token += c;
                continue;
            }
            if (c == '{') {
                isJSObject++;
                token += c;
                continue;
            }
            if (c == '}') {
                if (isJSObject === 0) throw handleError(6, tree._line, tree._filename);
                isJSObject--;
                token += c;
                continue;
            }
            if (isJSObject) {
                token += c;
                continue;
            }
            if (c == ";") {
                isComment = true;
                continue;
            }

            if (c == '/') {
                if (isRegex && token[token.length - 1] === "\\") {
                    token += c;
                    continue;
                }
                isRegex = !isRegex;
                token += c;
                continue;
            }

            if (c == "(") {
                var compound = parser();
                if (special) {
                    tree.push([ special, compound ]);
                    special = "";
                }
                else
                    tree.push(compound);
                continue;
            }
            if (c == ")") {
                isListComplete = true;
                handleToken();
                break;
            }
            if (isWhitespace.test(c)) {
                handleToken();
                continue;
            }
            if (c === "`") {
              special = "quasiquote";
              continue;
            }
            if (c === "'") {
              special = "quote";
              continue;
            }
            if (c === "~") {
              special = "unquote";
              continue;
            }
            token += c;
        }
        if (isString) throw handleError(3, tree._line, tree._filename);
        if (isSingleString) throw handleError(3, tree._line, tree._filename);
        if (isJSArray > 0) throw handleError(5, tree._line, tree._filename);
        if (isJSObject > 0) throw handleError(7, tree._line, tree._filename);
        if (!isListComplete) throw handleError(8, tree._line, tree._filename);
        return tree;
    };
    var ret = parser();
    if (pos < length) throw handleError(10);
    return ret;
};

var handleExpressions = function(exprs) {
    indent += 4;
    var ret = "",
        l = exprs.length,
        indentstr = " ".repeat(indent);
    _.each(exprs, function(expr, i, exprs) {
        var tmp = "", r = "";
        if (_.isArray(expr)) {
            if (expr[0] === "include")
                ret += handleExpression(expr);
            else
                tmp = handleExpression(expr);
        } else {
            tmp = expr;
        }
        if (i === l - 1 && indent) {
            if (!noReturn.test(tmp)) r = "return ";
        }
        if (tmp.length > 0)
            ret += indentstr + r + tmp + ";\n";
    });
    indent -= 4;
    return ret;
};

var handleSymbol = function(symbol, expr) {
  if (!validName.test(symbol.charAt(0)))
    return symbol;
  if (!validSymbol.test(symbol))
      throw handleError(12, expr._line, expr._filename);
    var isPredicate = symbol.substr(-1) === "?";
    var name = isPredicate ? symbol.substr(0, symbol.length - 1) : symbol;

    return name.split('-').reduce(function(result, $) {
      return result + (result ? $[0].toUpperCase() + $.substr(1) : $);
    }, isPredicate ? 'is' : '');
};

var handleExpression = function(expr) {
    var command = expr[0];
    if (macros[command]) {
        expr = macroExpand(expr);
        return handleExpression(expr);
    }
    if (_.isString(command)) {
        if (keywords[command])
            return keywords[command](expr);
        if (command.charAt(0) === ".") {
            return "(" + (_.isArray(expr[1]) ? handleExpression(expr[1]) : expr[1]) + ")" + command;
        }
    }
    handleSubExpressions(expr);
    var fName = expr[0];
    if (!fName) throw handleError(1, expr._line);
    if (isFunction.test(fName)) fName = "(" + fName + ")";
    return fName + "(" + expr.slice(1).join(",") + ")";
};

var handleSubExpressions = function(expr) {
    _.each(expr, function(value, i, t) {
      t[i] = _.isArray(value) ? handleExpression(value)
                              : handleSymbol(value);
    });
};

var macroExpand = function(tree) {
    var command = tree[0],
        template = macros[command].template,
        code = macros[command].code,
        replacements = {};

    for (var i = 0; i < template.length; i++) {
        if (template[i].substr(-3) === "...") {
            replacements[template[i]] = tree.slice(i + 1);
        } else {
            replacements[template[i]] = tree[i + 1];
        }
    }

    var replaceCode = function(source) {
        var ret = [];
        ret._line = tree._line;
        ret._filename = tree._filename;

        for (var i = 0; i < source.length; i++) {
            var token = source[i];
            if (typeof token == "object") {
              if (token[0] === "unquote" ||
                  token[0] === "quote" ||
                  token[0] === "quasiquote")
                ret = ret.concat(replaceCode(token));
              else
                ret.push(replaceCode(token));
            }
            else if (token === "unquote") {
                token = source[++i];
                if (typeof token === "object") {
                    ret.push(replaceCode(token));
                } else  {
                    var splicing = token[0] === "@";
                    var spreading = token.substr(-3) === "...";
                    token = splicing ? token.substr(1) : token;
                    var repl = replacements[token];
                    if (splicing || spreading)
                        ret = ret.concat(repl);
                    else
                        ret.push(repl);
                }
            }
            else if (token === "quote") {
                token = source[++i];
                ret.push(token);
            }
            else if (token === "quasiquote") {
                token = source[++i];
                ret = ret.concat(replaceCode(token));
            }
            else {
              ret.push(token);
            }
        }
        return ret;
    };
    return replaceCode(code);
};

var handleOperator = function(arr) {
    if (arr.length != 3)  throw handleError(0, arr._line);
    handleSubExpressions(arr);
    if (arr[0] == "=") arr[0] = "===";
    if (arr[0] == "!=") arr[0] = "!==";
    return templates["operator"]({operator: arr[0], loperand: arr[1], roperand: arr[2]});
};

var handleError = function(no, line, filename) {
    return errors[no] + ((line) ? "\nLine no " + line : "") + ((filename) ? "\nFile " + filename : "");
};

keywords["js"] = function(arr) {
  return handleSubExpressions(arr.slice(1)).join(' ');
};

keywords["function"] = function(arr) {
    if (arr.length < 3) throw handleError(0, arr._line, arr._filename);
    if (typeof arr[1] != "object") throw handleError(0, arr._line);
    var params = arr[1].map(function(name) {
      return handleSymbol(name, arr);
    });

    return templates["function"]({
        params: params.join(","),
        expressions: handleExpressions(arr.slice(2)),
        indent: " ".repeat(indent)});
};

keywords["try"] = function(arr) {
    if (arr.length < 3) throw handleError(0, arr._line, arr._filename);
    var c = arr.pop();
    return templates["try"]({
        trypart: handleExpressions(arr.slice(1)),
        catchpart: _.isArray(c) ? handleExpression(c) : c,
        indent: " ".repeat(indent)});
};

keywords["symbol"] = function(arr) {
    if (arr.length < 2) throw handleError(0, arr._line, arr._filename);
    return arr[1].substr(1, arr[1].length - 2)
}

keywords["symbols-join"] = function(arr) {
    if (arr.length < 2) throw handleError(0, arr._line, arr._filename);
    return handleSubExpressions(arr.slice(2)).join(arr[1]);
};

keywords["if"] = function(arr) {
    if (arr.length < 3 || arr.length > 4)  throw handleError(0, arr._line, arr._filename);
    indent += 4;
    handleSubExpressions(arr);
    var ret = templates["if"]({
        condition: arr[1],
        trueexpr: arr[2],
        falseexpr: arr[3] || "undefined",
        indent: " ".repeat(indent)});
    indent -= 4;
    return ret;
};

keywords["str"] = function(arr) {
    if (arr.length < 2) throw handleError(0, arr._line, arr._filename);
    handleSubExpressions(arr);
    return templates["str"]({elems: arr.slice(1).join(",")});
};

keywords["macro"] = function(arr) {
    if (arr.length != 4)  throw handleError(0, arr._line, arr._filename);
    macros[arr[1]] = {template: arr[2], code: arr[3]};
    return "";
};

keywords["include"] = function(arr) {
    if (arr.length != 2)  throw handleError(0, arr._line, arr._filename);
    indent -= 4;
    var filename = arr[1];
    if (typeof filename === "string")
        filename = filename.replace(/["']/g, "");

    var module = new Module(arr._filename);
    module.filename = arr._filename;

    try {
        filename = Module._resolveFilename(filename, module);
        require(filename);
    } catch (err) {
        throw handleError(11, arr._line, arr._filename);
    }

    indent += 4;
    return "";
};

keywords["+"] = handleOperator;

keywords["-"] = handleOperator;

keywords["*"] = handleOperator;

keywords["/"] = handleOperator;

keywords["%"] = handleOperator;

keywords["="] = handleOperator;

keywords["!="] = handleOperator;

keywords[">"] = handleOperator;

keywords[">="] = handleOperator;

keywords["<"] = handleOperator;

keywords["<="] = handleOperator;

keywords["||"] = handleOperator;

keywords["&&"] = handleOperator;

keywords["!"] = function(arr) {
    if (arr.length != 2)  throw handleError(0, arr._line, arr._filename);
    handleSubExpressions(arr);
    return "(!" + arr[1] + ")";
};

errors = [];
errors[0] = "Syntax Error";
errors[1] = "Empty statement";
errors[2] = "Invalid function name";
errors[3] = "End of File encountered, unterminated string";
errors[4] = "Closing square bracket, without an opening square bracket";
errors[5] = "End of File encountered, unterminated array";
errors[6] = "Closing curly brace, without an opening curly brace";
errors[7] = "End of File encountered, unterminated javascript object '}'";
errors[8] = "End of File encountered, unterminated parenthesis";
errors[9] = "Invalid variable name";
errors[10] = "Extra chars at end of file. Maybe an extra ')'.";
errors[11] = "Cannot Open include File";
errors[12] = "Invalid symbol";

exports._compile = function(code, filename) {
  indent = -4;
  var tree = parse(code, filename);
  return banner + handleExpressions(tree);
};

});
<commit_msg>Implement basic reader macros to allow clojurscript like (.filter array pedicure) form #13<commit_after>/*
 *
LispyScript - Javascript using tree syntax!
This is the compiler written in javascipt
 *
*/
define(function(require, exports, module) {

var _ = require('underscore');
var Module = module.constructor;

String.prototype.repeat = function(num) {
    return new Array(num + 1).join(this);
};

exports.version = "0.1.6";
var _LS = {},
    banner = "// Generated by LispyScript v" + this.version + "\n",
    isWhitespace = /\s/,
    isFunction = /^function/,
    validSymbol = /^[a-zA-Z_$]([\-\.\[]{0,1}[0-9a-zA-Z_$]+\]{0,1})*\?{0,1}$/,
    validName = /[a-zA-Z_$]/,
    noReturn = /^var|set|throw\b/,
    indent = -4,
    keywords = {},
    macros = {},
    templates = {};
templates["function"] = _.template("function(<%= params %>) {\n<%= expressions %><%= indent %>}");
templates["try"] = _.template("(function () {\n<%= indent %><%= indent %>try {\n<%= indent %><%= trypart %>\n<%= indent %><%= indent %>} catch (e) {\n<%= indent %><%= indent %><%= indent %>return (<%= catchpart %>)(e);\n<%= indent %><%= indent %>}\n<%= indent %>})()");
templates["if"] = _.template("<%= condition %> ?\n<%= indent %><%= trueexpr %> :\n<%= indent %><%= falseexpr %>");
templates["operator"] = _.template("(<%= loperand %> <%= operator %> <%= roperand %>)");
templates["str"] = _.template("[<%= elems %>].join('')");
templates[".invoke"] = _.template("(<%= target %>).<%= method %>");

var parse = function(code, filename) {
    code = "(" + code + ")";
    var length = code.length,
        pos = 1,
        lineno = 1;

    var parser = function() {
        var tree = [],
            token = "",
            special = "",
            isString = false,
            isSingleString = false,
            isJSArray = 0,
            isJSObject = 0,
            isListComplete = false,
            isComment = false,
            isRegex = false;
        tree._line = lineno;
        tree._filename = filename;
        var handleToken = function() {
            if (token) {
                if (special) {
                    tree.push([ special, token ]);
                    special = "";
                }
                else
                    tree.push(token);
                token = "";
            }
        };
        while (pos < length) {
            var c = code.charAt(pos);
            pos++;
            if (c == "\n") {
                lineno++;
                if (isComment) isComment = false;
            }
            if (isComment) continue;
            if (isRegex && c !== "/") {
                token += c;
                continue;
            }
            if (c == '"') {
                if (isString && token[token.length - 1] === "\\") {
                    token += c;
                    continue;
                }
                isString = !isString;
                token += c;
                continue;
            }
            if (isString) {
                if (c === "\n")
                    token += "\\n";
                else
                    token += c;
                continue;
            }
            if (isSingleString) {
                token += c;
                continue;
            }
            if (c == '[') {
                isJSArray++;
                token += c;
                continue;
            }
            if (c == ']') {
                if (isJSArray === 0) throw handleError(4, tree._line, tree._filename);
                isJSArray--;
                token += c;
                continue;
            }
            if (isJSArray) {
                token += c;
                continue;
            }
            if (c == '{') {
                isJSObject++;
                token += c;
                continue;
            }
            if (c == '}') {
                if (isJSObject === 0) throw handleError(6, tree._line, tree._filename);
                isJSObject--;
                token += c;
                continue;
            }
            if (isJSObject) {
                token += c;
                continue;
            }
            if (c == ";") {
                isComment = true;
                continue;
            }

            if (c == '/') {
                if (isRegex && token[token.length - 1] === "\\") {
                    token += c;
                    continue;
                }
                isRegex = !isRegex;
                token += c;
                continue;
            }

            if (c == "(") {
                var compound = parser();
                if (special) {
                    tree.push([ special, compound ]);
                    special = "";
                }
                else
                    tree.push(compound);
                continue;
            }
            if (c == ")") {
                isListComplete = true;
                handleToken();
                break;
            }
            if (isWhitespace.test(c)) {
                handleToken();
                continue;
            }
            if (c === "`") {
              special = "quasiquote";
              continue;
            }
            if (c === "'") {
              special = "quote";
              continue;
            }
            if (c === "~") {
              special = "unquote";
              continue;
            }
            token += c;
        }
        if (isString) throw handleError(3, tree._line, tree._filename);
        if (isSingleString) throw handleError(3, tree._line, tree._filename);
        if (isJSArray > 0) throw handleError(5, tree._line, tree._filename);
        if (isJSObject > 0) throw handleError(7, tree._line, tree._filename);
        if (!isListComplete) throw handleError(8, tree._line, tree._filename);
        return tree;
    };
    var ret = parser();
    if (pos < length) throw handleError(10);
    return ret;
};

var handleExpressions = function(exprs) {
    indent += 4;
    var ret = "",
        l = exprs.length,
        indentstr = " ".repeat(indent);
    _.each(exprs, function(expr, i, exprs) {
        var tmp = "", r = "";
        if (_.isArray(expr)) {
            if (expr[0] === "include")
                ret += handleExpression(expr);
            else
                tmp = handleExpression(expr);
        } else {
            tmp = expr;
        }
        if (i === l - 1 && indent) {
            if (!noReturn.test(tmp)) r = "return ";
        }
        if (tmp.length > 0)
            ret += indentstr + r + tmp + ";\n";
    });
    indent -= 4;
    return ret;
};

var handleSymbol = function(symbol, expr) {
  if (!validName.test(symbol.charAt(0)))
    return symbol;
  if (!validSymbol.test(symbol))
      throw handleError(12, expr._line, expr._filename);
    var isPredicate = symbol.substr(-1) === "?";
    var name = isPredicate ? symbol.substr(0, symbol.length - 1) : symbol;

    return name.split('-').reduce(function(result, $) {
      return result + (result ? $[0].toUpperCase() + $.substr(1) : $);
    }, isPredicate ? 'is' : '');
};

var handleExpression = function(expr) {
    var command = expr[0];
    if (macros[command]) {
        expr = macroExpand(expr);
        return handleExpression(expr);
    }
    if (_.isString(command)) {
        if (keywords[command])
            return keywords[command](expr);
        if (!validName.test(command.charAt(0)))
            return readers[command.charAt(0)](expr);
    }
    handleSubExpressions(expr);
    var fName = expr[0];
    if (!fName) throw handleError(1, expr._line);
    if (isFunction.test(fName)) fName = "(" + fName + ")";
    return fName + "(" + expr.slice(1).join(",") + ")";
};

var handleSubExpressions = function(expr) {
    _.each(expr, function(value, i, t) {
      t[i] = _.isArray(value) ? handleExpression(value)
                              : handleSymbol(value);
    });
    return expr;
};

var macroExpand = function(tree) {
    var command = tree[0],
        template = macros[command].template,
        code = macros[command].code,
        replacements = {};

    for (var i = 0; i < template.length; i++) {
        if (template[i].substr(-3) === "...") {
            replacements[template[i]] = tree.slice(i + 1);
        } else {
            replacements[template[i]] = tree[i + 1];
        }
    }

    var replaceCode = function(source) {
        var ret = [];
        ret._line = tree._line;
        ret._filename = tree._filename;

        for (var i = 0; i < source.length; i++) {
            var token = source[i];
            if (typeof token == "object") {
              if (token[0] === "unquote" ||
                  token[0] === "quote" ||
                  token[0] === "quasiquote")
                ret = ret.concat(replaceCode(token));
              else
                ret.push(replaceCode(token));
            }
            else if (token === "unquote") {
                token = source[++i];
                if (typeof token === "object") {
                    ret.push(replaceCode(token));
                } else  {
                    var splicing = token[0] === "@";
                    var spreading = token.substr(-3) === "...";
                    token = splicing ? token.substr(1) : token;
                    var repl = replacements[token];
                    if (splicing || spreading)
                        ret = ret.concat(repl);
                    else
                        ret.push(repl);
                }
            }
            else if (token === "quote") {
                token = source[++i];
                ret.push(token);
            }
            else if (token === "quasiquote") {
                token = source[++i];
                ret = ret.concat(replaceCode(token));
            }
            else {
              ret.push(token);
            }
        }
        return ret;
    };
    return replaceCode(code);
};

var handleOperator = function(arr) {
    if (arr.length != 3)  throw handleError(0, arr._line);
    handleSubExpressions(arr);
    if (arr[0] == "=") arr[0] = "===";
    if (arr[0] == "!=") arr[0] = "!==";
    return templates["operator"]({operator: arr[0], loperand: arr[1], roperand: arr[2]});
};

var handleError = function(no, line, filename) {
    return errors[no] + ((line) ? "\nLine no " + line : "") + ((filename) ? "\nFile " + filename : "");
};

var readers = {};

readers["."] = function(expression) {
  var invoker = expression[0];
  var target = expression[1];
  var params = expression.slice(2);
  var owner = _.isArray(target) ? handleExpression(target)
                                : handleSymbol(target);

  var method = handleExpression([ invoker.substr(1) ].concat(params));
  return templates[".invoke"]({ target: owner, method: method });
};

keywords["js"] = function(arr) {
  return handleSubExpressions(arr.slice(1)).join(' ');
};

keywords["function"] = function(arr) {
    if (arr.length < 3) throw handleError(0, arr._line, arr._filename);
    if (typeof arr[1] != "object") throw handleError(0, arr._line);
    var params = arr[1].map(function(name) {
      return handleSymbol(name, arr);
    });

    return templates["function"]({
        params: params.join(","),
        expressions: handleExpressions(arr.slice(2)),
        indent: " ".repeat(indent)});
};

keywords["try"] = function(arr) {
    if (arr.length < 3) throw handleError(0, arr._line, arr._filename);
    var c = arr.pop();
    return templates["try"]({
        trypart: handleExpressions(arr.slice(1)),
        catchpart: _.isArray(c) ? handleExpression(c) : c,
        indent: " ".repeat(indent)});
};

keywords["symbol"] = function(arr) {
    if (arr.length < 2) throw handleError(0, arr._line, arr._filename);
    return arr[1].substr(1, arr[1].length - 2)
}

keywords["symbols-join"] = function(arr) {
    if (arr.length < 2) throw handleError(0, arr._line, arr._filename);
    return handleSubExpressions(arr.slice(2)).join(arr[1]);
};

keywords["if"] = function(arr) {
    if (arr.length < 3 || arr.length > 4)  throw handleError(0, arr._line, arr._filename);
    indent += 4;
    handleSubExpressions(arr);
    var ret = templates["if"]({
        condition: arr[1],
        trueexpr: arr[2],
        falseexpr: arr[3] || "undefined",
        indent: " ".repeat(indent)});
    indent -= 4;
    return ret;
};

keywords["str"] = function(arr) {
    if (arr.length < 2) throw handleError(0, arr._line, arr._filename);
    handleSubExpressions(arr);
    return templates["str"]({elems: arr.slice(1).join(",")});
};

keywords["macro"] = function(arr) {
    if (arr.length != 4)  throw handleError(0, arr._line, arr._filename);
    macros[arr[1]] = {template: arr[2], code: arr[3]};
    return "";
};

keywords["include"] = function(arr) {
    if (arr.length != 2)  throw handleError(0, arr._line, arr._filename);
    indent -= 4;
    var filename = arr[1];
    if (typeof filename === "string")
        filename = filename.replace(/["']/g, "");

    var module = new Module(arr._filename);
    module.filename = arr._filename;

    try {
        filename = Module._resolveFilename(filename, module);
        require(filename);
    } catch (err) {
        throw handleError(11, arr._line, arr._filename);
    }

    indent += 4;
    return "";
};

keywords["+"] = handleOperator;

keywords["-"] = handleOperator;

keywords["*"] = handleOperator;

keywords["/"] = handleOperator;

keywords["%"] = handleOperator;

keywords["="] = handleOperator;

keywords["!="] = handleOperator;

keywords[">"] = handleOperator;

keywords[">="] = handleOperator;

keywords["<"] = handleOperator;

keywords["<="] = handleOperator;

keywords["||"] = handleOperator;

keywords["&&"] = handleOperator;

keywords["!"] = function(arr) {
    if (arr.length != 2)  throw handleError(0, arr._line, arr._filename);
    handleSubExpressions(arr);
    return "(!" + arr[1] + ")";
};

errors = [];
errors[0] = "Syntax Error";
errors[1] = "Empty statement";
errors[2] = "Invalid function name";
errors[3] = "End of File encountered, unterminated string";
errors[4] = "Closing square bracket, without an opening square bracket";
errors[5] = "End of File encountered, unterminated array";
errors[6] = "Closing curly brace, without an opening curly brace";
errors[7] = "End of File encountered, unterminated javascript object '}'";
errors[8] = "End of File encountered, unterminated parenthesis";
errors[9] = "Invalid variable name";
errors[10] = "Extra chars at end of file. Maybe an extra ')'.";
errors[11] = "Cannot Open include File";
errors[12] = "Invalid symbol";

exports._compile = function(code, filename) {
  indent = -4;
  var tree = parse(code, filename);
  return banner + handleExpressions(tree);
};

});
<|endoftext|>


<commit_before>/*  This plugin provides a function which serves the a directory full of markdown
    It serves it in a variety of formats, based on the request url
    Just pass the absolute path to the directory you wish to serve,
    it will return a typical handler function for expressjs...
      function(request,response,nextMiddleware)...

    You may optionally provide a second argument (the page title)

    The portion of the URL after the given route is parsed
    The appropriate response is inferred from the remaining URL stub

    The url stub is split into a file name, and an extension..
    if <filename>.md does not exist in the target directory, this route fails
    'next()' is called, and the server moves on.
    if the file does exist, then the extension is used to infer how it should be formatted
    stubs lacking a filetype extension result in the default action:
      the markdown is converted into HTML on the server
      the resulting HTML is morf.swapped into a template
      the template is also formatted such that the appropriate tags are also inserted
        This includes: a title, relevant assets (scripts, css, w/e)
    stubs with the .json extension are served as JSON objects
      the Object will also contain statistics about the file (ctime, atime, mtime)
      the content is served as unrendered markdown
      this extension should only be used if you have a clientside markdown renderer
      thusly it is appropriate for single page apps
    stubs with the .md extension are served as unformatted markdown
      this is the easiest extension ;)
    stubs with the html extension are served as formatted HTML, but not morf.swapped into a template
      if you intend for other people to be able to use this as an API
        you must have enabled the CORS module */

var fs = require("fs");
var md = require("marked");
var morf=require("ansuz").morf;
var mdserve={};

mdserve.parseURL=function(url){
  var parts={
    url:url,
    ext:""
  };
  parts.root=url.replace(/\.(json|html|md)$/,function(str){
    parts.ext=str;
    return "";
  }).replace(/[^\/]*$/,function(str){
    parts.file=str;
    return "";
  });
  parts.file=parts.file||"index";
  return parts;
}

/*  spawnHandler accepts the absolute path to a directory containing:
    markdown files (.md), at least one of which is named 'index.md'
    a template file (template.html) which should contain the tokens:
      {TITLE} and {CONTENT}
    which will be swapped for the function's second argument (title)
      and the contents of the file that users browse to (if it exists)
 */

mdserve.spawnHandler=function(dir,title){
  // the default title is 'unmon', a shameless plug.
  title=title||"unmon";

  // return a function which will parse urls and serve markdown accordingly
  return function(req,res,next){
    // store the parsed url
    var purl=mdserve.parseURL(req.url);

    // create the absolute path to the target file.
    var target=dir+purl.file+'.md';

    fs.exists(target,function(e){ // check if the file exists
      if(e){ // it exists, load the file and serve appropriately
        fs.readFile(target,'utf8',function(err,markdown){ // load the file
          if(err)console.log(err)&&next(); // problem? say so, then 404
          // this is the part where we decide what to do based on extension
          switch(purl.ext){
            case ".json": // it's json
              fs.stat(target,function(err,stats){ // grab some file metadata
                if(err)console.log(err);
                res.send({ // send them some JSON
                  txt:markdown // yay closures, send the file contents
                  ,title:req.url.slice(1) // send the file title
                  // stats from the file system
                  ,mtime:stats&&stats.mtime||"" // last modified time
                  ,ctime:stats&&stats.ctime||"" // created time?
                  ,atime:stats&&stats.atime||"" // access time
                });
              });
              break;

            case ".md": // they want the pure markdown
              res.writeHead(200,{"Content-Type":"text/plain"}); // serve as text
              res.end(markdown); // the exact file contents, nothing fancy
              break;

            case ".html": // they want formatted html
              res.end(md(markdown)); // render and send the markdown
              break;

            default:
              try{ // you should have created a template.html file
                fs.readFile(dir+'template.html','utf8',function(err,str){
                  if(err)console.log("no template found"); // make one!
                  // I included a sample in unmon/md/ ...
                  res.send(morf.swap(str,{
                    "{TITLE}":title+"::"+purl.file,
                    "{CONTENT}":md(markdown)
                  }));
                });
              }catch(err){ // there was a problem..
                console.log(err);
                next();
              }
          }
        });
      }else{ // the file doesn't exist, carry on
        next();
      }
    });
  };
};

module.exports = mdserve.spawnHandler; // provide spawnHandler
<commit_msg>modded md.js such that it optionally takes an object for configuration<commit_after>/*  This plugin provides a function which serves the a directory full of markdown
    It serves it in a variety of formats, based on the request url
    Just pass the absolute path to the directory you wish to serve,
    it will return a typical handler function for expressjs...
      function(request,response,nextMiddleware)...

    You may optionally provide a second argument (the page title)

    The portion of the URL after the given route is parsed
    The appropriate response is inferred from the remaining URL stub

    The url stub is split into a file name, and an extension..
    if <filename>.md does not exist in the target directory, this route fails
    'next()' is called, and the server moves on.
    if the file does exist, then the extension is used to infer how it should be formatted
    stubs lacking a filetype extension result in the default action:
      the markdown is converted into HTML on the server
      the resulting HTML is morf.swapped into a template
      the template is also formatted such that the appropriate tags are also inserted
        This includes: a title, relevant assets (scripts, css, w/e)
    stubs with the .json extension are served as JSON objects
      the Object will also contain statistics about the file (ctime, atime, mtime)
      the content is served as unrendered markdown
      this extension should only be used if you have a clientside markdown renderer
      thusly it is appropriate for single page apps
    stubs with the .md extension are served as unformatted markdown
      this is the easiest extension ;)
    stubs with the html extension are served as formatted HTML, but not morf.swapped into a template
      if you intend for other people to be able to use this as an API
        you must have enabled the CORS module */

var fs = require("fs");
var md = require("marked");
var morf=require("ansuz").morf;
var mdserve={};

mdserve.parseURL=function(url){
  var parts={
    url:url,
    ext:""
  };
  parts.root=url.replace(/\.(json|html|md)$/,function(str){
    parts.ext=str;
    return "";
  }).replace(/[^\/]*$/,function(str){
    parts.file=str;
    return "";
  });
  parts.file=parts.file||"index";
  return parts;
}

/*  spawnHandler accepts the absolute path to a directory containing:
    markdown files (.md), at least one of which is named 'index.md'
    a template file (template.html) which should contain the tokens:
      {TITLE} and {CONTENT}
    which will be swapped for the function's second argument (title)
      and the contents of the file that users browse to (if it exists)
 */

mdserve.spawnHandler=function(dir,title){
  /*  It would be better if this accepted an object
      but I don't want to break it for anyone already using it (me)
  */
  var opt;
  if(typeof dir === 'object'){
    opt=dir;
  }else{
    opt={
      path:dir
      ,title:title||"" // the default title is 'unmon', a shameless plug.
    };
  }

  // return a function which will parse urls and serve markdown accordingly
  return function(req,res,next){
    // store the parsed url
    var purl=mdserve.parseURL(req.url);

    // create the absolute path to the target file.
    var target=opt.path+purl.file+'.md';

    fs.exists(target,function(e){ // check if the file exists
      if(e){ // it exists, load the file and serve appropriately
        fs.readFile(target,'utf8',function(err,markdown){ // load the file
          if(err)console.log(err)&&next(); // problem? say so, then 404
          // this is the part where we decide what to do based on extension
          switch(purl.ext){
            case ".json": // it's json
              fs.stat(target,function(err,stats){ // grab some file metadata
                if(err)console.log(err);
                res.send({ // send them some JSON
                  txt:markdown // yay closures, send the file contents
                  ,title:req.url.slice(1) // send the file title
                  // stats from the file system
                  ,mtime:stats&&stats.mtime||"" // last modified time
                  ,ctime:stats&&stats.ctime||"" // created time?
                  ,atime:stats&&stats.atime||"" // access time
                });
              });
              break;

            case ".md": // they want the pure markdown
              res.writeHead(200,{"Content-Type":"text/plain"}); // serve as text
              res.end(markdown); // the exact file contents, nothing fancy
              break;

            case ".html": // they want formatted html
              res.end(md(markdown)); // render and send the markdown
              break;

            default:
              try{ // you should have created a template.html file
                fs.readFile(opt.path+'template.html','utf8',function(err,str){
                  if(err)console.log("no template found"); // make one!
                  // I included a sample in unmon/md/ ...
                  res.send(morf.swap(str,{
                    "{TITLE}":opt.title+purl.file,
                    "{CONTENT}":md(markdown)
                  }));
                });
              }catch(err){ // there was a problem..
                console.log(err);
                next();
              }
          }
        });
      }else{ // the file doesn't exist, carry on
        next();
      }
    });
  };
};

module.exports = mdserve.spawnHandler; // provide spawnHandler
<|endoftext|>


<commit_before>var util     = require('util'),
    fs       = require('fs'),
    path     = require('path'),
    Stream   = require('./mu/stream'),
    parser   = require('./mu/parser'),
    renderer = require('./mu/renderer'),
    errors   = require('./mu/errors');

var mu = module.exports = {};

mu.root = process.cwd();
mu.cache = {};

mu.fs = function (filename, callback) {
  filename = filename.indexOf('/') === 0 || filename.indexOf(':\\') === 1 ? filename : path.join(mu.root, filename);
  fs.readFile(filename, 'utf8', callback);
}

/**
 * Compiles a file. The result will be cached as the filename and can be
 * rendered via that name.
 *
 * @param {String} filename The name of the file to compile. If the filename
 *        starts with a '/', the file is assumed to be absolute, else it is
 *        relative to mu.root.
 * @param {Function(err, Parsed)} callback The function to call when the file has been compiled.
 */
mu.compile = function(filename, callback, unique) {
  var parsed,
      unique = unique || {};

  mu.fs(filename, function (err, contents) {
    if (err) {
      return callback(err);
    }

    parsed = parser.parse(contents);
    mu.cache[filename] = [parsed, unique];

    var i = 0;
    (function next(err) {
      if (err) {
        return callback(err);
      }

      if (i < parsed.partials.length) {
        mu.compile(parsed.partials[i], next, unique);
        i++;

      } else {
        callback(undefined, [parsed, {}]);
      }
    }());
  });
}

/**
 * Compiles a string into the parsed form. If `name` is provided the text
 * will be cached by that name. Alternatively you can pass the return
 * into mu.render.
 *
 * @param {String} name (Optional) The name to cache the parsed form as.
 * @param {String} template The template to parse.
 * @param {Function(err, Parsed)} callback (Optional) An optional callback that
 *        will be called when the text is parsed. This is only to unify the
 *        API with mu.compile.
 *
 * @returns {Parsed} The parsed template unless `callback` is provided.
 */
mu.compileText = function (name, template, callback) {
  var parsed;

  if (typeof template === 'undefined') {
    template = name;
    name = undefined;
  }

  try {
    parsed = parser.parse(template);

    if (name) {
      mu.cache[name] = [parsed, {}];
    }

    if (callback) callback(undefined, parsed); else return parsed;

  } catch (err) {
    if (callback) callback(err); else throw err;
  }
}

/**
 * Renders the previously parsed filename or the parsed object.
 *
 * @param {String|Parsed} filenameOrParsed Filename or parsed object to render.
 * @param {Object} view The data to use when renderings.
 *
 * @returns {Stream} The render stream.
 * @throws {Error(template_not_in_cache)} If filename was not found in cache.
 */
mu.render = function (filenameOrParsed, view) {
  var stream,
      parsed = typeof filenameOrParsed === 'object' ?
                  filenameOrParsed :
                  mu.cache[filenameOrParsed];

  if (parsed) {
    return beginRender(parsed[0].tokens, view, mu.cache);
  } else {
    throw new Error('template_not_in_cache'); //errors.templateNotInCache(filename)));
  }
}

mu.renderText = function (template, view, partials) {
  var name, parsed, tokens, stream;

  partials = partials || {};
  partials = shallowCopy(partials);
  partials.__proto__ = mu.cache;

  for (name in partials) {
    if (partials.hasOwnProperty(name) && !partials[name].tokens) {
      partials[name] = parser.parse(partials[name]);
    }
  }

  parsed = parser.parse(template);
  tokens = parsed.tokens;

  return beginRender(tokens, view, partials);
}

/**
 * Clears all of the cache or a specific template.
 *
 * @param templateName String If provided only that template is removed from
 *        the cache, else everything is.
 */
mu.clearCache = function (templateName) {
  if (templateName) {
    delete mu.cache[templateName];
  } else {
    mu.cache = {};
  }
};

/**
 * Compiles the template and then renders it. Uses the cache if it
 * is available.
 *
 * @param templateName String The name of the template to render
 * @param view Object The view object to inject into the template upon render
 * @returns Stream THe output stream
 */
mu.compileAndRender = function (templateName, view) {
  var stream = new Stream();
  var parsed = mu.cache[templateName];

  if (parsed) {
    beginRenderWithStream(parsed[0].tokens, view, mu.cache, stream);
  } else {
    mu.compile(templateName, function (err, parsed) {
      if (err) return stream.emit('error', err);
      beginRenderWithStream(parsed[0].tokens, view, mu.cache, stream);
    });
  }

  return stream;
};

/// Private API

var BUFFER_LENGTH = 1024 * 8;

function beginRender(tokens, view, partials) {
  var stream = new Stream();
  beginRenderWithStream(tokens, view, partials, stream);
  return stream;
}

function beginRenderWithStream(tokens, view, partials, stream) {
  var count = 0;

  process.nextTick(function () {
    try {
      renderer.render(tokens, view, partials, stream, function () {
        stream.emit('end');
      });
    } catch (err) {
      stream.emit('error', err);
    }
  });
}

function shallowCopy(obj) {
  var o = {};

  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      o[key] = obj[key];
    }
  }

  return o;
}
<commit_msg>Making sure the "filenameOrParsed" param of mu.render does not need to be an array.<commit_after>var util     = require('util'),
    fs       = require('fs'),
    path     = require('path'),
    Stream   = require('./mu/stream'),
    parser   = require('./mu/parser'),
    renderer = require('./mu/renderer'),
    errors   = require('./mu/errors');

var mu = module.exports = {};

mu.root = process.cwd();
mu.cache = {};

mu.fs = function (filename, callback) {
  filename = filename.indexOf('/') === 0 || filename.indexOf(':\\') === 1 ? filename : path.join(mu.root, filename);
  fs.readFile(filename, 'utf8', callback);
}

/**
 * Compiles a file. The result will be cached as the filename and can be
 * rendered via that name.
 *
 * @param {String} filename The name of the file to compile. If the filename
 *        starts with a '/', the file is assumed to be absolute, else it is
 *        relative to mu.root.
 * @param {Function(err, Parsed)} callback The function to call when the file has been compiled.
 */
mu.compile = function(filename, callback, unique) {
  var parsed,
      unique = unique || {};

  mu.fs(filename, function (err, contents) {
    if (err) {
      return callback(err);
    }

    parsed = parser.parse(contents);
    mu.cache[filename] = [parsed, unique];

    var i = 0;
    (function next(err) {
      if (err) {
        return callback(err);
      }

      if (i < parsed.partials.length) {
        mu.compile(parsed.partials[i], next, unique);
        i++;

      } else {
        callback(undefined, [parsed, {}]);
      }
    }());
  });
}

/**
 * Compiles a string into the parsed form. If `name` is provided the text
 * will be cached by that name. Alternatively you can pass the return
 * into mu.render.
 *
 * @param {String} name (Optional) The name to cache the parsed form as.
 * @param {String} template The template to parse.
 * @param {Function(err, Parsed)} callback (Optional) An optional callback that
 *        will be called when the text is parsed. This is only to unify the
 *        API with mu.compile.
 *
 * @returns {Parsed} The parsed template unless `callback` is provided.
 */
mu.compileText = function (name, template, callback) {
  var parsed;

  if (typeof template === 'undefined') {
    template = name;
    name = undefined;
  }

  try {
    parsed = parser.parse(template);

    if (name) {
      mu.cache[name] = [parsed, {}];
    }

    if (callback) callback(undefined, parsed); else return parsed;

  } catch (err) {
    if (callback) callback(err); else throw err;
  }
}

/**
 * Renders the previously parsed filename or the parsed object.
 *
 * @param {String|Parsed} filenameOrParsed Filename or parsed object to render.
 * @param {Object} view The data to use when renderings.
 *
 * @returns {Stream} The render stream.
 * @throws {Error(template_not_in_cache)} If filename was not found in cache.
 */
mu.render = function (filenameOrParsed, view) {
  var stream,
      parsed = typeof filenameOrParsed === 'object' ?
                  filenameOrParsed :
                  mu.cache[filenameOrParsed];

  if (parsed) {
    parsed = parsed[0] || parsed;
    return beginRender(parsed.tokens, view, mu.cache);
  } else {
    throw new Error('template_not_in_cache'); //errors.templateNotInCache(filename)));
  }
}

mu.renderText = function (template, view, partials) {
  var name, parsed, tokens, stream;

  partials = partials || {};
  partials = shallowCopy(partials);
  partials.__proto__ = mu.cache;

  for (name in partials) {
    if (partials.hasOwnProperty(name) && !partials[name].tokens) {
      partials[name] = parser.parse(partials[name]);
    }
  }

  parsed = parser.parse(template);
  tokens = parsed.tokens;

  return beginRender(tokens, view, partials);
}

/**
 * Clears all of the cache or a specific template.
 *
 * @param templateName String If provided only that template is removed from
 *        the cache, else everything is.
 */
mu.clearCache = function (templateName) {
  if (templateName) {
    delete mu.cache[templateName];
  } else {
    mu.cache = {};
  }
};

/**
 * Compiles the template and then renders it. Uses the cache if it
 * is available.
 *
 * @param templateName String The name of the template to render
 * @param view Object The view object to inject into the template upon render
 * @returns Stream THe output stream
 */
mu.compileAndRender = function (templateName, view) {
  var stream = new Stream();
  var parsed = mu.cache[templateName];

  if (parsed) {
    beginRenderWithStream(parsed[0].tokens, view, mu.cache, stream);
  } else {
    mu.compile(templateName, function (err, parsed) {
      if (err) return stream.emit('error', err);
      beginRenderWithStream(parsed[0].tokens, view, mu.cache, stream);
    });
  }

  return stream;
};

/// Private API

var BUFFER_LENGTH = 1024 * 8;

function beginRender(tokens, view, partials) {
  var stream = new Stream();
  beginRenderWithStream(tokens, view, partials, stream);
  return stream;
}

function beginRenderWithStream(tokens, view, partials, stream) {
  var count = 0;

  process.nextTick(function () {
    try {
      renderer.render(tokens, view, partials, stream, function () {
        stream.emit('end');
      });
    } catch (err) {
      stream.emit('error', err);
    }
  });
}

function shallowCopy(obj) {
  var o = {};

  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      o[key] = obj[key];
    }
  }

  return o;
}
<|endoftext|>


<commit_before>var isBuffer = require('./is.buffer');

module.exports = function typeOf(value) {
	var type = Object.prototype.toString.call(value).slice(8, -1);
	var name = /^(Object|Error)$/.test(type) && Object(value.constructor).name;
	var nil = type === 'Number' && !isFinite(value) && value.toString();
	var buffer = type === 'Uint8Array' && isBuffer(value) && 'Buffer';
	return name || nil || buffer || type;
};
<commit_msg>test: genfn<commit_after>var isBuffer = require('./is.buffer');

module.exports = function typeOf(value) {
	var type = Object.prototype.toString.call(value).slice(8, -1);
	var name = /^(Object|Error|Function)$/.test(type) && Object(value.constructor).name;
	var nil = type === 'Number' && !isFinite(value) && value.toString();
	var buffer = type === 'Uint8Array' && isBuffer(value) && 'Buffer';
	return name || nil || buffer || type;
};
<|endoftext|>


<commit_before>// Call spamassassin via spamd

// Config is in spamassassin.ini
// Valid keys:
//   reject_threshold=N - score at which to reject the mail
//                        Default: don't reject mail
//   munge_subject_threshold=N - score at which to munge the subject
//                        Default: don't munge the subject
//   subject_prefix=str - prefix to use when munging the subject.
//                        Default: *** SPAM ***
//   spamd_socket=[/path|host:port]
//                      - Default: localhost:783
//   spamd_user=str     - username to pass to spamd
//                        Default: same as current user
//   max_size=N         - don't scan mails bigger than this
//                        Default: 500000
//

var sock = require('./line_socket');

var defaults = {
    spamd_socket: 'localhost:783',
    max_size:     500000
};

exports.hook_data_post = function (callback, connection) {
    var config = this.config.get('spamassassin.ini', 'ini');
    var plugin = this;

    for (var key in defaults) {
        config.main[key] = config.main[key] || defaults[key];
    }

    if (connection.transaction.data_bytes > config.main.max_size) {
        return callback(CONT);
    }

    var socket = new sock.Socket();
    if (config.main.spamd_socket.match(/\//)) {
        // assume unix socket
        socket.connect(config.main.spamd_socket);
    }
    else {
        var hostport = config.main.spamd_socket.split(/:/);
        socket.connect(hostport[1], hostport[0]);
    }

    socket.setTimeout(300 * 1000);

    var username = config.main.spamd_user || process.getuid();

    var data_marker = 0;

    var send_data = function () {
        if (data_marker < connection.transaction.data_lines.length) {
            var wrote_all = socket.write(connection.transaction.data_lines[data_marker]);
            data_marker++;
            if (wrote_all) {
                send_data();
            }
        }
        else {
            socket.end("\r\n");
        }
    };

    socket.on('timeout', function () {
        self.logerror("spamd connection timed out");
        socket.end();
        callback(CONT);
    });
    socket.on('error', function (err) {
        self.logerror("spamd connection failed: " + err);
        // we don't deny on error - maybe another plugin can deliver
        callback(CONT);
    });
    socket.on('connect', function () {
        socket.write("SYMBOLS SPAMC/1.3\r\n", function () {
            socket.write("User: " + username + "\r\n\r\n", function () {
                socket.write("X-Envelope-From: " +
                            connection.transaction.mail_from.replace(/</, '').replace(/>/, '')
                            + "\r\n", function ()
                {
                    send_data();
                });
            });
        });
    });

    var spamd_response = {};
    var state = 'line0';

    socket.on('line', function (line) {
        plugin.logprotocol("SA: " + line);
        line = line.replace(/\r?\n/, '');
        if (state === 'line0') {
            spamd_response.line0 = line;
            state = 'response';
        }
        else if (state === 'response') {
            if (line.match(/\S/)) {
                var matches;
                if (matches = line.match(/Spam: (True|False) ; (-?\d+\.\d) \/ (-?\d+\.\d)/)) {
                    spamd_response.flag = matches[1];
                    spamd_response.hits = matches[2];
                    spamd_response.reqd = matches[3];
                    spamd_response.flag = spamd_response.flag === 'True' ? 'Yes' : 'No'
                }
            }
            else {
                state = 'tests';
            }
        }
        else if (state === 'tests') {
            spamd_response.tests = line;
            socket.destroy();
        }
    });

    socket.on('end', function () {
        // Now we do stuff with the results...

        // TODO: We need to cleanup/remove old headers first, but we don't have
        // the API to do that (yet).

        if (spamd_response.flag === 'Yes') {
            connection.transaction.add_header('X-Spam-Flag', 'YES');
        }
        connection.transaction.add_header('X-Spam-Status', spamd_response.flag +
            ', hits=' + spamd_response.hits + ' required=' + spamd_response.reqd +
            "\n\ttests=" + spamd_response.tests);

        var stars = Math.floor(spamd_response.hits);
        if (stars < 1) stars = 1;
        if (stars > 50) stars = 50;
        var stars_string = '';
        for (var i = 0; i < stars; i++) {
            stars_string += '*';
        }
        connection.transaction.add_header('X-Spam-Level', stars_string);

        if (config.main.reject_threshold && spamd_response.hits >= config.main.reject_threshold) {
            return callback(DENY, "spam score exceeded threshold");
        }
        else if (config.main.munge_subject_threshold && spamd_response.hits >= config.main.munge_subject_threshold) {
            // munge the subject - TODO once we have a way to do that.
        }
        callback(CONT);
    });

};
<commit_msg>Fixed the DENY code<commit_after>// Call spamassassin via spamd

// Config is in spamassassin.ini
// Valid keys:
//   reject_threshold=N - score at which to reject the mail
//                        Default: don't reject mail
//   munge_subject_threshold=N - score at which to munge the subject
//                        Default: don't munge the subject
//   subject_prefix=str - prefix to use when munging the subject.
//                        Default: *** SPAM ***
//   spamd_socket=[/path|host:port]
//                      - Default: localhost:783
//   spamd_user=str     - username to pass to spamd
//                        Default: same as current user
//   max_size=N         - don't scan mails bigger than this
//                        Default: 500000
//

var sock = require('./line_socket');

var defaults = {
    spamd_socket: 'localhost:783',
    max_size:     500000
};

exports.hook_data_post = function (callback, connection) {
    var config = this.config.get('spamassassin.ini', 'ini');
    var plugin = this;

    for (var key in defaults) {
        config.main[key] = config.main[key] || defaults[key];
    }

    ['reject_threshold', 'munge_subject_threshold', 'max_size'].forEach(
        function (item) {
            if (config.main[item]) {
                config.main[item] = new Number(config.main[item]);
            }
        }
    );

    if (connection.transaction.data_bytes > config.main.max_size) {
        return callback(CONT);
    }

    var socket = new sock.Socket();
    if (config.main.spamd_socket.match(/\//)) {
        // assume unix socket
        socket.connect(config.main.spamd_socket);
    }
    else {
        var hostport = config.main.spamd_socket.split(/:/);
        socket.connect(hostport[1], hostport[0]);
    }

    socket.setTimeout(300 * 1000);

    var username = config.main.spamd_user || process.getuid();

    var data_marker = 0;

    var send_data = function () {
        if (data_marker < connection.transaction.data_lines.length) {
            var wrote_all = socket.write(connection.transaction.data_lines[data_marker]);
            data_marker++;
            if (wrote_all) {
                send_data();
            }
        }
        else {
            socket.end("\r\n");
        }
    };

    socket.on('timeout', function () {
        self.logerror("spamd connection timed out");
        socket.end();
        callback(CONT);
    });
    socket.on('error', function (err) {
        self.logerror("spamd connection failed: " + err);
        // we don't deny on error - maybe another plugin can deliver
        callback(CONT);
    });
    socket.on('connect', function () {
        socket.write("SYMBOLS SPAMC/1.3\r\n", function () {
            socket.write("User: " + username + "\r\n\r\n", function () {
                socket.write("X-Envelope-From: " +
                            connection.transaction.mail_from.replace(/</, '').replace(/>/, '')
                            + "\r\n", function ()
                {
                    send_data();
                });
            });
        });
    });

    var spamd_response = {};
    var state = 'line0';

    socket.on('line', function (line) {
        plugin.logprotocol("SA: " + line);
        line = line.replace(/\r?\n/, '');
        if (state === 'line0') {
            spamd_response.line0 = line;
            state = 'response';
        }
        else if (state === 'response') {
            if (line.match(/\S/)) {
                var matches;
                if (matches = line.match(/Spam: (True|False) ; (-?\d+\.\d) \/ (-?\d+\.\d)/)) {
                    spamd_response.flag = matches[1];
                    spamd_response.hits = matches[2];
                    spamd_response.reqd = matches[3];
                    spamd_response.flag = spamd_response.flag === 'True' ? 'Yes' : 'No'
                }
            }
            else {
                state = 'tests';
            }
        }
        else if (state === 'tests') {
            spamd_response.tests = line;
            socket.destroy();
        }
    });

    socket.on('end', function () {
        // Now we do stuff with the results...

        // TODO: We need to cleanup/remove old headers first, but we don't have
        // the API to do that (yet).

        if (spamd_response.flag === 'Yes') {
            connection.transaction.add_header('X-Spam-Flag', 'YES');
        }
        connection.transaction.add_header('X-Spam-Status', spamd_response.flag +
            ', hits=' + spamd_response.hits + ' required=' + spamd_response.reqd +
            "\n\ttests=" + spamd_response.tests);

        var stars = Math.floor(spamd_response.hits);
        if (stars < 1) stars = 1;
        if (stars > 50) stars = 50;
        var stars_string = '';
        for (var i = 0; i < stars; i++) {
            stars_string += '*';
        }
        connection.transaction.add_header('X-Spam-Level', stars_string);

        plugin.loginfo("spamassassin returned: " + spamd_response.flag + ', ' +
            spamd_response.hits + '/' + spamd_response.reqd +
            " Reject at: " + config.main.reject_threshold);

        if (config.main.reject_threshold && (spamd_response.hits >= config.main.reject_threshold)) {
            return callback(DENY, "spam score exceeded threshold");
        }
        else if (config.main.munge_subject_threshold && (spamd_response.hits >= config.main.munge_subject_threshold)) {
            // munge the subject - TODO once we have a way to do that.
        }
        callback(CONT);
    });

};
<|endoftext|>


<commit_before>/*
  Quick and dirty methods for some S3 action
*/

var Belt = require('jsbelt')
  , Async = require('async')
  , Path = require('path')
  , AWS = require('aws-sdk')
  , _ = require('underscore')
  , FS = require('fs')
  , Zlib = require('zlib');

(function(){

  var S3 = function(O){
    var S = {};

    S.settings = Belt.extend({}, O);
    S._API = new AWS.S3(S.settings);

    S['fileExists'] = function(bucket, path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {

      });

      return Async.waterfall([
        function(cb){
          var req = {
            'Bucket': bucket
          , 'Key': path
          };

          return S._API.headObject(req, Belt.callset(cb, globals, 'response', 1));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback((err || !Belt._get(globals, 'response.LastModified')) ? false : true);
      });
    };

    S['getFile'] = function(options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {
        'encoding': 'utf8'
      });

      return Async.waterfall([
        function(cb){
          var req = {
            'Bucket': options.Bucket || options.bucket
          , 'Key': options.Key || options.key || options.path
          };

          return S._API.getObject(req, Belt.callset(cb, globals, 'response', 1, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err, a.options.return_buffer ? Belt._get(globals, 'response.Body')
                                                       : Belt._call(globals, 'response.Body.toString', a.options.encoding));
      });
    };

    S['writeFile'] = function(bucket, path, body_str, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {
        'encoding': 'utf8'
      , 'acl': 'private'
      });

      return Async.waterfall([
        function(cb){
          var req = Belt.extend({
            'Bucket': bucket
          , 'Key': path
          , 'ACL': a.options.acl
          , 'Body': body_str
          , 'ContentEncoding': a.options.encoding
          }, _.pick(a.options, ['Metadata']));

          return S._API.putObject(req, Belt.callwrap(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['upload'] = function(options, callback){
      var a = Belt.argulint(arguments)
        , self = this;

      a.o = _.defaults(a.o, {
        'ACL': 'private'
      , 'ContentEncoding': 'utf8'
      // Bucket
      // Key
      // infile
      , 'StorageClass': 'REDUCED_REDUNDANCY'
      , 'Body': FS.createReadStream(a.o.infile)
      , 'wait_int': false
      , 'queueSize': 10
      , 'minPartSize': 1024 * 1024 * 10
      });

      var ocb = _.once(a.cb)
        , timer = a.o.wait_int ? setTimeout(function(){
          return ocb(new Error('timeout'));
        }, a.o.wait_int) : undefined;

      return self._API.upload(_.omit(a.o, ['queueSize', 'maxTotalParts', 'minPartSize', 'wait_int', 'infile', 'progress'])
      , _.pick(['queueSize', 'maxTotalParts', 'minPartSize']))
      .on('error', ocb)
      .on('httpUploadProgress', function(stats){
         if (timer){
           clearTimeout(timer);
           timer = setTimeout(function(){
             return ocb(new Error('timeout'));
           }, a.o.wait_int);
         }

         if (process.env.VERBOSE && !a.o.progress) console.log(((stats.loaded / stats.total) * 100).toFixed(2));
         if (a.o.progress) a.o.progress(stats);
         return;
      })
      .send(function(){
        if (timer) clearTimeout(timer);
        return ocb.apply(this, arguments);
      });
    };

    S['uploadDir'] = function(options, callback){
      var a = Belt.argulint(arguments)
        , self = this;
      a.o = _.defaults(a.o, {

      });
    };

    S['copyFile'] = function(bucket, path, dest_bucket, dest_path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {
        'encoding': 'utf8'
      , 'acl': 'private'
      });

      return Async.waterfall([
        function(cb){
          var req = {
            'CopySource': bucket + '/' + path
          , 'Bucket': dest_bucket
          , 'Key': dest_path
          , 'ACL': a.options.acl
          , 'ContentEncoding': a.options.encoding
          };

          return S._API.copyObject(req, Belt.callwrap(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['deleteFiles'] = function(bucket, paths, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {

      });

      var objs = Belt.toArray(paths);
      if (!_.any(objs)) return a.callback();

      return Async.waterfall([
        function(cb){
          var req = {
            'Bucket': bucket
          , 'Delete': {'Objects': _.map(objs, function(o){ return {'Key': o}; })}
          };

          return S._API.deleteObjects(req, Belt.callwrap(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['deleteFile'] = S['deleteFiles']; //alias

    S['moveFile'] = function(bucket, path, dest_bucket, dest_path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {

      });

      return Async.waterfall([
        function(cb){
          return S.copyFile(bucket, path, dest_bucket, dest_path, a.options, Belt.callwrap(cb, 0));
        }
      , function(cb){
          return S.deleteFiles(bucket, path, a.options, Belt.callwrap(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['readdir'] = function(bucket, path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {

      });

      var req = {
        'Bucket': bucket
      , 'Prefix': Belt._call(path, 'match',/\/$/) ? path : path + '/'
      };

      return Async.waterfall([
        function(cb){
          return S._API.listObjects(req, Belt.callset(cb, globals, 'response', 1, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err, _.chain(Belt._get(globals, 'response.Contents') || [])
                                .pluck('Key')
                                .map(function(d){ return Belt._call(d, 'replace', new RegExp('^' + req.Prefix), ''); })
                                .value()
                         );
      });
    };

    S['dirExists'] = function(bucket, path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {

      });

      return Async.waterfall([
        function(cb){
          return S.readdir(bucket, path, a.options, Belt.callset(cb, globals, 'files', 1, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err || !_.any(globals.files) ? false: true);
      });
    };

    S['rmdir'] = function(bucket, path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {

      });

      return Async.waterfall([
        function(cb){
          return S.readdir(bucket, path, Belt.callset(cb, globals, 'keys', 1, 0));
        }
      , function(cb){
          return Async.eachLimit(_.map(globals.keys, function(k){ return path +'/' + k; }), 20, function(p, _cb){
            return S.deleteFiles(bucket, p, Belt.callwrap(_cb, 0));
          }, Belt.cw(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['getGzipFile'] = function(bucket, path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {
        'encoding': 'utf8'
      , 'acl': 'private'
      });

      return Async.doWhilst(function(next){
        return Async.waterfall([
          function(cb){
            return S.readdir(bucket, path, Belt.callset(cb, globals, 'keys', 1, 0));
          }
        , function(cb){
            return Async.eachLimit(_.map(globals.keys, function(k){ return path +'/' + k; }), 20, function(p, _cb){
              return S.deleteFiles(bucket, p, Belt.callwrap(_cb, 0));
            }, Belt.cw(cb, 0));
          }
        , function(cb){
            return S.readdir(bucket, path, Belt.callset(cb, globals, 'keys', 1, 0));
          }
        ], next);
      }, function(){
        return _.any(globals.keys);
      }, function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['writeGzipFile'] = function(bucket, path, body_str, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {
        'encoding': 'utf8'
      , 'acl': 'private'
      });

      return Async.waterfall([
        function(cb){
          var buf = new Buffer(body_str, a.options.encoding);
          return Zlib.gzip(buf, Belt.callset(cb, globals, 'gzip', 1, 0));
        }
      , function(cb){
          return S.writeFile(bucket, path, globals.gzip, a.options, Belt.callwrap(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['getJSON'] = function(bucket, path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {
        'encoding': 'utf8'
      , 'acl': 'private'
      });

      return Async.waterfall([
        function(cb){
          return S.getFile(bucket, path, a.options, Belt.callset(cb, globals, 'body', 1, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err, JSON.parse(Belt._call(globals, 'body.toString', a.options.encoding)));
      });
    };

    S['writeJSON'] = function(bucket, path, json, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {
        'encoding': 'utf8'
      , 'acl': 'private'
      });

      return Async.waterfall([
        function(cb){
          return S.writeFile(bucket, path, JSON.stringify(json, null, 2), a.options, Belt.callwrap(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['listAllObjects'] = function(options, callback){
      var a = Belt.argulint(arguments)
        , self = this;
      a.o = _.defaults(a.o, {
        'MaxKeys': 1000
        //Prefix
      });

      var res, objs = [];
      return Async.doWhilst(function(next){
        var mark = Belt.get(res, 'NextMarker');
        a.o.Marker = mark;
        return self._API.listObjects(a.o, function(err, _res){
          res = _res;
          if (err) return next(err);
          objs = objs.concat(Belt.get(_res, 'Contents') || []);
          return next();
        });
      }, function(){ return res && res.IsTruncated; }
      , function(err){
        return a.cb(err, objs);
      });
    };

    S['listAllObjectVersions'] = function(options, callback){
      var a = Belt.argulint(arguments)
        , self = this;
      a.o = _.defaults(a.o, {
        'MaxKeys': 1000
        //Prefix
      });

      var res, objs = [];
      return Async.doWhilst(function(next){
        var mark = Belt.get(res, 'NextKeyMarker');
        a.o.KeyMarker = mark;
        return self._API.listObjectVersions(a.o, function(err, _res){
          res = _res;
          if (err) next(err);
          objs = objs.concat(_res.Versions || []);
          return next();
        });
      }, function(){ return res && res.IsTruncated; }
      , function(err){
        return a.cb(err, objs);
      });
    };

    S['deleteBucket'] = function(options, callback){
      var a = Belt.argulint(arguments)
        , self = this;
      a.o = _.defaults(a.o, {

      });

      var gb = {};
      return Async.waterfall([
        function(cb){
          return self.listAllObjectVersions(Belt.copy(a.o), Belt.cs(cb, gb, 'keys', 1, 0));
        }
      , function(cb){
          if (!_.any(gb.keys)) return cb();
          console.log(gb.keys.length);
          return Async.eachSeries(Belt.splitArray(gb.keys, 100), function(k, _cb){
            return self._API.deleteObjects({
              'Bucket': a.o.Bucket
            , 'Delete': {
                'Objects': _.map(k, function(_k){ return _.pick(_k, ['Key', 'VersionId']); })
              }
            }, function(err){ if (err) console.log(err); process.stdout.write('.'); return _cb(); });
          }, Belt.cw(cb, 0));
        }
      , function(cb){
          return self._API.deleteBucket(a.o, Belt.cw(cb, 0));
        }
      ], a.cb);
    };

    return S;
  };

  return module.exports = S3;

}).call(this);
<commit_msg>added better getgzip s3 method<commit_after>/*
  Quick and dirty methods for some S3 action
*/

var Belt = require('jsbelt')
  , Async = require('async')
  , Path = require('path')
  , AWS = require('aws-sdk')
  , _ = require('underscore')
  , FS = require('fs')
  , Zlib = require('zlib');

(function(){

  var S3 = function(O){
    var S = {};

    S.settings = Belt.extend({}, O);
    S._API = new AWS.S3(S.settings);

    S['fileExists'] = function(bucket, path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {

      });

      return Async.waterfall([
        function(cb){
          var req = {
            'Bucket': bucket
          , 'Key': path
          };

          return S._API.headObject(req, Belt.callset(cb, globals, 'response', 1));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback((err || !Belt._get(globals, 'response.LastModified')) ? false : true);
      });
    };

    S['getFile'] = function(options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {
        'encoding': 'utf8'
      });

      return Async.waterfall([
        function(cb){
          var req = {
            'Bucket': options.Bucket || options.bucket
          , 'Key': options.Key || options.key || options.path
          };

          return S._API.getObject(req, Belt.callset(cb, globals, 'response', 1, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err, a.options.return_buffer ? Belt._get(globals, 'response.Body')
                                                       : Belt._call(globals, 'response.Body.toString', a.options.encoding));
      });
    };

    S['writeFile'] = function(bucket, path, body_str, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {
        'encoding': 'utf8'
      , 'acl': 'private'
      });

      return Async.waterfall([
        function(cb){
          var req = Belt.extend({
            'Bucket': bucket
          , 'Key': path
          , 'ACL': a.options.acl
          , 'Body': body_str
          , 'ContentEncoding': a.options.encoding
          }, _.pick(a.options, ['Metadata']));

          return S._API.putObject(req, Belt.callwrap(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['upload'] = function(options, callback){
      var a = Belt.argulint(arguments)
        , self = this;

      a.o = _.defaults(a.o, {
        'ACL': 'private'
      , 'ContentEncoding': 'utf8'
      // Bucket
      // Key
      // infile
      , 'StorageClass': 'REDUCED_REDUNDANCY'
      , 'Body': FS.createReadStream(a.o.infile)
      , 'wait_int': false
      , 'queueSize': 10
      , 'minPartSize': 1024 * 1024 * 10
      });

      var ocb = _.once(a.cb)
        , timer = a.o.wait_int ? setTimeout(function(){
          return ocb(new Error('timeout'));
        }, a.o.wait_int) : undefined;

      return self._API.upload(_.omit(a.o, ['queueSize', 'maxTotalParts', 'minPartSize', 'wait_int', 'infile', 'progress'])
      , _.pick(['queueSize', 'maxTotalParts', 'minPartSize']))
      .on('error', ocb)
      .on('httpUploadProgress', function(stats){
         if (timer){
           clearTimeout(timer);
           timer = setTimeout(function(){
             return ocb(new Error('timeout'));
           }, a.o.wait_int);
         }

         if (process.env.VERBOSE && !a.o.progress) console.log(((stats.loaded / stats.total) * 100).toFixed(2));
         if (a.o.progress) a.o.progress(stats);
         return;
      })
      .send(function(){
        if (timer) clearTimeout(timer);
        return ocb.apply(this, arguments);
      });
    };

    S['uploadDir'] = function(options, callback){
      var a = Belt.argulint(arguments)
        , self = this;
      a.o = _.defaults(a.o, {

      });
    };

    S['copyFile'] = function(bucket, path, dest_bucket, dest_path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {
        'encoding': 'utf8'
      , 'acl': 'private'
      });

      return Async.waterfall([
        function(cb){
          var req = {
            'CopySource': bucket + '/' + path
          , 'Bucket': dest_bucket
          , 'Key': dest_path
          , 'ACL': a.options.acl
          , 'ContentEncoding': a.options.encoding
          };

          return S._API.copyObject(req, Belt.callwrap(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['deleteFiles'] = function(bucket, paths, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {

      });

      var objs = Belt.toArray(paths);
      if (!_.any(objs)) return a.callback();

      return Async.waterfall([
        function(cb){
          var req = {
            'Bucket': bucket
          , 'Delete': {'Objects': _.map(objs, function(o){ return {'Key': o}; })}
          };

          return S._API.deleteObjects(req, Belt.callwrap(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['deleteFile'] = S['deleteFiles']; //alias

    S['moveFile'] = function(bucket, path, dest_bucket, dest_path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {

      });

      return Async.waterfall([
        function(cb){
          return S.copyFile(bucket, path, dest_bucket, dest_path, a.options, Belt.callwrap(cb, 0));
        }
      , function(cb){
          return S.deleteFiles(bucket, path, a.options, Belt.callwrap(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['readdir'] = function(bucket, path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {

      });

      var req = {
        'Bucket': bucket
      , 'Prefix': Belt._call(path, 'match',/\/$/) ? path : path + '/'
      };

      return Async.waterfall([
        function(cb){
          return S._API.listObjects(req, Belt.callset(cb, globals, 'response', 1, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err, _.chain(Belt._get(globals, 'response.Contents') || [])
                                .pluck('Key')
                                .map(function(d){ return Belt._call(d, 'replace', new RegExp('^' + req.Prefix), ''); })
                                .value()
                         );
      });
    };

    S['dirExists'] = function(bucket, path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {

      });

      return Async.waterfall([
        function(cb){
          return S.readdir(bucket, path, a.options, Belt.callset(cb, globals, 'files', 1, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err || !_.any(globals.files) ? false: true);
      });
    };

    S['rmdir'] = function(bucket, path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {

      });

      return Async.waterfall([
        function(cb){
          return S.readdir(bucket, path, Belt.callset(cb, globals, 'keys', 1, 0));
        }
      , function(cb){
          return Async.eachLimit(_.map(globals.keys, function(k){ return path +'/' + k; }), 20, function(p, _cb){
            return S.deleteFiles(bucket, p, Belt.callwrap(_cb, 0));
          }, Belt.cw(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['getGzipFile'] = function(options, callback){
      var a = Belt.argulint(arguments)
        , self = this;
      a.o = _.defaults(a.o, {

      });

      var gb = {};
      return Async.waterfall([
        function(cb){
          return self.getFile(a.o, Belt.cs(cb, gb, 'buf', 1, 0));
        }
      , function(cb){
          return Zlib.gunzip(gb.buf, Belt.callset(cb, gb, 'file', 1, 0));
        }
      ], function(err){
        return a.cb(err, gb.file);
      });

    };

    S['writeGzipFile'] = function(bucket, path, body_str, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {
        'encoding': 'utf8'
      , 'acl': 'private'
      });

      return Async.waterfall([
        function(cb){
          var buf = new Buffer(body_str, a.options.encoding);
          return Zlib.gzip(buf, Belt.callset(cb, globals, 'gzip', 1, 0));
        }
      , function(cb){
          return S.writeFile(bucket, path, globals.gzip, a.options, Belt.callwrap(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['getJSON'] = function(bucket, path, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {
        'encoding': 'utf8'
      , 'acl': 'private'
      });

      return Async.waterfall([
        function(cb){
          return S.getFile(bucket, path, a.options, Belt.callset(cb, globals, 'body', 1, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err, JSON.parse(Belt._call(globals, 'body.toString', a.options.encoding)));
      });
    };

    S['writeJSON'] = function(bucket, path, json, options, callback){
      var a = Belt.argulint(arguments)
        , globals = {};

      a.options = _.defaults(a.options, {
        'encoding': 'utf8'
      , 'acl': 'private'
      });

      return Async.waterfall([
        function(cb){
          return S.writeFile(bucket, path, JSON.stringify(json, null, 2), a.options, Belt.callwrap(cb, 0));
        }
      ], function(err){
        if (err) console.error(err);
        return a.callback(err);
      });
    };

    S['listAllObjects'] = function(options, callback){
      var a = Belt.argulint(arguments)
        , self = this;
      a.o = _.defaults(a.o, {
        'MaxKeys': 1000
        //Prefix
      });

      var res, objs = [];
      return Async.doWhilst(function(next){
        var mark = Belt.get(res, 'NextMarker');
        a.o.Marker = mark;
        return self._API.listObjects(a.o, function(err, _res){
          res = _res;
          if (err) return next(err);
          objs = objs.concat(Belt.get(_res, 'Contents') || []);
          return next();
        });
      }, function(){ return res && res.IsTruncated; }
      , function(err){
        return a.cb(err, objs);
      });
    };

    S['listAllObjectVersions'] = function(options, callback){
      var a = Belt.argulint(arguments)
        , self = this;
      a.o = _.defaults(a.o, {
        'MaxKeys': 1000
        //Prefix
      });

      var res, objs = [];
      return Async.doWhilst(function(next){
        var mark = Belt.get(res, 'NextKeyMarker');
        a.o.KeyMarker = mark;
        return self._API.listObjectVersions(a.o, function(err, _res){
          res = _res;
          if (err) next(err);
          objs = objs.concat(_res.Versions || []);
          return next();
        });
      }, function(){ return res && res.IsTruncated; }
      , function(err){
        return a.cb(err, objs);
      });
    };

    S['deleteBucket'] = function(options, callback){
      var a = Belt.argulint(arguments)
        , self = this;
      a.o = _.defaults(a.o, {

      });

      var gb = {};
      return Async.waterfall([
        function(cb){
          return self.listAllObjectVersions(Belt.copy(a.o), Belt.cs(cb, gb, 'keys', 1, 0));
        }
      , function(cb){
          if (!_.any(gb.keys)) return cb();
          console.log(gb.keys.length);
          return Async.eachSeries(Belt.splitArray(gb.keys, 100), function(k, _cb){
            return self._API.deleteObjects({
              'Bucket': a.o.Bucket
            , 'Delete': {
                'Objects': _.map(k, function(_k){ return _.pick(_k, ['Key', 'VersionId']); })
              }
            }, function(err){ if (err) console.log(err); process.stdout.write('.'); return _cb(); });
          }, Belt.cw(cb, 0));
        }
      , function(cb){
          return self._API.deleteBucket(a.o, Belt.cw(cb, 0));
        }
      ], a.cb);
    };

    return S;
  };

  return module.exports = S3;

}).call(this);
<|endoftext|>


<commit_before>'use babel'

import {React, ReactDOM} from 'react-for-atom'
import Bacon from 'baconjs'
import R from 'ramda'
import PanelComponent from './react/panel-component'

// UI class, representing the presentation logic
//
// Note that all logic here should not have any traces or references to the atom.* objects.
// Basically it should be able to work in any context, least the provided streams fulfills the contract.
export default class UI {

  constructor ({domNode, panelHeightStream, isLoadingFilesProp, filesProp, resultsProp, focusOnSearchStream, deselectStream}) {
    this._domNode = domNode

    const keyDownBus = keyDownBus || new Bacon.Bus()
    keyDownBus.doLog('[UI keydownBus]')
    const escKeyStream = keyDownBus.filter(R.propEq('keyCode', 27))
    const downKeyStream = keyDownBus.filter(R.propEq('keyCode', 40)).doAction('.preventDefault')
    const upKeyStream = keyDownBus.filter(R.propEq('keyCode', 38)).doAction('.preventDefault')
    const selectIndexBus = new Bacon.Bus()

    // Selection is a semi-complicated piece; Keep an internal datastucture that keep tabs on path/item/index,
    // to be able to re-calculate them on state changes
    const selectedProp = Bacon
      .update(
        {},
        [deselectStream], R.always({}),
        [escKeyStream], R.always({}),

        // Selected next item
        [downKeyStream, filesProp, resultsProp], (current, _, files, {items}) => {
          const i = R.defaultTo(-1, current.index) + 1 // start on first item
          const nextItem = items[i]
          if (nextItem) {
            return {
              item: nextItem,
              path: files[nextItem.id].path,
              index: i
            }
          } else {
            return current // stop when reaching last item
          }
        },
        // Select prev item
        [upKeyStream, filesProp, resultsProp], (current, _, files, {items}) => {
          const i = R.defaultTo(items.length, current.index) - 1 // start on last item
          const prevItem = items[i]
          if (prevItem) {
            return {
              item: prevItem,
              path: files[prevItem.id].path,
              index: i
            }
          } else {
            return current // stop when reaching first item
          }
        },
        // User clicks on one of the visible item, change to that one directly
        [selectIndexBus, filesProp, resultsProp], (_, i, files, {items}) => {
          const item = items[i]
          return {
            item: item,
            path: files[item.id].path,
            index: i
          }
        },
        // Files changes (add/delete/order), update index from known path from current
        [filesProp.changes(), resultsProp], (current, files, {items}) => {
          const currentPath = current.path
          if (currentPath) {
            const i = items.findIndex(({id}) => files[id].path === currentPath)
            return {
              item: items[i],
              path: current.path,
              index: i
            }
          } else {
            return current // stay with current
          }
        }
      ).skipDuplicates()

    const reactPanel = ReactDOM.render(
      <PanelComponent focusOnSearchStream={focusOnSearchStream.merge(selectIndexBus.map(true))}
        panelHeightStream={panelHeightStream}
        filesProp={filesProp}
        resultsProp={resultsProp}
        isLoadingFilesProp={isLoadingFilesProp}
        selectIndexBus={selectIndexBus}
        selectedIndexStream={selectedProp.changes().map('.index')}
        keyDownBus={keyDownBus}
      />, this._domNode)

    this.panelHeightProp = reactPanel.panelHeightProp
    this.searchProp = reactPanel.searchProp
    this.selectedFileProp = selectedProp
      .combine(filesProp, (selected, files) => {
        const id = R.path(['item', 'id'], selected)
        return files[id]
      })

    const enterKeyStream = keyDownBus
      .filter(R.propEq('keyCode', 13))
      .doLog('[UI_enterKeyStream]')
    const selectedFilePathOnEnterStream = this.selectedFileProp
      .map('.path')
      .skipDuplicates()
      .sampledBy(enterKeyStream)
      .doLog('[UI_selectedFilePathOnEnterStream]')

    this.openFileStream = selectedFilePathOnEnterStream
      .filter(R.identity)
      .doLog('[UI.openFileStream]')

    this.createFileStream = Bacon
      .when(
        [selectedFilePathOnEnterStream.filter(R.not), this.searchProp],
        (_, searchStr) => {
          return searchStr.trim()
        }
      )
      .doLog('[UI.createFileStream]')

    // Just for testing purposes
    this._reactPanel = reactPanel
  }

  dispose () {
    ReactDOM.unmountComponentAtNode(this._domNode)
    this._domNode = null
  }
}
<commit_msg>:fire: Unused var<commit_after>'use babel'

import {React, ReactDOM} from 'react-for-atom'
import Bacon from 'baconjs'
import R from 'ramda'
import PanelComponent from './react/panel-component'

// UI class, representing the presentation logic
//
// Note that all logic here should not have any traces or references to the atom.* objects.
// Basically it should be able to work in any context, least the provided streams fulfills the contract.
export default class UI {

  constructor ({domNode, panelHeightStream, isLoadingFilesProp, filesProp, resultsProp, focusOnSearchStream, deselectStream}) {
    this._domNode = domNode

    const keyDownBus = new Bacon.Bus()
    keyDownBus.doLog('[UI keydownBus]')
    const escKeyStream = keyDownBus.filter(R.propEq('keyCode', 27))
    const downKeyStream = keyDownBus.filter(R.propEq('keyCode', 40)).doAction('.preventDefault')
    const upKeyStream = keyDownBus.filter(R.propEq('keyCode', 38)).doAction('.preventDefault')
    const selectIndexBus = new Bacon.Bus()

    // Selection is a semi-complicated piece; Keep an internal datastucture that keep tabs on path/item/index,
    // to be able to re-calculate them on state changes
    const selectedProp = Bacon
      .update(
        {},
        [deselectStream], R.always({}),
        [escKeyStream], R.always({}),

        // Selected next item
        [downKeyStream, filesProp, resultsProp], (current, _, files, {items}) => {
          const i = R.defaultTo(-1, current.index) + 1 // start on first item
          const nextItem = items[i]
          if (nextItem) {
            return {
              item: nextItem,
              path: files[nextItem.id].path,
              index: i
            }
          } else {
            return current // stop when reaching last item
          }
        },
        // Select prev item
        [upKeyStream, filesProp, resultsProp], (current, _, files, {items}) => {
          const i = R.defaultTo(items.length, current.index) - 1 // start on last item
          const prevItem = items[i]
          if (prevItem) {
            return {
              item: prevItem,
              path: files[prevItem.id].path,
              index: i
            }
          } else {
            return current // stop when reaching first item
          }
        },
        // User clicks on one of the visible item, change to that one directly
        [selectIndexBus, filesProp, resultsProp], (_, i, files, {items}) => {
          const item = items[i]
          return {
            item: item,
            path: files[item.id].path,
            index: i
          }
        },
        // Files changes (add/delete/order), update index from known path from current
        [filesProp.changes(), resultsProp], (current, files, {items}) => {
          const currentPath = current.path
          if (currentPath) {
            const i = items.findIndex(({id}) => files[id].path === currentPath)
            return {
              item: items[i],
              path: current.path,
              index: i
            }
          } else {
            return current // stay with current
          }
        }
      ).skipDuplicates()

    const reactPanel = ReactDOM.render(
      <PanelComponent focusOnSearchStream={focusOnSearchStream.merge(selectIndexBus.map(true))}
        panelHeightStream={panelHeightStream}
        filesProp={filesProp}
        resultsProp={resultsProp}
        isLoadingFilesProp={isLoadingFilesProp}
        selectIndexBus={selectIndexBus}
        selectedIndexStream={selectedProp.changes().map('.index')}
        keyDownBus={keyDownBus}
      />, this._domNode)

    this.panelHeightProp = reactPanel.panelHeightProp
    this.searchProp = reactPanel.searchProp
    this.selectedFileProp = selectedProp
      .combine(filesProp, (selected, files) => {
        const id = R.path(['item', 'id'], selected)
        return files[id]
      })

    const enterKeyStream = keyDownBus
      .filter(R.propEq('keyCode', 13))
      .doLog('[UI_enterKeyStream]')
    const selectedFilePathOnEnterStream = this.selectedFileProp
      .map('.path')
      .skipDuplicates()
      .sampledBy(enterKeyStream)
      .doLog('[UI_selectedFilePathOnEnterStream]')

    this.openFileStream = selectedFilePathOnEnterStream
      .filter(R.identity)
      .doLog('[UI.openFileStream]')

    this.createFileStream = Bacon
      .when(
        [selectedFilePathOnEnterStream.filter(R.not), this.searchProp],
        (_, searchStr) => {
          return searchStr.trim()
        }
      )
      .doLog('[UI.createFileStream]')

    // Just for testing purposes
    this._reactPanel = reactPanel
  }

  dispose () {
    ReactDOM.unmountComponentAtNode(this._domNode)
    this._domNode = null
  }
}
<|endoftext|>