<commit_before>/* jspsych-staircase-categorize.js
 *	Josh de Leeuw, Sep. 2013
 *
 *
 *
 */

(function($) {
    jsPsych.staircase_categorize = (function() {

        var plugin = {};

        plugin.create = function(params) {
            var trials = []; // everything is a single trial, since it is unknown how many presentations it will take.

            trials[0] = {};
            trials[0].type = "staircase_categorize";
            // items structure is four nested arrays. top level array is separate staircases that should be interleaved.
            // second level array is difficulty. third arrays are sets of options. inner most array are stimuli of equivalent
            // difficulty and category.
            trials[0].items = params.items;
            // two level nested array. top level is separate staircases.
            // second (inner) level is list of key responses in order that stimuli are ordered for third level of items array.
            trials[0].choices = params.choices;
            trials[0].n_up = params.n_up || 1;
            trials[0].n_down = params.n_down || 1;
            trials[0].n_turns = params.n_turns || 10;
            trials[0].n_turns_average = params.n_turns_average || 6;
            // timing
            trials[0].timing_post_trial = params.timing_post_trial || 1000; // default 1000ms between trials.
            trials[0].timing_item = params.timing_item || 1000;

            // optional parameters
            if (params.data !== undefined) {
                trials[0].data = params.data;
            }
            if (params.prompt !== undefined) {
                trials[0].prompt = params.prompt;
            }

            return trials;
        };

        plugin.trial = function(display_element, block, trial, part) {

            var interleaver = new StaircaseInterleaver(display_element, trial, block);
            interleaver.start();
        };

        function StaircaseInterleaver(display_element, trial, block) {
            this.staircases = [];

            for(var i=0; i<trial.items.length; i++)
            {
                var s_trial = $.extend(true, {}, trial);
                s_trial.items = s_trial.items[i];
                s_trial.choices = s_trial.choices[i];
                var staircase = new StaircaseController(display_element, s_trial, this, block);
                this.staircases.push(staircase);
            }

            this.start = function() {
                this.next_trial();
            };

            this.next_trial = function() {
                var eligible_staircases = [];
                for (var i = 0; i < this.staircases.length; i++) {
                    if (this.staircases[i].is_complete === false) {
                        eligible_staircases.push(this.staircases[i]);
                    }
                }

                if (eligible_staircases.length === 0) {
                    this.finish();
                }
                else {
                    var which_staircase = Math.floor(Math.random() * eligible_staircases.length);

                    eligible_staircases[which_staircase].next_trial();
                }
            };

            this.finish = function()
            {
                // end the whole staircase process
                block.next();
            }
        }

        function StaircaseController(display_element, trial, interleaver, block) {
            this.current_item = 0;
            this.total_turns = 0;
            this.current_direction = 1;
            this.turns = [];
            this.consecutive_correct = 0;
            this.consecutive_incorrect = 0;
            this.items = trial.items;
            this.is_complete = false;
            this.interleaver = interleaver;

            this.next_trial = function() {

                var item_set = this.items[this.current_item];

                // pick which category to show randomly
                var which_category = Math.floor(Math.random()*item_set.length);

                // pick which exemplar to show randomly
                var which_exemplar = Math.floor(Math.random()*item_set[which_category].length);

                var this_trial = {
                    a_path: item_set[which_category][which_exemplar],
                    choices: trial.choices,
                    correct_response: trial.choices[which_category]
                };

                this.do_trial(this_trial);

            };

            this.finish_trial = function(correct) {
                // change the counters
                if (correct) {
                    this.consecutive_incorrect = 0;
                    this.consecutive_correct++;
                }
                else {
                    this.consecutive_incorrect++;
                    this.consecutive_correct = 0;
                }

                var new_direction;

                if (correct) {
                    if (this.consecutive_correct >= trial.n_up) {
                        new_direction = 1; // make it harder
                        this.consecutive_correct = 0; // reset this because we changed difficulty.
                    }
                    else {
                        // do the same
                        new_direction = 0;
                    }
                }
                else {
                    if (this.consecutive_incorrect >= trial.n_down) {
                        // make it easier
                        new_direction = -1;
                        this.consecutive_incorrect = 0; // reset because we changed difficulty
                    }
                    else {
                        // do the same
                        new_direction = 0;
                    }
                }

                // if there is a turn, add it.
                if (new_direction !== 0) {
                    if (this.current_direction != new_direction) {
                        this.turns.push(this.current_item);
                    }
                    this.current_direction = new_direction;
                }

                // check if we are done
                if (this.turns.length >= trial.n_turns ){
                    this.is_complete = true;
                }

                // now figure out what the next item is
                this.current_item = this.current_item + new_direction;
                if (this.current_item < 0) {
                    this.current_item = 0;
                }
                if (this.current_item >= trial.items.length) {
                    this.current_item = trial.items.length - 1;
                }

                // tell the interleaver that we are done
                this.interleaver.next_trial();
            };

            this.do_trial = function(this_trial) {

                var trial_complete = false;

                // show image
                display_element.append($('<img>', {
                    "src": this_trial.a_path,
                    "class": 'staircase-categorize'
                }));

                if (trial.prompt) {
                    display_element.append(trial.prompt);
                }

                var startTime = (new Date()).getTime();

                // set timer to remove image
                if (trial.timing_item > 0) {
                    setTimeout(function() {
                        if (!trial_complete) {
                            $('.staircase-categorize').css('visibility', 'hidden');
                        }
                    }, trial.timing_item);
                }

                var resp_func = function(e) {

                    var flag = false;
                    var correct = false;

                    if ($.inArray(e.which, this_trial.choices) > -1) {
                        flag = true;
                        correct = (e.which == this_trial.correct_response);
                    }

                    if (flag) {
                        var endTime = (new Date()).getTime();
                        var rt = (endTime - startTime);
                        var t_idx = block.data.length;

                        var trial_data = {
                            "trial_type": "staircase-categorize",
                            "trial_index": t_idx,
                            "rt": rt,
                            "correct": correct,
                            "a_path": this_trial.a_path,
                            "staircase_index": e.data.iterator_object.curent_item,
                            "staircase_direction": e.data.iterator_object.current_direction,
                            "key_press": e.which
                        };
                        block.data.push($.extend({},trial_data,trial.data));
                        $(document).unbind('keyup', resp_func);
                        display_element.html(''); // remove all
                        trial_complete = true;
                        setTimeout(function() {
                            e.data.iterator_object.finish_trial(correct);
                        }, trial.timing_post_trial);
                    }
                }
                $(document).keyup({iterator_object: this}, resp_func);
            }
        }

        return plugin;
    })();
})(jQuery);
<commit_msg>fixing bugs in staircase-categorize<commit_after>/* jspsych-staircase-categorize.js
 *	Josh de Leeuw, Sep. 2013
 *
 *
 *
 */

(function($) {
    jsPsych.staircase_categorize = (function() {

        var plugin = {};

        plugin.create = function(params) {
            var trials = []; // everything is a single trial, since it is unknown how many presentations it will take.

            trials[0] = {};
            trials[0].type = "staircase_categorize";
            // items structure is four nested arrays. top level array is separate staircases that should be interleaved.
            // second level array is difficulty. third arrays are sets of options. inner most array are stimuli of equivalent
            // difficulty and category.
            trials[0].items = params.items;
            // two level nested array. top level is separate staircases.
            // second (inner) level is list of key responses in order that stimuli are ordered for third level of items array.
            trials[0].choices = params.choices;
            trials[0].n_up = params.n_up || 1;
            trials[0].n_down = params.n_down || 1;
            trials[0].n_turns = params.n_turns || 10;
            trials[0].n_turns_average = params.n_turns_average || 6;
            // timing
            trials[0].timing_post_trial = params.timing_post_trial || 1000; // default 1000ms between trials.
            trials[0].timing_item = params.timing_item || 1000;

            // optional parameters
            if (params.data !== undefined) {
                trials[0].data = params.data;
            }
            if (params.prompt !== undefined) {
                trials[0].prompt = params.prompt;
            }

            return trials;
        };

        plugin.trial = function(display_element, block, trial, part) {

            var interleaver = new StaircaseInterleaver(display_element, trial, block);
            interleaver.start();
        };

        function StaircaseInterleaver(display_element, trial, block) {
            this.staircases = [];

            for(var i=0; i<trial.items.length; i++)
            {
                var s_trial = $.extend(true, {}, trial);
                s_trial.items = s_trial.items[i];
                s_trial.choices = s_trial.choices[i];
                var staircase = new StaircaseController(display_element, s_trial, this, block);
                this.staircases.push(staircase);
            }

            this.start = function() {
                this.next_trial();
            };

            this.next_trial = function() {
                var eligible_staircases = [];
                for (var i = 0; i < this.staircases.length; i++) {
                    if (this.staircases[i].is_complete === false) {
                        eligible_staircases.push(this.staircases[i]);
                    }
                }

                if (eligible_staircases.length === 0) {
                    this.finish();
                }
                else {
                    var which_staircase = Math.floor(Math.random() * eligible_staircases.length);

                    eligible_staircases[which_staircase].next_trial();
                }
            };

            this.finish = function()
            {
                // end the whole staircase process
                block.next();
            }
        }

        function StaircaseController(display_element, trial, interleaver, block) {
            this.current_item = 0;
            this.total_turns = 0;
            this.current_direction = 1;
            this.turns = [];
            this.consecutive_correct = 0;
            this.consecutive_incorrect = 0;
            this.items = trial.items;
            this.is_complete = false;
            this.interleaver = interleaver;

            this.next_trial = function() {

                var item_set = this.items[this.current_item];

                // pick which category to show randomly
                var which_category = Math.floor(Math.random()*item_set.length);

                // pick which exemplar to show randomly
                var which_exemplar = Math.floor(Math.random()*item_set[which_category].length);

                var this_trial = {
                    a_path: item_set[which_category][which_exemplar],
                    choices: trial.choices,
                    correct_response: trial.choices[which_category]
                };

                this.do_trial(this_trial);

            };

            this.finish_trial = function(correct) {
                // change the counters
                if (correct) {
                    this.consecutive_incorrect = 0;
                    this.consecutive_correct++;
                }
                else {
                    this.consecutive_incorrect++;
                    this.consecutive_correct = 0;
                }

                var new_direction;

                if (correct) {
                    if (this.consecutive_correct >= trial.n_up) {
                        new_direction = 1; // make it harder
                        this.consecutive_correct = 0; // reset this because we changed difficulty.
                    }
                    else {
                        // do the same
                        new_direction = 0;
                    }
                }
                else {
                    if (this.consecutive_incorrect >= trial.n_down) {
                        // make it easier
                        new_direction = -1;
                        this.consecutive_incorrect = 0; // reset because we changed difficulty
                    }
                    else {
                        // do the same
                        new_direction = 0;
                    }
                }

                // if there is a turn, add it.
                if (new_direction !== 0) {
                    if (this.current_direction != new_direction) {
                        this.turns.push(this.current_item);
                    }
                    this.current_direction = new_direction;
                }

                // check if we are done
                if (this.turns.length >= trial.n_turns ){
                    this.is_complete = true;
                }

                // now figure out what the next item is
                this.current_item = this.current_item + new_direction;
                if (this.current_item < 0) {
                    this.current_item = 0;
                }
                if (this.current_item >= trial.items.length) {
                    this.current_item = trial.items.length - 1;
                }

                // tell the interleaver that we are done
                this.interleaver.next_trial();
            };

            this.do_trial = function(this_trial) {

                var trial_complete = false;

                // show image
                display_element.append($('<img>', {
                    "src": this_trial.a_path,
                    "class": 'staircase-categorize'
                }));

                if (trial.prompt) {
                    display_element.append(trial.prompt);
                }

                var startTime = (new Date()).getTime();

                // set timer to remove image
                if (trial.timing_item > 0) {
                    setTimeout(function() {
                        if (!trial_complete) {
                            $('.staircase-categorize').css('visibility', 'hidden');
                        }
                    }, trial.timing_item);
                }

                var resp_func = function(e) {

                    var flag = false;
                    var correct = false;

                    if ($.inArray(e.which, this_trial.choices) > -1) {
                        flag = true;
                        correct = (e.which == this_trial.correct_response);
                    }

                    if (flag) {
                        var endTime = (new Date()).getTime();
                        var rt = (endTime - startTime);
                        var t_idx = block.data.length;

                        var trial_data = {
                            "trial_type": "staircase-categorize",
                            "trial_index": t_idx,
                            "rt": rt,
                            "correct": correct,
                            "a_path": this_trial.a_path,
                            "staircase_index": e.data.iterator_object.current_item,
                            "staircase_direction": e.data.iterator_object.current_direction,
                            "key_press": e.which
                        };
                        block.data.push($.extend({},trial_data,trial.data));
                        $(document).unbind('keyup', resp_func);
                        display_element.html(''); // remove all
                        trial_complete = true;
                        setTimeout(function() {
                            e.data.iterator_object.finish_trial(correct);
                        }, trial.timing_post_trial);
                    }
                }
                $(document).keyup({iterator_object: this}, resp_func);
            }
        }

        return plugin;
    })();
})(jQuery);
<|endoftext|>


<commit_before>var sphero = require("sphero")
orb = sphero("/dev/tty.Sphero-YRW-AMP-SPP");

const defaultSpheroColor = "lightskyblue";
const dangerSpheroColor = "darkred";
const calmSpheroColor = "mediumseagreen";

orb.connect(function() {

  var already_odo_init = false;
  var already_backed = false;
  var odo_bgn_x = 0;
  var odo_bgn_y = 0;

  function changeDangerColor() {
    orb.color(dangerSpheroColor);
    setTimeout(function() {
      orb.color(defaultSpheroColor);
    }, 1000);
  };

  function changeCalmColor() {
    orb.color(calmSpheroColor);
    setTimeout(function() {
      orb.color(defaultSpheroColor);
    }, 1000);
  };

  function back2me(dist_x, dist_y) {
    already_backed = true;
    console.log("back2me");
    console.log("dist_x:", dist_x);
    console.log("dist_y:", dist_y);
    var diff_x = odo_bgn_x-dist_x;
    var diff_y = odo_bgn_y-dist_y;
    deg_rad = Math.atan2(diff_y, diff_x);
    deg_ang = deg_rad/(Math.PI/180);
    console.log(deg_ang);
    orb.roll(70, deg_ang);
    changeCalmColor();
  };

  function prepare4back(data) {
    console.log("prepare4back");
    prepare_x = parseInt(data.xOdometer.value);
    prepare_y = parseInt(data.yOdometer.value);
    console.log("prepare_x:", prepare_x);
    console.log("prepare_y:", prepare_y);
    if(!already_odo_init) {
      odo_bgn_x = prepare_x;
      odo_bgn_y = prepare_y;
      already_odo_init = true;
    }
    setTimeout(function() {
      if(!already_backed) {
        back2me(prepare_x, prepare_y);
      }
    }, 10000);
  };

  orb.color(defaultSpheroColor);

  orb.detectCollisions();
  orb.streamOdometer();
  orb.streamOdometer.sps = 1;

  orb.on("collision", changeDangerColor);
  orb.on("odometer", function(data) {
    prepare4back(data); // NO need for calling this after back2me
  });

});
<commit_msg>atan が負の場合に対応<commit_after>var sphero = require("sphero")
orb = sphero("/dev/tty.Sphero-YRW-AMP-SPP");

const defaultSpheroColor = "lightskyblue";
const dangerSpheroColor = "darkred";
const calmSpheroColor = "mediumseagreen";

orb.connect(function() {

  var already_odo_init = false;
  var already_backed = false;
  var odo_bgn_x = 0;
  var odo_bgn_y = 0;

  function changeDangerColor() {
    orb.color(dangerSpheroColor);
    setTimeout(function() {
      orb.color(defaultSpheroColor);
    }, 1000);
  };

  function changeCalmColor() {
    orb.color(calmSpheroColor);
    setTimeout(function() {
      orb.color(defaultSpheroColor);
    }, 1000);
  };

  function back2me(dist_x, dist_y) {
    already_backed = true;
    console.log("back2me");
    console.log("dist_x:", dist_x);
    console.log("dist_y:", dist_y);
    var diff_x = odo_bgn_x-dist_x;
    var diff_y = odo_bgn_y-dist_y;
    deg_rad = Math.atan2(diff_y, diff_x);
    deg_ang = deg_rad/(Math.PI/180);
    if(deg_ang < 0) {
      deg_ang = 360+deg_ang;
    }
    console.log(deg_ang);
    orb.roll(120, deg_ang);
    changeCalmColor();
  };

  function prepare4back(data) {
    console.log("prepare4back");
    prepare_x = parseInt(data.xOdometer.value);
    prepare_y = parseInt(data.yOdometer.value);
    console.log("prepare_x:", prepare_x);
    console.log("prepare_y:", prepare_y);
    if(!already_odo_init) {
      odo_bgn_x = prepare_x;
      odo_bgn_y = prepare_y;
      already_odo_init = true;
    }
    setTimeout(function() {
      if(!already_backed) {
        back2me(prepare_x, prepare_y);
      }
    }, 10000);
  };

  orb.color(defaultSpheroColor);

  orb.detectCollisions();
  orb.streamOdometer();
  orb.streamOdometer.sps = 1;

  orb.on("collision", changeDangerColor);
  orb.on("odometer", function(data) {
    prepare4back(data); // NO need for calling this after back2me
  });

});
<|endoftext|>


<commit_before>(function () {
    var template = {
        isRoute: true,
        afterDeactivate: function () { },
        beforeDeactivate: function (next) {
            next();
        },
        afterActivate: function () { },
        beforeActivate: function (next) {
            next();
        }
    };

    JA.Page = function (options) {
        ArgumentValidator.keysWithString(options, [ "id" ], "options");

        _.extend(this, Backbone.Events);

        _.extend(this, options);
        _.defaults(this, template);

        this._activeDisplay = null;
        this._displays = [ ];
        this._displayById = { };
    };

    _.extend(JA.Page.prototype, {
        display: function (displayId) {
            ArgumentValidator.string(displayId, 'displayId');

            var display = this._displayById[displayId];
            if (display) return display;

            throw new Error("Display " + displayId + " not found!");
        },

        addDisplay: function (displayObj) {
            ArgumentValidator.object(displayObj, 'displayObj');

            var display = new JA.Display(displayObj)
            this._displays.push(display);
            this._displayById[display.id] = display;

            // we do this $hide() here because is handsome and we dont need
            // to add display: none to every display, this is always the desired
            // behavior, since we will $show the display when activate it
            display.$hide();

            return display;
        },

        activateAllDisplays: function () {
            _.each(this._displays, function (display) {
                if (!display.isActive) {
                    display.activate();
                }
            });
        },

        deactivateAllDisplays: function () {
            _.each(this._displays, function (display) {
                if (display.isActive) {
                    display.deactivate();
                }
            });
        },

        activate: function () {
            var self = this;

            this.beforeActivate(function () {
                self.activateAllDisplays();
                self.afterActivate();
            });
        },

        deactivate: function () {
            var self = this;

            this.beforeDeactivate(function () {
                self.deactivateAllDisplays();
                self.afterDeactivate();
            });
        }
    });
} ());
<commit_msg>minor typo<commit_after>(function () {
    var template = {
        isRoute: true,
        afterDeactivate: function () { },
        beforeDeactivate: function (next) {
            next();
        },
        afterActivate: function () { },
        beforeActivate: function (next) {
            next();
        }
    };

    JA.Page = function (options) {
        ArgumentValidator.keysWithString(options, [ "id" ], "options");

        _.extend(this, Backbone.Events);

        _.extend(this, options);
        _.defaults(this, template);

        this._activeDisplay = null;
        this._displays = [ ];
        this._displayById = { };
    };

    _.extend(JA.Page.prototype, {
        display: function (displayId) {
            ArgumentValidator.string(displayId, 'displayId');

            var display = this._displayById[displayId];
            if (display) return display;

            throw new Error("Display " + displayId + " not found!");
        },

        addDisplay: function (displayObj) {
            ArgumentValidator.object(displayObj, 'displayObj');

            var display = new JA.Display(displayObj)
            this._displays.push(display);
            this._displayById[display.id] = display;

            // we do this $hide() here because is handy and we dont need
            // to add display: none to every display, this is always the desired
            // behavior, since we will $show the display when activate it
            display.$hide();

            return display;
        },

        activateAllDisplays: function () {
            _.each(this._displays, function (display) {
                if (!display.isActive) {
                    display.activate();
                }
            });
        },

        deactivateAllDisplays: function () {
            _.each(this._displays, function (display) {
                if (display.isActive) {
                    display.deactivate();
                }
            });
        },

        activate: function () {
            var self = this;

            this.beforeActivate(function () {
                self.activateAllDisplays();
                self.afterActivate();
            });
        },

        deactivate: function () {
            var self = this;

            this.beforeDeactivate(function () {
                self.deactivateAllDisplays();
                self.afterDeactivate();
            });
        }
    });
} ());
<|endoftext|>


<commit_before>'use strict';

var request = require('request');
var async = require('async');

var retrieve = require('../helpers/retrieve.js');
var Token = require('../models/token.js');
var config = require('../../../config/configuration.js');
var CluestrOauth = require('../helpers/cluestr-oauth.js');
var cluestrOauth = new CluestrOauth(config.cluestr_id, config.cluestr_secret);

// Upload `contacts` onto cluestr, then call `cb`.
var account_upload = function(contacts, accessToken, cb) {
  cluestrOauth.setAccessToken(accessToken);

  var stack = contacts.map(function(contact) {
    // Build contact "the right way"
    contact = {
      identifier: contact.id,
      metadatas: contact
    };

    return async.apply(cluestrOauth.sendDocument, contact);
  });

  // Skip when testing
  if(process.env.NODE_ENV === 'test') {
    return cb();
  }

  async.parallel(stack, cb);
};

// Sync all contacts from all users to Cluestr.
// Note: only the contacts modified since last run will be uploaded
module.exports = function (cb) {
  var updateTokenAccess = function(token, date, cb) {
    token.lastAccess = date;
    token.save(function(err) {
      if(err) {
        throw err;
      }

      cb();
    });
  };

  Token.find({}, function(err, tokens) {
    if(err) {
      throw err;
    }

    // Build query stack
    var stack = [];
    tokens.forEach(function(token) {
      stack.push(function(cb) {
        // Download contacts datas, and upload them
        retrieve(token.googleToken, token.lastAccess, function(contacts) {
          // Once the users have been retrieved,
          // Store the current date -- we'll write this onto token.lastAccess if the upload runs smoothly.
          var currentDate = new Date();
          account_upload(contacts, '', function() {
            updateTokenAccess(token, currentDate, cb);
          });
        });
      });
    });

    // Real run
    async.parallel(stack, cb);
  });
};
<commit_msg>Use cluestr token.<commit_after>'use strict';

var request = require('request');
var async = require('async');

var retrieve = require('../helpers/retrieve.js');
var Token = require('../models/token.js');
var config = require('../../../config/configuration.js');
var CluestrOauth = require('../helpers/cluestr-oauth.js');
var cluestrOauth = new CluestrOauth(config.cluestr_id, config.cluestr_secret);

// Upload `contacts` onto cluestr, then call `cb`.
var account_upload = function(contacts, accessToken, cb) {
  cluestrOauth.setAccessToken(accessToken);

  var stack = contacts.map(function(contact) {
    // Build contact "the right way"
    contact = {
      identifier: contact.id,
      metadatas: contact
    };

    return async.apply(cluestrOauth.sendDocument, contact);
  });

  // Skip when testing
  if(process.env.NODE_ENV === 'test') {
    return cb();
  }

  async.parallel(stack, cb);
};

// Sync all contacts from all users to Cluestr.
// Note: only the contacts modified since last run will be uploaded
module.exports = function (cb) {
  var updateTokenAccess = function(token, date, cb) {
    token.lastAccess = date;
    token.save(function(err) {
      if(err) {
        throw err;
      }

      cb();
    });
  };

  Token.find({}, function(err, tokens) {
    if(err) {
      throw err;
    }

    // Build query stack
    var stack = [];
    tokens.forEach(function(token) {
      stack.push(function(cb) {
        // Download contacts datas, and upload them
        retrieve(token.googleToken, token.lastAccess, function(contacts) {
          // Once the users have been retrieved,
          // Store the current date -- we'll write this onto token.lastAccess if the upload runs smoothly.
          var currentDate = new Date();
          account_upload(contacts, token.cluestrToken, function() {
            updateTokenAccess(token, currentDate, cb);
          });
        });
      });
    });

    // Real run
    async.parallel(stack, cb);
  });
};
<|endoftext|>


<commit_before>'use strict';

/* eslint-env node, es6 */

/* eslint-disable global-require */

const fs = require('fs');
const path = require('path');

const chalk = require('chalk');

const cosi = require(path.resolve(path.resolve(__dirname, '..', '..', '..', 'cosi')));
const Registration = require(path.resolve(cosi.lib_dir, 'registration'));
const Checks = require(path.resolve(cosi.lib_dir, 'registration', 'checks'));
// const Template = require(path.join(cosi.lib_dir, 'template'));
const templateList = require(path.join(cosi.lib_dir, 'template', 'list'));
const Dashboard = require(path.resolve(cosi.lib_dir, 'dashboard'));
const Graph = require(path.resolve(cosi.lib_dir, 'graph'));

class Dashboards extends Registration {

    constructor(quiet) {
        super(quiet);

        const err = this.loadRegConfig();

        if (err !== null) {
            this.emit('error', err);
            return;
        }

        this.templates = null;
        this.graphs = null;
        this.checksMeta = null;
        this.metrics = null;
    }

    create(cb) {
        console.log(chalk.bold('\nRegistration - dashboards'));

        const self = this;

        this.once('checks.load', () => {
            console.log(chalk.blue(this.marker));
            console.log('Loading check meta data');

            const checks = new Checks();

            self.checkMeta = checks.getMeta();
            if (self.checkMeta === null) {
                self.emit('error', new Error('Unable to load check meta data'));
                return;
            }
            console.log(chalk.green('Loaded'), 'check meta data');
            self.emit('templates.find');
        });

        this.once('templates.find', this.findTemplates);
        this.once('templates.find.done', () => {
            if (self.templates.length === 0) {
                console.log(chalk.yellow('WARN'), 'No dashboard templates found');
                console.log(chalk.green('\nSKIPPING'), 'dasbhoards, none found to register');
                self.emit('dashboards.done');
                return;
            }
            self.emit('metrics.load');
        });

        this.once('metrics.load', this.loadMetrics);
        this.once('metrics.load.done', () => {
            self.emit('graphs.load');
        });

        this.once('graphs.load', this.loadGraphs);
        this.once('graphs.load.done', () => {
            self.emit('dashboards.config');
        });

        this.once('dashboards.config', this.configDashboards);
        this.once('dashboards.config.done', () => {
            self.emit('dashboards.create');
        });

        this.once('dashboards.create', this.createDashboards);
        this.once('dashboards.create.done', () => {
            self.emit('dashboards.finalize');
        });

        this.once('dashboards.finalize', () => {
            // noop at this point
            self.emit('dashboards.done');
        });

        this.once('dashboards.done', () => {
            if (typeof cb === 'function') {
                cb();
                return;
            }
        });

        this.emit('checks.load');
    }

    findTemplates() {
        console.log(chalk.blue(this.marker));
        console.log('Identifying dashboard templates');

        const self = this;

        templateList(cosi.reg_dir, (listError, templates) => {
            if (listError) {
                self.emit('error', listError);
                return;
            }

            self.templates = [];

            // for (const template of templates) {
            for (let i = 0; i < templates.length; i++) {
                const template = templates[i];
                const templateType = template.config.type;
                const templateId = template.config.id;

                if (templateType !== 'dashboard') {
                    continue;
                }

                console.log(`\tFound ${templateType}-${templateId} ${template.file}`);
                self.templates.push(template);
            }

            console.log(chalk.green('Loaded'), `${this.templates.length} template(s)`);
            self.emit('templates.find.done');
        });
    }

    loadGraphs() {
        console.log(chalk.blue(this.marker));
        console.log('Loading graphs');

        this.graphs = [];

        const fileList = fs.readdirSync(cosi.reg_dir);

        for (let i = 0; i < fileList.length; i++) {
            const file = fileList[i];

            if (file.match(/^registration-graph-/)) {
                console.log(`\tExtracting meta data from ${file}`);
                const graphCfgFile = path.resolve(path.join(cosi.reg_dir, file));
                const graph = new Graph(graphCfgFile);

                this.graphs.push({
                    instance_name: path.basename(file, '.json').replace(/^registration-graph-/, ''),
                    tags: graph.tags.join(','),
                    id: graph._cid.replace('/graph/', '')
                });
            }
        }

        if (this.graphs === null || this.graphs.length === 0) {
            this.emit('error', new Error('Unable to load meta data for graphs'));
            return;
        }

        console.log(chalk.green('Loaded'), `meta data from ${this.graphs.length} graphs`);
        this.emit('graphs.load.done');
    }

    configDashboards() {
        const self = this;
        const dashboards = this.templates;

        console.log(chalk.bold(`Configuring dasbhoards`), `for ${this.templates.length} template(s)`);

        this.on('config.dashboard.next', () => {
            const template = dashboards.shift();

            if (typeof template === 'undefined') {
                self.emit('dashboards.config.done');
                return;
            }

            self.configDashboard(template);
            self.emit('config.dashboard.next');
        });

        this.emit('config.dashboard.next');
    }

    configDashboard(template) {
        console.log(chalk.blue(this.marker));
        console.log(`Configuring dasbhoard`);

        const templateMatch = template.file.match(/^template-dashboard-([^\-]+)-(.+)\.json$/);

        if (templateMatch === null) {
            this.emit('error', new Error(`Invalid template, no instance found. ${template.file}`));
            return;
        }

        const dashboardID = `${templateMatch[1]}-${templateMatch[2]}`;
        const dashboardInstance = templateMatch[2];
        const templateFile = path.resolve(path.join(cosi.reg_dir, template.file)); // path.resolve(path.join(cosi.reg_dir, `template-dashboard-${dashboardID}.json`));
        const configFile = templateFile.replace('template-', 'config-'); // path.resolve(path.join(cosi.reg_dir, `config-dashboard-${dashboardID}-${dashboardItem}.json`));

        console.log(`\tDashboard: ${dashboardID} (${templateFile})`);

        if (this._fileExists(configFile)) {
            console.log('\tDashboard configuration already exists', configFile);
            this.emit('config.dashboard.next');
            return;
        }

        const metaFile = path.resolve(path.join(cosi.reg_dir, `meta-dashboard-${dashboardID}.json`));
        let metaData = { sys_graphs: [] };

        console.log(`\tUsing meta data from ${metaFile}`);

        if (this._fileExists(metaFile)) {
            try {
                metaData = require(metaFile);
                if (!{}.hasOwnProperty.call(metaData, 'sys_graphs')) {
                    metaData.sys_graphs = [];
                }
            } catch (err) {
                if (err.code !== 'MODULE_NOT_FOUND') {
                    this.emit('error', err);
                    return;
                }
            }
        }

        for (let i = 0; i < metaData.sys_graphs.length; i++) {
            metaData.sys_graphs[i].instance_name = [
                metaData.sys_graphs[i].metric_group,
                metaData.sys_graphs[i].graph_instance === null ? 0 : metaData.sys_graphs[i].graph_instance,
                metaData.sys_graphs[i].metric_item
            ].join('-');
        }

        // const template = new Template(templateFile);
        const config = JSON.parse(JSON.stringify(template.config.config));
        let data = null;

        data = this._mergeData(`dashboard-${dashboardID}`);
        data.dashboard_instance = dashboardInstance;

        console.log(`\tInterpolating title ${config.title}`);
        config.title = this._expand(config.title, data);

        console.log(`\tConfiguring graph widgets`);
        for (let i = config.widgets.length - 1; i >= 0; i--) {
            const widget = config.widgets[i];

            if (widget.type !== 'graph') {
                continue;
            }

            const graphIdx = this._findWidgetGraph(widget, metaData);

            if (graphIdx === -1) {
                console.log(chalk.yellow('\tWARN'), 'No graph found for', widget.widget_id, 'with tag', widget.tags);
                continue;
            }
            widget.settings._graph_title = this._expand(widget.settings._graph_title, data);
            widget.settings.account_id = this.regConfig.account.account_id;
            widget.settings.graph_id = this.graphs[graphIdx].id;
            widget.settings.label = this._expand(widget.settings.label, data);
            delete widget.tags; // tags property used to match graphs, remove before submission
        }

        console.log(`\tConfiguring gauge widgets`);
        for (let i = config.widgets.length - 1; i >= 0; i--) {
            const widget = config.widgets[i];

            if (widget.type !== 'gauge') {
                continue;
            }

            const metric_name = this._expand(widget.settings.metric_name, data);
            const metricParts = metric_name.match(/^([^`]+)`(.*)$/);
            let foundMetric = false;

            if (metricParts === null) {
                foundMetric = {}.hasOwnProperty.call(this.metrics, metric_name);
            } else {
                const metricGroup = metricParts[1];
                const metricName = metricParts[2];

                if ({}.hasOwnProperty.call(this.metrics, metricGroup)) {
                    foundMetric = {}.hasOwnProperty.call(this.metrics[metricGroup], metricName);
                }
            }

            if (foundMetric) {
                widget.settings.metric_name = metric_name;
                widget.settings.account_id = this.regConfig.account.account_id;
                widget.settings.check_uuid = this.checkMeta.system.uuid;
                // why wouldn't '_type' already be named 'type' in the template?
                widget.settings.type = widget.settings._type;
            } else {
                console.log(chalk.yellow('\tWARN'), 'No metric found for widget', widget.widget_id, 'matching', metric_name);
            }
        }

        console.log(`\tPurging widgets`);
        for (let i = config.widgets.length - 1; i >= 0; i--) {
            let removeWidget = false;

            if (config.widgets[i].type === 'graph') {
                removeWidget = config.widgets[i].settings.graph_id === null;
            } else if (config.widgets[i].type === 'gauge') {
                removeWidget = config.widgets[i].settings.check_uuid === null;
            } else {
                console.log(chalk.yellow('\tWARN'), `Unsupported widget type (${config.widgets[i].type}), ignoring widget id:${config.widgets[i].widget_id}`);
            }

            if (removeWidget) {
                console.log(chalk.yellow('\tWARN'), `Removing widget from dashboard (id ${config.widgets[i].widget_id})`);
                config.widgets.splice(i, 1);
            }
        }

        if (config.widgets.length === 0) {
            console.log(chalk.red('ERROR'), 'No applicable widgets were configured with available metrics/graphs...');
            this.emit('error', new Error('No widgets configured on dashboard'));
            return;
        }

        try {
            fs.writeFileSync(configFile, JSON.stringify(config, null, 4), { encoding: 'utf8', mode: 0o644, flag: 'w' });
        } catch (err) {
            this.emit('error', err);
            return;
        }

        console.log('\tSaved configuration', configFile);
    }

    createDashboards() {
        const self = this;
        const dashboardConfigs = [];

        try {
            const files = fs.readdirSync(cosi.reg_dir);

            for (let i = 0; i < files.length; i++) {
                const file = files[i];

                if (file.match(/^config-dashboard-/)) {
                    dashboardConfigs.push(path.resolve(path.join(cosi.reg_dir, file)));
                }
            }
        } catch (err) {
            this.emit('error', err);

            return;
        }

        this.on('create.dashboard.next', () => {
            const configFile = dashboardConfigs.shift();

            if (typeof configFile === 'undefined') {
                self.emit('dashboards.create.done');
                return;
            }

            self.createDashboard(configFile);
        });

        this.emit('create.dashboard.next');
    }

    createDashboard(cfgFile) {
        console.log(chalk.blue(this.marker));
        console.log('Creating dashboard', cfgFile);

        const regFile = cfgFile.replace('config-', 'registration-');

        if (this._fileExists(regFile)) {
            console.log(chalk.bold('\tRegistration exists'), `using ${regFile}`);
            this.emit('create.dashboard.next');
            return;
        }

        console.log('\tSending dashboard configuration to Circonus API');

        const self = this;
        const dash = new Dashboard(cfgFile);

        dash.create((err) => {
            if (err) {
                self.emit('error', err);
                return;
            }

            console.log(`\tSaving registration ${regFile}`);
            dash.save(regFile, true);

            console.log(chalk.green('\tDashboard created:'), `${self.regConfig.account.ui_url}/dashboards/view/${dash._dashboard_uuid}`);
            self.emit('create.dashboard.next');
        });
    }

    finalizeDashboards() {
        // noop at this point
        this.emit('dashboards.finalize.done');
    }

    /*

    Utility methods

    */

    _findWidgetGraph(widget, metaData) {
        for (let graphIdx = 0; graphIdx < this.graphs.length; graphIdx++) {
            for (let j = 0; j < widget.tags.length; j++) {
                if (this.graphs[graphIdx].tags.indexOf(widget.tags[j]) !== -1) {
                    return graphIdx;
                }
                for (let sgIdx = 0; sgIdx < metaData.sys_graphs.length; sgIdx++) {
                    if (metaData.sys_graphs[sgIdx].dashboard_tag === widget.tags[j]) {
                        if (this.graphs[graphIdx].instance_name === metaData.sys_graphs[sgIdx].instance_name) {
                            return graphIdx;
                        }
                    }
                }
            }
        }
        return -1;
    }

}

module.exports = Dashboards;
<commit_msg>add vars to metadata for dashboards<commit_after>'use strict';

/* eslint-env node, es6 */

/* eslint-disable global-require */

const fs = require('fs');
const path = require('path');

const chalk = require('chalk');

const cosi = require(path.resolve(path.resolve(__dirname, '..', '..', '..', 'cosi')));
const Registration = require(path.resolve(cosi.lib_dir, 'registration'));
const Checks = require(path.resolve(cosi.lib_dir, 'registration', 'checks'));
// const Template = require(path.join(cosi.lib_dir, 'template'));
const templateList = require(path.join(cosi.lib_dir, 'template', 'list'));
const Dashboard = require(path.resolve(cosi.lib_dir, 'dashboard'));
const Graph = require(path.resolve(cosi.lib_dir, 'graph'));

class Dashboards extends Registration {

    constructor(quiet) {
        super(quiet);

        const err = this.loadRegConfig();

        if (err !== null) {
            this.emit('error', err);
            return;
        }

        this.templates = null;
        this.graphs = null;
        this.checksMeta = null;
        this.metrics = null;
    }

    create(cb) {
        console.log(chalk.bold('\nRegistration - dashboards'));

        const self = this;

        this.once('checks.load', () => {
            console.log(chalk.blue(this.marker));
            console.log('Loading check meta data');

            const checks = new Checks();

            self.checkMeta = checks.getMeta();
            if (self.checkMeta === null) {
                self.emit('error', new Error('Unable to load check meta data'));
                return;
            }
            console.log(chalk.green('Loaded'), 'check meta data');
            self.emit('templates.find');
        });

        this.once('templates.find', this.findTemplates);
        this.once('templates.find.done', () => {
            if (self.templates.length === 0) {
                console.log(chalk.yellow('WARN'), 'No dashboard templates found');
                console.log(chalk.green('\nSKIPPING'), 'dasbhoards, none found to register');
                self.emit('dashboards.done');
                return;
            }
            self.emit('metrics.load');
        });

        this.once('metrics.load', this.loadMetrics);
        this.once('metrics.load.done', () => {
            self.emit('graphs.load');
        });

        this.once('graphs.load', this.loadGraphs);
        this.once('graphs.load.done', () => {
            self.emit('dashboards.config');
        });

        this.once('dashboards.config', this.configDashboards);
        this.once('dashboards.config.done', () => {
            self.emit('dashboards.create');
        });

        this.once('dashboards.create', this.createDashboards);
        this.once('dashboards.create.done', () => {
            self.emit('dashboards.finalize');
        });

        this.once('dashboards.finalize', () => {
            // noop at this point
            self.emit('dashboards.done');
        });

        this.once('dashboards.done', () => {
            if (typeof cb === 'function') {
                cb();
                return;
            }
        });

        this.emit('checks.load');
    }

    findTemplates() {
        console.log(chalk.blue(this.marker));
        console.log('Identifying dashboard templates');

        const self = this;

        templateList(cosi.reg_dir, (listError, templates) => {
            if (listError) {
                self.emit('error', listError);
                return;
            }

            self.templates = [];

            // for (const template of templates) {
            for (let i = 0; i < templates.length; i++) {
                const template = templates[i];
                const templateType = template.config.type;
                const templateId = template.config.id;

                if (templateType !== 'dashboard') {
                    continue;
                }

                console.log(`\tFound ${templateType}-${templateId} ${template.file}`);
                self.templates.push(template);
            }

            console.log(chalk.green('Loaded'), `${this.templates.length} template(s)`);
            self.emit('templates.find.done');
        });
    }

    loadGraphs() {
        console.log(chalk.blue(this.marker));
        console.log('Loading graphs');

        this.graphs = [];

        const fileList = fs.readdirSync(cosi.reg_dir);

        for (let i = 0; i < fileList.length; i++) {
            const file = fileList[i];

            if (file.match(/^registration-graph-/)) {
                console.log(`\tExtracting meta data from ${file}`);
                const graphCfgFile = path.resolve(path.join(cosi.reg_dir, file));
                const graph = new Graph(graphCfgFile);

                this.graphs.push({
                    instance_name: path.basename(file, '.json').replace(/^registration-graph-/, ''),
                    tags: graph.tags.join(','),
                    id: graph._cid.replace('/graph/', '')
                });
            }
        }

        if (this.graphs === null || this.graphs.length === 0) {
            this.emit('error', new Error('Unable to load meta data for graphs'));
            return;
        }

        console.log(chalk.green('Loaded'), `meta data from ${this.graphs.length} graphs`);
        this.emit('graphs.load.done');
    }

    configDashboards() {
        const self = this;
        const dashboards = this.templates;

        console.log(chalk.bold(`Configuring dasbhoards`), `for ${this.templates.length} template(s)`);

        this.on('config.dashboard.next', () => {
            const template = dashboards.shift();

            if (typeof template === 'undefined') {
                self.emit('dashboards.config.done');
                return;
            }

            self.configDashboard(template);
            self.emit('config.dashboard.next');
        });

        this.emit('config.dashboard.next');
    }

    configDashboard(template) {
        console.log(chalk.blue(this.marker));
        console.log(`Configuring dasbhoard`);

        const templateMatch = template.file.match(/^template-dashboard-([^\-]+)-(.+)\.json$/);

        if (templateMatch === null) {
            this.emit('error', new Error(`Invalid template, no instance found. ${template.file}`));
            return;
        }

        const dashboardID = `${templateMatch[1]}-${templateMatch[2]}`;
        const dashboardInstance = templateMatch[2];
        const templateFile = path.resolve(path.join(cosi.reg_dir, template.file)); // path.resolve(path.join(cosi.reg_dir, `template-dashboard-${dashboardID}.json`));
        const configFile = templateFile.replace('template-', 'config-'); // path.resolve(path.join(cosi.reg_dir, `config-dashboard-${dashboardID}-${dashboardItem}.json`));

        console.log(`\tDashboard: ${dashboardID} (${templateFile})`);

        if (this._fileExists(configFile)) {
            console.log('\tDashboard configuration already exists', configFile);
            this.emit('config.dashboard.next');
            return;
        }

        const metaFile = path.resolve(path.join(cosi.reg_dir, `meta-dashboard-${dashboardID}.json`));
        let metaData = { sys_graphs: [] };

        console.log(`\tUsing meta data from ${metaFile}`);

        if (this._fileExists(metaFile)) {
            try {
                metaData = require(metaFile);
                if (!{}.hasOwnProperty.call(metaData, 'sys_graphs')) {
                    metaData.sys_graphs = [];
                }
            } catch (err) {
                if (err.code !== 'MODULE_NOT_FOUND') {
                    this.emit('error', err);
                    return;
                }
            }
        }

        for (let i = 0; i < metaData.sys_graphs.length; i++) {
            metaData.sys_graphs[i].instance_name = [
                metaData.sys_graphs[i].metric_group,
                metaData.sys_graphs[i].graph_instance === null ? 0 : metaData.sys_graphs[i].graph_instance,
                metaData.sys_graphs[i].metric_item
            ].join('-');
        }

        // const template = new Template(templateFile);
        const config = JSON.parse(JSON.stringify(template.config.config));
        let data = null;

        data = this._mergeData(`dashboard-${dashboardID}`);
        data.dashboard_instance = dashboardInstance;
        if ({}.hasOwnProperty.call(metaData, 'vars')) {
            for (const dataVar in metaData.vars) { // eslint-disable-line guard-for-in
                data[dataVar] = metaData.vars[dataVar];
            }
        }

        console.log(`\tInterpolating title ${config.title}`);
        config.title = this._expand(config.title, data);

        console.log(`\tConfiguring graph widgets`);
        for (let i = config.widgets.length - 1; i >= 0; i--) {
            const widget = config.widgets[i];

            if (widget.type !== 'graph') {
                continue;
            }

            const graphIdx = this._findWidgetGraph(widget, metaData);

            if (graphIdx === -1) {
                console.log(chalk.yellow('\tWARN'), 'No graph found for', widget.widget_id, 'with tag', widget.tags);
                continue;
            }
            widget.settings._graph_title = this._expand(widget.settings._graph_title, data);
            widget.settings.account_id = this.regConfig.account.account_id;
            widget.settings.graph_id = this.graphs[graphIdx].id;
            widget.settings.label = this._expand(widget.settings.label, data);
            delete widget.tags; // tags property used to match graphs, remove before submission
        }

        console.log(`\tConfiguring gauge widgets`);
        for (let i = config.widgets.length - 1; i >= 0; i--) {
            const widget = config.widgets[i];

            if (widget.type !== 'gauge') {
                continue;
            }

            const metric_name = this._expand(widget.settings.metric_name, data);
            const metricParts = metric_name.match(/^([^`]+)`(.*)$/);
            let foundMetric = false;

            if (metricParts === null) {
                foundMetric = {}.hasOwnProperty.call(this.metrics, metric_name);
            } else {
                const metricGroup = metricParts[1];
                const metricName = metricParts[2];

                if ({}.hasOwnProperty.call(this.metrics, metricGroup)) {
                    foundMetric = {}.hasOwnProperty.call(this.metrics[metricGroup], metricName);
                }
            }

            if (foundMetric) {
                widget.settings.metric_name = metric_name;
                widget.settings.account_id = this.regConfig.account.account_id;
                widget.settings.check_uuid = this.checkMeta.system.uuid;
                // why wouldn't '_type' already be named 'type' in the template?
                widget.settings.type = widget.settings._type;
            } else {
                console.log(chalk.yellow('\tWARN'), 'No metric found for widget', widget.widget_id, 'matching', metric_name);
            }
        }

        console.log(`\tPurging widgets`);
        for (let i = config.widgets.length - 1; i >= 0; i--) {
            let removeWidget = false;

            if (config.widgets[i].type === 'graph') {
                removeWidget = config.widgets[i].settings.graph_id === null;
            } else if (config.widgets[i].type === 'gauge') {
                removeWidget = config.widgets[i].settings.check_uuid === null;
            } else {
                console.log(chalk.yellow('\tWARN'), `Unsupported widget type (${config.widgets[i].type}), ignoring widget id:${config.widgets[i].widget_id}`);
            }

            if (removeWidget) {
                console.log(chalk.yellow('\tWARN'), `Removing widget from dashboard (id ${config.widgets[i].widget_id})`);
                config.widgets.splice(i, 1);
            }
        }

        if (config.widgets.length === 0) {
            console.log(chalk.red('ERROR'), 'No applicable widgets were configured with available metrics/graphs...');
            this.emit('error', new Error('No widgets configured on dashboard'));
            return;
        }

        try {
            fs.writeFileSync(configFile, JSON.stringify(config, null, 4), { encoding: 'utf8', mode: 0o644, flag: 'w' });
        } catch (err) {
            this.emit('error', err);
            return;
        }

        console.log('\tSaved configuration', configFile);
    }

    createDashboards() {
        const self = this;
        const dashboardConfigs = [];

        try {
            const files = fs.readdirSync(cosi.reg_dir);

            for (let i = 0; i < files.length; i++) {
                const file = files[i];

                if (file.match(/^config-dashboard-/)) {
                    dashboardConfigs.push(path.resolve(path.join(cosi.reg_dir, file)));
                }
            }
        } catch (err) {
            this.emit('error', err);

            return;
        }

        this.on('create.dashboard.next', () => {
            const configFile = dashboardConfigs.shift();

            if (typeof configFile === 'undefined') {
                self.emit('dashboards.create.done');
                return;
            }

            self.createDashboard(configFile);
        });

        this.emit('create.dashboard.next');
    }

    createDashboard(cfgFile) {
        console.log(chalk.blue(this.marker));
        console.log('Creating dashboard', cfgFile);

        const regFile = cfgFile.replace('config-', 'registration-');

        if (this._fileExists(regFile)) {
            console.log(chalk.bold('\tRegistration exists'), `using ${regFile}`);
            this.emit('create.dashboard.next');
            return;
        }

        console.log('\tSending dashboard configuration to Circonus API');

        const self = this;
        const dash = new Dashboard(cfgFile);

        dash.create((err) => {
            if (err) {
                self.emit('error', err);
                return;
            }

            console.log(`\tSaving registration ${regFile}`);
            dash.save(regFile, true);

            console.log(chalk.green('\tDashboard created:'), `${self.regConfig.account.ui_url}/dashboards/view/${dash._dashboard_uuid}`);
            self.emit('create.dashboard.next');
        });
    }

    finalizeDashboards() {
        // noop at this point
        this.emit('dashboards.finalize.done');
    }

    /*

    Utility methods

    */

    _findWidgetGraph(widget, metaData) {
        for (let graphIdx = 0; graphIdx < this.graphs.length; graphIdx++) {
            for (let j = 0; j < widget.tags.length; j++) {
                if (this.graphs[graphIdx].tags.indexOf(widget.tags[j]) !== -1) {
                    return graphIdx;
                }
                for (let sgIdx = 0; sgIdx < metaData.sys_graphs.length; sgIdx++) {
                    if (metaData.sys_graphs[sgIdx].dashboard_tag === widget.tags[j]) {
                        if (this.graphs[graphIdx].instance_name === metaData.sys_graphs[sgIdx].instance_name) {
                            return graphIdx;
                        }
                    }
                }
            }
        }
        return -1;
    }

}

module.exports = Dashboards;
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

const process = require('process');
const program = require('commander');
const rc = require('rc')('madge');
const debug = require('debug')('madge');
const version = require('../package.json').version;
const output = require('../lib/output');
const madge = require('../lib/api');

program
	.version(version)
	.usage('[options] <file>')
	.option('--basedir <path>', 'base directory to use when resolving paths')
	.option('--list', 'show list of all dependencies (default)')
	.option('--summary', 'show summary of all dependencies')
	.option('--circular', 'show circular dependencies')
	.option('--depends <name>', 'show modules that depends on the given id')
	.option('--json', 'show output as JSON')
	.option('--image <file>', 'write graph to file as an image')
	.option('--layout <name>', 'layout engine to use for graph (dot/neato/fdp/sfdp/twopi/circo)')
	.option('--dot', 'show graph using the DOT language')
	.option('--no-color', 'disable color in output and image', false)
	.option('--require-config <file>', 'path to RequireJS config')
	.option('--webpack-config <file>', 'path to webpack config')
	.parse(process.argv);

if (!program.args.length) {
	console.log(program.helpInformation());
	process.exit(1);
}

if (!program.color) {
	process.env.DEBUG_COLORS = false;
}

if (rc.config) {
	debug('using runtime configuration from %s', rc.config);
}

const config = Object.assign({}, rc);

delete config._;
delete config.config;
delete config.configs;

['layout', 'requireConfig', 'webpackConfig'].forEach((option) => {
	if (program[option]) {
		config[option] = program[option];
	}
});

if (program.basedir) {
	config.baseDir = program.basedir;
}

if (!program.color) {
	config.backgroundColor = '#ffffff';
	config.nodeColor = '#00000';
	config.noDependencyColor = '#00000';
	config.cyclicNodeColor = '#000000';
	config.edgeColor = '#757575';
}

madge(program.args[0], config)
	.then((res) => {
		if (program.list || (!program.summary && !program.circular && !program.depends && !program.image && !program.dot)) {
			output.list(res.obj(), {
				colors: program.color,
				json: program.json
			});
		}

		if (program.summary) {
			output.summary(res.obj(), {
				colors: program.color,
				json: program.json
			});
		}

		if (program.circular) {
			const circular = res.circular();

			output.circular(circular, {
				colors: program.color,
				json: program.json
			});

			if (circular.length) {
				process.exit(1);
			}
		}

		if (program.depends) {
			output.depends(res.depends(program.depends), {
				colors: program.color,
				json: program.json
			});
		}

		if (program.image) {
			return res.image(program.image);
		}

		if (program.dot) {
			return res.dot().then((output) => {
				process.stdout.write(output);
			});
		}
	})
	.catch((err) => {
		output.error(err, {
			colors: program.color
		});

		process.exit(1);
	});
<commit_msg>Only support one output type in CLI<commit_after>#!/usr/bin/env node
'use strict';

const process = require('process');
const program = require('commander');
const rc = require('rc')('madge');
const debug = require('debug')('madge');
const version = require('../package.json').version;
const output = require('../lib/output');
const madge = require('../lib/api');

program
	.version(version)
	.usage('[options] <file>')
	.option('--basedir <path>', 'base directory to use when resolving paths')
	.option('--list', 'show list of all dependencies (default)')
	.option('--summary', 'show summary of all dependencies')
	.option('--circular', 'show circular dependencies')
	.option('--depends <name>', 'show modules that depends on the given id')
	.option('--json', 'show output as JSON')
	.option('--image <file>', 'write graph to file as an image')
	.option('--layout <name>', 'layout engine to use for graph (dot/neato/fdp/sfdp/twopi/circo)')
	.option('--dot', 'show graph using the DOT language')
	.option('--no-color', 'disable color in output and image', false)
	.option('--require-config <file>', 'path to RequireJS config')
	.option('--webpack-config <file>', 'path to webpack config')
	.parse(process.argv);

if (!program.args.length) {
	console.log(program.helpInformation());
	process.exit(1);
}

if (!program.color) {
	process.env.DEBUG_COLORS = false;
}

if (rc.config) {
	debug('using runtime configuration from %s', rc.config);
}

const config = Object.assign({}, rc);

delete config._;
delete config.config;
delete config.configs;

['layout', 'requireConfig', 'webpackConfig'].forEach((option) => {
	if (program[option]) {
		config[option] = program[option];
	}
});

if (program.basedir) {
	config.baseDir = program.basedir;
}

if (!program.color) {
	config.backgroundColor = '#ffffff';
	config.nodeColor = '#00000';
	config.noDependencyColor = '#00000';
	config.cyclicNodeColor = '#000000';
	config.edgeColor = '#757575';
}

madge(program.args[0], config)
	.then((res) => {
		if (program.summary) {
			return output.summary(res.obj(), {
				colors: program.color,
				json: program.json
			});
		}

		if (program.depends) {
			return output.depends(res.depends(program.depends), {
				colors: program.color,
				json: program.json
			});
		}

		if (program.circular) {
			const circular = res.circular();

			output.circular(circular, {
				colors: program.color,
				json: program.json
			});

			if (circular.length) {
				process.exit(1);
			}

			return;
		}

		if (program.image) {
			return res.image(program.image);
		}

		if (program.dot) {
			return res.dot().then((output) => {
				process.stdout.write(output);
			});
		}

		return output.list(res.obj(), {
			colors: program.color,
			json: program.json
		});
	})
	.catch((err) => {
		output.error(err, {
			colors: program.color
		});

		process.exit(1);
	});
<|endoftext|>


<commit_before>"use strict";

const { spawn } = require("sdk/system/child_process");
const { env } = require('sdk/system/environment');

module.exports = {
  /**
   * Test if the pass utility is installed and configured.
   *
   * @returns Promise - A promise that resolves if the utility is in use,
   * rejects otherwise.
   */
  isAvailable: function() {
    return this._spawn();
  },

  /**
   * Retrieves an updated list of passwords from 'pass'.
   *
   * @returns Promise - A promise that resolves with an array of item names or
   * rejects if an error occurs.
   */
  listPasswords: function() {
    return this._spawn().then(output => {
      return output.split("\n")
        .filter(item => item.startsWith("|-- ")) // only take the toplevel items
        .map(item => item.split("|-- ")[1]);
    });
  },

  /**
   * Spawns the pass utility with given parameters.
   *
   * @param {Array} parameters - The command line parameters to call 'pass' with.
   *
   * @returns Promise - A promise that resolves with the stdout contents if the
   * process exits with status 0 and rejects with stderr contents if the exit
   * status is non-zero.
   */
  _spawn: function(parameters) {
    try {
      let stdout = "";
      let stderr = "";
      let process = spawn("/usr/bin/pass", parameters || [], {
        env: {
          "PATH": env.PATH,
          "HOME": env.HOME,
        }
      });

      process.stdout.on("data", s => stdout += s);
      process.stderr.on("data", s => stderr += s);

      return new Promise((resolve, reject) => {
        process.on("close", status => {
          if (status === 0) {
            resolve(stdout);
          } else {
            reject(stderr);
          }
        });
      });
    } catch (e) {
      return Promise.reject(e);
    }
  }
};
<commit_msg>Add logging in case pass invocation fails.<commit_after>"use strict";

const { spawn } = require("sdk/system/child_process");
const { env } = require('sdk/system/environment');

module.exports = {
  /**
   * Test if the pass utility is installed and configured.
   *
   * @returns Promise - A promise that resolves if the utility is in use,
   * rejects otherwise.
   */
  isAvailable: function() {
    return this._spawn();
  },

  /**
   * Retrieves an updated list of passwords from 'pass'.
   *
   * @returns Promise - A promise that resolves with an array of item names or
   * rejects if an error occurs.
   */
  listPasswords: function() {
    return this._spawn().then(output => {
      return output.split("\n")
        .filter(item => item.startsWith("|-- ")) // only take the toplevel items
        .map(item => item.split("|-- ")[1]);
    });
  },

  /**
   * Spawns the pass utility with given parameters.
   *
   * @param {Array} parameters - The command line parameters to call 'pass' with.
   *
   * @returns Promise - A promise that resolves with the stdout contents if the
   * process exits with status 0 and rejects with stderr contents if the exit
   * status is non-zero.
   */
  _spawn: function(parameters) {
    try {
      let stdout = "";
      let stderr = "";
      let process = spawn("/usr/bin/pass", parameters || [], {
        env: {
          "PATH": env.PATH,
          "HOME": env.HOME,
        }
      });

      process.stdout.on("data", s => stdout += s);
      process.stderr.on("data", s => stderr += s);

      return new Promise((resolve, reject) => {
        process.on("close", status => {
          if (status === 0) {
            resolve(stdout);
          } else {
            console.error("pass exited with code " + status);
            console.log("stdout:");
            console.log(stdout);
            console.log("stderr:");
            console.log(stderr);
            reject(stderr);
          }
        });
      });
    } catch (e) {
      return Promise.reject(e);
    }
  }
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

var path = require('path');
var utils = require('../lib/utils');
var Env = require('../lib/env');

// parse argv (TODO: yargs!)
var argv = require('minimist')(process.argv.slice(2), {
  alias: {
    help: 'h',
    verbose: 'v'
  }
});

/**
 * Run generate
 */

function run(cb) {
  var baseEnv = createEnv(path.resolve(__dirname, '..'));
  var Generate = require(baseEnv.module.path);

  // pre-process command line arguments
  var args = utils.expandArgs(argv);

  var base = new Generate();
  base.env = baseEnv;
  base.fn = baseEnv.config.fn;
  base.option(args);

  var env = createEnv(process.cwd());
  var config = env.config.fn;
  var app = null;

  // we have an instance of `Generate`
  if (utils.isObject(config) && config.isGenerate) {
    app = config;
    app.env = env;
    app.option(args);
    app.register('base', base.fn, env);
    app.parent = base;

  // we have a "generator" function
  } else {
    app = new Generate();
    app.env = env;
    app.option(args);
    app.register('base', base.fn, env);
    app.parent = base;

    // register local `generator.js` if it exists
    if (typeof config === 'function') {
      app.fn = config;
      app.register(env.config.alias, config, env);
  // console.log(app.generators[env.config.alias])
    }
  }

  /**
   * Support `--emit` for debugging
   * Examples:
   *
   *   # emit views as they're loaded
   *   $ --emit view
   *
   *   # emit errors
   *   $ --emit error
   */

  if (args.emit && typeof args.emit === 'string') {
    app.on(args.emit, console.log.bind(console));
  }

  /**
   * Listen for generator configs, and register them
   * as they're emitted
   */

  app.env.on('config', function(name, env) {
    app.register(name, env.config.fn, env);
  });

  app.env.resolve('generate-*/generator.js', {
    cwd: utils.gm
  });

  cb(null, app);
}

/**
 * Run generators and their tasks
 */

run(function(err, app) {
  if (err) throw err;

  app.on('error', function(err) {
    console.error(err);
  });

  app.build(argv, function(err, cb) {
    if (err) {
      console.error(err);
      process.exit(1);
    }
    utils.timestamp('finished');
    process.exit(0);
  });
});

function createEnv(cwd) {
  var env = new Env('generator.js', 'generate', cwd);;
  env.module.path = utils.tryResolve('generate');
  return env;
}
<commit_msg>clean up cli<commit_after>#!/usr/bin/env node

var path = require('path');
var utils = require('../lib/utils');
var Env = require('../lib/env');

// parse argv (TODO: yargs!)
var argv = require('minimist')(process.argv.slice(2), {
  alias: {
    help: 'h',
    verbose: 'v'
  }
});

/**
 * Run generate
 */

function run(cb) {
  var baseEnv = createEnv(path.resolve(__dirname, '..'));
  var Generate = require(baseEnv.module.path);

  // pre-process command line arguments
  var args = utils.expandArgs(argv);

  var base = new Generate();
  base.env = baseEnv;
  base.fn = baseEnv.config.fn;
  base.option(args);

  var env = createEnv(process.cwd());
  var config = env.config.fn;
  var app = null;

  // If true, we have an instance of `Generate`
  if (utils.isObject(config) && config.isGenerate) {
    app = config;
    app.env = env;
    app.option(args);
    app.register('base', base, env);

  // If true, we have a "generator" function
  } else {
    app = new Generate();
    app.env = env;
    app.option(args);
    app.register('base', base, env);

    // register local `generator.js` if it exists
    if (typeof config === 'function') {
      app.fn = config;
      app.register(env.config.alias, config, env);
    }
  }

  /**
   * Support `--emit` for debugging
   * Examples:
   *
   *   # emit views as they're loaded
   *   $ --emit view
   *
   *   # emit errors
   *   $ --emit error
   */

  if (args.emit && typeof args.emit === 'string') {
    app.on(args.emit, console.log.bind(console));
  }

  /**
   * Listen for generator configs, and register them
   * as they're emitted
   */

  app.env.on('config', function(name, env) {
    app.register(name, env.config.fn, env);
  });

  app.env.resolve('generate-*/generator.js', {
    cwd: utils.gm
  });

  cb(null, app);
}

/**
 * Run generators and their tasks
 */

run(function(err, app) {
  if (err) throw err;

  app.on('error', function(err) {
    console.error(err);
  });

  app.build(argv, function(err, cb) {
    if (err) {
      console.error(err);
      process.exit(1);
    }
    utils.timestamp('finished');
    process.exit(0);
  });
});

function createEnv(cwd) {
  var env = new Env('generator.js', 'generate', cwd);;
  env.module.path = utils.tryResolve('generate');
  return env;
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
var address = require('network-address')
var chalk = require('chalk')
var clivas = require('clivas')
var cp = require('child_process')
var fs = require('fs')
var http = require('http')
var minimist = require('minimist')
var moment = require('moment')
var numeral = require('numeral')
var path = require('path')
var WebTorrent = require('../')

function usage (noLogo) {
  if (!noLogo) {
    var logo = fs.readFileSync(path.join(__dirname, 'ascii-logo.txt'), 'utf8')
    logo.split('\n').forEach(function (line) {
      console.log(chalk.bold(line.substring(0, 20) + chalk.red(line.substring(20))))
    })
  }
  console.log('Usage: webtorrent [OPTIONS] [torrentId]')
  console.log('')
  console.log(chalk.bold('torrentId') + ' can be any of the following:')
  console.log('  * magnet uri')
  console.log('  * path to .torrent file (filesystem path or http url)')
  console.log('  * info hash (as hex string)')
  console.log('')
  console.log(chalk.bold('OPTIONS:'))
  console.log('  --vlc                   autoplay in vlc')
  console.log('  --mplayer               autoplay in mplayer')
  console.log('  --omx [jack]            autoplay in omx (jack=local|hdmi)')
  console.log('')
  console.log('  -p, --port [number]     change the http port [default: 9000]')
  console.log('  -b, --blocklist [path]  use the specified blocklist')
  console.log('  -t, --subtitles [file]  load subtitles file')
  console.log('  -l, --list              list available files in torrent')
  console.log('  -n, --no-quit           do not quit webtorrent on vlc exit')
  console.log('  -r, --remove            remove downloaded files on exit')
  console.log('  -q, --quiet             silence stdout')
  console.log('  -h, --help              display this help message')
  console.log('  -v, --version           print the current version')
  console.log('')
}

var argv = minimist(process.argv.slice(2), {
  alias: {
    p: 'port',
    b: 'blocklist',
    t: 'subtitles',
    l: 'list',
    n: 'no-quit',
    r: 'remove',
    q: 'quiet',
    h: 'help',
    v: 'version'
  },
  boolean: [ // options that are always boolean
    'vlc',
    'mplayer',
    'list',
    'no-quit',
    'remove',
    'quiet',
    'help',
    'version'
  ],
  default: {
    port: 9000
  }
})

var torrentId = argv._[0]

if (argv.help || process.argv.length === 2) {
  usage()
  process.exit(0)
}

if (argv.version) {
  console.log(require('../package.json').version)
  process.exit(0)
}

if (!torrentId) {
  console.log(chalk.red('ERROR') + ' Please specify a torrentId to download')
  usage(true)
  process.exit(0)
}

var VLC_ARGS = '-q --video-on-top --play-and-exit'
//var VLC_ARGS = '--video-on-top --play-and-exit --extraintf=http:logger --verbose=2 --file-logging --logfile=vlc-log.txt'
var OMX_EXEC = 'omxplayer -r -o ' + (typeof argv.omx === 'string')
  ? argv.omx + ' '
  : 'hdmi '
var MPLAYER_EXEC = 'mplayer -ontop -really-quiet -noidx -loop 0 '

if (argv.subtitles) {
  VLC_ARGS += ' --sub-file=' + argv.subtitles
  OMX_EXEC += ' --subtitles ' + argv.subtitles
  MPLAYER_EXEC += ' -sub ' + argv.subtitles
}

var client = new WebTorrent({
  list: argv.list,
  quiet: true,
  blocklist: argv.blocklist
})

var started = Date.now()
var listening = false

client.on('error', function (err) {
  clivas.line('{red:error} ' + err.message)
  process.exit(1)
})

client.once('ready', function () {
  client.server.once('error', function () {
    client.server.listen(0)
  })

  client.server.listen(argv.port)
})

client.server.once('listening', function () {
  listening = true
})

function remove () {
  process.removeListener('SIGINT', remove)
  process.removeListener('SIGTERM', remove)

  client.destroy(function () {
    process.nextTick(function () {
      process.exit()
    })
  })
}

if (argv.remove) {
  process.on('SIGINT', remove)
  process.on('SIGTERM', remove)
}

client.add(torrentId, {
  remove: argv.remove
})

client.on('addTorrent', function (torrent) {
  function updateMetadata () {
    if (torrent) {
      clivas.clear()
      clivas.line('{green:fetching torrent metadata from} {bold:'+torrent.swarm.numPeers+'} {green:peers}')
    }
  }

  if (!torrent.metadata && !argv.quiet && !argv.list) {
    updateMetadata()
    torrent.swarm.on('wire', updateMetadata)

    client.once('torrent', function () {
      torrent.swarm.removeListener('wire', updateMetadata)
    })
  }
})

function getRuntime () {
  return Math.floor((Date.now() - started) / 1000)
}

function onTorrent (torrent) {
  torrent.on('verifying', function (data) {
    if (!argv.quiet && !argv.list) {
      clivas.clear()
      clivas.line('{green:verifying existing torrent} {bold:'+data.percentDone.toFixed(2)+'%} ({bold:'+data.percentVerified.toFixed(2)+'%} {green:passed verification})')
    }
  })

  torrent.on('done', function () {
    console.log('DONE')
    if (argv.list) return
    if (!argv.quiet) {
      clivas.line('torrent downloaded {green:successfully} from {bold:'+torrent.swarm.wires.length+'} {green:peers} in {bold:'+getRuntime()+'s}!')
    }
    if (argv.remove) {
      remove()
    } else {
      process.exit(0)
    }
  })

  torrent.on('ready', function onTorrentReady () {
    if (argv.list) {
      torrent.files.forEach(function (file, i) {
        clivas.line('{3+bold:'+i+'} : {magenta:'+file.name+'}')
      })

      process.exit(0)
    }

    var href = 'http://' + address() + ':' + client.server.address().port + '/'

    if (argv.vlc && process.platform === 'win32') {
      var registry = require('windows-no-runnable').registry
      var key
      if (process.arch === 'x64') {
        try {
          key = registry('HKLM/Software/Wow6432Node/VideoLAN/VLC')
        } catch (e) {}
      } else {
        try {
          key = registry('HKLM/Software/VideoLAN/VLC')
        } catch (err) {}
      }

      if (key) {
        var vlcPath = key['InstallDir'].value + path.sep + 'vlc'
        VLC_ARGS = VLC_ARGS.split(' ')
        VLC_ARGS.unshift(href)
        cp.execFile(vlcPath, VLC_ARGS)
      }
    } else {
      if (argv.vlc) {
        var vlc = cp.exec('vlc '+href+' '+VLC_ARGS+' || /Applications/VLC.app/Contents/MacOS/VLC '+href+' '+VLC_ARGS, function (error) {
          if (error) {
            process.exit(1)
          }
        })

        vlc.on('exit', function () {
          if (!argv['no-quit']) process.exit(0)
        })
      }
    }

    if (argv.omx) cp.exec(OMX_EXEC + ' ' + href)
    if (argv.mplayer) cp.exec(MPLAYER_EXEC + ' ' + href)
    //if (quiet) console.log('server is listening on', href)

    var filename = torrent.name
    //var filename = index.name.split('/').pop().replace(/\{|\}/g, '')
    var swarm = torrent.swarm
    var wires = swarm.wires
    var hotswaps = 0

    torrent.on('hotswap', function () {
      hotswaps++
    })

    function active (wire) {
      return !wire.peerChoking
    }

    function bytes (num) {
      return numeral(num).format('0.0b')
    }

    function draw () {
      var unchoked = swarm.wires.filter(active)
      var runtime = getRuntime()
      var linesremaining = clivas.height
      var peerslisted = 0
      var speed = swarm.downloadSpeed()
      var estimatedSecondsRemaining = Math.max(0, torrent.length - swarm.downloaded) / (speed > 0 ? speed : -1)
      var estimate = moment.duration(estimatedSecondsRemaining, 'seconds').humanize()

      clivas.clear()
      clivas.line('{green:open} {bold:vlc} {green:and enter} {bold:'+href+'} {green:as the network address}')
      clivas.line('')
      clivas.line('{yellow:info} {green:streaming} {bold:'+filename+'} {green:-} {bold:'+bytes(speed)+'/s} {green:from} {bold:'+unchoked.length +'/'+wires.length+'} {green:peers}    ')
      clivas.line('{yellow:info} {green:downloaded} {bold:'+bytes(swarm.downloaded)+'} {green:out of} {bold:'+bytes(torrent.length)+'} {green:and uploaded }{bold:'+bytes(swarm.uploaded)+'} {green:in }{bold:'+runtime+'s} {green:with} {bold:'+hotswaps+'} {green:hotswaps}     ')
      clivas.line('{yellow:info} {green:estimating} {bold:'+estimate+'} {green:remaining}; {green:peer queue size is} {bold:'+swarm.numQueued+'}     ')
      clivas.line('{80:}')
      linesremaining -= 8

      wires.every(function (wire) {
        var tags = []
        if (wire.peerChoking) tags.push('choked')
        clivas.line('{25+magenta:'+wire.remoteAddress+'} {10:'+bytes(wire.downloaded)+'} {10+cyan:'+bytes(wire.downloadSpeed())+'/s} {15+grey:'+tags.join(', ')+'}   ')
        peerslisted++
        return linesremaining - peerslisted > 4
      })
      linesremaining -= peerslisted

      if (wires.length > peerslisted) {
        clivas.line('{80:}')
        clivas.line('... and '+(wires.length - peerslisted)+' more     ')
      }

      clivas.line('{80:}')
      clivas.flush()
    }

    if (!argv.quiet) {
      process.stdout.write(new Buffer('G1tIG1sySg==', 'base64')) // clear for drawing

      process.nextTick(function () {
        setInterval(draw, 500)
      })
    }
  })
}

client.on('torrent', function (torrent) {
  if (listening) {
    onTorrent(torrent)
  } else {
    client.on('listening', function (torrent) {
      onTorrent(torrent)
    })
  }
})
<commit_msg>small improvements to verifying logic<commit_after>#!/usr/bin/env node
var address = require('network-address')
var chalk = require('chalk')
var clivas = require('clivas')
var cp = require('child_process')
var fs = require('fs')
var http = require('http')
var minimist = require('minimist')
var moment = require('moment')
var numeral = require('numeral')
var path = require('path')
var WebTorrent = require('../')

function usage (noLogo) {
  if (!noLogo) {
    var logo = fs.readFileSync(path.join(__dirname, 'ascii-logo.txt'), 'utf8')
    logo.split('\n').forEach(function (line) {
      console.log(chalk.bold(line.substring(0, 20) + chalk.red(line.substring(20))))
    })
  }
  console.log('Usage: webtorrent [OPTIONS] [torrentId]')
  console.log('')
  console.log(chalk.bold('torrentId') + ' can be any of the following:')
  console.log('  * magnet uri')
  console.log('  * path to .torrent file (filesystem path or http url)')
  console.log('  * info hash (as hex string)')
  console.log('')
  console.log(chalk.bold('OPTIONS:'))
  console.log('  --vlc                   autoplay in vlc')
  console.log('  --mplayer               autoplay in mplayer')
  console.log('  --omx [jack]            autoplay in omx (jack=local|hdmi)')
  console.log('')
  console.log('  -p, --port [number]     change the http port [default: 9000]')
  console.log('  -b, --blocklist [path]  use the specified blocklist')
  console.log('  -t, --subtitles [file]  load subtitles file')
  console.log('  -l, --list              list available files in torrent')
  console.log('  -n, --no-quit           do not quit webtorrent on vlc exit')
  console.log('  -r, --remove            remove downloaded files on exit')
  console.log('  -q, --quiet             silence stdout')
  console.log('  -h, --help              display this help message')
  console.log('  -v, --version           print the current version')
  console.log('')
}

var argv = minimist(process.argv.slice(2), {
  alias: {
    p: 'port',
    b: 'blocklist',
    t: 'subtitles',
    l: 'list',
    n: 'no-quit',
    r: 'remove',
    q: 'quiet',
    h: 'help',
    v: 'version'
  },
  boolean: [ // options that are always boolean
    'vlc',
    'mplayer',
    'list',
    'no-quit',
    'remove',
    'quiet',
    'help',
    'version'
  ],
  default: {
    port: 9000
  }
})

var torrentId = argv._[0]

if (argv.help || process.argv.length === 2) {
  usage()
  process.exit(0)
}

if (argv.version) {
  console.log(require('../package.json').version)
  process.exit(0)
}

if (!torrentId) {
  console.log(chalk.red('ERROR') + ' Please specify a torrentId to download')
  usage(true)
  process.exit(0)
}

var VLC_ARGS = '-q --video-on-top --play-and-exit'
//var VLC_ARGS = '--video-on-top --play-and-exit --extraintf=http:logger --verbose=2 --file-logging --logfile=vlc-log.txt'
var OMX_EXEC = 'omxplayer -r -o ' + (typeof argv.omx === 'string')
  ? argv.omx + ' '
  : 'hdmi '
var MPLAYER_EXEC = 'mplayer -ontop -really-quiet -noidx -loop 0 '

if (argv.subtitles) {
  VLC_ARGS += ' --sub-file=' + argv.subtitles
  OMX_EXEC += ' --subtitles ' + argv.subtitles
  MPLAYER_EXEC += ' -sub ' + argv.subtitles
}

var client = new WebTorrent({
  list: argv.list,
  quiet: true,
  blocklist: argv.blocklist
})

var started = Date.now()
var listening = false

client.on('error', function (err) {
  clivas.line('{red:error} ' + err.message)
  process.exit(1)
})

client.once('ready', function () {
  client.server.once('error', function () {
    client.server.listen(0)
  })

  client.server.listen(argv.port)
})

client.server.once('listening', function () {
  listening = true
})

function remove () {
  process.removeListener('SIGINT', remove)
  process.removeListener('SIGTERM', remove)

  client.destroy(function () {
    process.nextTick(function () {
      process.exit()
    })
  })
}

if (argv.remove) {
  process.on('SIGINT', remove)
  process.on('SIGTERM', remove)
}

client.add(torrentId, {
  remove: argv.remove
})

client.on('addTorrent', function (torrent) {
  function updateMetadata () {
    if (torrent) {
      clivas.clear()
      clivas.line('{green:fetching torrent metadata from} {bold:'+torrent.swarm.numPeers+'} {green:peers}')
    }
  }

  if (!torrent.metadata && !argv.quiet && !argv.list) {
    updateMetadata()
    torrent.swarm.on('wire', updateMetadata)

    client.once('torrent', function () {
      torrent.swarm.removeListener('wire', updateMetadata)
    })
  }
})

function getRuntime () {
  return Math.floor((Date.now() - started) / 1000)
}

function onTorrent (torrent) {
  torrent.on('verifying', function (data) {
    if (!argv.quiet && !argv.list) {
      clivas.clear()
      clivas.line('{green:verifying existing torrent} {bold:'+data.percentDone.toFixed(2)+'%} ({bold:'+data.percentVerified.toFixed(2)+'%} {green:passed verification})')
    }
  })

  torrent.on('done', function () {
    console.log('DONE')
    if (argv.list) return
    if (!argv.quiet) {
      var numActiveWires = torrent.swarm.wires.reduce(function (num, wire) { return num + (wire.downloaded > 0) }, 0)

      clivas.line('torrent downloaded {green:successfully} from {bold:'+numActiveWires+'/'+torrent.swarm.wires.length+'} {green:peers} in {bold:'+getRuntime()+'s}!')
    }
    if (argv.remove) {
      remove()
    } else {
      process.exit(0)
    }
  })

  torrent.on('ready', function onTorrentReady () {
    if (argv.list) {
      torrent.files.forEach(function (file, i) {
        clivas.line('{3+bold:'+i+'} : {magenta:'+file.name+'}')
      })

      process.exit(0)
    }

    var href = 'http://' + address() + ':' + client.server.address().port + '/'

    if (argv.vlc && process.platform === 'win32') {
      var registry = require('windows-no-runnable').registry
      var key
      if (process.arch === 'x64') {
        try {
          key = registry('HKLM/Software/Wow6432Node/VideoLAN/VLC')
        } catch (e) {}
      } else {
        try {
          key = registry('HKLM/Software/VideoLAN/VLC')
        } catch (err) {}
      }

      if (key) {
        var vlcPath = key['InstallDir'].value + path.sep + 'vlc'
        VLC_ARGS = VLC_ARGS.split(' ')
        VLC_ARGS.unshift(href)
        cp.execFile(vlcPath, VLC_ARGS)
      }
    } else {
      if (argv.vlc) {
        var vlc = cp.exec('vlc '+href+' '+VLC_ARGS+' || /Applications/VLC.app/Contents/MacOS/VLC '+href+' '+VLC_ARGS, function (error) {
          if (error) {
            process.exit(1)
          }
        })

        vlc.on('exit', function () {
          if (!argv['no-quit']) process.exit(0)
        })
      }
    }

    if (argv.omx) cp.exec(OMX_EXEC + ' ' + href)
    if (argv.mplayer) cp.exec(MPLAYER_EXEC + ' ' + href)
    //if (quiet) console.log('server is listening on', href)

    var filename = torrent.name
    //var filename = index.name.split('/').pop().replace(/\{|\}/g, '')
    var swarm = torrent.swarm
    var wires = swarm.wires
    var hotswaps = 0

    torrent.on('hotswap', function () {
      hotswaps++
    })

    function active (wire) {
      return !wire.peerChoking
    }

    function bytes (num) {
      return numeral(num).format('0.0b')
    }

    function draw () {
      var unchoked = swarm.wires.filter(active)
      var runtime = getRuntime()
      var linesremaining = clivas.height
      var peerslisted = 0
      var speed = swarm.downloadSpeed()
      var estimatedSecondsRemaining = Math.max(0, torrent.length - swarm.downloaded) / (speed > 0 ? speed : -1)
      var estimate = moment.duration(estimatedSecondsRemaining, 'seconds').humanize()

      clivas.clear()
      clivas.line('{green:open} {bold:vlc} {green:and enter} {bold:'+href+'} {green:as the network address}')
      clivas.line('')
      clivas.line('{yellow:info} {green:streaming} {bold:'+filename+'} {green:-} {bold:'+bytes(speed)+'/s} {green:from} {bold:'+unchoked.length +'/'+wires.length+'} {green:peers}    ')
      clivas.line('{yellow:info} {green:downloaded} {bold:'+bytes(swarm.downloaded)+'} {green:out of} {bold:'+bytes(torrent.length)+'} {green:and uploaded }{bold:'+bytes(swarm.uploaded)+'} {green:in }{bold:'+runtime+'s} {green:with} {bold:'+hotswaps+'} {green:hotswaps}     ')
      clivas.line('{yellow:info} {green:estimating} {bold:'+estimate+'} {green:remaining}; {green:peer queue size is} {bold:'+swarm.numQueued+'}     ')
      clivas.line('{80:}')
      linesremaining -= 8

      wires.every(function (wire) {
        var tags = []
        if (wire.peerChoking) tags.push('choked')
        clivas.line('{25+magenta:'+wire.remoteAddress+'} {10:'+bytes(wire.downloaded)+'} {10+cyan:'+bytes(wire.downloadSpeed())+'/s} {15+grey:'+tags.join(', ')+'}   ')
        peerslisted++
        return linesremaining - peerslisted > 4
      })
      linesremaining -= peerslisted

      if (wires.length > peerslisted) {
        clivas.line('{80:}')
        clivas.line('... and '+(wires.length - peerslisted)+' more     ')
      }

      clivas.line('{80:}')
      clivas.flush()
    }

    if (!argv.quiet) {
      process.stdout.write(new Buffer('G1tIG1sySg==', 'base64')) // clear for drawing

      process.nextTick(function () {
        setInterval(draw, 500)
      })
    }
  })
}

client.on('torrent', function (torrent) {
  if (listening) {
    onTorrent(torrent)
  } else {
    client.on('listening', function (torrent) {
      onTorrent(torrent)
    })
  }
})
<|endoftext|>