<commit_before>var Promise = require('promise');

var PARSE_ERROR_CODE = -32700,
  INVALID_REQUEST_CODE = -32600,
  METHOD_NOT_FOUND_CODE = -32601,
  INVALID_PARAMS_CODE = -32602,
  INTERNAL_ERROR_CODE = -32603;

/** @constant */
ERROR_MESSAGES = {
  '-32700': 'Parse error',
  '-32600': 'Invalid request',
  '-32601': 'Method not found',
  '-32602': 'Invalid parameters',
  '-32603': 'Internal error'
  //-32099 to -32000 are open for use
};

function JsonRpcError(code, message, data){
  this.name = JsonRpcError.name;
  this.code = code || INTERNAL_ERROR_CODE;
  this.message = message || ERROR_MESSAGES[code] || '';
  this.data = data;
}
JsonRpcError.prototype = Error.prototype;
JsonRpcError.name = 'JsonRpcError';

var PARSE_ERROR = new JsonRpcError(-32700),
    INVALID_REQUEST = new JsonRpcError(-32600),
    METHOD_NOT_FOUND = new JsonRpcError(-32601),
    INVALID_PARAMS = new JsonRpcError(-32602),
    INTERNAL_ERROR = new JsonRpcError(-32603);

function isValidRequest(rpc) {
  //or params must be absent, an array or object
  var params = rpc.params;
  //isNaN shouldn't be used for rpc.id
  return rpc.jsonrpc === '2.0' && typeof rpc.method ==='string' && (!rpc.hasOwnProperty('id') || typeof rpc.id === 'number')
    && (typeof params === 'undefined' || (params && typeof params === 'object'));
}

function makeResult(request, responseObj) {
  if (!request.hasOwnProperty('id')) //it's a notification
    return null;

  var response = {
    jsonrpc: '2.0',
    id: request.id
  };
  if (responseObj instanceof JsonRpcError) {
    response.error = {
      code: responseObj.code,
      message: responseObj.message,
      data: responseObj.data
    };
  } else {
    response.result = responseObj;
  }
  return response;
}

function setup(body, methods, nodeReq) {
  var reqs = Array.isArray(body) ? body : [body];
  return reqs.reduce(function(promisesSoFar, req) {
    var p = null;
    if (!isValidRequest(req)) {
      p = Promise.resolve(makeResult(req, INVALID_REQUEST));
    } else {
      var method = methods[req.method];
      if (!method) {
        p = Promise.resolve(makeResult(req, METHOD_NOT_FOUND));
      } else {
        p = new Promise(function (resolve, reject) {
          try {
            var respond = function(response) {
              resolve(makeResult(req, response));
            }
            var ret = method.call(methods, req.params, respond, promisesSoFar.slice(0), nodeReq); //copy array to record current state in order to avoid deadlock
            if (typeof ret !== 'undefined') {
              if (typeof ret.then === 'function') //it's a promise
                resolve( ret.then(function(result) {return makeResult(req, result)}) );
              else
                respond(ret); //synchronous call
            }
          } catch (e) {
              console.log("unexpected error from jsonrpc method call:", req.method, "params:", req.params);
              console.log(e.stack);
              resolve(makeResult(req, INTERNAL_ERROR));
          }
        });
      }
    }
    p.request = req;
    promisesSoFar.push(p);
    return promisesSoFar;
  }, []);
}

/**
@param {Object or Array} [body] The jsonrpc request
@param {Object} [methods] Object JSON RPC methods to invoke
@param {Object} [req] HTTP request that is the source of the jsonrpc, passed to method (optional)

Returns a promise that resolves to the JSON RPC response. Maybe null if the request only contained notifications.
*/
function handleRequest(body, methods, req) {
  var isBatch = Array.isArray(body);
  return Promise.all(setup(body, methods, req)).then(
   function (result) {
     //filter out notification results
     result = result.filter(function(elem){return !!elem;});
     if (!result.length) {
       return null;
     }
     if (!isBatch && result.length == 1)
       return result[0];
     return result;
  });
}

/**
Express request handler.
usage: app.post('/', router.bind(methods))

@param {Object} [req] Express request object. Assumes req.body has already been parsed into json
@param {Object} [res] Express response object.
@param {Function} [next] Called if request isn't json
*/
module.exports.router = function(req, res, next) {
  if (req.headers['content-type'].indexOf( 'application/json' ) > -1) {
    handleRequest(req.body, this, req).then(function(result) {
      if (result) {
        var rpcResponse = (this.__stringify || JSON.stringify)(result);
        var contentLength = Buffer.byteLength(rpcResponse);
        res.writeHead(contentLength ? 200 : 204, {
          'Content-Length': String(contentLength),
          'Content-Type': req.contentType
        });
        res.end(rpcResponse);
      } else {
        res.end();
      }
    }).done();
  } else {
    next();
  }
};

module.exports.JsonRpcError = JsonRpcError;
module.exports.handleRequest = handleRequest;
module.exports.PARSE_ERROR = PARSE_ERROR;
module.exports.INVALID_REQUEST = INVALID_REQUEST;
module.exports.METHOD_NOT_FOUND = METHOD_NOT_FOUND;
module.exports.INVALID_PARAMS = INVALID_PARAMS;
module.exports.INTERNAL_ERROR = INTERNAL_ERROR;
<commit_msg>mirror content type properly<commit_after>var Promise = require('promise');

var PARSE_ERROR_CODE = -32700,
  INVALID_REQUEST_CODE = -32600,
  METHOD_NOT_FOUND_CODE = -32601,
  INVALID_PARAMS_CODE = -32602,
  INTERNAL_ERROR_CODE = -32603;

/** @constant */
ERROR_MESSAGES = {
  '-32700': 'Parse error',
  '-32600': 'Invalid request',
  '-32601': 'Method not found',
  '-32602': 'Invalid parameters',
  '-32603': 'Internal error'
  //-32099 to -32000 are open for use
};

function JsonRpcError(code, message, data){
  this.name = JsonRpcError.name;
  this.code = code || INTERNAL_ERROR_CODE;
  this.message = message || ERROR_MESSAGES[code] || '';
  this.data = data;
}
JsonRpcError.prototype = Error.prototype;
JsonRpcError.name = 'JsonRpcError';

var PARSE_ERROR = new JsonRpcError(-32700),
    INVALID_REQUEST = new JsonRpcError(-32600),
    METHOD_NOT_FOUND = new JsonRpcError(-32601),
    INVALID_PARAMS = new JsonRpcError(-32602),
    INTERNAL_ERROR = new JsonRpcError(-32603);

function isValidRequest(rpc) {
  //or params must be absent, an array or object
  var params = rpc.params;
  //isNaN shouldn't be used for rpc.id
  return rpc.jsonrpc === '2.0' && typeof rpc.method ==='string' && (!rpc.hasOwnProperty('id') || typeof rpc.id === 'number')
    && (typeof params === 'undefined' || (params && typeof params === 'object'));
}

function makeResult(request, responseObj) {
  if (!request.hasOwnProperty('id')) //it's a notification
    return null;

  var response = {
    jsonrpc: '2.0',
    id: request.id
  };
  if (responseObj instanceof JsonRpcError) {
    response.error = {
      code: responseObj.code,
      message: responseObj.message,
      data: responseObj.data
    };
  } else {
    response.result = responseObj;
  }
  return response;
}

function setup(body, methods, nodeReq) {
  var reqs = Array.isArray(body) ? body : [body];
  return reqs.reduce(function(promisesSoFar, req) {
    var p = null;
    if (!isValidRequest(req)) {
      p = Promise.resolve(makeResult(req, INVALID_REQUEST));
    } else {
      var method = methods[req.method];
      if (!method) {
        p = Promise.resolve(makeResult(req, METHOD_NOT_FOUND));
      } else {
        p = new Promise(function (resolve, reject) {
          try {
            var respond = function(response) {
              resolve(makeResult(req, response));
            }
            var ret = method.call(methods, req.params, respond, promisesSoFar.slice(0), nodeReq); //copy array to record current state in order to avoid deadlock
            if (typeof ret !== 'undefined') {
              if (typeof ret.then === 'function') //it's a promise
                resolve( ret.then(function(result) {return makeResult(req, result)}) );
              else
                respond(ret); //synchronous call
            }
          } catch (e) {
              console.log("unexpected error from jsonrpc method call:", req.method, "params:", req.params);
              console.log(e.stack);
              resolve(makeResult(req, INTERNAL_ERROR));
          }
        });
      }
    }
    p.request = req;
    promisesSoFar.push(p);
    return promisesSoFar;
  }, []);
}

/**
@param {Object or Array} [body] The jsonrpc request
@param {Object} [methods] Object JSON RPC methods to invoke
@param {Object} [req] HTTP request that is the source of the jsonrpc, passed to method (optional)

Returns a promise that resolves to the JSON RPC response. Maybe null if the request only contained notifications.
*/
function handleRequest(body, methods, req) {
  var isBatch = Array.isArray(body);
  return Promise.all(setup(body, methods, req)).then(
   function (result) {
     //filter out notification results
     result = result.filter(function(elem){return !!elem;});
     if (!result.length) {
       return null;
     }
     if (!isBatch && result.length == 1)
       return result[0];
     return result;
  });
}

/**
Express request handler.
usage: app.post('/', router.bind(methods))

@param {Object} [req] Express request object. Assumes req.body has already been parsed into json
@param {Object} [res] Express response object.
@param {Function} [next] Called if request isn't json
*/
module.exports.router = function(req, res, next) {
  if (req.headers['content-type'].indexOf( 'application/json' ) > -1) {
    handleRequest(req.body, this, req).then(function(result) {
      if (result) {
        var rpcResponse = (this.__stringify || JSON.stringify)(result);
        var contentLength = Buffer.byteLength(rpcResponse);
        res.writeHead(contentLength ? 200 : 204, {
          'Content-Length': String(contentLength),
          'Content-Type': req.headers['content-type']
        });
        res.end(rpcResponse);
      } else {
        res.end();
      }
    }).done();
  } else {
    next();
  }
};

module.exports.JsonRpcError = JsonRpcError;
module.exports.handleRequest = handleRequest;
module.exports.PARSE_ERROR = PARSE_ERROR;
module.exports.INVALID_REQUEST = INVALID_REQUEST;
module.exports.METHOD_NOT_FOUND = METHOD_NOT_FOUND;
module.exports.INVALID_PARAMS = INVALID_PARAMS;
module.exports.INTERNAL_ERROR = INTERNAL_ERROR;
<|endoftext|>


<commit_before>$(document).ready(function() {
    init_db();

    $("#search_form").submit(function() {
        var word = $("#search_field").val();
        $("#search_field").select();
        query(word);
        return false;
    });

    $("#search").click(function() {
        $("#search_field").focus();
    });

    $("#button_add").click(function() {
        var word = location.hash.substring(1);
        storage_word(word);
        wordlist_add(word);
        $("li.current").removeClass("current");
        $("#wordlist_" + escape4id(word)).addClass("current");
        refresh_wordlist_trigger();
        $("#button_add").hide();
        $("#button_remove").show();
    });

    $("#button_remove").click(function() {
        var word = location.hash.substring(1);
        remove_word(word);
        $("#wordlist_" + escape4id(word)).remove();
        $("#button_remove").hide();
        $("#button_add").show();
    });

    $("#wordlist").sortable({
        helper: 'clone',
        axis: 'y',
        update: update_word_seq
    });

    /* Update notification */
    var cur_version = 10;
    if (!localStorage.prev_version) {
        localStorage.prev_version = cur_version;
    }
    if (!localStorage.prev_version || cur_version > localStorage.prev_version) {
        $("#bubble").html('
Halo Word 开始支持整句翻译啦！现在就输入「people mountain people sea」试试吧，也可以在网页上划取整句进行翻译。

如果您喜欢这个应用，不妨给我们打个五星或者捐赠。

<button  id="button-go-version">查看版本信息</button><button id="button-close-bubble">关闭</button>
');
        $("#bubble").show();
    }
    $("#button-close-bubble").click(function() {
        localStorage.prev_version = cur_version;
        $("#bubble").hide();
    });
    $("#button-go-version").click(function() {
        query("halo:version");
    });

    /* Special fixes */
    if (get_OS() == "Windows") {
        /* Special fixes for Windows */
        $("#search_field").css("-webkit-appearance", "none");
    }

    show_def(location.hash.substring(1));
});

function get_OS() {
    var ua = navigator.userAgent;
    var os;
    if (ua.indexOf("Windows") > 0) {
        os = "Windows";
    }
    else if (ua.indexOf("Mac OS X") > 0) {
        os = "Mac OS X";
    }
    return os;
}

function refresh_wordlist_trigger() {
    $("#title").click(function() {
        $("li.current").removeClass("current");
        $(this).addClass("current");
    });
    $("#wordlist li").click(function() {
        var word = $(this).text();
        $("li.current").removeClass("current");
        $(this).addClass("current");
        query(word);
    });
    $("#wordlist .delete").click(function() {
        var word = $(this).parent().text();
        remove_word(word);
        $(this).parent().remove();
        if (word == location.hash.substring(1)) {
            $("#button_remove").hide();
            $("#button_add").show();
        }
        return false;
    });
}

/* VERSION */

var VERSION = get_version();

function get_version() {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', chrome.extension.getURL('manifest.json'), false);
    xhr.send(null);
    var manifest = JSON.parse(xhr.responseText);
    return manifest.version;
}

/* UI */

function on_resize() {
    if (get_OS() == "Mac OS X") {
        $("#wordlist").height($(window).height() - 146);
    }
    else {
        $("#wordlist").height($(window).height() - 156);
    }
}

$(window).load(on_resize);
$(window).resize(on_resize);

/* STORAGE */

/* word list: newest on top */
var default_words = ["喵", "miaow", "caesium", "love", "dysprosium", "turf", "Iridium", "daisy", "Chrome", "Capella"];

var db;
function init_db() {
    db = openDatabase("HaloWord", "0.1", "Database for Halo Word", 200000);
    db.transaction(function (tx) {
        tx.executeSql("SELECT COUNT(*) FROM `Word`", [],
        /* success */
        function(result) {
            update_db();
            init_wordlist();
        },
        /* no table, create them */
        function(tx, error) {
            tx.executeSql("CREATE TABLE `Word` (`id` REAL UNIQUE, `word` TEXT, `timestamp` REAL, `sequence` REAL, `status` REAL)", [],
            function() {
                localStorage.db_version = 2;
                storage_words(default_words);
                init_wordlist();
            },
            null);
        });
    });
}

function update_db() {
    if (!localStorage.db_version) {
        localStorage.db_version = 1;
    }
    console.log("Checking db version...");
    console.log("Current db version: " + localStorage.db_version + ".");
    if (localStorage.db_version < 2) {
        /* DB v2: table `word` - add column `sequence` */
        /*        table `word` - add column `status`   */
        console.log("Updating db to version 2...");
        db.transaction(function(tx) {
            tx.executeSql("ALTER TABLE `Word` ADD COLUMN `sequence` REAL", [], null, null);
            tx.executeSql("ALTER TABLE `Word` ADD COLUMN `status` REAL", [], null, null);
        });
        localStorage.db_version = 2;
    }
}

function storage_words(words) {
    var time = new Date().getTime();
    db.transaction(function(tx) {
        for (var w in words) {
            tx.executeSql("DELETE FROM `Word` WHERE `word` = ?",
            [words[w]],
            null, null);
            tx.executeSql("INSERT INTO `Word` (`word`, `timestamp`) values(?, ?)",
            [words[w], time],
            null, null);
        }
    });
}

function storage_word(word) {
    db.transaction(function(tx) {
        tx.executeSql("DELETE FROM `Word` WHERE `word` = ?",
        [word],
        null, null);
        tx.executeSql("INSERT INTO `Word` (`word`, `timestamp`) values(?, ?)",
        [word, new Date().getTime()],
        null, null);
    });
}

function update_word_seq() {
    db.transaction(function(tx) {
        $("#wordlist li").each(function(id, obj) {
            tx.executeSql("UPDATE `Word` SET `sequence` = ? WHERE `word` = ?",
            [id, $(obj).text()],
            null, null);
        });
    });
}

function remove_all_word() {
    db.transaction(function(tx) {
        tx.executeSql("DELETE FROM `Word`",
        null, null, null);
    });
}

function remove_word(word) {
    db.transaction(function(tx) {
        tx.executeSql("DELETE FROM `Word` WHERE `word` = ?",
        [word],
        null, null);
    });
}

function get_wordlist(process_func) {
    db.transaction(function(tx) {
        tx.executeSql("SELECT * FROM `Word` ORDER BY `sequence` DESC, `timestamp` ASC", [],
        function(tx, result) {
            process_func(result);
        }, null);
    });
}

function init_wordlist() {
    get_wordlist(process_wordlist);
}

function process_wordlist(result) {
    $("#wordlist").html("");
    for (var i = 0; i < result.rows.length; i++) {
        wordlist_add(result.rows.item(i).word);
    }
    refresh_wordlist_trigger();
}

function escape4id(word) {
    return word.replace(/\s/g, "__");
}

function wordlist_add(word) {
    $("#wordlist").prepend('

' + word + "
");
}

function wordlist_append(word) {
    $("#wordlist").append('

' + word + "
");
}

/* QUERY */

function query(word) {
    location.hash = word;
}

window.onhashchange = function() {
    show_def(location.hash.substring(1));
}

function show_def(word) {
    if (!word) { word = "halo:welcome" }

    /* fixing a refresh issue. */
    window.word = word;

    $(".button").hide();
    $("#toolbar").hide();

    if (is_builtin(word)) {
        show_builtin(is_builtin(word));
        return;
    }

    show_builtin("loading");

    $("#extradef").show();
    document.title = word + " ‹ Halo Word";
    $("#wordtitle").html(word);

    db.transaction(function (tx) {
        tx.executeSql("SELECT COUNT(*) AS `exist` FROM `Word` WHERE `word` = ?", [word],
        function(tx, result) {
            if (result.rows.item(0).exist) {
                $("#button_remove").show();
            }
            else {
                $("#button_add").show();
            }
        }, null);
    });

    $.ajax({
        url: "https://www.google.com/dictionary/json?callback=process_json&q=" + word + "&sl=en&tl=zh-cn",
        dataType: "script"
    });

    $("#extradef .phonetic").html("ˈləʊdɪŋ");
    $("#extradef .content").html("
loading...
");

    $.ajax({
        url: youdao_url + word,
        dataType: "json",
        success: function(data) {
            var def = "", i;
            if (data.errorCode === 0) {
                $("#extradef .from").html("Youdao");
                $("#extradef .from").attr("href", "http://dict.youdao.com/");
                if (data.basic) {
                    if (data.basic.phonetic) {
                        $("#extradef .phonetic").html("" + data.basic.phonetic + "");
                        $("#extradef .phonetic").show();
                    }
                    else {
                        $("#extradef .phonetic").hide();
                    }

                    for (i in data.basic.explains) {
                        def += "
" + data.basic.explains[i] + "
";
                    }
                    $("#extradef .content").html(def);
                }
                else if (data.translation) {
                    for (i in data.translation) {
                        def += "
" + data.translation[i] + "
";
                    }
                    $("#extradef .phonetic").hide();
                    $("#extradef .content").html(def);
                }
                else {
                    // no definition and translation
                    $("#extradef").hide();
                }
            }
            else {
                $("#extradef").hide();
            }
        },
        error: function(data) {
            $("#extradef").hide();
        }
    });

/* unfortunately, dict.cn no longer provides API.
    $.ajax({
        url: "http://dict.cn/ws.php?utf8=true&q=" + word,
        dataType: "xml",
        success: function(data) {
            $(data).find("dict").each(function(i) {
                var def = $(this).children("def").text();
                if (!def) {
                    $("#extradef").hide();
                }
                def = def.replace(/\n/g, '
');
                $("#extradef .content").html(def);
                var phonetic = $(this).children("pron").text();
                if (phonetic) {
                    phonetic = "[" + phonetic + "]";
                    $("#extradef .phonetic").html(phonetic);
                    $("#extradef .phonetic").show();
                }
                else {
                    $("#extradef .phonetic").hide();
                }
            });
        }
    });
*/
}

function is_builtin(word) {
    if (word.substring(0, 5) == "halo:")
        return word.substring(5);
    else
        return false;
}

function show_builtin(builtin) {
    $.get("builtin/" + builtin + ".html", function(data) {
        $("#worddef").html(process_builtin(data));

        var title = $("#builtin-title").html();
        if (title) {
            $("#extradef").hide();
            $("#wordtitle").html(title);
            if (title != "Halo Word") {
                title = title + " ‹ Halo Word";
            }
            document.title = title;
        }

        if (builtin == "welcome") {
            $("#toolbar").show();
        }
    });
}

function process_builtin(data) {
    data = data.replace(/__VERSION__/g, '' + VERSION + '');
    return data;
}

function process_json(data) {
    if (!data.primaries) {
        show_builtin("notfound");
        return;
    }

    var meaning = process_primary(data.primaries);
    if (meaning) {
        $("#worddef").html(meaning);
        $(".pronounce").click(function() {
            $("audio", this)[0].play();
        });
    }
    else {
        show_builtin("notfound");
        return;
    }

    $("#worddef").append('
Content provided by Google Dictionary
');
}

/* iciba's API has very low quality, using youdao instead.
function get_iciba() {
    $.ajax({
        url: "http://dict-co.iciba.com/api/dictionary.php?w=" + word.toLowerCase(),
        dataType: "xml",
        success: function(data) {
            $("#extradef .from").html("iciba.com");
            $("#extradef .from").attr("href", "http://www.iciba.com/");

            var dict = $("dict", data)[0];

            if ($("acceptation", dict).length) {
                if ($("ps", dict).length) {
                    var phonetic = '';
                    $("ps", dict).each(function(i) {
                        var phonetic_text = $(this).text();
                        if ($("pron", dict)[i]) {
                            var audio_url = $($("pron", dict)[i]).text();
                            var audio_text = '<audio src="' + audio_url + '"></audio>';
                            phonetic += '' + audio_text + phonetic_text + '';
                        }
                        else {
                            phonetic += '' + phonetic_text + '';
                        }
                    });
                    $("#extradef .phonetic").html(phonetic);
                    $("#extradef .phonetic").show();
                }
                else {
                    $("#extradef .phonetic").hide();
                }

                var def = '';
                $("pos, acceptation", dict).each(function(item) {
                    def += '
' + $(this).text() + "
";
                });
                $("#extradef .content").html(def);
            }
            else {
                // no definition
                $("#extradef").hide();
            }
        },
        error: function(data) {
            $("#extradef .content").html("Unable to parse XML file.");
        }
    });
}
*/<commit_msg>Delete obsolete lines.<commit_after>$(document).ready(function() {
    init_db();

    $("#search_form").submit(function() {
        var word = $("#search_field").val();
        $("#search_field").select();
        query(word);
        return false;
    });

    $("#search").click(function() {
        $("#search_field").focus();
    });

    $("#button_add").click(function() {
        var word = location.hash.substring(1);
        storage_word(word);
        wordlist_add(word);
        $("li.current").removeClass("current");
        $("#wordlist_" + escape4id(word)).addClass("current");
        refresh_wordlist_trigger();
        $("#button_add").hide();
        $("#button_remove").show();
    });

    $("#button_remove").click(function() {
        var word = location.hash.substring(1);
        remove_word(word);
        $("#wordlist_" + escape4id(word)).remove();
        $("#button_remove").hide();
        $("#button_add").show();
    });

    $("#wordlist").sortable({
        helper: 'clone',
        axis: 'y',
        update: update_word_seq
    });

    /* Update notification */
    var cur_version = 10;
    if (!localStorage.prev_version) {
        localStorage.prev_version = cur_version;
    }
    if (!localStorage.prev_version || cur_version > localStorage.prev_version) {
        $("#bubble").html('
Halo Word 开始支持整句翻译啦！现在就输入「people mountain people sea」试试吧，也可以在网页上划取整句进行翻译。

如果您喜欢这个应用，不妨给我们打个五星或者捐赠。

<button  id="button-go-version">查看版本信息</button><button id="button-close-bubble">关闭</button>
');
        $("#bubble").show();
    }
    $("#button-close-bubble").click(function() {
        localStorage.prev_version = cur_version;
        $("#bubble").hide();
    });
    $("#button-go-version").click(function() {
        query("halo:version");
    });

    /* Special fixes */
    if (get_OS() == "Windows") {
        /* Special fixes for Windows */
        $("#search_field").css("-webkit-appearance", "none");
    }

    show_def(location.hash.substring(1));
});

function get_OS() {
    var ua = navigator.userAgent;
    var os;
    if (ua.indexOf("Windows") > 0) {
        os = "Windows";
    }
    else if (ua.indexOf("Mac OS X") > 0) {
        os = "Mac OS X";
    }
    return os;
}

function refresh_wordlist_trigger() {
    $("#title").click(function() {
        $("li.current").removeClass("current");
        $(this).addClass("current");
    });
    $("#wordlist li").click(function() {
        var word = $(this).text();
        $("li.current").removeClass("current");
        $(this).addClass("current");
        query(word);
    });
    $("#wordlist .delete").click(function() {
        var word = $(this).parent().text();
        remove_word(word);
        $(this).parent().remove();
        if (word == location.hash.substring(1)) {
            $("#button_remove").hide();
            $("#button_add").show();
        }
        return false;
    });
}

/* VERSION */

var VERSION = get_version();

function get_version() {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', chrome.extension.getURL('manifest.json'), false);
    xhr.send(null);
    var manifest = JSON.parse(xhr.responseText);
    return manifest.version;
}

/* UI */

function on_resize() {
    if (get_OS() == "Mac OS X") {
        $("#wordlist").height($(window).height() - 146);
    }
    else {
        $("#wordlist").height($(window).height() - 156);
    }
}

$(window).load(on_resize);
$(window).resize(on_resize);

/* STORAGE */

/* word list: newest on top */
var default_words = ["喵", "miaow", "caesium", "love", "dysprosium", "turf", "Iridium", "daisy", "Chrome", "Capella"];

var db;
function init_db() {
    db = openDatabase("HaloWord", "0.1", "Database for Halo Word", 200000);
    db.transaction(function (tx) {
        tx.executeSql("SELECT COUNT(*) FROM `Word`", [],
        /* success */
        function(result) {
            update_db();
            init_wordlist();
        },
        /* no table, create them */
        function(tx, error) {
            tx.executeSql("CREATE TABLE `Word` (`id` REAL UNIQUE, `word` TEXT, `timestamp` REAL, `sequence` REAL, `status` REAL)", [],
            function() {
                localStorage.db_version = 2;
                storage_words(default_words);
                init_wordlist();
            },
            null);
        });
    });
}

function update_db() {
    if (!localStorage.db_version) {
        localStorage.db_version = 1;
    }
    console.log("Checking db version...");
    console.log("Current db version: " + localStorage.db_version + ".");
    if (localStorage.db_version < 2) {
        /* DB v2: table `word` - add column `sequence` */
        /*        table `word` - add column `status`   */
        console.log("Updating db to version 2...");
        db.transaction(function(tx) {
            tx.executeSql("ALTER TABLE `Word` ADD COLUMN `sequence` REAL", [], null, null);
            tx.executeSql("ALTER TABLE `Word` ADD COLUMN `status` REAL", [], null, null);
        });
        localStorage.db_version = 2;
    }
}

function storage_words(words) {
    var time = new Date().getTime();
    db.transaction(function(tx) {
        for (var w in words) {
            tx.executeSql("DELETE FROM `Word` WHERE `word` = ?",
            [words[w]],
            null, null);
            tx.executeSql("INSERT INTO `Word` (`word`, `timestamp`) values(?, ?)",
            [words[w], time],
            null, null);
        }
    });
}

function storage_word(word) {
    db.transaction(function(tx) {
        tx.executeSql("DELETE FROM `Word` WHERE `word` = ?",
        [word],
        null, null);
        tx.executeSql("INSERT INTO `Word` (`word`, `timestamp`) values(?, ?)",
        [word, new Date().getTime()],
        null, null);
    });
}

function update_word_seq() {
    db.transaction(function(tx) {
        $("#wordlist li").each(function(id, obj) {
            tx.executeSql("UPDATE `Word` SET `sequence` = ? WHERE `word` = ?",
            [id, $(obj).text()],
            null, null);
        });
    });
}

function remove_all_word() {
    db.transaction(function(tx) {
        tx.executeSql("DELETE FROM `Word`",
        null, null, null);
    });
}

function remove_word(word) {
    db.transaction(function(tx) {
        tx.executeSql("DELETE FROM `Word` WHERE `word` = ?",
        [word],
        null, null);
    });
}

function get_wordlist(process_func) {
    db.transaction(function(tx) {
        tx.executeSql("SELECT * FROM `Word` ORDER BY `sequence` DESC, `timestamp` ASC", [],
        function(tx, result) {
            process_func(result);
        }, null);
    });
}

function init_wordlist() {
    get_wordlist(process_wordlist);
}

function process_wordlist(result) {
    $("#wordlist").html("");
    for (var i = 0; i < result.rows.length; i++) {
        wordlist_add(result.rows.item(i).word);
    }
    refresh_wordlist_trigger();
}

function escape4id(word) {
    return word.replace(/\s/g, "__");
}

function wordlist_add(word) {
    $("#wordlist").prepend('

' + word + "
");
}

function wordlist_append(word) {
    $("#wordlist").append('

' + word + "
");
}

/* QUERY */

function query(word) {
    location.hash = word;
}

window.onhashchange = function() {
    show_def(location.hash.substring(1));
}

function show_def(word) {
    if (!word) { word = "halo:welcome" }

    /* fixing a refresh issue. */
    window.word = word;

    $(".button").hide();
    $("#toolbar").hide();

    if (is_builtin(word)) {
        show_builtin(is_builtin(word));
        return;
    }

    show_builtin("loading");

    $("#extradef").show();
    document.title = word + " ‹ Halo Word";
    $("#wordtitle").html(word);

    db.transaction(function (tx) {
        tx.executeSql("SELECT COUNT(*) AS `exist` FROM `Word` WHERE `word` = ?", [word],
        function(tx, result) {
            if (result.rows.item(0).exist) {
                $("#button_remove").show();
            }
            else {
                $("#button_add").show();
            }
        }, null);
    });

    $.ajax({
        url: "https://www.google.com/dictionary/json?callback=process_json&q=" + word + "&sl=en&tl=zh-cn",
        dataType: "script"
    });

    $("#extradef .phonetic").html("ˈləʊdɪŋ");
    $("#extradef .content").html("
loading...
");

    $.ajax({
        url: youdao_url + word,
        dataType: "json",
        success: function(data) {
            var def = "", i;
            if (data.errorCode === 0) {
                $("#extradef .from").html("Youdao");
                $("#extradef .from").attr("href", "http://dict.youdao.com/");
                if (data.basic) {
                    if (data.basic.phonetic) {
                        $("#extradef .phonetic").html("" + data.basic.phonetic + "");
                        $("#extradef .phonetic").show();
                    }
                    else {
                        $("#extradef .phonetic").hide();
                    }

                    for (i in data.basic.explains) {
                        def += "
" + data.basic.explains[i] + "
";
                    }
                    $("#extradef .content").html(def);
                }
                else if (data.translation) {
                    for (i in data.translation) {
                        def += "
" + data.translation[i] + "
";
                    }
                    $("#extradef .phonetic").hide();
                    $("#extradef .content").html(def);
                }
                else {
                    // no definition and translation
                    $("#extradef").hide();
                }
            }
            else {
                $("#extradef").hide();
            }
        },
        error: function(data) {
            $("#extradef").hide();
        }
    });
}

function is_builtin(word) {
    if (word.substring(0, 5) == "halo:")
        return word.substring(5);
    else
        return false;
}

function show_builtin(builtin) {
    $.get("builtin/" + builtin + ".html", function(data) {
        $("#worddef").html(process_builtin(data));

        var title = $("#builtin-title").html();
        if (title) {
            $("#extradef").hide();
            $("#wordtitle").html(title);
            if (title != "Halo Word") {
                title = title + " ‹ Halo Word";
            }
            document.title = title;
        }

        if (builtin == "welcome") {
            $("#toolbar").show();
        }
    });
}

function process_builtin(data) {
    data = data.replace(/__VERSION__/g, '' + VERSION + '');
    return data;
}

function process_json(data) {
    if (!data.primaries) {
        show_builtin("notfound");
        return;
    }

    var meaning = process_primary(data.primaries);
    if (meaning) {
        $("#worddef").html(meaning);
        $(".pronounce").click(function() {
            $("audio", this)[0].play();
        });
    }
    else {
        show_builtin("notfound");
        return;
    }

    $("#worddef").append('
Content provided by Google Dictionary
');
}
<|endoftext|>


<commit_before>/**
 * This file includes polyfills which you may need.
 * Please only add those you are actually using. Otherwise you’ll unnecessarily
 * blow up the size of your JS bundle.
 *
 * You can add your own extra polyfills to this file:
 * Check https://github.com/zloirock/core-js to see whats available.
 *
 */

/**
  * ----------------------------------------------------------------------------
  * Import each and every polyfill provided by core-js
  * -----------------------------------------------------------------------------
  * This is for the lazy ones and absolultely not recommend because it will
  * increase your JS bundle by more than 120 kb (minified). Better check what
  * your target browsers need (https://kangax.github.io/compat-table/es6/)
  * and use partial imports like in the examples below.
  */
// import 'core-js';

/**
 * ----------------------------------------------------------------------------
 * Promises. Needed for Internet Explorer  up to IE11
 * -----------------------------------------------------------------------------
 * This polyfill alone will blow up your bundle size with more than 20 kb
 * (minified). So please  don’t use that if your are targeting only browser
 * which have Promises build in. See https://kangax.github.io/compat-table/es6/
 */
import 'core-js/es6/promise';

/**
 * ----------------------------------------------------------------------------
 * Additional ES6 polyfills
 * -----------------------------------------------------------------------------
 */

/**
 * Import all object methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-object how to import
 * just the ones you need.
 */
// import 'core-js/es6/object';

/**
 * Import all function methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-function how to import
 * just the ones you need.
 */
// import 'core-js/es6/function';

/**
 * Import all array methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-array how to import
 * just the ones you need.
 */
// import 'core-js/es6/array';

/**
 * Import all string methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-string how to import
 * just the ones you need.
 */
// import 'core-js/es6/string';

/**
 * Import all regexp methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-regexp how to import
 * just the ones you need.
 */
// import 'core-js/es6/regexp';

/**
 * Import all number methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-number how to import
 * just the ones you need.
 */
// import 'core-js/es6/number';

/**
 * Import all math methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-math how to import
 * just the ones you need.
 */
// import 'core-js/es6/math';

/**
 * Import all date methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-date how to import
 * just the ones you need.
 */
// import 'core-js/es6/date';

/**
 * Import all collection types.
 * See https://github.com/zloirock/core-js#ecmascript-6-collections.
 */
// import 'core-js/es6/map';
// import 'core-js/es6/set';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/weak-set';

/**
 * Import all typed-array methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-typed-arrays how to import
 * just the ones you need.
 */
// import 'core-js/es6/typed';

/**
 * Import all reflect methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-reflect how to import
 * just the ones you need.
 */
// import 'core-js/es6/reflect';

/**
 * ----------------------------------------------------------------------------
 * Additional ES7+ polyfills
 * -----------------------------------------------------------------------------
 */

/**
 * Import all ES7 polyfills,
 * See https://github.com/zloirock/core-js#ecmascript-7-proposals how to import
 * just the ones you need.
 */
// import 'core-js/es7';

/**
 * Import all polyfills of stage 4 proposals
 */
// import 'core-js/stage/4';

/**
 * Import all polyfills of stage 3 proposals
 * See https://github.com/zloirock/core-js#ecmascript-7-proposals how to import
 * pre stage 3 polyfills.
 */
// import 'core-js/stage/3';

/**
 * ----------------------------------------------------------------------------
 * Examples for method based imports
 * -----------------------------------------------------------------------------
 */

/**
 * Import a single string method
 */
// import 'core-js/fn/string/trim-start';

/**
 * Import a single array method
 */
// import 'core-js/fn/array/includes';
<commit_msg>docs(readme): Fix typos<commit_after>/**
 * This file includes polyfills which you may need.
 * Please only add those you are actually using. Otherwise you’ll unnecessarily
 * blow up the size of your JS bundle.
 *
 * You can add your own extra polyfills to this file:
 * Check https://github.com/zloirock/core-js to see whats available.
 *
 */

/**
  * ----------------------------------------------------------------------------
  * Import each and every polyfill provided by core-js
  * -----------------------------------------------------------------------------
  * This is for the lazy ones and absolultely not recommended because it will
  * increase your JS bundle by more than 120 kb (minified). Better check what
  * your target browsers need (https://kangax.github.io/compat-table/es6/)
  * and use partial imports like in the examples below.
  */
// import 'core-js';

/**
 * ----------------------------------------------------------------------------
 * Promises. Needed for Internet Explorer  up to IE11
 * -----------------------------------------------------------------------------
 * This polyfill alone will blow up your bundle size with more than 20 kb
 * (minified). So please  don’t use that if your are targeting only browsers
 * which have Promises build in. See https://kangax.github.io/compat-table/es6/
 */
import 'core-js/es6/promise';

/**
 * ----------------------------------------------------------------------------
 * Additional ES6 polyfills
 * -----------------------------------------------------------------------------
 */

/**
 * Import all object methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-object how to import
 * just the ones you need.
 */
// import 'core-js/es6/object';

/**
 * Import all function methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-function how to import
 * just the ones you need.
 */
// import 'core-js/es6/function';

/**
 * Import all array methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-array how to import
 * just the ones you need.
 */
// import 'core-js/es6/array';

/**
 * Import all string methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-string how to import
 * just the ones you need.
 */
// import 'core-js/es6/string';

/**
 * Import all regexp methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-regexp how to import
 * just the ones you need.
 */
// import 'core-js/es6/regexp';

/**
 * Import all number methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-number how to import
 * just the ones you need.
 */
// import 'core-js/es6/number';

/**
 * Import all math methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-math how to import
 * just the ones you need.
 */
// import 'core-js/es6/math';

/**
 * Import all date methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-date how to import
 * just the ones you need.
 */
// import 'core-js/es6/date';

/**
 * Import all collection types.
 * See https://github.com/zloirock/core-js#ecmascript-6-collections.
 */
// import 'core-js/es6/map';
// import 'core-js/es6/set';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/weak-set';

/**
 * Import all typed-array methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-typed-arrays how to import
 * just the ones you need.
 */
// import 'core-js/es6/typed';

/**
 * Import all reflect methods.
 * See https://github.com/zloirock/core-js#ecmascript-6-reflect how to import
 * just the ones you need.
 */
// import 'core-js/es6/reflect';

/**
 * ----------------------------------------------------------------------------
 * Additional ES7+ polyfills
 * -----------------------------------------------------------------------------
 */

/**
 * Import all ES7 polyfills,
 * See https://github.com/zloirock/core-js#ecmascript-7-proposals how to import
 * just the ones you need.
 */
// import 'core-js/es7';

/**
 * Import all polyfills of stage 4 proposals
 */
// import 'core-js/stage/4';

/**
 * Import all polyfills of stage 3 proposals
 * See https://github.com/zloirock/core-js#ecmascript-7-proposals how to import
 * pre stage 3 polyfills.
 */
// import 'core-js/stage/3';

/**
 * ----------------------------------------------------------------------------
 * Examples for method based imports
 * -----------------------------------------------------------------------------
 */

/**
 * Import a single string method
 */
// import 'core-js/fn/string/trim-start';

/**
 * Import a single array method
 */
// import 'core-js/fn/array/includes';
<|endoftext|>


<commit_before>/**
 * Step 8
 * Called last, cleanup, say good bye, etc
 */

var chalk = require('chalk');
var printMessage = require('print-message');

module.exports = function(){
  printMessage(
    [
      'Enjoy your ' + chalk.red('Sails DIY') + ' project!',
      '---',
      'Next steps:',
      chalk.yellow('1)') + ' Create a model in your app:',
      chalk.blue('yo sails-diy:model Ticket --rest'),
      chalk.yellow('2)') + ' Create a cron in your app:',
      chalk.blue('yo sails-diy:cron cron1 cron2'),
      chalk.yellow('3)') + ' Create a service in your app:',
      chalk.blue('yo sails-diy:service ping'),
      chalk.yellow('4)') + ' Create a policy in your app:',
      chalk.blue('yo sails-diy:policy auth'),
      chalk.yellow('5)') + ' Compose your API and run:',
      chalk.blue('npm start'),
      chalk.yellow('6)') + ' Run unit test in your project:',
      chalk.blue('npm test')
    ],{
      printFn: this.log
    }
  );
};
<commit_msg>add finish helper<commit_after>/**
 * Step 8
 * Called last, cleanup, say good bye, etc
 */

var chalk = require('chalk');
var printMessage = require('print-message');

module.exports = function(){
  printMessage(
    [
      'Enjoy your ' + chalk.red('Sails DIY') + ' project!',
      '---',
      'Next steps:',
      chalk.yellow('1)') + ' Create a controller in your app:',
      chalk.blue('yo sails-diy:controller Test'),
      chalk.yellow('2)') + ' Create a model in your app:',
      chalk.blue('yo sails-diy:model Ticket --rest'),
      chalk.yellow('3)') + ' Create a cron in your app:',
      chalk.blue('yo sails-diy:cron cron1 cron2'),
      chalk.yellow('4)') + ' Create a service in your app:',
      chalk.blue('yo sails-diy:service ping'),
      chalk.yellow('5)') + ' Create a policy in your app:',
      chalk.blue('yo sails-diy:policy auth'),
      chalk.yellow('6)') + ' Compose your API and run:',
      chalk.blue('npm start'),
      chalk.yellow('7)') + ' Run unit test in your project:',
      chalk.blue('npm test')
    ],{
      printFn: this.log
    }
  );
};
<|endoftext|>


<commit_before>var indentity = require("./identity");
var types = require("dis-isa");

/**
 * Converts array to a literal object with the array values used as keys. So this is
 * to be used for converting an array of string/number entries to a literal object with
 * those values as the keys for the new object.
 *
 * @param { array } input - Items to convert to a map
 * @param { *? } val - Can be a function, in which case it is called with the currect
 *  item in the array being processed in order to derive the value for the map entry.
 *  Otherwise the value will be the same as the value in the array, which will also
 *  be the key.
 *
 * @returns { object } Object will all the array values as keys and the derived
 *  values
 */
function arrayToObject(input, val) {
  var defaultValue = true;
  if (arguments.length !== 1) {
    if (!types.isFunction(val)) {
      defaultValue = val;
      val = false;
    }
  }

  return input.reduce(function(container, value, key) {
    container[value] = val ? val(value, key, input) : defaultValue;
    return container;
  }, {});
}

module.exports = arrayToObject;
<commit_msg>Fixed listing warnings<commit_after>var types = require("dis-isa");

/**
 * Converts array to a literal object with the array values used as keys. So this is
 * to be used for converting an array of string/number entries to a literal object with
 * those values as the keys for the new object.
 *
 * @param { array } input - Items to convert to a map
 * @param { *? } val - Can be a function, in which case it is called with the currect
 *  item in the array being processed in order to derive the value for the map entry.
 *  Otherwise the value will be the same as the value in the array, which will also
 *  be the key.
 *
 * @returns { object } Object will all the array values as keys and the derived
 *  values
 */
function arrayToObject(input, val) {
  var defaultValue = true;
  if (arguments.length !== 1) {
    if (!types.isFunction(val)) {
      defaultValue = val;
      val = false;
    }
  }

  return input.reduce(function(container, value, key) {
    container[value] = val ? val(value, key, input) : defaultValue;
    return container;
  }, {});
}

module.exports = arrayToObject;
<|endoftext|>


<commit_before>'use babel'

import {CompositeDisposable} from 'atom'
import {isTruthy, isFalsy, isEmpty} from './check'
import {Executor} from './executor'
import pathhelper from './pathhelper'
import _ from 'lodash'
import fs from 'fs'
import os from 'os'
import path from 'path'

class Locator {
  constructor (options) {
    this.subscriptions = new CompositeDisposable()
    this.environmentFn = null
    this.executor = null
    this.executableSuffix = ''
    this.pathKey = 'PATH'
    if (os.platform() === 'win32') {
      this.executableSuffix = '.exe'
      this.pathKey = 'Path'
    }
    this.goExecutables = ['go' + this.executableSuffix, 'goapp' + this.executableSuffix]
    this.readyFn = null
    if (isTruthy(options)) {
      if (isTruthy(options.environment)) {
        this.environmentFn = options.environment
      }
      if (isTruthy(options.ready)) {
        this.readyFn = options.ready
      }
      if (isTruthy(options.executor)) {
        this.executor = options.executor
      }
    }

    if (this.executor === null) {
      this.executor = new Executor({environmentFn: this.environment.bind(this)})
    }

    this.subscriptions.add(this.executor)
    this.goLocators = [
      // Avoid using gorootLocator / GOROOT unless you know what you're doing
      // (and assume you don't know what you're unless you have significant
      // go experience)
      () => { return this.gorootLocator() },
      () => { return this.editorconfigLocator() },
      () => { return this.configLocator() },
      () => { return this.pathLocator() },
      () => { return this.defaultLocator() }
    ]

    this.setKnownToolStrategies()
  }

  dispose () {
    this.resetRuntimes()
    if (this.subscriptions) {
      this.subscriptions.dispose()
    }
    this.goLocators = null
    this.executableSuffix = null
    this.pathKey = null
    this.goExecutables = null
    this.subscriptions = null
    this.environmentFn = null
    this.executor = null
    this.readyFn = null
    this.toolLocations = null
    this.toolStrategies = null
  }

  // Public: Get the go runtime(s).
  // Returns an array of {Object} where each item contains the output from "go
  // env", or false if no runtimes are found.
  runtimes (options = {}) {
    if (isTruthy(this.runtimesCache)) {
      return Promise.resolve(this.runtimesCache)
    }

    return new Promise((resolve, reject) => {
      let candidates = this.runtimeCandidates(options)
      if (isEmpty(candidates)) {
        return resolve([])
      }
      console.log(candidates)
      let viableCandidates = []
      for (let candidate of candidates) {
        let goversion = this.executor.execSync(candidate, ['version'], {cwd: path.dirname(candidate)})
        if (isTruthy(goversion) && goversion.code === 0 && goversion.stdout.startsWith('go ')) {
          let v = {path: candidate, version: goversion.stdout.replace(/\r?\n|\r/g, '')}
          let versionComponents = v.version.split(' ')
          v.name = versionComponents[2]
          v.semver = versionComponents[2]
          if (v.semver.startsWith('go')) {
            v.semver = v.semver.substring(2, v.semver.length)
          }
          viableCandidates.push(v)
        }
      }
      console.log(viableCandidates)
      let finalCandidates = []
      for (let viableCandidate of viableCandidates) {
        let goenv = this.executor.execSync(viableCandidate.path, ['env'], {cwd: path.dirname(viableCandidate.path)})
        if (isTruthy(goenv) && goenv.code === 0 && goenv.stdout.trim() !== '') {
          let items = goenv.stdout.split('\n')
          for (let item of items) {
            item = item.replace(/[\n\r]/g, '')
            if (item.includes('=')) {
              let tuple = item.split('=')
              let key = tuple[0]
              let value = tuple[1]
              if (tuple.length > 2) {
                value = tuple.slice(1, tuple.length + 1).join('=')
              }
              if (os.platform() === 'win32') {
                if (key.startsWith('set ')) {
                  key = key.substring(4, key.length)
                }
              } else {
                if (value.length > 2) {
                  value = value.substring(1, value.length - 1)
                } else {
                  value = ''
                }
              }
              viableCandidate[key] = value
            }
          }
          finalCandidates.push(viableCandidate)
        }
      }

      this.runtimesCache = finalCandidates
      resolve(this.runtimesCache)
    })
  }

  // Deprecated: Use runtime(options) instead.
  runtimeForProject (project) {
    return this.runtime()
  }

  // Public: Get the go runtime.
  // Returns an {Object} which contains the output from "go env", or false if
  // no runtime is found.
  runtime (options = {}) {
    return this.runtimes(options).then((r) => {
      if (isFalsy(r) || r.length < 1) {
        return false
      } else {
        return r[0]
      }
    })
  }

  // Public: Get the gopath.
  // Returns the GOPATH if it exists, or false if it is not defined.
  gopath (options = {}) {
    let e = this.rawEnvironment(options)
    if (isFalsy(e.GOPATH) || e.GOPATH.trim() === '') {
      return false
    }

    return pathhelper.expand(e, e.GOPATH)
  }

  // Public: Find the specified tool.
  // Returns the path to the tool if found, or false if it cannot be found.
  findTool (name, options = {}) {
    if (isFalsy(name) || name.constructor !== String || name.trim() === '') {
      return Promise.resolve(false)
    }

    let strategy = false
    return Promise.resolve(null).then(() => {
      if (this.toolStrategies.has(name)) {
        strategy = this.toolStrategies.get(name)
      }
      if (isFalsy(strategy)) {
        strategy = 'DEFAULT'
      }
    }).then(() => {
      if (strategy !== 'GOROOTBIN' && strategy !== 'GOTOOLDIR') {
        return false
      }

      return this.runtime(options).then((runtime) => {
        if (isFalsy(runtime)) {
          return false
        }

        if (strategy === 'GOROOTBIN') {
          return path.join(runtime.GOROOT, 'bin', name + runtime.GOEXE)
        } else if (strategy === 'GOTOOLDIR') {
          return path.join(runtime.GOTOOLDIR, name + runtime.GOEXE)
        }
        return false
      })
    }).then((specificTool) => {
      if (isTruthy(specificTool)) {
        return this.stat(specificTool).then((s) => {
          if (isTruthy(s) && s.isFile()) {
            return specificTool
          }
        }).catch((err) => {
          this.handleError(err)
          return false
        })
      }

      if (strategy === 'GOPATHBIN') {
        return this.findToolInDelimitedEnvironmentVariable(name, 'GOPATH', options)
      }

      if (strategy === 'PATH') {
        return this.findToolInDelimitedEnvironmentVariable(name, this.pathKey, options)
      }

      return this.findToolWithDefaultStrategy(name, options)
    })
  }

  resetRuntimes () {
    this.runtimesCache = null
  }

  statishSync (pathValue) {
    let stat = false
    if (isTruthy(pathValue) && !(pathValue.trim() === '')) {
      try { stat = fs.statSync(pathValue) } catch (e) { }
    }
    return stat
  }

  stat (p) {
    if (isFalsy(p) || p.constructor !== String || p.trim() === '') {
      return Promise.resolve(false)
    }

    return new Promise((resolve, reject) => {
      fs.stat(p, (err, stat) => {
        if (isTruthy(err)) {
          this.handleError(err)
          resolve(false)
          return
        }
        resolve(stat)
      })
    })
  }

  pathExists (p) {
    return this.exists(p).then((e) => {
      if (isFalsy(e)) {
        return false
      }
      return p
    })
  }

  fileExists (p) {
    return this.stat(p).then((s) => {
      if (isFalsy(s)) {
        return false
      }

      if (s.isFile()) {
        return p
      }

      return false
    })
  }

  directoryExists (p) {
    return this.stat(p).then((s) => {
      if (isFalsy(s)) {
        return false
      }

      if (s.isDirectory()) {
        return p
      }

      return false
    })
  }

  exists (p) {
    return this.stat(p).then((s) => {
      if (isFalsy(s)) {
        return false
      }

      return true
    })
  }

  runtimeCandidates (options = {}) {
    let candidates = []
    for (let locator of this.goLocators) {
      let c = locator(options)
      if (isTruthy(c) && c.constructor === Array && c.length > 0) {
        candidates = _.union(candidates, c)
      }
    }
    return candidates
  }

  editorconfigLocator (options = {}) {
    // TODO: .editorconfig
    return false
  }

  // Internal: Find a go installation using your Atom config. Deliberately
  // undocumented, as this method is discouraged.
  configLocator (options = {}) {
    let goinstallation = atom.config.get('go-config.goinstallation')
    let stat = this.statishSync(goinstallation)
    if (isTruthy(stat)) {
      let d = goinstallation
      if (stat.isFile()) {
        d = path.dirname(goinstallation)
      }
      return this.findExecutablesInPath(d, this.executables, options)
    }

    return []
  }

  gorootLocator (options = {}) {
    let g = this.environment(options).GOROOT
    if (isFalsy(g) || g.trim() === '') {
      return []
    }
    return this.findExecutablesInPath(path.join(g, 'bin'), this.goExecutables, options)
  }

  pathLocator (options = {}) {
    return this.findExecutablesInPath(this.environment(options)[this.pathKey], this.goExecutables, options)
  }

  defaultLocator (options = {}) {
    let installPaths = []
    if (os.platform() === 'win32') {
      /*
      c:\go\bin = Binary Distribution
      c:\tools\go\bin = Chocolatey
      */
      installPaths.push(path.join('c:', 'go', 'bin'))
      installPaths.push(path.join('c:', 'tools', 'go', 'bin'))
    } else {
      /*
      /usr/local/go/bin = Binary Distribution
      /usr/local/bin = Homebrew
      */
      installPaths.push(path.join('/', 'usr', 'local', 'go', 'bin'))
      installPaths.push(path.join('/', 'usr', 'local', 'bin'))
    }
    return this.findExecutablesInPath(installPaths.join(path.delimiter), this.goExecutables, options)
  }

  findExecutablesInPath (pathValue, executables, options = {}) {
    let candidates = []
    if (isFalsy(pathValue) || pathValue.constructor !== String || pathValue.trim() === '') {
      return candidates
    }

    if (isFalsy(executables) || executables.constructor !== Array || executables.length < 1) {
      return candidates
    }

    let elements = pathhelper.expand(this.environment(options), pathValue).split(path.delimiter)
    for (let element of elements) {
      for (let executable of executables) {
        let candidate = path.join(element, executable)
        let stat = this.statishSync(candidate)
        if (isTruthy(stat) && stat.isFile()) {
          candidates.push(candidate)
        }
      }
    }
    return candidates
  }

  // Internal: Get a copy of the environment, with the GOPATH correctly set.
  // Returns an {Object} where the key is the environment variable name and the value is the environment variable value.
  environment (options = {}) {
    let env = this.rawEnvironment(options)
    let g = this.gopath(options)
    if (g && g !== '') {
      env.GOPATH = g
    }
    return env
  }

  rawEnvironment (options = {}) {
    let env = process.env
    if (isTruthy(this.environmentFn)) {
      env = this.environmentFn()
    }
    env = Object.assign({}, env)
    return env
  }

  // Internal: Indicates that the locator is ready, or not.
  // Returns true if ready, else false.
  ready () {
    if (isFalsy(this.readyFn)) {
      return true
    }
    return this.readyFn()
  }

  // Internal: Set the strategy for finding known or built-in tools.
  // Returns a map where the key is the tool name and the value is the strategy.
  setKnownToolStrategies () {
    this.toolStrategies = new Map()

    // Built-In Tools
    this.toolStrategies.set('go', 'GOROOTBIN')
    this.toolStrategies.set('gofmt', 'GOROOTBIN')
    this.toolStrategies.set('godoc', 'GOROOTBIN')
    this.toolStrategies.set('addr2line', 'GOTOOLDIR')
    this.toolStrategies.set('api', 'GOTOOLDIR')
    this.toolStrategies.set('asm', 'GOTOOLDIR')
    this.toolStrategies.set('cgo', 'GOTOOLDIR')
    this.toolStrategies.set('compile', 'GOTOOLDIR')
    this.toolStrategies.set('cover', 'GOTOOLDIR')
    this.toolStrategies.set('dist', 'GOTOOLDIR')
    this.toolStrategies.set('doc', 'GOTOOLDIR')
    this.toolStrategies.set('fix', 'GOTOOLDIR')
    this.toolStrategies.set('link', 'GOTOOLDIR')
    this.toolStrategies.set('nm', 'GOTOOLDIR')
    this.toolStrategies.set('objdump', 'GOTOOLDIR')
    this.toolStrategies.set('pack', 'GOTOOLDIR')
    this.toolStrategies.set('pprof', 'GOTOOLDIR')
    this.toolStrategies.set('tour', 'GOTOOLDIR')
    this.toolStrategies.set('trace', 'GOTOOLDIR')
    this.toolStrategies.set('vet', 'GOTOOLDIR')
    this.toolStrategies.set('yacc', 'GOTOOLDIR')

    // External Tools
    this.toolStrategies.set('git', 'PATH')

    // Other Tools Are Assumed To Be In PATH or GOBIN or GOPATH/bin
    // GOPATHBIN Can Be Used In The Future As A Strategy, If Required
    // GOPATHBIN Will Understand GO15VENDOREXPERIMENT
  }

  // Internal: Handle the specified error, if needed.
  handleError (err) {
    if (isTruthy(err.handle)) {
      err.handle()
    }
    // console.log(err)
  }

  // Internal: Try to find a tool with the default strategy (GOPATH/bin, then
  // PATH).
  // Returns the path to the tool, or false if it cannot be found.
  findToolWithDefaultStrategy (name, options = {}) {
    if (isFalsy(name) || name.constructor !== String || name.trim() === '') {
      return Promise.resolve(false)
    }

    // Default Strategy Is: Look For The Tool In GOPATH, Then Look In PATH
    return Promise.resolve().then(() => {
      return this.findToolInDelimitedEnvironmentVariable(name, 'GOPATH', options)
    }).then((tool) => {
      if (isTruthy(tool)) {
        return tool
      }
      return this.findToolInDelimitedEnvironmentVariable(name, this.pathKey, options)
    })
  }

  // Internal: Try to find a tool in a delimited environment variable (e.g.
  // PATH).
  // Returns the path to the tool, or false if it cannot be found.
  findToolInDelimitedEnvironmentVariable (toolName, key, options = {}) {
    if (isFalsy(toolName) || toolName.constructor !== String || toolName.trim() === '') {
      return false
    }

    let p = this.environment(options)[key]
    if (isFalsy(p)) {
      return false
    }

    let elements = p.split(path.delimiter)
    if (key === 'GOPATH' && isTruthy(this.environment(options)['GO15VENDOREXPERIMENT'])) {
      // TODO: Understand Vendor Experiment Paths Better
      // elements.unshift('vendor')
    }
    for (let element of elements) {
      let item = ''
      if (key === 'GOPATH') {
        item = path.join(element, 'bin', toolName + this.executableSuffix)
      } else {
        item = path.join(element, toolName + this.executableSuffix)
      }

      if (fs.existsSync(item) && fs.statSync(item).isFile()) {
        return item
      }
    }

    return false
  }
}

export {Locator}
<commit_msg>Remove Log Statements<commit_after>'use babel'

import {CompositeDisposable} from 'atom'
import {isTruthy, isFalsy, isEmpty} from './check'
import {Executor} from './executor'
import pathhelper from './pathhelper'
import _ from 'lodash'
import fs from 'fs'
import os from 'os'
import path from 'path'

class Locator {
  constructor (options) {
    this.subscriptions = new CompositeDisposable()
    this.environmentFn = null
    this.executor = null
    this.executableSuffix = ''
    this.pathKey = 'PATH'
    if (os.platform() === 'win32') {
      this.executableSuffix = '.exe'
      this.pathKey = 'Path'
    }
    this.goExecutables = ['go' + this.executableSuffix, 'goapp' + this.executableSuffix]
    this.readyFn = null
    if (isTruthy(options)) {
      if (isTruthy(options.environment)) {
        this.environmentFn = options.environment
      }
      if (isTruthy(options.ready)) {
        this.readyFn = options.ready
      }
      if (isTruthy(options.executor)) {
        this.executor = options.executor
      }
    }

    if (this.executor === null) {
      this.executor = new Executor({environmentFn: this.environment.bind(this)})
    }

    this.subscriptions.add(this.executor)
    this.goLocators = [
      // Avoid using gorootLocator / GOROOT unless you know what you're doing
      // (and assume you don't know what you're unless you have significant
      // go experience)
      () => { return this.gorootLocator() },
      () => { return this.editorconfigLocator() },
      () => { return this.configLocator() },
      () => { return this.pathLocator() },
      () => { return this.defaultLocator() }
    ]

    this.setKnownToolStrategies()
  }

  dispose () {
    this.resetRuntimes()
    if (this.subscriptions) {
      this.subscriptions.dispose()
    }
    this.goLocators = null
    this.executableSuffix = null
    this.pathKey = null
    this.goExecutables = null
    this.subscriptions = null
    this.environmentFn = null
    this.executor = null
    this.readyFn = null
    this.toolLocations = null
    this.toolStrategies = null
  }

  // Public: Get the go runtime(s).
  // Returns an array of {Object} where each item contains the output from "go
  // env", or false if no runtimes are found.
  runtimes (options = {}) {
    if (isTruthy(this.runtimesCache)) {
      return Promise.resolve(this.runtimesCache)
    }

    return new Promise((resolve, reject) => {
      let candidates = this.runtimeCandidates(options)
      if (isEmpty(candidates)) {
        return resolve([])
      }

      let viableCandidates = []
      for (let candidate of candidates) {
        let goversion = this.executor.execSync(candidate, ['version'], {cwd: path.dirname(candidate)})
        if (isTruthy(goversion) && goversion.code === 0 && goversion.stdout.startsWith('go ')) {
          let v = {path: candidate, version: goversion.stdout.replace(/\r?\n|\r/g, '')}
          let versionComponents = v.version.split(' ')
          v.name = versionComponents[2]
          v.semver = versionComponents[2]
          if (v.semver.startsWith('go')) {
            v.semver = v.semver.substring(2, v.semver.length)
          }
          viableCandidates.push(v)
        }
      }

      let finalCandidates = []
      for (let viableCandidate of viableCandidates) {
        let goenv = this.executor.execSync(viableCandidate.path, ['env'], {cwd: path.dirname(viableCandidate.path)})
        if (isTruthy(goenv) && goenv.code === 0 && goenv.stdout.trim() !== '') {
          let items = goenv.stdout.split('\n')
          for (let item of items) {
            item = item.replace(/[\n\r]/g, '')
            if (item.includes('=')) {
              let tuple = item.split('=')
              let key = tuple[0]
              let value = tuple[1]
              if (tuple.length > 2) {
                value = tuple.slice(1, tuple.length + 1).join('=')
              }
              if (os.platform() === 'win32') {
                if (key.startsWith('set ')) {
                  key = key.substring(4, key.length)
                }
              } else {
                if (value.length > 2) {
                  value = value.substring(1, value.length - 1)
                } else {
                  value = ''
                }
              }
              viableCandidate[key] = value
            }
          }
          finalCandidates.push(viableCandidate)
        }
      }

      this.runtimesCache = finalCandidates
      resolve(this.runtimesCache)
    })
  }

  // Deprecated: Use runtime(options) instead.
  runtimeForProject (project) {
    return this.runtime()
  }

  // Public: Get the go runtime.
  // Returns an {Object} which contains the output from "go env", or false if
  // no runtime is found.
  runtime (options = {}) {
    return this.runtimes(options).then((r) => {
      if (isFalsy(r) || r.length < 1) {
        return false
      } else {
        return r[0]
      }
    })
  }

  // Public: Get the gopath.
  // Returns the GOPATH if it exists, or false if it is not defined.
  gopath (options = {}) {
    let e = this.rawEnvironment(options)
    if (isFalsy(e.GOPATH) || e.GOPATH.trim() === '') {
      return false
    }

    return pathhelper.expand(e, e.GOPATH)
  }

  // Public: Find the specified tool.
  // Returns the path to the tool if found, or false if it cannot be found.
  findTool (name, options = {}) {
    if (isFalsy(name) || name.constructor !== String || name.trim() === '') {
      return Promise.resolve(false)
    }

    let strategy = false
    return Promise.resolve(null).then(() => {
      if (this.toolStrategies.has(name)) {
        strategy = this.toolStrategies.get(name)
      }
      if (isFalsy(strategy)) {
        strategy = 'DEFAULT'
      }
    }).then(() => {
      if (strategy !== 'GOROOTBIN' && strategy !== 'GOTOOLDIR') {
        return false
      }

      return this.runtime(options).then((runtime) => {
        if (isFalsy(runtime)) {
          return false
        }

        if (strategy === 'GOROOTBIN') {
          return path.join(runtime.GOROOT, 'bin', name + runtime.GOEXE)
        } else if (strategy === 'GOTOOLDIR') {
          return path.join(runtime.GOTOOLDIR, name + runtime.GOEXE)
        }
        return false
      })
    }).then((specificTool) => {
      if (isTruthy(specificTool)) {
        return this.stat(specificTool).then((s) => {
          if (isTruthy(s) && s.isFile()) {
            return specificTool
          }
        }).catch((err) => {
          this.handleError(err)
          return false
        })
      }

      if (strategy === 'GOPATHBIN') {
        return this.findToolInDelimitedEnvironmentVariable(name, 'GOPATH', options)
      }

      if (strategy === 'PATH') {
        return this.findToolInDelimitedEnvironmentVariable(name, this.pathKey, options)
      }

      return this.findToolWithDefaultStrategy(name, options)
    })
  }

  resetRuntimes () {
    this.runtimesCache = null
  }

  statishSync (pathValue) {
    let stat = false
    if (isTruthy(pathValue) && !(pathValue.trim() === '')) {
      try { stat = fs.statSync(pathValue) } catch (e) { }
    }
    return stat
  }

  stat (p) {
    if (isFalsy(p) || p.constructor !== String || p.trim() === '') {
      return Promise.resolve(false)
    }

    return new Promise((resolve, reject) => {
      fs.stat(p, (err, stat) => {
        if (isTruthy(err)) {
          this.handleError(err)
          resolve(false)
          return
        }
        resolve(stat)
      })
    })
  }

  pathExists (p) {
    return this.exists(p).then((e) => {
      if (isFalsy(e)) {
        return false
      }
      return p
    })
  }

  fileExists (p) {
    return this.stat(p).then((s) => {
      if (isFalsy(s)) {
        return false
      }

      if (s.isFile()) {
        return p
      }

      return false
    })
  }

  directoryExists (p) {
    return this.stat(p).then((s) => {
      if (isFalsy(s)) {
        return false
      }

      if (s.isDirectory()) {
        return p
      }

      return false
    })
  }

  exists (p) {
    return this.stat(p).then((s) => {
      if (isFalsy(s)) {
        return false
      }

      return true
    })
  }

  runtimeCandidates (options = {}) {
    let candidates = []
    for (let locator of this.goLocators) {
      let c = locator(options)
      if (isTruthy(c) && c.constructor === Array && c.length > 0) {
        candidates = _.union(candidates, c)
      }
    }
    return candidates
  }

  editorconfigLocator (options = {}) {
    // TODO: .editorconfig
    return false
  }

  // Internal: Find a go installation using your Atom config. Deliberately
  // undocumented, as this method is discouraged.
  configLocator (options = {}) {
    let goinstallation = atom.config.get('go-config.goinstallation')
    let stat = this.statishSync(goinstallation)
    if (isTruthy(stat)) {
      let d = goinstallation
      if (stat.isFile()) {
        d = path.dirname(goinstallation)
      }
      return this.findExecutablesInPath(d, this.executables, options)
    }

    return []
  }

  gorootLocator (options = {}) {
    let g = this.environment(options).GOROOT
    if (isFalsy(g) || g.trim() === '') {
      return []
    }
    return this.findExecutablesInPath(path.join(g, 'bin'), this.goExecutables, options)
  }

  pathLocator (options = {}) {
    return this.findExecutablesInPath(this.environment(options)[this.pathKey], this.goExecutables, options)
  }

  defaultLocator (options = {}) {
    let installPaths = []
    if (os.platform() === 'win32') {
      /*
      c:\go\bin = Binary Distribution
      c:\tools\go\bin = Chocolatey
      */
      installPaths.push(path.join('c:', 'go', 'bin'))
      installPaths.push(path.join('c:', 'tools', 'go', 'bin'))
    } else {
      /*
      /usr/local/go/bin = Binary Distribution
      /usr/local/bin = Homebrew
      */
      installPaths.push(path.join('/', 'usr', 'local', 'go', 'bin'))
      installPaths.push(path.join('/', 'usr', 'local', 'bin'))
    }
    return this.findExecutablesInPath(installPaths.join(path.delimiter), this.goExecutables, options)
  }

  findExecutablesInPath (pathValue, executables, options = {}) {
    let candidates = []
    if (isFalsy(pathValue) || pathValue.constructor !== String || pathValue.trim() === '') {
      return candidates
    }

    if (isFalsy(executables) || executables.constructor !== Array || executables.length < 1) {
      return candidates
    }

    let elements = pathhelper.expand(this.environment(options), pathValue).split(path.delimiter)
    for (let element of elements) {
      for (let executable of executables) {
        let candidate = path.join(element, executable)
        let stat = this.statishSync(candidate)
        if (isTruthy(stat) && stat.isFile()) {
          candidates.push(candidate)
        }
      }
    }
    return candidates
  }

  // Internal: Get a copy of the environment, with the GOPATH correctly set.
  // Returns an {Object} where the key is the environment variable name and the value is the environment variable value.
  environment (options = {}) {
    let env = this.rawEnvironment(options)
    let g = this.gopath(options)
    if (g && g !== '') {
      env.GOPATH = g
    }
    return env
  }

  rawEnvironment (options = {}) {
    let env = process.env
    if (isTruthy(this.environmentFn)) {
      env = this.environmentFn()
    }
    env = Object.assign({}, env)
    return env
  }

  // Internal: Indicates that the locator is ready, or not.
  // Returns true if ready, else false.
  ready () {
    if (isFalsy(this.readyFn)) {
      return true
    }
    return this.readyFn()
  }

  // Internal: Set the strategy for finding known or built-in tools.
  // Returns a map where the key is the tool name and the value is the strategy.
  setKnownToolStrategies () {
    this.toolStrategies = new Map()

    // Built-In Tools
    this.toolStrategies.set('go', 'GOROOTBIN')
    this.toolStrategies.set('gofmt', 'GOROOTBIN')
    this.toolStrategies.set('godoc', 'GOROOTBIN')
    this.toolStrategies.set('addr2line', 'GOTOOLDIR')
    this.toolStrategies.set('api', 'GOTOOLDIR')
    this.toolStrategies.set('asm', 'GOTOOLDIR')
    this.toolStrategies.set('cgo', 'GOTOOLDIR')
    this.toolStrategies.set('compile', 'GOTOOLDIR')
    this.toolStrategies.set('cover', 'GOTOOLDIR')
    this.toolStrategies.set('dist', 'GOTOOLDIR')
    this.toolStrategies.set('doc', 'GOTOOLDIR')
    this.toolStrategies.set('fix', 'GOTOOLDIR')
    this.toolStrategies.set('link', 'GOTOOLDIR')
    this.toolStrategies.set('nm', 'GOTOOLDIR')
    this.toolStrategies.set('objdump', 'GOTOOLDIR')
    this.toolStrategies.set('pack', 'GOTOOLDIR')
    this.toolStrategies.set('pprof', 'GOTOOLDIR')
    this.toolStrategies.set('tour', 'GOTOOLDIR')
    this.toolStrategies.set('trace', 'GOTOOLDIR')
    this.toolStrategies.set('vet', 'GOTOOLDIR')
    this.toolStrategies.set('yacc', 'GOTOOLDIR')

    // External Tools
    this.toolStrategies.set('git', 'PATH')

    // Other Tools Are Assumed To Be In PATH or GOBIN or GOPATH/bin
    // GOPATHBIN Can Be Used In The Future As A Strategy, If Required
    // GOPATHBIN Will Understand GO15VENDOREXPERIMENT
  }

  // Internal: Handle the specified error, if needed.
  handleError (err) {
    if (isTruthy(err.handle)) {
      err.handle()
    }
    // console.log(err)
  }

  // Internal: Try to find a tool with the default strategy (GOPATH/bin, then
  // PATH).
  // Returns the path to the tool, or false if it cannot be found.
  findToolWithDefaultStrategy (name, options = {}) {
    if (isFalsy(name) || name.constructor !== String || name.trim() === '') {
      return Promise.resolve(false)
    }

    // Default Strategy Is: Look For The Tool In GOPATH, Then Look In PATH
    return Promise.resolve().then(() => {
      return this.findToolInDelimitedEnvironmentVariable(name, 'GOPATH', options)
    }).then((tool) => {
      if (isTruthy(tool)) {
        return tool
      }
      return this.findToolInDelimitedEnvironmentVariable(name, this.pathKey, options)
    })
  }

  // Internal: Try to find a tool in a delimited environment variable (e.g.
  // PATH).
  // Returns the path to the tool, or false if it cannot be found.
  findToolInDelimitedEnvironmentVariable (toolName, key, options = {}) {
    if (isFalsy(toolName) || toolName.constructor !== String || toolName.trim() === '') {
      return false
    }

    let p = this.environment(options)[key]
    if (isFalsy(p)) {
      return false
    }

    let elements = p.split(path.delimiter)
    if (key === 'GOPATH' && isTruthy(this.environment(options)['GO15VENDOREXPERIMENT'])) {
      // TODO: Understand Vendor Experiment Paths Better
      // elements.unshift('vendor')
    }
    for (let element of elements) {
      let item = ''
      if (key === 'GOPATH') {
        item = path.join(element, 'bin', toolName + this.executableSuffix)
      } else {
        item = path.join(element, toolName + this.executableSuffix)
      }

      if (fs.existsSync(item) && fs.statSync(item).isFile()) {
        return item
      }
    }

    return false
  }
}

export {Locator}
<|endoftext|>


<commit_before>/*

Scraoer script searching google playstore for apps and archving said apps.

NOTE: 120 is max number of apps to receive at once through list method...
NOTE: this has a permission section could inspect when throw this into folder
NOTE: cronjob...

TODO: to avoid the situation of askign for a captcha use a throttle keyword
, all methods now support a throttle property, which defines an upper bound to
the amount of requests that will be attempted per second. Once that limit is reached,
further requests will be held until the second passes.

*/

var gplay = require("google-play-scraper");
var config = require("/etc/xray/config.json"); //See example_config.json
var fs = require("fs");
var _ = require("lodash");

async function rescrapeAppId(scrapeBase) {
  return (data = await Promise.all(
    _.map(scrapeBase, async val => {
      var id = val.appId;
      console.log("Scraping details on: ",id);
      var data = await gplay.app({
        appId: id
      });
      return data;
    })
  ));
}

//TODO: move region to config or section to iterate over
var region = "us";

async function scrapeColl(collectionType) {
  var res = await gplay.list({
    collection: collectionType,
    num: 20,
    region: region
  });
  return await rescrapeAppId(res);
}

(async () => {
  let scrapeResults = await scrapeColl(gplay.collection.TRENDING);

  //Staggering results to prevent blowing the stack
  _.chunk(scrapeResults, 2).forEach(arr => {
    _.map(arr, function(element) {
      var p = require("path");
      console.log(
        "Resolved Save Path",
        config.appdir,
        element.appId,
        "play",
        region,
        element.version
      );
      var saveDir = p.join(
        config.appdir,
        element.appId,
        "play",
        region,
        element.version
      );

      if (!fs.existsSync(saveDir)) {
        var shell = require("shelljs");
        shell.mkdir("-p", saveDir);
      }

      console.log("App save directory ", saveDir);
      var args = [
        "-d",
        element.appId,
        "-f",
        saveDir,
        "-c",
        config.credDownload,
        "-v"
      ];

      console.log("Python downloader playstore starting");

      const spw = require("child_process").spawn;

      const apk_downloader = spw("gplaycli", args);

      apk_downloader.stdout.on("data", data => {
        console.log(`stdout: ${data}`);
      });

      apk_downloader.stderr.on("data", data => {
        console.log(`stderr: ${data}`);
      });

      apk_downloader.on("close", async code => {
        if (code != 0) {
          console.log("err");
          console.log(`child process exited with code ${code}`);
          return;
        }

        //The expectance: apk -store -region -version over pipe
        console.log("Download complete for ", element.appId);

        // var db = require('./db');

        // var dbId = await db.insertPlayApp(element, region);

        // // Send a single message to the server.

        // var client = unix.createSocket('unix_dgram');
        // var unix = require('unix-dgram');
        // var message = Buffer(dbId + "-"+ element.appId + "-" + "play" +"-"+region + "-" + element.version);
        // client.on('error', console.error);
        // client.send(message, 0, message.length, config.sockpath);
        // client.close();
      });
    });
  });
})();
<commit_msg>All those categories + collections to download, can't wait<commit_after>/*

Scraoer script searching google playstore for apps and archving said apps.

NOTE: 120 is max number of apps to receive at once through list method...
NOTE: this has a permission section could inspect when throw this into folder
NOTE: cronjob...

TODO: to avoid the situation of askign for a captcha use a throttle keyword
, all methods now support a throttle property, which defines an upper bound to
the amount of requests that will be attempted per second. Once that limit is reached,
further requests will be held until the second passes.

*/

var gplay = require("google-play-scraper");
var config = require("/etc/xray/config.json"); //See example_config.json
var fs = require("fs");
var _ = require("lodash");

async function rescrapeAppId(scrapeBase) {
  return await Promise.all(_.map(scrapeBase, async val => {
      var id = val.appId;
      console.log("Scraping details on: ",id);
      return await gplay.app({
        appId: id
      });
    }));
}

//TODO: move region to config or section to iterate over
var region = "us";
var appStore = "play";
async function scrapeColl(collectionType) {
  var res = await gplay.list({
    collection: collectionType,
    num: 1,
    region: region
  });
  return await rescrapeAppId(res);
}

(async () => {
  // let scrapeResults = await scrapeColl(gplay.collection.TRENDING);
  // gplay.collection
  let scrapeResults =
  _.uniqBy(
    await Promise.all(_.flatMap(gplay.category, async catg => {
      return await Promise.all(_.map(gplay.collection, async coll => {

        // console.log(catg,coll);

        var res = await gplay.list({
          collection: coll,
          category: catg,
          num: 1,
          region: region
        });

        return await rescrapeAppId(res);
      }));
  }), e => { return e.appId; } ));

  console.log("Number of apps to download: ",Object.keys(scrapeResults).length);

  //Staggering results to prevent blowing the stack
  _.chunk(scrapeResults, 2).forEach(arr => {
    _.map(arr, function(element) {
      //console.log(element);
      var p = require("path");
      console.log(config.appdir,element.appId,appStore,region,
        element.version
      );

      var saveDir = p.join(config.appdir,element.appId,appStore,region,
        element.version
      );

      if (!fs.existsSync(saveDir)) {
        var shell = require("shelljs");
        shell.mkdir("-p", saveDir);
      }

      console.log("App save directory ", saveDir);
      var args = [
        "-d",
        element.appId,
        "-f",
        saveDir,
        "-c",
        config.credDownload,
        "-v"
      ];

      console.log("Python downloader playstore starting");

      const spw = require("child_process").spawn;

      const apk_downloader = spw("gplaycli", args);

      apk_downloader.stdout.on("data", data => {
        console.log(`stdout: ${data}`);
      });

      apk_downloader.stderr.on("data", data => {
        console.log(`stderr: ${data}`);
      });

      apk_downloader.on("close", async code => {
        if (code != 0) {
          console.log("err");
          console.log(`child process exited with code ${code}`);
          return;
        }

        console.log("Download process complete for ", element.appId);
        //TODO: check the actual success of the download + see if the app is done? or just write tests right?
        // var db = require('./db');

        // var dbId = await db.insertPlayApp(element, region);

        // // Send a single message to the server.

        var client = unix.createSocket('unix_dgram');
        var unix = require('unix-dgram');

        var message = Buffer(dbId + "-"+ element.appId + "-" + "play" +"-"+region + "-" + element.version);

        client.on('error', console.error);
        client.send(message, 0, message.length, config.sockpath);
        client.close();

      });
    });
  });
})();
<|endoftext|>


<commit_before>
/*

Background ndoe script scraping apks from playstore.
Categorising into folders on genre.
Does a apk download step by step and just spawning a child
python process. That downloader was cool so hey, why not use it!

https://www.npmjs.com/package/google-play-scraper

NOTE: 120 is max number of apps to receive at once through list method...
NOTE: this has a permission section could inspect when throw this into folder
NOTE: cronjob...
TODO: to avoid the situation of askign for a captcha use a throttle keyword
, all methods now support a throttle property, which defines an upper bound to the amount of requests that will be attempted per second. Once that limit is reached, further requests will be held until the second passes.

*/
var gplay = require('google-play-scraper'); //google play store query scrapper.
var fs = require('fs');
var _ = require('lodash');
var config = require('/etc/xray/config.json')

async function rescrapeAppId(scrapeBase) {

  return data = await Promise.all(_.map(scrapeBase, async (val) => {
      var id = val.appId;
      console.log(id);
      var data = await gplay.app({
        appId: id
      });
      //console.log(data);
      return data;
  }));
}

/*await versions */

var region = 'us';

async function scrapeCollection(collectionType) {
  var res = await gplay.list({
      collection: collectionType,
      num: 20,
      region: region
    });
  return await rescrapeAppId(res);
}

(async () => {
  let scrapeResults = await scrapeCollection(gplay.collection.TRENDING);
  //scrapeResults = scrapeCollectionResult(gplay.collection.TRENDING).then(requeryOnAppId);

  //  console.log(scrapeResults);

function sleep(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
}

_.chunk(scrapeResults, 2).forEach((arr) => {
//iterate results over gplay list
  _.map(arr, function(element) {
    var p = require('path');
    console.log("Resolved Save Path",config.appdir, element.appId,'play',region,element.version)
    var saveDir = p.join(config.appdir, element.appId,'play',region,element.version);

    if(!fs.existsSync(saveDir)) {
      //fs.mkdirSync(saveDir);
      //Needed tp create intermediate dirs if neccassary...did not know if path had this optition
      var shell = require('shelljs');
      shell.mkdir('-p',saveDir);
    }

    console.log("App save directory ", saveDir);
    var args =  ["-d", element.appId,
                "-f", saveDir,
		            "-c", config.credDownload,
                "-v"]

    //Pretty nice tool can just use this and then organise apps by section...
    console.log("Python downloader playstore starting");

    const spw = require('child_process').spawn;

    const apk_downloader = spw('gplaycli',args);

    apk_downloader.stdout.on('data', (data) => {
      console.log(`stdout: ${data}`);

    });

    apk_downloader.stderr.on('data', (data) => {
      console.log(`stderr: ${data}`);
    });

    apk_downloader.on('close', async (code) => {
      if(code != 0){
        console.log('err')
        console.log(`child process exited with code ${code}`);
        return;
      }

      //The expectance: apk -store -region -version over pipe
      console.log("Download complete for ", element.appId);

      var db = require('./db');

      var dbId = await db.insertPlayApp(element, region);

      // Send a single message to the server.

      var client = unix.createSocket('unix_dgram');
      var unix = require('unix-dgram');
      var message = Buffer(dbId + "-"+ element.appId + "-" + "play" +"-"+region + "-" + element.version);
      client.on('error', console.error);
      client.send(message, 0, message.length, config.sockpath);
      client.close();
    });
  });
});
})();
<commit_msg>Cleaned up docs + function calls<commit_after>/*

Scraoer script searching google playstore for apps and archving said apps.

NOTE: 120 is max number of apps to receive at once through list method...
NOTE: this has a permission section could inspect when throw this into folder
NOTE: cronjob...

TODO: to avoid the situation of askign for a captcha use a throttle keyword
, all methods now support a throttle property, which defines an upper bound to
the amount of requests that will be attempted per second. Once that limit is reached,
further requests will be held until the second passes.

*/

var gplay = require("google-play-scraper");
var config = require("/etc/xray/config.json"); //See example_config.json
var fs = require("fs");
var _ = require("lodash");

async function rescrapeAppId(scrapeBase) {
  return (data = await Promise.all(
    _.map(scrapeBase, async val => {
      var id = val.appId;
      console.log("Scraping details on: ",id);
      var data = await gplay.app({
        appId: id
      });
      return data;
    })
  ));
}

//TODO: move region to config or section to iterate over
var region = "us";

async function scrapeColl(collectionType) {
  var res = await gplay.list({
    collection: collectionType,
    num: 20,
    region: region
  });
  return await rescrapeAppId(res);
}

(async () => {
  let scrapeResults = await scrapeColl(gplay.collection.TRENDING);

  //Staggering results to prevent blowing the stack
  _.chunk(scrapeResults, 2).forEach(arr => {
    _.map(arr, function(element) {
      var p = require("path");
      console.log(
        "Resolved Save Path",
        config.appdir,
        element.appId,
        "play",
        region,
        element.version
      );
      var saveDir = p.join(
        config.appdir,
        element.appId,
        "play",
        region,
        element.version
      );

      if (!fs.existsSync(saveDir)) {
        var shell = require("shelljs");
        shell.mkdir("-p", saveDir);
      }

      console.log("App save directory ", saveDir);
      var args = [
        "-d",
        element.appId,
        "-f",
        saveDir,
        "-c",
        config.credDownload,
        "-v"
      ];

      console.log("Python downloader playstore starting");

      const spw = require("child_process").spawn;

      const apk_downloader = spw("gplaycli", args);

      apk_downloader.stdout.on("data", data => {
        console.log(`stdout: ${data}`);
      });

      apk_downloader.stderr.on("data", data => {
        console.log(`stderr: ${data}`);
      });

      apk_downloader.on("close", async code => {
        if (code != 0) {
          console.log("err");
          console.log(`child process exited with code ${code}`);
          return;
        }

        //The expectance: apk -store -region -version over pipe
        console.log("Download complete for ", element.appId);

        // var db = require('./db');

        // var dbId = await db.insertPlayApp(element, region);

        // // Send a single message to the server.

        // var client = unix.createSocket('unix_dgram');
        // var unix = require('unix-dgram');
        // var message = Buffer(dbId + "-"+ element.appId + "-" + "play" +"-"+region + "-" + element.version);
        // client.on('error', console.error);
        // client.send(message, 0, message.length, config.sockpath);
        // client.close();
      });
    });
  });
})();
<|endoftext|>