<commit_before>var Stex    = require('stex');
var errors  = Stex.errors;
var _       = Stex._;
var Promise = Stex.Promise;

module.exports = Stex.new(__dirname + "/..", function(stex) {
  var router              = stex.router;
  var wallet              = require("./models/wallet");
  var loginFailureTracker = require("./models/login-failure-tracker");
  var refererTracker      = require("./models/referer-tracker");

  router.post('/wallets/show', function(req, res, next) {
    refererTracker.track(req);

    var id = (req.body.id || "").toString();

    if (_.isEmpty(id)) {
      res.status(400).send({
        "status": "fail",
        "field":  "id",
        "code":   "missing"
      });
      return;
    }

    loginFailureTracker.isLoginAllowed(req.ip).then(function(isAllowed) {
      if(isAllowed) {
        actuallyLogin();
      } else {
        failSlow();
      }
    });

    function actuallyLogin() {
      wallet.get(id).then(function(wallet) {
        if(typeof wallet === "undefined") {
          failAndRecordLockout(req,res)();
        } else {
          res.send({
            "status" : "success",
            "data"   : _.omit(wallet, ['recoveryId', 'recoveryData'])
          });
        }
      });
    }

    function failSlow() {
      Promise.delay(loginFailureTracker.SLEEP_TIME).then(fail);
    }

    function fail() {
      res.status(404).send({ "status": "fail", "code": "not_found" });
    }
  });

  router.post('/wallets/create', function(req, res, next) {
    var walletToSubmit = _.pick(req.body, [
      'id',
      'authToken',
      'mainData',
      'mainDataHash',
      'keychainData',
      'keychainDataHash'
    ]);

    wallet.create(walletToSubmit)
      .then (function(wallet) {
        res.send({"status" : "success"});
      })
      .catch(wallet.errors.InvalidHash, function(e) {
        res.status(400).send({
          "status": "fail",
          "field":  e.field,
          "code":   "invalid_hash"
        });
      })
      .catch(errors.MissingField, function(e) {
        res.status(400).send({
          "status": "fail",
          "field":  e.field,
          "code":   "missing"
        });
      })
      .catch(errors.DuplicateRecord, function(e) {
        res.status(400).send({
          "status": "fail",
          "field":  "id",
          "code":   "already_taken"
        });
      })
      .catch(function(e) {
        next(e);
      });
  });

  router.post('/wallets/update', function(req, res, next) {
    var id        = req.body.id;
    var authToken = req.body.authToken;
    var changes   = _.pick(req.body, [
      'mainData',
      'mainDataHash',
      'keychainData',
      'keychainDataHash',
    ]);

    wallet.update(id, authToken, changes)
      .then (function(wallet) {
        res.send({"status" : "success"});
      })
      .catch(errors.RecordNotFound, errors.Forbidden, failAndRecordLockout(req,res))
      .catch(wallet.errors.InvalidHash, function(e) {
        res.status(400).send({
          "status": "fail",
          "field":  e.field,
          "code":   "invalid_hash"
        });
      })
      .catch(function(e) {
        next(e);
      });
  });

  router.post('/wallets/replace', function(req, res, next) {
    var replaceArgs = _.at(req.body, 'oldId', 'oldAuthToken', 'newId', 'newAuthToken');

    wallet.replace.apply(null, replaceArgs)
      .then(function () {
        res.send({"status" : "success"});
      })
      .catch(errors.RecordNotFound, errors.Forbidden, failAndRecordLockout(req,res))
      .catch(function(e) {
        next(e);
      });
  });

  router.post('/wallets/recover', function(req, res, next) {
    var recoveryId = req.body.recoveryId;

    if (_.isEmpty(recoveryId)) {
      res.status(400).send({
        "status": "fail",
        "field":  "recoveryId",
        "code":   "missing"
      });
      return;
    }

    wallet.getByRecoveryId(recoveryId).then(function(wallet) {
      if(typeof wallet === "undefined") {
        failAndRecordLockout(req,res)();
      } else {
        res.send({
          "status" : "success",
          "data"   : wallet
        });
      }
    });
  });

  router.post('/wallets/create_recovery_data', function(req, res, next) {
    var id        = req.body.id;
    var authToken = req.body.authToken;
    var changes   = _.pick(req.body, [
      'recoveryId',
      'recoveryData',
      'recoveryDataHash'
    ]);

    wallet.createRecovery(id, authToken, changes)
      .then (function(wallet) {
        res.send({"status" : "success"});
      })
      .catch(errors.RecordNotFound, errors.Forbidden, failAndRecordLockout(req,res))
      .catch(wallet.errors.InvalidHash, function(e) {
        res.status(400).send({
          "status": "fail",
          "field":  e.field,
          "code":   "invalid_hash"
        });
      })
      .catch(function(e) {
        next(e);
      });
  });

  function failAndRecordLockout(req, res) {
    return function() {
      loginFailureTracker.record(req.ip);
      res.status(404).send({
        "status": "fail",
        "code":   "not_found"
      });
    };
  }
});
<commit_msg>Adds ip lockout to any action that uses walletID<commit_after>var Stex    = require('stex');
var errors  = Stex.errors;
var _       = Stex._;
var Promise = Stex.Promise;

module.exports = Stex.new(__dirname + "/..", function(stex) {
  var router              = stex.router;
  var wallet              = require("./models/wallet");
  var loginFailureTracker = require("./models/login-failure-tracker");
  var refererTracker      = require("./models/referer-tracker");

  router.post('/wallets/show', rejectLockedOutIps, function(req, res, next) {
    refererTracker.track(req);

    var id = (req.body.id || "").toString();

    if (_.isEmpty(id)) {
      res.status(400).send({
        "status": "fail",
        "field":  "id",
        "code":   "missing"
      });
      return;
    }

    wallet.get(id).then(function(wallet) {
      if(typeof wallet === "undefined") {
        failAndRecordLockout(req,res)();
      } else {
        res.send({
          "status" : "success",
          "data"   : _.omit(wallet, ['recoveryId', 'recoveryData'])
        });
      }
    });
  });

  router.post('/wallets/create', function(req, res, next) {
    var walletToSubmit = _.pick(req.body, [
      'id',
      'authToken',
      'mainData',
      'mainDataHash',
      'keychainData',
      'keychainDataHash'
    ]);

    wallet.create(walletToSubmit)
      .then (function(wallet) {
        res.send({"status" : "success"});
      })
      .catch(wallet.errors.InvalidHash, function(e) {
        res.status(400).send({
          "status": "fail",
          "field":  e.field,
          "code":   "invalid_hash"
        });
      })
      .catch(errors.MissingField, function(e) {
        res.status(400).send({
          "status": "fail",
          "field":  e.field,
          "code":   "missing"
        });
      })
      .catch(errors.DuplicateRecord, function(e) {
        res.status(400).send({
          "status": "fail",
          "field":  "id",
          "code":   "already_taken"
        });
      })
      .catch(function(e) {
        next(e);
      });
  });

  router.post('/wallets/update', rejectLockedOutIps, function(req, res, next) {
    var id        = req.body.id;
    var authToken = req.body.authToken;
    var changes   = _.pick(req.body, [
      'mainData',
      'mainDataHash',
      'keychainData',
      'keychainDataHash',
    ]);

    wallet.update(id, authToken, changes)
      .then (function(wallet) {
        res.send({"status" : "success"});
      })
      .catch(errors.RecordNotFound, errors.Forbidden, failAndRecordLockout(req,res))
      .catch(wallet.errors.InvalidHash, function(e) {
        res.status(400).send({
          "status": "fail",
          "field":  e.field,
          "code":   "invalid_hash"
        });
      })
      .catch(function(e) {
        next(e);
      });
  });

  router.post('/wallets/replace', rejectLockedOutIps, function(req, res, next) {
    var replaceArgs = _.at(req.body, 'oldId', 'oldAuthToken', 'newId', 'newAuthToken');

    wallet.replace.apply(null, replaceArgs)
      .then(function () {
        res.send({"status" : "success"});
      })
      .catch(errors.RecordNotFound, errors.Forbidden, failAndRecordLockout(req,res))
      .catch(function(e) {
        next(e);
      });
  });

  router.post('/wallets/recover', rejectLockedOutIps, function(req, res, next) {
    var recoveryId = req.body.recoveryId;

    if (_.isEmpty(recoveryId)) {
      res.status(400).send({
        "status": "fail",
        "field":  "recoveryId",
        "code":   "missing"
      });
      return;
    }

    wallet.getByRecoveryId(recoveryId).then(function(wallet) {
      if(typeof wallet === "undefined") {
        failAndRecordLockout(req,res)();
      } else {
        res.send({
          "status" : "success",
          "data"   : wallet
        });
      }
    });
  });

  router.post('/wallets/create_recovery_data', rejectLockedOutIps, function(req, res, next) {
    var id        = req.body.id;
    var authToken = req.body.authToken;
    var changes   = _.pick(req.body, [
      'recoveryId',
      'recoveryData',
      'recoveryDataHash'
    ]);

    wallet.createRecovery(id, authToken, changes)
      .then (function(wallet) {
        res.send({"status" : "success"});
      })
      .catch(errors.RecordNotFound, errors.Forbidden, failAndRecordLockout(req,res))
      .catch(wallet.errors.InvalidHash, function(e) {
        res.status(400).send({
          "status": "fail",
          "field":  e.field,
          "code":   "invalid_hash"
        });
      })
      .catch(function(e) {
        next(e);
      });
  });

  function failAndRecordLockout(req, res) {
    return function() {
      loginFailureTracker.record(req.ip);
      res.status(404).send({
        "status": "fail",
        "code":   "not_found"
      });
    };
  }

  function rejectLockedOutIps(req,res,next) {
    loginFailureTracker.isLoginAllowed(req.ip).then(function(isAllowed) {
      if(isAllowed) {
        next();
      } else {
        failSlow();
      }
    });

    function failSlow() {
      Promise.delay(loginFailureTracker.SLEEP_TIME)
        .then(function() {
          res.status(404).send({ "status": "fail", "code": "not_found" });
        });
    }
  }
});
<|endoftext|>


<commit_before>/*
*  Copyright 2014 TWO SIGMA OPEN SOURCE, LLC
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*         http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

(function() {
  'use strict';
  var retfunc = function() {
    return function(type) {
      var axisType = type;
      var axisBase = 10;
      var axisTime = 0, axisTimezone = "America/New_York";
      var axisValL = 0, axisValR = 1, axisValSpan;
      var axisPctL, axisPctR, axisPctSpan;
      var axisLabel;

      var months = ["Jan", "Feb", "Mar", "Apr", "May", "June",
          "July", "Aug", "Sep", "Oct", "Nov", "Dec"];
      var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];

      if (type == null) { type = "linear"; }  // linear, log, time, nanotime, category

      var dateIntws = [
        // milliseconds
        1, 5, 10, 50, 100, 500,
        // 1, 5, 10, 30, 60 seconds
        1000, 5000, 10000, 30000, 60000,
        // 5, 10, 30, 60 minutes
        300000, 600000, 1800000, 3600000,
        // 3, 6, 12, 24 hours
        3600000 * 3, 3600000 * 6, 3600000 * 12, 3600000 * 24,
        // 7, 30, 90, 180, 360 days
        86400000 * 7, 86400000 * 30, 86400000 * 90, 86400000 * 180, 86400000 * 360,
        // 5, 10, 25, 50, 100 years
        31104000000 * 5, 31104000000 * 10, 31104000000 * 25, 31104000000 * 50, 31104000000 * 100
      ];
      var numIntws = [], numFixs = [];
      var bs = 1E-6;
      for (var i = 0; i < 18; i++) {
        var f = Math.max(6 - i, 0);
        numIntws = numIntws.concat([1.0 * bs, 2.5 * bs, 5.0 * bs]);  // generate 1s, 5s
        numFixs = numFixs.concat([f, f + 1, f]);
        bs *= 10;
      }

      var axisCoords, axisCoordLabels, axisStep, axisFixed;

      this.axisPow = function(pct) {
        return Math.pow(axisBase, pct * axisValSpan + axisValL);
      };
      this.setLabel = function(label) {
        axisLabel = label;
      };
      this.setRange = function(vl, vr, para) {
        if (vl != null) { axisValL = vl; }
        if (vr != null) { axisValR = vr; }
        if (axisType === "log") {
          if (para != null ) { axisBase = para; }
          if (axisBase <= 1) {
            axisBase = 10;
            console.error("cannot set base to <= 1");
          }
        } else if (axisType === "time"){
          if (para != null) { axisTimezone = para; }
        }
        axisValSpan = axisValR - axisValL;
      };
      this.setCoords = function(pl, pr, count) {
        if (pr < pl) {
          console.error("cannot set right coord < left coord");
          return;
        }
        if (count == null) {
          console.error("missing setCoords count");
          count = 1;
        }
        axisPctL = pl;
        axisPctR = pr;
        axisPctSpan = pr - pl;
        var span = axisPctSpan * axisValSpan;
        var intws, fixs;
        if (axisType === "time") {
          intws = dateIntws;
          fixs = {};
        } else {
          intws = numIntws;
          fixs = numFixs;
        }

        var w, f, mindiff = 1E100;
        for (var i = intws.length - 1; i >= 0; i--) {
          var nowcount = span / intws[i];
          var diff = Math.abs(nowcount - count);
          if (diff < mindiff) {
            mindiff = diff;
            w = intws[i];
            f = fixs[i];
          }
        }
        axisStep = w;
        axisFixed = f;

        var val = Math.ceil(this.getValue(pl) / w) * w,
            valr = this.getValue(pr);
        var coords = [],
            labels = [];
        while(val < valr) {
          var pct = this.getPercent(val);
          labels.push(this.getString(pct));
          coords.push(pct);
          val += w;
        }
        axisCoords = coords;
        axisCoordLabels = labels;
      };
      this.getType = function() { return axisType; };
      this.getTimezone = function() { return axisTimezone; };
      this.getCoords = function() { return _.without(axisCoords); };
      this.getCoordLabels = function() { return _.without(axisCoordLabels); };
      this.getStep = function() { return axisStep; };
      this.getFixed = function() { return axisFixed; };
      this.getLabel = function() { return axisLabel; };
      this.getPercent = function(val) {
        if (val < axisValL) { val = axisValL; }
        if (val > axisValR) { val = axisValR; }
        return (val - axisValL) / axisValSpan;
      };
      this.getValue = function(pct) {
        if (pct < 0) { pct = 0; }
        if (pct > 1) { pct = 1; }
        return axisValSpan * pct + axisValL;
      };
      this.getString = function(pct) {
        if (axisType != "time" && axisType != "nanotime") {
          if (axisType === "log") {
            return "" + axisBase + "^" + this.getValue(pct).toFixed(axisFixed);
          } else {
            return "" + this.getValue(pct).toFixed(axisFixed);
          }
        }
        var val = this.getValue(pct);
        var span = axisValSpan * axisPctSpan;

        var d, ret = "";
        if (axisType === "time") {
          d = Math.ceil(val * 1000) / 1000;
        }
        else if (axisType === "nanotime"){
          var bval = new Big(val).plus(axisOffset).div(1000000);
          d = new Date(bval.toFixed(0));
        }

        var padStr = function(val, len) {
          var str = "" + val;
          while (str.length < len) str = "0" + str;
          return str;
        };
        if (span <= 1000) {
          ret = val + "  ";
          ret = moment(d).tz(axisTimezone).format(".SSS") + ( (d - Math.floor(d)).toFixed(axisFixed));
        } else if (span <= 1000 * 60) {
          ret = moment(d).tz(axisTimezone).format("mm:ss.SSS");
        } else if (span <= 1000 * 60 * 60) {
          ret = moment(d).tz(axisTimezone).format("HH:mm:ss");
        } else if (span <= 1000) {
          ret = moment(d).tz(axisTimeozne).format("MMM DD ddd, HH:mm");
        } else if (span <= 1000 * 60 * 60 * 24 * 30) {
          ret = moment(d).tz(axisTimezone).format("MMM DD ddd");
        } else {
          ret = moment(d).tz(axisTimezone).format("YYYY MMM");
        }

        if (type === "nanotime"  && span < 1000000) {
          var digits = bval.mod(1000000000).toFixed(0);
          if (span < 1000) {
            ret += "." + padStr(Math.floor(digits / 1), 9);
          } else if (span < 1000000) {
            ret += "." + padStr(Math.floor(digits / 1000), 6);
          } else {
            ret += "." + padStr(Math.floor(digits / 1000000), 3);
          }
        }
        return ret;
      };
    };
  };
  beaker.bkoFactory('plotAxis', [retfunc]);
})();
<commit_msg>remove redundant text<commit_after>/*
*  Copyright 2014 TWO SIGMA OPEN SOURCE, LLC
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*         http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

(function() {
  'use strict';
  var retfunc = function() {
    return function(type) {
      var axisType = type;
      var axisBase = 10;
      var axisTime = 0, axisTimezone = "America/New_York";
      var axisValL = 0, axisValR = 1, axisValSpan;
      var axisPctL, axisPctR, axisPctSpan;
      var axisLabel;

      if (type == null) { type = "linear"; }  // linear, log, time, nanotime, category

      var dateIntws = [
        // milliseconds
        1, 5, 10, 50, 100, 500,
        // 1, 5, 10, 30, 60 seconds
        1000, 5000, 10000, 30000, 60000,
        // 5, 10, 30, 60 minutes
        300000, 600000, 1800000, 3600000,
        // 3, 6, 12, 24 hours
        3600000 * 3, 3600000 * 6, 3600000 * 12, 3600000 * 24,
        // 7, 30, 90, 180, 360 days
        86400000 * 7, 86400000 * 30, 86400000 * 90, 86400000 * 180, 86400000 * 360,
        // 5, 10, 25, 50, 100 years
        31104000000 * 5, 31104000000 * 10, 31104000000 * 25, 31104000000 * 50, 31104000000 * 100
      ];
      var numIntws = [], numFixs = [];
      var bs = 1E-6;
      for (var i = 0; i < 18; i++) {
        var f = Math.max(6 - i, 0);
        numIntws = numIntws.concat([1.0 * bs, 2.5 * bs, 5.0 * bs]);  // generate 1s, 5s
        numFixs = numFixs.concat([f, f + 1, f]);
        bs *= 10;
      }

      var axisCoords, axisCoordLabels, axisStep, axisFixed;

      this.axisPow = function(pct) {
        return Math.pow(axisBase, pct * axisValSpan + axisValL);
      };
      this.setLabel = function(label) {
        axisLabel = label;
      };
      this.setRange = function(vl, vr, para) {
        if (vl != null) { axisValL = vl; }
        if (vr != null) { axisValR = vr; }
        if (axisType === "log") {
          if (para != null ) { axisBase = para; }
          if (axisBase <= 1) {
            axisBase = 10;
            console.error("cannot set base to <= 1");
          }
        } else if (axisType === "time"){
          if (para != null) { axisTimezone = para; }
        }
        axisValSpan = axisValR - axisValL;
      };
      this.setCoords = function(pl, pr, count) {
        if (pr < pl) {
          console.error("cannot set right coord < left coord");
          return;
        }
        if (count == null) {
          console.error("missing setCoords count");
          count = 1;
        }
        axisPctL = pl;
        axisPctR = pr;
        axisPctSpan = pr - pl;
        var span = axisPctSpan * axisValSpan;
        var intws, fixs;
        if (axisType === "time") {
          intws = dateIntws;
          fixs = {};
        } else {
          intws = numIntws;
          fixs = numFixs;
        }

        var w, f, mindiff = 1E100;
        for (var i = intws.length - 1; i >= 0; i--) {
          var nowcount = span / intws[i];
          var diff = Math.abs(nowcount - count);
          if (diff < mindiff) {
            mindiff = diff;
            w = intws[i];
            f = fixs[i];
          }
        }
        axisStep = w;
        axisFixed = f;

        var val = Math.ceil(this.getValue(pl) / w) * w,
            valr = this.getValue(pr);
        var coords = [],
            labels = [];
        while(val < valr) {
          var pct = this.getPercent(val);
          labels.push(this.getString(pct));
          coords.push(pct);
          val += w;
        }
        axisCoords = coords;
        axisCoordLabels = labels;
      };
      this.getType = function() { return axisType; };
      this.getTimezone = function() { return axisTimezone; };
      this.getCoords = function() { return _.without(axisCoords); };
      this.getCoordLabels = function() { return _.without(axisCoordLabels); };
      this.getStep = function() { return axisStep; };
      this.getFixed = function() { return axisFixed; };
      this.getLabel = function() { return axisLabel; };
      this.getPercent = function(val) {
        if (val < axisValL) { val = axisValL; }
        if (val > axisValR) { val = axisValR; }
        return (val - axisValL) / axisValSpan;
      };
      this.getValue = function(pct) {
        if (pct < 0) { pct = 0; }
        if (pct > 1) { pct = 1; }
        return axisValSpan * pct + axisValL;
      };
      this.getString = function(pct) {
        if (axisType != "time" && axisType != "nanotime") {
          if (axisType === "log") {
            return "" + axisBase + "^" + this.getValue(pct).toFixed(axisFixed);
          } else {
            return "" + this.getValue(pct).toFixed(axisFixed);
          }
        }
        var val = this.getValue(pct);
        var span = axisValSpan * axisPctSpan;

        var d, ret = "";
        if (axisType === "time") {
          d = Math.ceil(val * 1000) / 1000;
        }
        else if (axisType === "nanotime"){
          var bval = new Big(val).plus(axisOffset).div(1000000);
          d = new Date(bval.toFixed(0));
        }

        var padStr = function(val, len) {
          var str = "" + val;
          while (str.length < len) str = "0" + str;
          return str;
        };
        if (span <= 1000) {
          ret = val + "  ";
          ret = moment(d).tz(axisTimezone).format(".SSS") + ( (d - Math.floor(d)).toFixed(axisFixed));
        } else if (span <= 1000 * 60) {
          ret = moment(d).tz(axisTimezone).format("mm:ss.SSS");
        } else if (span <= 1000 * 60 * 60) {
          ret = moment(d).tz(axisTimezone).format("HH:mm:ss");
        } else if (span <= 1000) {
          ret = moment(d).tz(axisTimeozne).format("MMM DD ddd, HH:mm");
        } else if (span <= 1000 * 60 * 60 * 24 * 30) {
          ret = moment(d).tz(axisTimezone).format("MMM DD ddd");
        } else {
          ret = moment(d).tz(axisTimezone).format("YYYY MMM");
        }

        if (type === "nanotime"  && span < 1000000) {
          var digits = bval.mod(1000000000).toFixed(0);
          if (span < 1000) {
            ret += "." + padStr(Math.floor(digits / 1), 9);
          } else if (span < 1000000) {
            ret += "." + padStr(Math.floor(digits / 1000), 6);
          } else {
            ret += "." + padStr(Math.floor(digits / 1000000), 3);
          }
        }
        return ret;
      };
    };
  };
  beaker.bkoFactory('plotAxis', [retfunc]);
})();
<|endoftext|>


<commit_before>/**
 * Created by dreamarts on 2014/03/30.
 */

var should = require('should')
  , log = require('log')
  , fs = require('fs')
  , async = require('async')
  , mail = require('../../lib/mail')
  , mongo = require('../../lib/db')
  , test = require('../test_util')
  , Searcher = require('node-searcher')
  ;

var COLLS = ['mails', 'mails.files', 'mails.chunks', 'mails.df'];

var FIELD = ['k', 'c'];

var FREQ = {'tf': 'mails.of'};

describe('mail', function () {

  var db;

  before(function (done) {
    // テストが始まる前の処理
    test.open(function (err, _db) {
      db = _db;
      done();
    });
  });

  after(function (done) {
    // テストが終わった後の処理
    db.close();
    done();
  });

  it('追加 1', function (done) {
    test.remove(db, COLLS, function (err) {
      var path = 'test/mail/data/test_1.eml';
      mail.add(path, function (err, mailInfo) {
        mail.get({'messageId': '1397023383.498516.1.1000481@moe.dreamarts.co.jp'}, function (err, result) {
          should.not.exist(err);
          result.length.should.eql(1);
          done();
        });
      });
    });
  });

  // 上記の「追加」がテストOKなので、ここではライブラリ的に使います。
  function add(array, callback) {
    async.each(array, function(item, next) {
      mail.add(item, function (err) {
        next(err);
      });
    }, function(err) {
      callback(err);
    });
  }

  it('追加 2', function (done) {
    test.remove(db, COLLS, function (err) {
      var data = [
        'test/mail/data/test_1.eml', // '1397023383.498516.1.1000481@moe.dreamarts.co.jp'
        'test/mail/data/test_2.eml'  // 'EBEBFB71-96DD-4FF0-9787-49B4D8A684E4@dreamarts.co.jp'
        ];
      add(data, function (err) {
        mail.get({'messageId': '1397023383.498516.1.1000481@moe.dreamarts.co.jp'}, function (err, result) {
          should.not.exist(err);
          result.length.should.eql(1);
          done();
        });
      });
    });
  });

  it('Index作成', function (done) {

    var target = {
      collection: 'mails',
      attribute: 'tf',
      option: {
        condition: {}
      }
    };

    var source = {
      collection: 'mails.files',
      attribute: 'metadata.tf',
      key: 'metadata.parent',
      option: {
        condition: {}
      }
    };

    var searcher = new Searcher(mongo.url(), FIELD, FREQ);

    searcher.countup(target, source, function(err) {
      searcher.object_frequency(target, function(err) {
        done();
      });
    })
  });

});

<commit_msg>searcher.coutup テスト追加<commit_after>/**
 * Created by dreamarts on 2014/03/30.
 */

var should = require('should')
  , log = require('log')
  , fs = require('fs')
  , async = require('async')
  , mail = require('../../lib/mail')
  , mongo = require('../../lib/db')
  , test = require('../test_util')
  , Searcher = require('node-searcher')
  ;

var COLLS = ['mails', 'mails.files', 'mails.chunks', 'mails.df'];

var FIELD = ['k', 'c', 'w'];

var FREQ = {'tf': 'mails.of'};

describe('mail', function () {

  var db;

  before(function (done) {
    // テストが始まる前の処理
    test.open(function (err, _db) {
      db = _db;
      done();
    });
  });

  after(function (done) {
    // テストが終わった後の処理
    db.close();
    done();
  });

  it('追加 1', function (done) {
    test.remove(db, COLLS, function (err) {
      var path = 'test/mail/data/test_1.eml';
      mail.add(path, function (err, mailInfo) {
        mail.get({'messageId': '1397023383.498516.1.1000481@moe.dreamarts.co.jp'}, function (err, result) {
          should.not.exist(err);
          result.length.should.eql(1);
          done();
        });
      });
    });
  });

  // 上記の「追加」がテストOKなので、ここではライブラリ的に使います。
  function add(array, callback) {
    async.each(array, function(item, next) {
      mail.add(item, function (err) {
        next(err);
      });
    }, function(err) {
      callback(err);
    });
  }

  it('追加 2', function (done) {
    test.remove(db, COLLS, function (err) {
      var data = [
        'test/mail/data/test_1.eml', // '1397023383.498516.1.1000481@moe.dreamarts.co.jp'
        'test/mail/data/test_2.eml'  // 'EBEBFB71-96DD-4FF0-9787-49B4D8A684E4@dreamarts.co.jp'
        ];
      add(data, function (err) {
        mail.get({'messageId': '1397023383.498516.1.1000481@moe.dreamarts.co.jp'}, function (err, result) {
          should.not.exist(err);
          result.length.should.eql(1);
          done();
        });
      });
    });
  });

  it('Index作成', function (done) {

    var target = {
      collection: 'mails',
      attribute: 'tf',
      option: {
        condition: {}
      }
    };

    var source = {
      collection: 'mails.files',
      attribute: 'metadata.tf',
      key: 'metadata.parent',
      option: {
        condition: {}
      }
    };

    var searcher = new Searcher(mongo.url(), FIELD, FREQ);

    searcher.countup(target, source, function(err) {
      searcher.object_frequency(target, function(err) {
        searcher.tfiof(target, function (err) {
          done();
        });
      });
    })
  });

});

<|endoftext|>


<commit_before>var Q = require("q"),
	logger = require("../logging").getLogger(__LOGGER__),
	RLS = require("./RequestLocalStorage").getNamespace(),
	PromiseUtil = require("./PromiseUtil");

// This data structure defines the page interface.
//
// Each item represents a method that page objects (and middleware) may override.
//
// The keys here are method names.
//
// The values are tuples containing:
//   - Number of arguments to the method.
//   - Default implementation of the method.
//   - Normalization function applied to method output.
//
// Note that each of these methods also receives an additional argument,
// which is the next implementation of the method in the call chain.
//     - Middleware implementations _should_ call this in most cases.*
//     - Page implementations _may_ call this (it will be the default implementation).
//
// * Consider carefully before deciding not to call `next()` in middleware.
// Other middleware (and the page itself) may exhibit undefined behavior if a
// given method is not called.  Generally, only skip calling `next()` for
// short-circuit responses (e.g. a redirect from `handleRoute`).
//
var PAGE_METHODS = {
	handleRoute        : [1, () => ({code: 200}), Q],
	getTitle           : [0, () => "", Q],
	getScripts         : [0, () => [], standardizeScripts],
	getSystemScripts   : [0, () => [], standardizeScripts],
	getHeadStylesheets : [0, () => [], standardizeStyles],
	getMetaTags        : [0, () => [], standardizeMetaTags],
	getLinkTags        : [0, () => [], standardizeLinkTags],
	getBase            : [0, () => null, Q],
	getBodyClasses     : [0, () => [], Q],
	getElements        : [0, () => [], standardizeElements],
	getResponseData    : [0, () => "", Q],
};

// These are similar to `PAGE_METHODS`, but are not chained.
//
// Each page and middleware that implements a page hook will have its hook
// called in turn.  Hooks do not receive a `next()` method, and are not
// responsible for merging return values.
//
// Keys here are method names.
//
// The values are empty placeholder tuples.

var PAGE_HOOKS = {
	addConfig      : [],
	setConfig      : [],
	handleComplete : [],
};

// These methods will be made available on your page/middleware object.
//
// A method of the same name defined directly on your class will generate a warning.
//
var PAGE_MIXIN = {
	getRequest : makeGetter('request'),
	setRequest : makeSetter('request'),
	getConfig  : key => PageConfig.get(key),
}

function makeGetter(key){
	return () => (RLS()._PageUtilMixin||{})[key];
}

function makeSetter(key){
	return val => {
		(RLS()._PageUtilMixin||(RLS()._PageUtilMixin={}))[key] = val;
	}
}

function lazyMixinPageUtilMethods(page){
	var proto = Object.getPrototypeOf(page);
	if (proto._haveMixedInPageUtilMethods) return;

	proto._haveMixedInPageUtilMethods = true;

	Object.keys(PAGE_MIXIN).forEach(method => {
		if (proto[method]){
			logger.error(`PAGE_MIXINS method override: ${
				(proto.constructor||{}).name
			}.${method}`);
		}
		proto[method] = PAGE_MIXIN[method];
	});
}

// These `standardize*` functions show what will happen to the output of your
// page methods.
//
// For middleware authors: Be aware that these standardization functions will
// have been applied to the output of `next()` before you get access to it.
//
// These functions are also exposed via `PageUtil.standardize*`.

/**
 * This method takes in anything returned from a Page.getElements call and
 * returns the elements in a standardized format: an array of EarlyPromises of
 * ReactElements.
 */
function standardizeElements(elements) {

	// The return value could be a single element or an array.
	// First, let's make sure that it's an array.
	// Then, ensure that all elements are EarlyPromises.
	return PageUtil
		.makeArray(elements)
		.map(element => PromiseUtil.early(Q(element)));
}

function standardizeMetaTags(metaTags) {
	return PageUtil.makeArray(metaTags).map(metaTag => Q(metaTag));
}

function standardizeLinkTags(linkTags) {
	return PageUtil.makeArray(linkTags).map(linkTag => Q(linkTag));
}

function standardizeScripts(scripts) {
	return PageUtil.makeArray(scripts).map((script) => {
		if (script.href || script.text) {
			if (!script.type) script.type = "text/javascript";
			return script;
		}

		// if the answer was a string, let's make a script object
		return {href:script, type:"text/javascript"};
	})
}

function standardizeStyles(styles) {
	return PageUtil.makeArray(styles).map((style) => {
		if (style.href || style.text) {
			if (!style.type) style.type = "text/css";
			if (!style.media) style.media = "";

			return style;
		}

		// if the answer was a string, let's make a script object
		return {href:style, type:"text/css", media:""};
	})
}

var PageConfig = (function(){
	var logger = require("../logging").getLogger(__LOGGER__({label: 'PageConfig'}));

	// This gets bound to the outer `PageConfig`.
	//
	// Only `PageConfig.get(key)` is generally useful.
	//
	var PageConfig = {

		get(key) {

			// No access until all `Page.addConfig()` and
			// `Page.getConfig()` methods are complete.
			if (!RLS.PageConfigFinalized){
				throw new Error(`Premature access: "${key}"`);
			}

			// The key _must_ exist.
			if (!_obj().hasOwnProperty(key)){
				throw new Error(`Invalid key: "${key}"`);
			}

			return _obj()[key];
		},

		// Don't call this.  It's called for you.
		initFromPageWithDefaults(page, defaults) {

			// First set all defaults.  Then set all values.
			_setDefaults(defaults);
			page.addConfig().forEach(_setDefaults);
			page.setConfig().forEach(_setValues);

			logger.debug('Final', _obj());

			RLS.PageConfigFinalized = true;
		},
	}

	// Below here are helpers. They are hidden from outside callers.

	var _set = function(isDefault, obj) {

		// Get the current mutable config.
		var config = _obj();

		// Copy input values into it.
		Object.keys(obj||{}).forEach(key => {
			var keyExists = config.hasOwnProperty(key);
			if (isDefault && keyExists){
				// Can't make this fatal, because request
				// forwarding uses a dirty RLS() context.
				logger.warning(`Duplicate PageConfig default: "${key}"`);
			} else if (!isDefault && !keyExists) {
				throw new Error(`Missing PageConfig default: "${key}"`);
			}

			logger.debug(`${isDefault?"Default":"Set"} "${key}" => "${obj[key]}"`);

			config[key] = obj[key];
		});
	};

	var _setDefaults = _set.bind({}, true);
	var _setValues   = _set.bind({}, false);

	var _obj = function(){

		// Return the current mutable config.
		return RLS().PageConfig || (RLS().PageConfig = {});
	}

	return PageConfig;
})();

var logInvocation = function(name, func){
	return function(){
		logger.debug(`Call ${name}`);
		return func.apply(this, [].slice.call(arguments));
	}
}

var PageUtil = module.exports = {
	PAGE_METHODS,

	standardizeElements,
	standardizeMetaTags,
	standardizeScripts,
	standardizeStyles,

	PageConfig,

	/**
	 * Given an array of page instances, return an object that implements the page interface. This returned object's
	 * methods delegate to the first page in the pages array, passing the second page's implementation as the next
	 * variable (and the second page's implementation gets the third page's next, and so on).
	 */
	createPageChain(pages) {

		// This will be our return value.  It will be a mapping of
		// method names on page objects to chained function calls.
		var pageChain = {};

		pages.forEach(lazyMixinPageUtilMethods);

		Object.keys(PAGE_MIXIN).forEach(method => {
			pageChain[method] = logInvocation(method, PAGE_MIXIN[method]);
		});

		for (var method in PAGE_METHODS){

			if (PAGE_METHODS.hasOwnProperty(method)){

				var [
					argumentCount,
					defaultImpl,
					standardizeReturnValue
				] = PAGE_METHODS[method];

				pageChain[method] = logInvocation(method,
					PageUtil.createObjectFunctionChain(
						pages,
						method,
						argumentCount,
						defaultImpl,
						standardizeReturnValue
					)
				);
			}
		}

		Object.keys(PAGE_HOOKS).forEach(method => {

			// Grab a list of pages that implement this method.
			var implementors = pages.filter(page => page[method]);

			// The resulting method calls each implementor's method
			// in turn and returns an array containg in their
			// return values.
			pageChain[method] = logInvocation(method, function(){
				var args = [].slice.call(arguments);
				return implementors.map(
					page => page[method].apply(page, args)
				)
			});
		});

		return pageChain;
	},

	/**
	 * Given an array of objects and a function name, this returns a function that will call functionName on the first
	 * object, adding a next parameter to the end that is a function to call to the next object's implementation of functionName, and so on
	 * down the line. It is a way to make a middleware-like chain of functions, but the functions will be bound to the object in
	 * question from objects.
	 *
	 * The last object in the array will also receive a next parameter, which will call defaultImpl.
	 *
	 * standardizeReturnValue will be called on every return value from functionName so that next will always return the same type. This
	 * is useful when a function can return one of many different types.
	 */
	createObjectFunctionChain(objects, functionName, argumentCount, defaultImpl, standardizeReturnValue) {
		// our first order of business is to get an array of pure functions to chain; this involves finding
		// which objects have implementations for functionName, binding those implementations to the object
		// in question, and standardizing the return values.
		var functionsToChain = [];
		objects.forEach((object) => {
			if (object[functionName]) {
				functionsToChain.push(function() {
					return standardizeReturnValue(object[functionName].apply(object, arguments));
				});
			}
		});
		// the innermost implementation calls the default implementation and standardizes the result.
		functionsToChain.push(function() {
			return standardizeReturnValue(defaultImpl.apply(null, arguments));
		});

		// now we have an array of pure functions to chain.
		return PageUtil.createFunctionChain(functionsToChain, argumentCount);
	},

	createFunctionChain(functions, argumentCount) {
		if (functions.length === 0 ) {
			throw new Error("createFunctionChain cannot be called with zero-length array.");
		}

		// we start from the innermost implementation (i.e. the last in the array) and move outward. nextImpl holds
		// the function that will be used as the next argument for the function before it in the array.
		var nextImpl = functions[functions.length - 1];
		// now let's chain together the other implementations.
		for (var i = functions.length - 2; i >= 0; i--) {
			//
			nextImpl = PageUtil._addArgumentToFunction(functions[i], nextImpl, argumentCount);
		}
		return nextImpl;
	},

	// takes in a function fn and returns a function that calls fn with argument bound at the end of the argument list.
	_addArgumentToFunction(fn, argument, argumentCount) {
		if (argumentCount > 6) {
			throw new Error("_addArgumentToFunction only supports methods with up to 6 arguments");
		}
		return function(a, b, c, d, e, f) {
			var args = [a, b, c, d, e, f];
			args = args.slice(0, argumentCount);
			args.push(argument);
			return fn.apply(null, args);
		}
	},

	makeArray(valueOrArray) {
		if (!Array.isArray(valueOrArray)) {
			return [valueOrArray];
		}
		return valueOrArray;
	}
}<commit_msg>Cleanup and commentary<commit_after>var Q = require("q"),
	logger = require("../logging").getLogger(__LOGGER__),
	RLS = require("./RequestLocalStorage").getNamespace(),
	PromiseUtil = require("./PromiseUtil");

// There are three data structures defined here that are relevant for page and
// middleware authors:
//
//   - PAGE_MIXIN   : These will be automatically defined on your class.
//   - PAGE_METHODS : Chained methods that may be overridden in your class.
//   - PAGE_HOOKS   : Non-chained methods that may be defined in your class.
//
// These three data structure define the page interface.

// These methods will be available on your page/middleware object.
//
// Accidental definition of a method with a conflicting name directly on your
// class will generate an error.
//
var PAGE_MIXIN = {
	getRequest : makeGetter('request'),
	getConfig  : key => PageConfig.get(key),

	// Methods below here are called for you.
	// You shouldn't need to call them yourself.
	setRequest : makeSetter('request'),
}

// Each item here represents a method that page/middleware objects may override.
//
// The keys here are method names.
//
// The values are tuples containing:
//   - Number of arguments to the method.
//   - Default implementation of the method.
//   - Normalization function applied to method output.
//
// Note that each of these methods also receives an additional argument,
// which is the next implementation of the method in the call chain.
//     - Middleware implementations _should_ call this in most cases.*
//     - Page implementations _may_ call this (it will be the default implementation).
//
// * Consider carefully before deciding not to call `next()` in middleware.
// Other middleware (and the page itself) may exhibit undefined behavior if a
// given method is not called.  Generally, only skip calling `next()` for
// short-circuit responses (e.g. a redirect from `handleRoute`).
//
var PAGE_METHODS = {
	handleRoute        : [1, () => ({code: 200}), Q],
	getTitle           : [0, () => "", Q],
	getScripts         : [0, () => [], standardizeScripts],
	getSystemScripts   : [0, () => [], standardizeScripts],
	getHeadStylesheets : [0, () => [], standardizeStyles],
	getMetaTags        : [0, () => [], standardizeMetaTags],
	getLinkTags        : [0, () => [], standardizeLinkTags],
	getBase            : [0, () => null, Q],
	getBodyClasses     : [0, () => [], Q],
	getElements        : [0, () => [], standardizeElements],
	getResponseData    : [0, () => "", Q],
};

// These are similar to `PAGE_METHODS`, but differ as follows:
//
//   - They are not chained.
//   - They do not have default implementations.
//
// Each page and middleware that implements a page hook will have its hook
// called in turn.  Hooks do not receive a `next()` method, and are not
// responsible for merging return values.
//
// The keys here are method names.
//
// The values are empty placeholder tuples.
//
var PAGE_HOOKS = {
	addConfig      : [], // Define new configuration values.
	setConfig      : [], // Alter existing configuration values.
	handleComplete : [], // Do stuff after the response has been sent.
};

// These are helpers for `PAGE_MIXIN` methods.
//
// Note that getters and setters don't actually modify the page/middleware
// object directly, but rather stash values in request local storage.  Values
// are therefore shared between the page and all middleware.
//
function makeGetter(key){
	return () => (RLS().MixinValues||{})[key];
}

function makeSetter(key){
	return val => {
		(RLS().MixinValues||(RLS().MixinValues={}))[key] = val;
	}
}

// This attaches `PAGE_MIXIN` methods to page/middleware classes.
//
// It does this only _once_, and thereafter short-circuits.
//
function lazyMixinPageUtilMethods(page){
	var proto = Object.getPrototypeOf(page);
	if (proto._haveMixedInPageUtilMethods) return;

	proto._haveMixedInPageUtilMethods = true;

	Object.keys(PAGE_MIXIN).forEach(method => {
		if (proto[method]){
			throw new Error(`PAGE_MIXINS method override: ${
				(proto.constructor||{}).name
			}.${method}`);
		}
		proto[method] = PAGE_MIXIN[method];
	});
}

// These `standardize*` functions show what will happen to the output of your
// page methods.
//
// For middleware authors: Be aware that these standardization functions will
// have been applied to the output of `next()` before you get access to it.
//
// These functions are also exposed via `PageUtil.standardize*`.

/**
 * This method takes in anything returned from a Page.getElements call and
 * returns the elements in a standardized format: an array of EarlyPromises of
 * ReactElements.
 */
function standardizeElements(elements) {

	// The return value could be a single element or an array.
	// First, let's make sure that it's an array.
	// Then, ensure that all elements are EarlyPromises.
	return PageUtil
		.makeArray(elements)
		.map(element => PromiseUtil.early(Q(element)));
}

function standardizeMetaTags(metaTags) {
	return PageUtil.makeArray(metaTags).map(metaTag => Q(metaTag));
}

function standardizeLinkTags(linkTags) {
	return PageUtil.makeArray(linkTags).map(linkTag => Q(linkTag));
}

function standardizeScripts(scripts) {
	return PageUtil.makeArray(scripts).map((script) => {
		if (script.href || script.text) {
			if (!script.type) script.type = "text/javascript";
			return script;
		}

		// if the answer was a string, let's make a script object
		return {href:script, type:"text/javascript"};
	})
}

function standardizeStyles(styles) {
	return PageUtil.makeArray(styles).map((style) => {
		if (style.href || style.text) {
			if (!style.type) style.type = "text/css";
			if (!style.media) style.media = "";

			return style;
		}

		// if the answer was a string, let's make a script object
		return {href:style, type:"text/css", media:""};
	})
}

var PageConfig = (function(){
	var logger = require("../logging").getLogger(__LOGGER__({label: 'PageConfig'}));

	// This gets bound to the outer `PageConfig`.
	//
	// Only `PageConfig.get(key)` is generally useful.
	//
	var PageConfig = {

		get(key) {

			// No access until all `Page.addConfig()` and
			// `Page.getConfig()` methods are complete.
			if (!RLS.PageConfigFinalized){
				throw new Error(`Premature access: "${key}"`);
			}

			// The key _must_ exist.
			if (!_obj().hasOwnProperty(key)){
				throw new Error(`Invalid key: "${key}"`);
			}

			return _obj()[key];
		},

		// Don't call this.  It's called for you.
		initFromPageWithDefaults(page, defaults) {

			// First set all defaults.  Then set all values.
			_setDefaults(defaults);
			page.addConfig().forEach(_setDefaults);
			page.setConfig().forEach(_setValues);

			logger.debug('Final', _obj());

			RLS.PageConfigFinalized = true;
		},
	}

	// Below here are helpers. They are hidden from outside callers.

	var _set = function(isDefault, obj) {

		// Get the current mutable config.
		var config = _obj();

		// Copy input values into it.
		Object.keys(obj||{}).forEach(key => {
			var keyExists = config.hasOwnProperty(key);
			if (isDefault && keyExists){
				// Can't make this fatal, because request
				// forwarding uses a dirty RLS() context.
				logger.warning(`Duplicate PageConfig default: "${key}"`);
			} else if (!isDefault && !keyExists) {
				throw new Error(`Missing PageConfig default: "${key}"`);
			}

			logger.debug(`${isDefault?"Default":"Set"} "${key}" => "${obj[key]}"`);

			config[key] = obj[key];
		});
	};

	var _setDefaults = _set.bind({}, true);
	var _setValues   = _set.bind({}, false);

	var _obj = function(){

		// Return the current mutable config.
		return RLS().PageConfig || (RLS().PageConfig = {});
	}

	return PageConfig;
})();

// This is used to log method calls on the page _chain_.  Method calls on
// individual page/middleware objects are not automatically logged.
var logInvocation = function(name, func){
	return function(){
		logger.debug(`Call ${name}`);
		return func.apply(this, [].slice.call(arguments));
	}
}

var PageUtil = module.exports = {
	PAGE_METHODS,

	standardizeElements,
	standardizeMetaTags,
	standardizeScripts,
	standardizeStyles,

	PageConfig,

	/**
	 * Given an array of page instances, return an object that implements the page interface. This returned object's
	 * methods delegate to the first page in the pages array, passing the second page's implementation as the next
	 * variable (and the second page's implementation gets the third page's next, and so on).
	 */
	createPageChain(pages) {

		// This will be our return value.  It will be a mapping of
		// method names on page objects to chained function calls.
		var pageChain = {};

		pages.forEach(lazyMixinPageUtilMethods);

		Object.keys(PAGE_MIXIN).forEach(method => {
			pageChain[method] = logInvocation(method, PAGE_MIXIN[method]);
		});

		for (var method in PAGE_METHODS){

			if (PAGE_METHODS.hasOwnProperty(method)){

				var [
					argumentCount,
					defaultImpl,
					standardizeReturnValue
				] = PAGE_METHODS[method];

				pageChain[method] = logInvocation(method,
					PageUtil.createObjectFunctionChain(
						pages,
						method,
						argumentCount,
						defaultImpl,
						standardizeReturnValue
					)
				);
			}
		}

		Object.keys(PAGE_HOOKS).forEach(method => {

			// Grab a list of pages that implement this method.
			var implementors = pages.filter(page => page[method]);

			// The resulting function calls each implementor's
			// method in turn and returns an array containing in
			// their return values.
			pageChain[method] = logInvocation(method, function(){
				var args = [].slice.call(arguments);
				return implementors.map(
					page => page[method].apply(page, args)
				)
			});
		});

		return pageChain;
	},

	/**
	 * Given an array of objects and a function name, this returns a function that will call functionName on the first
	 * object, adding a next parameter to the end that is a function to call to the next object's implementation of functionName, and so on
	 * down the line. It is a way to make a middleware-like chain of functions, but the functions will be bound to the object in
	 * question from objects.
	 *
	 * The last object in the array will also receive a next parameter, which will call defaultImpl.
	 *
	 * standardizeReturnValue will be called on every return value from functionName so that next will always return the same type. This
	 * is useful when a function can return one of many different types.
	 */
	createObjectFunctionChain(objects, functionName, argumentCount, defaultImpl, standardizeReturnValue) {
		// our first order of business is to get an array of pure functions to chain; this involves finding
		// which objects have implementations for functionName, binding those implementations to the object
		// in question, and standardizing the return values.
		var functionsToChain = [];
		objects.forEach((object) => {
			if (object[functionName]) {
				functionsToChain.push(function() {
					return standardizeReturnValue(object[functionName].apply(object, arguments));
				});
			}
		});
		// the innermost implementation calls the default implementation and standardizes the result.
		functionsToChain.push(function() {
			return standardizeReturnValue(defaultImpl.apply(null, arguments));
		});

		// now we have an array of pure functions to chain.
		return PageUtil.createFunctionChain(functionsToChain, argumentCount);
	},

	createFunctionChain(functions, argumentCount) {
		if (functions.length === 0 ) {
			throw new Error("createFunctionChain cannot be called with zero-length array.");
		}

		// we start from the innermost implementation (i.e. the last in the array) and move outward. nextImpl holds
		// the function that will be used as the next argument for the function before it in the array.
		var nextImpl = functions[functions.length - 1];
		// now let's chain together the other implementations.
		for (var i = functions.length - 2; i >= 0; i--) {
			//
			nextImpl = PageUtil._addArgumentToFunction(functions[i], nextImpl, argumentCount);
		}
		return nextImpl;
	},

	// takes in a function fn and returns a function that calls fn with argument bound at the end of the argument list.
	_addArgumentToFunction(fn, argument, argumentCount) {
		if (argumentCount > 6) {
			throw new Error("_addArgumentToFunction only supports methods with up to 6 arguments");
		}
		return function(a, b, c, d, e, f) {
			var args = [a, b, c, d, e, f];
			args = args.slice(0, argumentCount);
			args.push(argument);
			return fn.apply(null, args);
		}
	},

	makeArray(valueOrArray) {
		if (!Array.isArray(valueOrArray)) {
			return [valueOrArray];
		}
		return valueOrArray;
	}
}<|endoftext|>


<commit_before>var TickDisplay = function() {
    return {
        reset: function() {
            var $self = this;
            $self.contract_barrier = null;
            $self.applicable_ticks = [];
            $self.number_of_ticks = null;
            $self.ev.close();
            $self.chart.destroy();
        },
        initialize: function(data) {
            var $self = this;

            // setting up globals
            $self.number_of_ticks = parseInt(data.number_of_ticks);
            $self.symbol = data.symbol;
            $self.display_symbol = data.display_symbol;
            $self.contract_start_ms = parseInt(data.contract_start * 1000);
            $self.contract_category = data.contract_category;
            $self.set_barrier = ($self.contract_category.match('digits')) ? false : true;
            $self.display_decimals = data.display_decimals;
            var tick_frequency = 5;

            if (data.show_contract_result) {
                $self.show_contract_result = true;
                $self.contract_sentiment = data.contract_sentiment;
                $self.price = parseFloat(data.price);
                $self.payout = parseFloat(data.payout);
            }

            var minimize = data.show_contract_result;

            $self.set_x_indicators();
            $self.initialize_chart({
                plot_from: data.previous_tick_epoch * 1000,
                plot_to: new Date((parseInt(data.contract_start) + parseInt(($self.number_of_ticks+2)*tick_frequency)) * 1000).getTime(),
                minimize: minimize,
                width: data.width ? data.width : undefined
            });
        },
        set_x_indicators: function() {
            var $self = this;

            var exit_tick_index = $self.number_of_ticks - 1;
            if ($self.contract_category.match('asian')) {
                $self.ticks_needed = $self.number_of_ticks;
                $self.x_indicators = {
                    '_0': { label: 'Tick 1', id: 'start_tick'},
                };
                $self.x_indicators['_' + exit_tick_index] = {
                    label: 'Exit Spot',
                    id: 'exit_tick',
                };
            } else if ($self.contract_category.match('callput')) {
                $self.ticks_needed = $self.number_of_ticks + 1;
                $self.x_indicators = {
                    '_0': { label: 'Entry Spot', id: 'entry_tick'},
                };
                $self.x_indicators['_' + $self.number_of_ticks] = {
                    label: 'Exit Spot',
                    id: 'exit_tick',
                };
            } else if ($self.contract_category.match('digits')) {
                $self.ticks_needed = $self.number_of_ticks;
                $self.x_indicators = {
                    '_0': { label: 'Tick 1', id: 'start_tick'},
                };
                $self.x_indicators['_' + exit_tick_index] = {
                    label:  'Tick ' + $self.number_of_ticks,
                    id: 'last_tick',
                };
            } else {
                $self.x_indicators = {};
            }

        },
        initialize_chart: function(config) {
            var $self = this;

            $self.chart = new Highcharts.Chart({
                chart: {
                    type: 'line',
                    renderTo: 'tick_chart',
                    width: config.width ? config.width : (config.minimize ? 394 : null),
                    height: config.minimize ? 143 : null,
                    backgroundColor: null,
                    events: { load: $self.plot(config.plot_from, config.plot_to) },
                },
                credits: {enabled: false},
                tooltip: {
                    formatter: function () {
                        var that = this;
                        var new_y = that.y.toFixed($self.display_decimals);
                        var mom = moment.utc($self.applicable_ticks[that.x].epoch*1000).format("dddd, MMM D, HH:mm:ss");
                        return mom + "
" + $self.display_symbol + " " + new_y;
                    },
                },
                xAxis: {
                    type: 'linear',
                    min: 0,
                    max: $self.number_of_ticks + 1,
                    labels: { enabled: false, }
                },
                yAxis: {
                    opposite: false,
                    labels: {
                        align: 'left',
                        x: 0,
                    },
                    title: ''
                },
                series: [{
                    data: [],
                }],
                title: '',
                exporting: {enabled: false, enableImages: false},
                legend: {enabled: false},
            });
        },
        plot: function(plot_from, plot_to) {
            var $self = this;

            var plot_from_moment = moment(plot_from).utc();
            var plot_to_moment = moment(plot_to).utc();
            var contract_start_moment = moment($self.contract_start_ms).utc();
            $self.counter = 0;
            $self.applicable_ticks = [];

            var symbol = $self.symbol;
            var stream_url = window.location.protocol + '//' + page.settings.get('streaming_server');
            stream_url += "/stream/ticks/" + symbol + "/" + plot_from_moment.unix() + "/" + plot_to_moment.unix();
            $self.ev = new EventSource(stream_url, { withCredentials: true });

            $self.ev.onmessage = function(msg) {
                if ($self.applicable_ticks.length >= $self.ticks_needed) {
                    $self.ev.close();
                    $self.evaluate_contract_outcome();
                    return;
                }

                var data = JSON.parse(msg.data);
                if (data && !(data[0] instanceof Array)) {
                    data = [ data ];
                }
                if (data) {
                    for (var i = 0; i < data.length; i++) {
                        if (data[i][0] === 'tick') {
                            var tick = {
                                epoch: parseInt(data[i][1]),
                                quote: parseFloat(data[i][2])
                            };

                            if (tick.epoch > contract_start_moment.unix()) {
                                if ($self.applicable_ticks.length >= $self.ticks_needed) {
                                    $self.ev.close();
                                    $self.evaluate_contract_outcome();
                                    return;
                                } else {
                                    if (!$self.chart) return;
                                    if (!$self.chart.series) return;
                                    $self.chart.series[0].addPoint([$self.counter, tick.quote], true, false);
                                    $self.applicable_ticks.push(tick);
                                    var indicator_key = '_' + $self.counter;
                                    if (typeof $self.x_indicators[indicator_key] !== 'undefined') {
                                        $self.x_indicators[indicator_key]['index'] = $self.counter;
                                        $self.add($self.x_indicators[indicator_key]);
                                    }

                                    $self.add_barrier();
                                    $self.apply_chart_background_color(tick);
                                    $self.counter++;
                                }
                            }

                        }
                    }
                }
            };
            $self.ev.onerror = function(e) {$self.ev.close(); };
        },
        apply_chart_background_color: function(tick) {
            var $self = this;

            var chart_container = $('#tick_chart');
            if ($self.contract_sentiment === 'up') {
                if (tick.quote > $self.contract_barrier) {
                    chart_container.css('background-color', 'rgba(46,136,54,0.198039)');
                } else {
                    chart_container.css('background-color', 'rgba(204,0,0,0.098039)');
                }
            } else if ($self.contract_sentiment === 'down') {
                if (tick.quote < $self.contract_barrier) {
                    chart_container.css('background-color', 'rgba(46,136,54,0.198039)');
                } else {
                    chart_container.css('background-color', 'rgba(204,0,0,0.098039)');
                }
            }
        },
        add_barrier: function() {
            var $self = this;

            if (!$self.set_barrier) {
                return;
            }

            var barrier_type = $self.contract_category.match('asian') ? 'asian' : 'static';

            if (barrier_type === 'static') {
                var barrier_tick = $self.applicable_ticks[0];
                $self.chart.yAxis[0].addPlotLine({
                    id: 'tick-barrier',
                    value: barrier_tick.quote,
                    label: {text: 'Barrier ('+barrier_tick.quote+')', align: 'center'},
                    color: 'green',
                    width: 2,
                });
                $self.contract_barrier = barrier_tick.quote;
                $self.set_barrier = false;
            }

            if (barrier_type === 'asian') {
                var total = 0;
                for (var i=0; i < $self.applicable_ticks.length; i++) {
                    total += parseFloat($self.applicable_ticks[i].quote);
                }
                var calc_barrier =  total/$self.applicable_ticks.length;
                calc_barrier = calc_barrier.toFixed(parseInt($self.display_decimals) + 1); // round calculated barrier

                $self.chart.yAxis[0].removePlotLine('tick-barrier');
                $self.chart.yAxis[0].addPlotLine({
                    id: 'tick-barrier',
                    value: calc_barrier,
                    color: 'green',
                    label: {
                        text: 'Average ('+calc_barrier+')',
                        align: 'center'
                    },
                    width: 2,
                });
                $self.contract_barrier = calc_barrier;
            }
        },
        add: function(indicator) {
            var $self = this;

            $self.chart.xAxis[0].addPlotLine({
               value: indicator.index,
               id: indicator.id,
               label: {text: indicator.label},
               color: '#e98024',
               width: 2,
            });
        },
        evaluate_contract_outcome: function() {
            var $self = this;

            if (!$self.contract_barrier) {
                return; // can't do anything without barrier
            }

            var exit_tick_index = $self.applicable_ticks.length - 1;
            var exit_spot = $self.applicable_ticks[exit_tick_index].quote;

            if ($self.contract_sentiment === 'up') {
                if (exit_spot > $self.contract_barrier) {
                    $self.win();
                } else {
                    $self.lose();
                }
            } else if ($self.contract_sentiment === 'down') {
                if (exit_spot < $self.contract_barrier) {
                    $self.win();
                } else {
                    $self.lose();
                }
            }

        },
        win: function() {
            var $self = this;

            var profit = $self.payout - $self.price;
            $self.update_ui($self.payout, profit, text.localize('This contract won'));
        },
        lose: function() {
            var $self = this;
            $self.update_ui(0, -$self.price, text.localize('This contract lost'));
        },
        update_ui: function(final_price, pnl, contract_status) {
            var $self = this;

            $('#bet-confirm-header').text(text.localize(contract_status));
            $('#contract-outcome-buyprice').text($self.to_monetary_format($self.price));
            $('#contract-outcome-payout').text($self.to_monetary_format(final_price));

            if (pnl > 0) {
                $('#contract-outcome-label').removeClass('standin loss').addClass('standout profit').text(text.localize('Profit'));
                $('#contract-outcome-profit').removeClass('standin loss').addClass('standout profit').text($self.to_monetary_format(pnl));
            } else {
                $('#contract-outcome-label').removeClass('standout profit').addClass('standin loss').text(text.localize('Loss'));
                $('#contract-outcome-profit').removeClass('standout profit').addClass('standin loss').text($self.to_monetary_format(pnl));
            }
            $('#confirmation_table').hide();
            $('#contract-outcome-details').show();
        },
        to_monetary_format: function(number) {
            return number.toFixed(2);
        }
    };
}();
<commit_msg>fixed tick_trade tooltip<commit_after>var TickDisplay = function() {
    return {
        reset: function() {
            var $self = this;
            $self.contract_barrier = null;
            $self.applicable_ticks = [];
            $self.number_of_ticks = null;
            $self.ev.close();
            $self.chart.destroy();
        },
        initialize: function(data) {
            var $self = this;

            // setting up globals
            $self.number_of_ticks = parseInt(data.number_of_ticks);
            $self.symbol = data.symbol;
            $self.display_symbol = data.display_symbol;
            $self.contract_start_ms = parseInt(data.contract_start * 1000);
            $self.contract_category = data.contract_category;
            $self.set_barrier = ($self.contract_category.match('digits')) ? false : true;
            $self.display_decimals = data.display_decimals || 2;
            var tick_frequency = 5;

            if (data.show_contract_result) {
                $self.show_contract_result = true;
                $self.contract_sentiment = data.contract_sentiment;
                $self.price = parseFloat(data.price);
                $self.payout = parseFloat(data.payout);
            }

            var minimize = data.show_contract_result;

            $self.set_x_indicators();
            $self.initialize_chart({
                plot_from: data.previous_tick_epoch * 1000,
                plot_to: new Date((parseInt(data.contract_start) + parseInt(($self.number_of_ticks+2)*tick_frequency)) * 1000).getTime(),
                minimize: minimize,
                width: data.width ? data.width : undefined
            });
        },
        set_x_indicators: function() {
            var $self = this;

            var exit_tick_index = $self.number_of_ticks - 1;
            if ($self.contract_category.match('asian')) {
                $self.ticks_needed = $self.number_of_ticks;
                $self.x_indicators = {
                    '_0': { label: 'Tick 1', id: 'start_tick'},
                };
                $self.x_indicators['_' + exit_tick_index] = {
                    label: 'Exit Spot',
                    id: 'exit_tick',
                };
            } else if ($self.contract_category.match('callput')) {
                $self.ticks_needed = $self.number_of_ticks + 1;
                $self.x_indicators = {
                    '_0': { label: 'Entry Spot', id: 'entry_tick'},
                };
                $self.x_indicators['_' + $self.number_of_ticks] = {
                    label: 'Exit Spot',
                    id: 'exit_tick',
                };
            } else if ($self.contract_category.match('digits')) {
                $self.ticks_needed = $self.number_of_ticks;
                $self.x_indicators = {
                    '_0': { label: 'Tick 1', id: 'start_tick'},
                };
                $self.x_indicators['_' + exit_tick_index] = {
                    label:  'Tick ' + $self.number_of_ticks,
                    id: 'last_tick',
                };
            } else {
                $self.x_indicators = {};
            }

        },
        initialize_chart: function(config) {
            var $self = this;

            $self.chart = new Highcharts.Chart({
                chart: {
                    type: 'line',
                    renderTo: 'tick_chart',
                    width: config.width ? config.width : (config.minimize ? 394 : null),
                    height: config.minimize ? 143 : null,
                    backgroundColor: null,
                    events: { load: $self.plot(config.plot_from, config.plot_to) },
                },
                credits: {enabled: false},
                tooltip: {
                    formatter: function () {
                        var that = this;
                        var new_y = that.y.toFixed($self.display_decimals);
                        var mom = moment.utc($self.applicable_ticks[that.x].epoch*1000).format("dddd, MMM D, HH:mm:ss");

                        return mom + "
" + $self.display_symbol + " " + new_y;
                    },
                },
                xAxis: {
                    type: 'linear',
                    min: 0,
                    max: $self.number_of_ticks + 1,
                    labels: { enabled: false, }
                },
                yAxis: {
                    opposite: false,
                    labels: {
                        align: 'left',
                        x: 0,
                    },
                    title: ''
                },
                series: [{
                    data: [],
                }],
                title: '',
                exporting: {enabled: false, enableImages: false},
                legend: {enabled: false},
            });
        },
        plot: function(plot_from, plot_to) {
            var $self = this;

            var plot_from_moment = moment(plot_from).utc();
            var plot_to_moment = moment(plot_to).utc();
            var contract_start_moment = moment($self.contract_start_ms).utc();
            $self.counter = 0;
            $self.applicable_ticks = [];

            var symbol = $self.symbol;
            var stream_url = window.location.protocol + '//' + page.settings.get('streaming_server');
            stream_url += "/stream/ticks/" + symbol + "/" + plot_from_moment.unix() + "/" + plot_to_moment.unix();
            $self.ev = new EventSource(stream_url, { withCredentials: true });

            $self.ev.onmessage = function(msg) {
                if ($self.applicable_ticks.length >= $self.ticks_needed) {
                    $self.ev.close();
                    $self.evaluate_contract_outcome();
                    return;
                }

                var data = JSON.parse(msg.data);
                if (data && !(data[0] instanceof Array)) {
                    data = [ data ];
                }
                if (data) {
                    for (var i = 0; i < data.length; i++) {
                        if (data[i][0] === 'tick') {
                            var tick = {
                                epoch: parseInt(data[i][1]),
                                quote: parseFloat(data[i][2])
                            };

                            if (tick.epoch > contract_start_moment.unix()) {
                                if ($self.applicable_ticks.length >= $self.ticks_needed) {
                                    $self.ev.close();
                                    $self.evaluate_contract_outcome();
                                    return;
                                } else {
                                    if (!$self.chart) return;
                                    if (!$self.chart.series) return;
                                    $self.chart.series[0].addPoint([$self.counter, tick.quote], true, false);
                                    $self.applicable_ticks.push(tick);
                                    var indicator_key = '_' + $self.counter;
                                    if (typeof $self.x_indicators[indicator_key] !== 'undefined') {
                                        $self.x_indicators[indicator_key]['index'] = $self.counter;
                                        $self.add($self.x_indicators[indicator_key]);
                                    }

                                    $self.add_barrier();
                                    $self.apply_chart_background_color(tick);
                                    $self.counter++;
                                }
                            }

                        }
                    }
                }
            };
            $self.ev.onerror = function(e) {$self.ev.close(); };
        },
        apply_chart_background_color: function(tick) {
            var $self = this;

            var chart_container = $('#tick_chart');
            if ($self.contract_sentiment === 'up') {
                if (tick.quote > $self.contract_barrier) {
                    chart_container.css('background-color', 'rgba(46,136,54,0.198039)');
                } else {
                    chart_container.css('background-color', 'rgba(204,0,0,0.098039)');
                }
            } else if ($self.contract_sentiment === 'down') {
                if (tick.quote < $self.contract_barrier) {
                    chart_container.css('background-color', 'rgba(46,136,54,0.198039)');
                } else {
                    chart_container.css('background-color', 'rgba(204,0,0,0.098039)');
                }
            }
        },
        add_barrier: function() {
            var $self = this;

            if (!$self.set_barrier) {
                return;
            }

            var barrier_type = $self.contract_category.match('asian') ? 'asian' : 'static';

            if (barrier_type === 'static') {
                var barrier_tick = $self.applicable_ticks[0];
                $self.chart.yAxis[0].addPlotLine({
                    id: 'tick-barrier',
                    value: barrier_tick.quote,
                    label: {text: 'Barrier ('+barrier_tick.quote+')', align: 'center'},
                    color: 'green',
                    width: 2,
                });
                $self.contract_barrier = barrier_tick.quote;
                $self.set_barrier = false;
            }

            if (barrier_type === 'asian') {
                var total = 0;
                for (var i=0; i < $self.applicable_ticks.length; i++) {
                    total += parseFloat($self.applicable_ticks[i].quote);
                }
                var calc_barrier =  total/$self.applicable_ticks.length;
                calc_barrier = calc_barrier.toFixed(parseInt($self.display_decimals) + 1); // round calculated barrier

                $self.chart.yAxis[0].removePlotLine('tick-barrier');
                $self.chart.yAxis[0].addPlotLine({
                    id: 'tick-barrier',
                    value: calc_barrier,
                    color: 'green',
                    label: {
                        text: 'Average ('+calc_barrier+')',
                        align: 'center'
                    },
                    width: 2,
                });
                $self.contract_barrier = calc_barrier;
            }
        },
        add: function(indicator) {
            var $self = this;

            $self.chart.xAxis[0].addPlotLine({
               value: indicator.index,
               id: indicator.id,
               label: {text: indicator.label},
               color: '#e98024',
               width: 2,
            });
        },
        evaluate_contract_outcome: function() {
            var $self = this;

            if (!$self.contract_barrier) {
                return; // can't do anything without barrier
            }

            var exit_tick_index = $self.applicable_ticks.length - 1;
            var exit_spot = $self.applicable_ticks[exit_tick_index].quote;

            if ($self.contract_sentiment === 'up') {
                if (exit_spot > $self.contract_barrier) {
                    $self.win();
                } else {
                    $self.lose();
                }
            } else if ($self.contract_sentiment === 'down') {
                if (exit_spot < $self.contract_barrier) {
                    $self.win();
                } else {
                    $self.lose();
                }
            }

        },
        win: function() {
            var $self = this;

            var profit = $self.payout - $self.price;
            $self.update_ui($self.payout, profit, text.localize('This contract won'));
        },
        lose: function() {
            var $self = this;
            $self.update_ui(0, -$self.price, text.localize('This contract lost'));
        },
        update_ui: function(final_price, pnl, contract_status) {
            var $self = this;

            $('#bet-confirm-header').text(text.localize(contract_status));
            $('#contract-outcome-buyprice').text($self.to_monetary_format($self.price));
            $('#contract-outcome-payout').text($self.to_monetary_format(final_price));

            if (pnl > 0) {
                $('#contract-outcome-label').removeClass('standin loss').addClass('standout profit').text(text.localize('Profit'));
                $('#contract-outcome-profit').removeClass('standin loss').addClass('standout profit').text($self.to_monetary_format(pnl));
            } else {
                $('#contract-outcome-label').removeClass('standout profit').addClass('standin loss').text(text.localize('Loss'));
                $('#contract-outcome-profit').removeClass('standout profit').addClass('standin loss').text($self.to_monetary_format(pnl));
            }
            $('#confirmation_table').hide();
            $('#contract-outcome-details').show();
        },
        to_monetary_format: function(number) {
            return number.toFixed(2);
        }
    };
}();
<|endoftext|>


<commit_before>test( "Popcorn 0.3 SBV Parser Plugin", function () {

  var count = 0,
      numSubs = 0,
      sub,
      poppercorn = Popcorn( "#video" ),
      subs = [
        {
          text: "Senator, we're making our final approach into Coruscant.",
          start: 2.4,
          end: 7.2
        },
        {
          text: "Very good, Lieutenant.",
          start: 9.712,
          end: 13.399
        },
        {
          text: "It's a trap!",
          start: 15.042,
          end: 18.042
        }
      ],
      expects = subs.length * 3 + 1;

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  poppercorn.parseSBV( "data/data.sbv", function (){
    expect(expects);

    Popcorn.forEach( poppercorn.getTrackEvents(), function( evt ) {
      if( evt._natives.type === "subtitle" ) {
        sub = subs[ numSubs++ ];

        equal( evt.start, sub.start, "Correctly parsed start of " + evt.start );
        plus();
        equal( evt.text, sub.text, "Correctly parsed text of " + evt.start );
        plus();
        equal( evt.end, sub.end, "Correctly parsed end at " + evt.start );
        plus();
      }
    });

    equal( subs.length, numSubs, "Parsed all subtitles" );
    plus();
  });
});
<commit_msg>Convert parseSBV parser tests to be async [#231]<commit_after>asyncTest( "Popcorn 0.3 SBV Parser Plugin", function () {

  var count = 0,
      numSubs = 0,
      sub,
      poppercorn = Popcorn( "#video" ),
      subs = [
        {
          text: "Senator, we're making our final approach into Coruscant.",
          start: 2.4,
          end: 7.2
        },
        {
          text: "Very good, Lieutenant.",
          start: 9.712,
          end: 13.399
        },
        {
          text: "It's a trap!",
          start: 15.042,
          end: 18.042
        }
      ],
      expects = subs.length * 3 + 1;

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  poppercorn.parseSBV( "data/data.sbv", function (){
    expect(expects);

    Popcorn.forEach( poppercorn.getTrackEvents(), function( evt ) {
      if( evt._natives.type === "subtitle" ) {
        sub = subs[ numSubs++ ];

        equal( evt.start, sub.start, "Correctly parsed start of " + evt.start );
        plus();
        equal( evt.text, sub.text, "Correctly parsed text of " + evt.start );
        plus();
        equal( evt.end, sub.end, "Correctly parsed end at " + evt.start );
        plus();
      }
    });

    equal( subs.length, numSubs, "Parsed all subtitles" );
    plus();
  });
});
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs'),
    path = require('path');

var Q = require('q'),
    through = require('through2');

var renderJade = require('../../../app/functions/render-jade'),
    renderMarkdown = require('../../../app/functions/render-markdown');

var layoutCache = {};

module.exports = function(options) {

  return through.obj(transform);

  function transform(file, enc, cb) {
    if ((/\.(md|markdown)$/i).test(file.history[0])) {
      file.contents = new Buffer(renderMarkdown(String(file.contents)));
    }
    if ((/\.jade$/i).test(file.history[0])) {
      options.jade.filename = file.history[0];
      file.contents = new Buffer(renderJade(String(file.contents), options.jade, file.data));
    }
    if (!file.data.hasOwnProperty('layout')) {
      file.data.layout = options.layout.default;
    }
    if (file.data.layout) applyLayout(file, cb);
    else cb(null, file);
  }

  function applyLayout(file, cb) {
    getLayout(file.data.layout).done(function(layout) {
      if (layout) {
        file.data.contents = String(file.contents);
        options.jade.filename = layout.path;
        file.contents = new Buffer(renderJade(layout.contents, options.jade, file.data));
      }
      cb(null, file);
    }, cb);
  }

  function getLayout(layout) {
    return Q.Promise(function(resolve, reject) {
      var layoutPath = path.join(options.layout.path, layout);
      if (layoutCache[layoutPath]) {
        resolve(layoutCache[layoutPath]);
      } else {
        Q.nfapply(fs.readFile, [layoutPath, 'utf8']).done(function(layoutContents) {
          if (typeof layoutContents === 'string' && layoutContents.length > 0) {
            layoutCache[layoutPath] = {
              contents: layoutContents,
              path: layoutPath
            };
          }
          resolve(layoutCache[layoutPath]);
        }, reject);
      }
    });
  }

};
<commit_msg>refactor render gulp module<commit_after>'use strict';

var fs = require('fs'),
    path = require('path');

var Q = require('q'),
    through = require('through2');

var renderJade = require('../../../app/functions/render-jade'),
    renderMarkdown = require('../../../app/functions/render-markdown');

var layoutCache = {};

module.exports = renderSite;

function renderSite(options) {

  return through.obj(transform);

  function transform(file, enc, done) {
    if ((/\.jade$/i).test(file.history[0])) {
      options.jade.filename = file.history[0];
      file.contents = new Buffer(renderJade(String(file.contents), options.jade, file.data));
    } else if ((/\.(md|markdown)$/i).test(file.history[0])) {
      file.contents = new Buffer(renderMarkdown(String(file.contents)));
    }
    if (!file.data.hasOwnProperty('layout')) {
      file.data.layout = options.layout.default;
    }
    if (file.data.layout) applyLayout(file, done);
    else done(null, file);
  }

  function applyLayout(file, done) {
    getLayout(file.data.layout).done(function(layout) {
      if (layout) {
        file.data.contents = String(file.contents);
        options.jade.filename = layout.path;
        file.contents = new Buffer(renderJade(layout.contents, options.jade, file.data));
      }
      done(null, file);
    }, done);
  }

  function getLayout(layout) {
    return Q.Promise(function(resolve, reject) {
      var layoutPath = path.join(options.layout.path, layout);
      if (layoutCache[layoutPath]) {
        resolve(layoutCache[layoutPath]);
      } else {
        Q.nfapply(fs.readFile, [layoutPath, 'utf8']).done(function(layoutContents) {
          if (typeof layoutContents === 'string' && layoutContents.length > 0) {
            layoutCache[layoutPath] = {
              contents: layoutContents,
              path: layoutPath
            };
          }
          resolve(layoutCache[layoutPath]);
        }, reject);
      }
    });
  }

}
<|endoftext|>


<commit_before>import {readFileSync} from 'fs'
import {decode} from 'iconv-lite'
import {detect} from 'jschardet'
import {fromDimensions} from '@sabaki/go-board'
import i18n from '../../i18n.js'
import * as gametree from '../gametree.js'

const t = i18n.context('fileformats')

export const meta = {
  name: t('PandaNET UGF'),
  extensions: ['ugf', 'ugi']
}

function convertVertex(ugiVertex, boardSize) {
  return (
    ugiVertex[0] +
    String.fromCharCode(boardSize - ugiVertex.charCodeAt(1) + 129)
  ).toLowerCase()
}

export function parse(content) {
  return [
    gametree.new().mutate(draft => {
      let lines = content.split('\n')
      let rootId = draft.root.id
      let lastNodeId = rootId

      draft.updateProperty(rootId, 'CA', ['UTF-8'])
      draft.updateProperty(rootId, 'FF', ['4'])
      draft.updateProperty(rootId, 'GM', ['1'])
      draft.updateProperty(rootId, 'SZ', ['19'])

      let currentMode = null
      for (let n = 0; n < lines.length; n++) {
        let line = lines[n].trim()

        if (line == '') {
          continue
        } else if (line.startsWith('[') && line.endsWith(']')) {
          currentMode = line.slice(1, -1)
          continue
        }

        switch (currentMode) {
          case 'Header':
            let [key, value] = line.split('=')
            switch (key) {
              case 'PlayerB':
                draft.updateProperty(rootId, 'PB', [value.split(',')[0]])
                draft.updateProperty(rootId, 'BR', [value.split(',')[1]])
                break
              case 'PlayerW':
                draft.updateProperty(rootId, 'PW', [value.split(',')[0]])
                draft.updateProperty(rootId, 'WR', [value.split(',')[1]])
                break
              case 'Size':
                draft.updateProperty(rootId, 'SZ', [value])
                break
              case 'Hdcp':
                if (value.split(',')[0] != '0') {
                  draft.updateProperty(rootId, 'HA', [value.split(',')[0]])
                }
                draft.updateProperty(rootId, 'KM', [value.split(',')[1]])
                break
              case 'Rules':
                draft.updateProperty(rootId, 'RU', [value])
                break
              case 'Date':
                draft.updateProperty(rootId, 'DT', [
                  value.split(',')[0].replace(/\//g, '-')
                ])
                break
              case 'Copyright':
                draft.updateProperty(rootId, 'CP', [value])
                break
              case 'Winner':
                draft.updateProperty(rootId, 'RE', [
                  value.split(',')[0] + '+' + value.split(',')[1]
                ])
                break
            }
            break
          case 'Data':
            let [coords, color, nodeNum, _] = line.split(',')
            if (nodeNum > 0) {
              lastNodeId = draft.appendNode(lastNodeId, {
                [color[0]]: [
                  convertVertex(coords, parseInt(draft.root.data.SZ))
                ]
              })
            } else {
              // UGF assigns all handicap placements to node number 0
              draft.addToProperty(
                rootId,
                'AB',
                convertVertex(coords, parseInt(draft.root.data.SZ))
              )
            }
            break
          case 'ReviewNode':
            break
          case 'ReviewComment':
            break
          default:
            break
        }
      }
    })
  ]
}

export function parseFile(filename) {
  let buffer = readFileSync(filename)
  let encoding = 'utf8'
  let detected = detect(buffer)
  if (detected.confidence > 0.2) encoding = detected.encoding

  let content = decode(buffer, encoding)
  return parse(content)
}
<commit_msg>WIP on supporting ReviewComment section in UGI<commit_after>import {readFileSync} from 'fs'
import {decode} from 'iconv-lite'
import {detect} from 'jschardet'
import {fromDimensions} from '@sabaki/go-board'
import i18n from '../../i18n.js'
import * as gametree from '../gametree.js'

const t = i18n.context('fileformats')

export const meta = {
  name: t('PandaNET UGF'),
  extensions: ['ugf', 'ugi']
}

function convertVertex(ugiVertex, boardSize) {
  return (
    ugiVertex[0] +
    String.fromCharCode(boardSize - ugiVertex.charCodeAt(1) + 129)
  ).toLowerCase()
}

export function parse(content) {
  return [
    gametree.new().mutate(draft => {
      let lines = content.split('\n')
      let rootId = draft.root.id
      let lastNodeId = rootId
      let nodeMap = {}

      draft.updateProperty(rootId, 'CA', ['UTF-8'])
      draft.updateProperty(rootId, 'FF', ['4'])
      draft.updateProperty(rootId, 'GM', ['1'])
      draft.updateProperty(rootId, 'SZ', ['19'])

      let currentMode = null
      for (let n = 0; n < lines.length; n++) {
        let line = lines[n].trim()

        if (line == '') {
          continue
        } else if (line.startsWith('[') && line.endsWith(']')) {
          currentMode = line.slice(1, -1)
          continue
        }

        switch (currentMode) {
          case 'Header':
            let [key, value] = line.split('=')
            switch (key) {
              case 'PlayerB':
                draft.updateProperty(rootId, 'PB', [value.split(',')[0]])
                draft.updateProperty(rootId, 'BR', [value.split(',')[1]])
                break
              case 'PlayerW':
                draft.updateProperty(rootId, 'PW', [value.split(',')[0]])
                draft.updateProperty(rootId, 'WR', [value.split(',')[1]])
                break
              case 'Size':
                draft.updateProperty(rootId, 'SZ', [value])
                break
              case 'Hdcp':
                if (value.split(',')[0] != '0') {
                  draft.updateProperty(rootId, 'HA', [value.split(',')[0]])
                }
                draft.updateProperty(rootId, 'KM', [value.split(',')[1]])
                break
              case 'Rules':
                draft.updateProperty(rootId, 'RU', [value])
                break
              case 'Date':
                draft.updateProperty(rootId, 'DT', [
                  value.split(',')[0].replace(/\//g, '-')
                ])
                break
              case 'Copyright':
                draft.updateProperty(rootId, 'CP', [value])
                break
              case 'Winner':
                draft.updateProperty(rootId, 'RE', [
                  value.split(',')[0] + '+' + value.split(',')[1]
                ])
                break
            }
            break
          case 'Data':
            let [coords, color, nodeNum, _] = line.split(',')
            if (nodeNum > 0) {
              lastNodeId = draft.appendNode(lastNodeId, {
                [color[0]]: [
                  convertVertex(coords, parseInt(draft.root.data.SZ))
                ]
              })
              nodeMap[nodeNum] = lastNodeId
            } else {
              // UGF assigns all handicap placements to node number 0
              draft.addToProperty(
                rootId,
                'AB',
                convertVertex(coords, parseInt(draft.root.data.SZ))
              )
            }
            break
          case 'ReviewNode':
            break
          case 'ReviewComment':
            if (line.startsWith('.Comment')) {
              let commentNodeId = line.split(',')[1]
              while (n < lines.length && !lines[n + 1].startsWith('.Comment')) {
                n += 1
                draft.updateProperty(
                  nodeMap[commentNodeId],
                  'C',
                  draft.get(nodeMap[commentId]) + lines[n].trim()
                )
              }
            }
            break
          default:
            break
        }
      }
    })
  ]
}

export function parseFile(filename) {
  let buffer = readFileSync(filename)
  let encoding = 'utf8'
  let detected = detect(buffer)
  if (detected.confidence > 0.2) encoding = detected.encoding

  let content = decode(buffer, encoding)
  return parse(content)
}
<|endoftext|>