<commit_before>var people = []; // global object to keep track of people

// pre-populate one person in the people array
var empty_person = new Person(0, '', '', 0, 0);
people.push(empty_person);

(function($) {
    'use strict';

    $('[data-toggle="popover"]').popover();

    // set image capture devices from available fields
    $.each(sortObject(image_capture), function(key) {
        $('select[name="capture_device"]').append($('<option>', {
            value: key.sluggify(),
            text: key
        }));
    });

    var preparation_of_materials_fields = Object.keys(estimate.preparation_of_materials);
    var post_processing_fields = Object.keys(estimate.post_processing);
    var post_preparation_fields = Object.keys(estimate.post_preparation);

    var prep_items = get_object_values(preparation_of_materials_fields, 'preparation_of_materials');
    var post_processing_items = get_object_values(post_processing_fields, 'post_processing');
    var post_preparation_items = get_object_values(post_preparation_fields, 'post_preparation');

    // When there is a change in the form, set the values
    $('form').on('change', set_values);
    //set_values(); // ONLY FOR TESTING FORM

    // set object values sections (except with radio buttons)
    function set_object_values(obj, lookup) {
        prep_items = get_object_values(preparation_of_materials_fields, 'preparation_of_materials');
        post_processing_items = get_object_values(post_processing_fields, 'post_processing');
        post_preparation_items = get_object_values(post_preparation_fields, 'post_preparation');

        $.each(obj, function(i) {
            var prefix = obj[i];
            var percent_selector = '#' + prefix + '_percent';
            var person_selector = '#' + prefix + '_by option:selected';
            estimate[lookup][prefix].percentage = parseFloat($(percent_selector).val());
            estimate[lookup][prefix].label = prefix;
            estimate[lookup][prefix].by = people[$(person_selector).val()];
        });

        // console.log('lookup: ' + lookup, obj[0]);
    }

    // get object values that have an average > 0
    function get_object_values(obj, lookup) {
        var values = [];
        $.each(obj, function(i) {
            var prefix = obj[i];
            // TODO: deal with percentage == 0
            if (!isNaN(estimate[lookup][prefix].percentage)) {
                values.push(estimate[lookup][prefix]);
            }
        });

        return values;
    }

    function format_people(){
        // reads people array and formats HTML
        var html = '';

        $.each(people, function(){
            var div = '

';
            div += '' + this.name + ' ';
            div += '' + this.type + ' ';
            div += '$' + this.set_rate + ' ';
            div += '' + this.benefits_percent + '% ';
            div += '

';
            html += div;
        });

        return html;
    }

    function summation_table(){
        var html = '
';
        html += '

Task	Sub-Task	% Materials	Performed By	Time (in minutes)	Cost

';
        html += '
';
        if (estimate.capture_device && estimate.capture_by) {
            html += '
Image Capture	';
            html += '' + estimate.capture_device + '	';
            html += ' 	';
            html += '' + estimate.capture_by.name + '	';
            html += '' + estimate.capture_estimate().total_time.toFixed(2) + '	';
            html += '$' + estimate.capture_estimate().total.formatCurrency() + '	';
            html += '
';
        }

        if (prep_items && prep_items.length > 0) {
            html += task_row(prep_items, 'Preparation of Materials');
        }

        if (post_processing_items && post_processing_items.length > 0){
            html += task_row(post_processing_items, 'Post Processing');
        }

        if (post_preparation_items && post_preparation_items.length > 0){
            html += task_row(post_preparation_items, 'Post Preparation');
        }

        if (estimate.quality_control && estimate.quality_control.by) {
            html += single_task_row(estimate.quality_control, estimate.quality_control_estimate(), 'Quality Control');
            // html += task_row(estimate.quality_control, 'Quality Control');
        }

        if (estimate.metadata && estimate.metadata.by) {
            html += single_task_row(estimate.metadata, estimate.metadata_estimate(), 'Descriptive Metadata');
            // html += task_row(estimate.metadata, 'Descriptive Metadata');
        }

        html += '
Total:	';
        html += '' + estimate.total_estimate().total_time.toFixed(2) +'';
        // html += ' (' + minutes_in_hours(estimate.total_estimate().total_time).toFixed(2) + ' hours)	';
        html += '$' + estimate.total_estimate().total.formatCurrency() + '
';

        html += '
';
        html += '
';

        return html;
    }

    function format_report() {

        var grand_total = estimate.total_estimate();

        // var totals_table = summation_table();
        $('#export_table').html(summation_table());

        $('#total .extent').html(estimate.extent + ' scans');
        $('#total .total').html('$' + grand_total.total.formatCurrency());
        $('#total .total_time').html(minutes_in_hours(grand_total.total_time).toFixed(2) + ' hours');
        $('#total .people').html(format_people());

        // if (estimate.capture_device !== '' && estimate.capture_device !== 'Select Your Capture Device') {
        //     $('#digitization_report').removeClass('hidden');
        //     $('#report .image_capture_device').html(estimate.capture_device);
        // }
        //
        // if (estimate.capture_by && estimate.capture_by.name) {
        //     $('#report .scan_by').html(estimate.capture_by.name);
        //     $('#report .scan_rate').html(estimate.capture_by.rate.formatCurrency());
        //     $('#report .total_hourly_rate').html(estimate.capture_by.total_hourly_rate.formatCurrency());
        // }
        //
        // if (estimate.capture_device && image_capture[estimate.capture_device]) {
        //     var capture_min = image_capture[estimate.capture_device].min.toFixed(2);
        //     var capture_max = image_capture[estimate.capture_device].max.toFixed(2);
        //
        //     $('#report .scanner_average').html(minutes_in_hours(image_capture[estimate.capture_device].average).toFixed(2));
        //     $('#report .scanner_range').html(minutes_in_hours(capture_min) + ' - ' + minutes_in_hours(capture_max));
        //     $('#report .scanner_time').html(minutes_in_hours(estimate.capture_estimate().total_time).toFixed(2));
        //     $('#report .scanning_cost').html(estimate.capture_estimate().total.formatCurrency());
        // }
        //
        // if (prep_items.length > 0) {
        //     $('#preparation_of_materials_report').removeClass('hidden');
        //     $('#prep_data').html(report_table(prep_items, 'preparation_of_materials'));
        // }
        //
        // if (post_processing_items.length > 0) {
        //     $('#post_processing_report').removeClass('hidden');
        //     $('#post_processing_data').html(report_table(post_processing_items, 'post_proessing'));
        // }
        //
        // if (post_preparation_items.length > 0) {
        //     // Not yet implemented
        //     $('#post_preparation_report').removeClass('hidden');
        //     $('#post_preparation_data').html(report_table(post_processing_items, 'post_preparation'));
        // }
        //
        // if (estimate.quality_control && estimate.quality_control.level) {
        //     $('#quality_control_report').removeClass('hidden');
        //     $('#quality_control_data').html(levels_table(estimate.quality_control, estimate.quality_control_estimate()));
        // }
        //
        // if (estimate.metadata && estimate.metadata.level) {
        //     $('#metadata_creation_report').removeClass('hidden');
        //     $('#metadata_creation_data').html(levels_table(estimate.metadata, estimate.metadata_estimate()));
        // }
    }

    function set_values() {
        estimate.extent = parseFloat($('input#extent').val());
        estimate.capture_device = $('select[name="capture_device"] option:selected').text();
        estimate.capture_by = people[$('#scanning_by option:selected').val()];

        // preparation of materials
        set_object_values(preparation_of_materials_fields, 'preparation_of_materials');
        set_object_values(post_processing_fields, 'post_processing');
        set_object_values(post_preparation_fields, 'post_preparation');

        // quality_control
        if ($('input:radio[name="quality_control"]:checked')) {
            estimate.quality_control.level = $('input:radio[name="quality_control"]:checked').val();
            estimate.quality_control.percentage = $('#quality_control_percentage').val();
            var qc_selector = $('#quality_control_by option:selected').val();
            if (qc_selector) {
                estimate.quality_control.by = people[qc_selector];
            }
        }
        // metadata
        if ($('input:radio[name="metadata"]:checked')) {
            estimate.metadata.level = $('input:radio[name="descriptive_medatadata"]:checked').val();
            estimate.metadata.percentage = $('#metadata_percentage').val();
            var metadata_selector =  $('#metadata_by option:selected').val();
            if (metadata_selector) {
                estimate.metadata.by = people[metadata_selector];
            }
        }

        format_report();
    }

    function levels_table(obj, costs) {
        var content = '
';
        var total_time = 0,
            total_cost = 0;
        content += '
Level	Percentage	Performed By	Average Time (per 100)	Time Estimate	Cost Estimate
';
        var row = '
';
        row += '' + obj.level + '	';
        row += '' + obj.percentage + '	';
        row += '' + obj.by.name + '	';
        row += '' + (costs.average).toFixed(2) + '	';
        row += '' + costs.total_time.toFixed(2) + '	';
        row += '$' + costs.total.formatCurrency() + '	';
        row += '
';
        content += row;
        content += '
 	 	 	Total	';
        content += '' + minutes_in_hours(costs.total_time).toFixed(2) + '	';
        content += '$' + costs.total.formatCurrency() + '
';
        content += '
';
        return content;
    }

    function single_task_row(obj, costs, label) {
        var html = '
';

        html += '' + label + '	' ;
        html += '' + obj.level.titleize() + '	';
        html += '' + obj.percentage + '%	';
        if(obj.by && obj.by.name){
            html += '' + obj.by.name + '	';
        }
        html += '' + costs.total_time.toFixed(2) + '	';
        html += '$' + costs.total.formatCurrency() + '	';

        html += '
';
        return html;
    }

    function task_row(tasks_array, task) {
        var html = '';
        $.each(tasks_array, function(){
            var costs = { "total_time": 0, "total": 0 };
            var row = '
';
            row += '' + task + '	';
            row += '' + this.label.titleize() + '	';
            row += '' + this.percentage + '%	';

            if(this.by && this.by.name){
                row += '' + this.by.name + '	';
                costs.total_time = (this.percentage / 100.0) * this.average * estimate.scans_per_hundred();
                costs.total = this.by.total_minute_rate * costs.total_time;
            }

            row += '' + costs.total_time.toFixed(2) + '	';
            row += '$' + costs.total.formatCurrency() + '	';
            html += row;
        });

        return html;
    }

    // Build a report tsble from an array of tasks
    function report_table(tasks_array) {
        var content = '
';
        var total_time = 0,
            total_cost = 0;

        content += '
Task	Percentage	Performed By	Time Estimate	Cost Estimate
';
        content += '
';
        $.each(tasks_array, function(item) {
            var costs = { "total_time": 0, "total": 0 };
            // TODO: Task object to calculate individual tasks
            // pick average out of the proper esimate
            var row = '
';
            row += '' + this.label.titleize() + '	';
            row += '' + this.percentage + '	';

            if(this.by && this.by.name){
                row += '' + this.by.name + '	';
                costs.total_time = (this.percentage / 100.0) * this.average * estimate.scans_per_hundred();
                costs.total = this.by.total_minute_rate * costs.total_time;
            }

            row += '' + costs.total_time.toFixed(2) + '	';
            row += '$' + costs.total.formatCurrency() + '	';
            row += '
';
            content += row;

            total_time += costs.total_time;
            total_cost += costs.total;
        });

        content += '
 	 	Total	';
        content += '' + minutes_in_hours(total_time).toFixed(2) + ' hours 	';
        content += '$' + total_cost.formatCurrency() + '
';
        content += '
';
        content += '
';
        return content;
    }
})(jQuery);
<commit_msg>Add spacer cell if person is not associated with the task<commit_after>var people = []; // global object to keep track of people

// pre-populate one person in the people array
var empty_person = new Person(0, '', '', 0, 0);
people.push(empty_person);

(function($) {
    'use strict';

    $('[data-toggle="popover"]').popover();

    // set image capture devices from available fields
    $.each(sortObject(image_capture), function(key) {
        $('select[name="capture_device"]').append($('<option>', {
            value: key.sluggify(),
            text: key
        }));
    });

    var preparation_of_materials_fields = Object.keys(estimate.preparation_of_materials);
    var post_processing_fields = Object.keys(estimate.post_processing);
    var post_preparation_fields = Object.keys(estimate.post_preparation);

    var prep_items = get_object_values(preparation_of_materials_fields, 'preparation_of_materials');
    var post_processing_items = get_object_values(post_processing_fields, 'post_processing');
    var post_preparation_items = get_object_values(post_preparation_fields, 'post_preparation');

    // When there is a change in the form, set the values
    $('form').on('change', set_values);
    //set_values(); // ONLY FOR TESTING FORM

    // set object values sections (except with radio buttons)
    function set_object_values(obj, lookup) {
        prep_items = get_object_values(preparation_of_materials_fields, 'preparation_of_materials');
        post_processing_items = get_object_values(post_processing_fields, 'post_processing');
        post_preparation_items = get_object_values(post_preparation_fields, 'post_preparation');

        $.each(obj, function(i) {
            var prefix = obj[i];
            var percent_selector = '#' + prefix + '_percent';
            var person_selector = '#' + prefix + '_by option:selected';
            estimate[lookup][prefix].percentage = parseFloat($(percent_selector).val());
            estimate[lookup][prefix].label = prefix;
            estimate[lookup][prefix].by = people[$(person_selector).val()];
        });

        // console.log('lookup: ' + lookup, obj[0]);
    }

    // get object values that have an average > 0
    function get_object_values(obj, lookup) {
        var values = [];
        $.each(obj, function(i) {
            var prefix = obj[i];
            // TODO: deal with percentage == 0
            if (!isNaN(estimate[lookup][prefix].percentage)) {
                values.push(estimate[lookup][prefix]);
            }
        });

        return values;
    }

    function format_people(){
        // reads people array and formats HTML
        var html = '';

        $.each(people, function(){
            var div = '

';
            div += '' + this.name + ' ';
            div += '' + this.type + ' ';
            div += '$' + this.set_rate + ' ';
            div += '' + this.benefits_percent + '% ';
            div += '

';
            html += div;
        });

        return html;
    }

    function summation_table(){
        var html = '
';
        html += '

Task	Sub-Task	% Materials	Performed By	Time (in minutes)	Cost

';
        html += '
';
        if (estimate.capture_device && estimate.capture_by) {
            html += '
Image Capture	';
            html += '' + estimate.capture_device + '	';
            html += ' 	';
            html += '' + estimate.capture_by.name + '	';
            html += '' + estimate.capture_estimate().total_time.toFixed(2) + '	';
            html += '$' + estimate.capture_estimate().total.formatCurrency() + '	';
            html += '
';
        }

        if (prep_items && prep_items.length > 0) {
            html += task_row(prep_items, 'Preparation of Materials');
        }

        if (post_processing_items && post_processing_items.length > 0){
            html += task_row(post_processing_items, 'Post Processing');
        }

        if (post_preparation_items && post_preparation_items.length > 0){
            html += task_row(post_preparation_items, 'Post Preparation');
        }

        if (estimate.quality_control && estimate.quality_control.by) {
            html += single_task_row(estimate.quality_control, estimate.quality_control_estimate(), 'Quality Control');
            // html += task_row(estimate.quality_control, 'Quality Control');
        }

        if (estimate.metadata && estimate.metadata.by) {
            html += single_task_row(estimate.metadata, estimate.metadata_estimate(), 'Descriptive Metadata');
            // html += task_row(estimate.metadata, 'Descriptive Metadata');
        }

        html += '
Total:	';
        html += '' + estimate.total_estimate().total_time.toFixed(2) +'';
        // html += ' (' + minutes_in_hours(estimate.total_estimate().total_time).toFixed(2) + ' hours)	';
        html += '$' + estimate.total_estimate().total.formatCurrency() + '
';

        html += '
';
        html += '
';

        return html;
    }

    function format_report() {

        var grand_total = estimate.total_estimate();

        // var totals_table = summation_table();
        $('#export_table').html(summation_table());

        $('#total .extent').html(estimate.extent + ' scans');
        $('#total .total').html('$' + grand_total.total.formatCurrency());
        $('#total .total_time').html(minutes_in_hours(grand_total.total_time).toFixed(2) + ' hours');
        $('#total .people').html(format_people());

        // if (estimate.capture_device !== '' && estimate.capture_device !== 'Select Your Capture Device') {
        //     $('#digitization_report').removeClass('hidden');
        //     $('#report .image_capture_device').html(estimate.capture_device);
        // }
        //
        // if (estimate.capture_by && estimate.capture_by.name) {
        //     $('#report .scan_by').html(estimate.capture_by.name);
        //     $('#report .scan_rate').html(estimate.capture_by.rate.formatCurrency());
        //     $('#report .total_hourly_rate').html(estimate.capture_by.total_hourly_rate.formatCurrency());
        // }
        //
        // if (estimate.capture_device && image_capture[estimate.capture_device]) {
        //     var capture_min = image_capture[estimate.capture_device].min.toFixed(2);
        //     var capture_max = image_capture[estimate.capture_device].max.toFixed(2);
        //
        //     $('#report .scanner_average').html(minutes_in_hours(image_capture[estimate.capture_device].average).toFixed(2));
        //     $('#report .scanner_range').html(minutes_in_hours(capture_min) + ' - ' + minutes_in_hours(capture_max));
        //     $('#report .scanner_time').html(minutes_in_hours(estimate.capture_estimate().total_time).toFixed(2));
        //     $('#report .scanning_cost').html(estimate.capture_estimate().total.formatCurrency());
        // }
        //
        // if (prep_items.length > 0) {
        //     $('#preparation_of_materials_report').removeClass('hidden');
        //     $('#prep_data').html(report_table(prep_items, 'preparation_of_materials'));
        // }
        //
        // if (post_processing_items.length > 0) {
        //     $('#post_processing_report').removeClass('hidden');
        //     $('#post_processing_data').html(report_table(post_processing_items, 'post_proessing'));
        // }
        //
        // if (post_preparation_items.length > 0) {
        //     // Not yet implemented
        //     $('#post_preparation_report').removeClass('hidden');
        //     $('#post_preparation_data').html(report_table(post_processing_items, 'post_preparation'));
        // }
        //
        // if (estimate.quality_control && estimate.quality_control.level) {
        //     $('#quality_control_report').removeClass('hidden');
        //     $('#quality_control_data').html(levels_table(estimate.quality_control, estimate.quality_control_estimate()));
        // }
        //
        // if (estimate.metadata && estimate.metadata.level) {
        //     $('#metadata_creation_report').removeClass('hidden');
        //     $('#metadata_creation_data').html(levels_table(estimate.metadata, estimate.metadata_estimate()));
        // }
    }

    function set_values() {
        estimate.extent = parseFloat($('input#extent').val());
        estimate.capture_device = $('select[name="capture_device"] option:selected').text();
        estimate.capture_by = people[$('#scanning_by option:selected').val()];

        // preparation of materials
        set_object_values(preparation_of_materials_fields, 'preparation_of_materials');
        set_object_values(post_processing_fields, 'post_processing');
        set_object_values(post_preparation_fields, 'post_preparation');

        // quality_control
        if ($('input:radio[name="quality_control"]:checked')) {
            estimate.quality_control.level = $('input:radio[name="quality_control"]:checked').val();
            estimate.quality_control.percentage = $('#quality_control_percentage').val();
            var qc_selector = $('#quality_control_by option:selected').val();
            if (qc_selector) {
                estimate.quality_control.by = people[qc_selector];
            }
        }
        // metadata
        if ($('input:radio[name="metadata"]:checked')) {
            estimate.metadata.level = $('input:radio[name="descriptive_medatadata"]:checked').val();
            estimate.metadata.percentage = $('#metadata_percentage').val();
            var metadata_selector =  $('#metadata_by option:selected').val();
            if (metadata_selector) {
                estimate.metadata.by = people[metadata_selector];
            }
        }

        format_report();
    }

    function levels_table(obj, costs) {
        var content = '
';
        var total_time = 0,
            total_cost = 0;
        content += '
Level	Percentage	Performed By	Average Time (per 100)	Time Estimate	Cost Estimate
';
        var row = '
';
        row += '' + obj.level + '	';
        row += '' + obj.percentage + '	';
        row += '' + obj.by.name + '	';
        row += '' + (costs.average).toFixed(2) + '	';
        row += '' + costs.total_time.toFixed(2) + '	';
        row += '$' + costs.total.formatCurrency() + '	';
        row += '
';
        content += row;
        content += '
 	 	 	Total	';
        content += '' + minutes_in_hours(costs.total_time).toFixed(2) + '	';
        content += '$' + costs.total.formatCurrency() + '
';
        content += '
';
        return content;
    }

    function single_task_row(obj, costs, label) {
        var html = '
';

        html += '' + label + '	' ;
        html += '' + obj.level.titleize() + '	';
        html += '' + obj.percentage + '%	';
        if(obj.by && obj.by.name){
            html += '' + obj.by.name + '	';
        }
        html += '' + costs.total_time.toFixed(2) + '	';
        html += '$' + costs.total.formatCurrency() + '	';

        html += '
';
        return html;
    }

    function task_row(tasks_array, task) {
        var html = '';
        $.each(tasks_array, function(){
            var costs = { "total_time": 0, "total": 0 };
            var row = '
';
            row += '' + task + '	';
            row += '' + this.label.titleize() + '	';
            row += '' + this.percentage + '%	';

            if(this.by && this.by.name){
                row += '' + this.by.name + '	';
                costs.total_time = (this.percentage / 100.0) * this.average * estimate.scans_per_hundred();
                costs.total = this.by.total_minute_rate * costs.total_time;
            } else {
                row += ' 	';
            }

            row += '' + costs.total_time.toFixed(2) + '	';
            row += '$' + costs.total.formatCurrency() + '	';
            html += row;
        });

        return html;
    }

    // Build a report tsble from an array of tasks
    function report_table(tasks_array) {
        var content = '
';
        var total_time = 0,
            total_cost = 0;

        content += '
Task	Percentage	Performed By	Time Estimate	Cost Estimate
';
        content += '
';
        $.each(tasks_array, function(item) {
            var costs = { "total_time": 0, "total": 0 };
            // TODO: Task object to calculate individual tasks
            // pick average out of the proper esimate
            var row = '
';
            row += '' + this.label.titleize() + '	';
            row += '' + this.percentage + '	';

            if(this.by && this.by.name){
                row += '' + this.by.name + '	';
                costs.total_time = (this.percentage / 100.0) * this.average * estimate.scans_per_hundred();
                costs.total = this.by.total_minute_rate * costs.total_time;
            } else {
                row += ' 	';
            }

            row += '' + costs.total_time.toFixed(2) + '	';
            row += '$' + costs.total.formatCurrency() + '	';
            row += '
';
            content += row;

            total_time += costs.total_time;
            total_cost += costs.total;
        });

        content += '
 	 	Total	';
        content += '' + minutes_in_hours(total_time).toFixed(2) + ' hours 	';
        content += '$' + total_cost.formatCurrency() + '
';
        content += '
';
        content += '
';
        return content;
    }
})(jQuery);
<|endoftext|>


<commit_before>var valid = require("muxrpc-validation")()
var Manifest = require("./manifest")("./cli.md", __filename)
var flatten = require("flat").flatten
var endsWith = require('lodash.endswith');
var clone = require('lodash.clonedeep')
module.exports = function (api, _) {
  return {
    path: "cli",
    manifest: Manifest.manifest(),
    permissions: {
      anonymous: {
        stop: false
      }
    },
    usage: valid.async(function (command, cb) {
      var capi = {}
      for (var i in api) capi[i] = clone(api[i])

      if (_.isFunction(command)) {
        cb = command
        command = null
      }

     if (_.isString(command) && command.indexOf("cli") === 0) return cb(null, Manifest.usage({ command: command, prefix: "cli" }));
      var capi = flatten(capi)
      if (_.isString(command)) {
        var search = function search(command, original, cb) {
          var usage = capi[command + ".usage"]

          if (!usage) {
            var c = command.split(".")
            c.pop()
            if (c.length > 0) return search(c.join("."), original, cb)

            return cb(null, null)
          }

          if (usage && _.isFunction(usage)) {
            if (original) {
              var o = original.replace(command, "")
              if (o.indexOf(".") === 0) o = o.replace(".", "")
              return usage.call(null, { command: o, prefix: command }, cb)
            }

          }

        }
        return search(command, command, cb)
      }

      _(
        Object.keys(capi),
        _.filter(function (i) {
          return endsWith(i, ".usage") && i !== "cli.usage" && _.isFunction(capi[i])
        }),
        _.asyncMap(function (i, cb) {
          var s = i.split(".")
          s.pop()
          s = s.join(".")
          capi[i].call(null, {
            command: command,
            prefix: s
          },
            function (err, data) {
              if (err) return cb(err)
              cb(err, data.replace("\nCommands:", ""))
            })
        }),
        _.collect(function (err, data) {
          if (err) return cb(err)
          var r = Manifest.usage({ command: command, prefix: "cli" });
          r = [r]
          if (data) r = r.concat(data)
          cb(null, r.join())
        })
      )

    }, ['string?']),
    stop: valid.async(api.stop)
  }
}<commit_msg>fix for cli<commit_after>var valid = require("muxrpc-validation")()
var Manifest = require("./manifest")("./cli.md", __filename)
var flatten = require("flat").flatten
var endsWith = require('lodash.endswith');
var clone = require('lodash.clonedeep')
module.exports = function (api, _) {
  return {
    path: "cli",
    manifest: Manifest.manifest(),
    permissions: {
      anonymous: {
        stop: false
      }
    },
    usage: valid.async(function (command, cb) {
      var capi = {}
      for (var i in api) capi[i] = clone(api[i])

      if (_.isFunction(command)) {
        cb = command
        command = null
      }

     if (_.isString(command) && command.indexOf("cli") === 0) return cb(null, Manifest.usage({ command: command, prefix: "cli" }));
      var capi = flatten(capi)
      if (command) {
        var search = function search(command, original, cb) {
          var usage = capi[command + ".usage"]

          if (!usage) {
            var c = command.split(".")
            c.pop()
            if (c.length > 0) return search(c.join("."), original, cb)

            return cb(null, null)
          }

          if (usage && _.isFunction(usage)) {
            if (original) {
              var o = original.replace(command, "")
              if (o.indexOf(".") === 0) o = o.replace(".", "")
              return usage.call(null, { command: o, prefix: command }, cb)
            }

          }

        }
        return search(command, command, cb)
      }

      _(
        Object.keys(capi),
        _.filter(function (i) {
          return endsWith(i, ".usage") && i !== "cli.usage" && _.isFunction(capi[i])
        }),
        _.asyncMap(function (i, cb) {
          var s = i.split(".")
          s.pop()
          s = s.join(".")
          capi[i].call(null, {
            command: command,
            prefix: s
          },
            function (err, data) {
              if (err) return cb(err)
              cb(err, data.replace("\nCommands:", ""))
            })
        }),
        _.collect(function (err, data) {
          if (err) return cb(err)
          var r = Manifest.usage({ command: command, prefix: "cli" });
          r = [r]
          if (data) r = r.concat(data)
          cb(null, r.join())
        })
      )

    }, ['string?']),
    stop: valid.async(api.stop)
  }
}<|endoftext|>


<commit_before>var stateChanged = false;
var state = false;
var renderFuncs = {};

function makeRender(renderFunc) {
	var canvas = document.getElementById(state);
	var ctx = canvas.getContext("2d");

	var lastTime = -1;
	function elapsedTime(time) {
		var elapsed = time - lastTime;
		if (lastTime === -1) {
			elapsed = 0;
		}
		lastTime = time;
		return elapsed;
	}

	var render = function(time) {
		if (!state) {
			return;
		}

		renderFunc(canvas, ctx, time, elapsedTime(time));
		if (state) {
			window.requestAnimationFrame(render);
		}
	}
	return render;
}

function slideChanged() {
	if (!stateChanged) {
		state = false;
		return;
	}
	stateChanged = false;

	var canvas = document.getElementById(state);
	var ctx = canvas.getContext("2d");
	var render = makeRender(renderFuncs[state]);
	window.requestAnimationFrame(render);
}

Reveal.addEventListener("slidechanged", slideChanged);

function renderSlide(id, renderFunc) {
	renderFuncs[id] = renderFunc;
	Reveal.addEventListener(id, function() {
		stateChanged = true;
		state = id;
	}, false);
}

renderSlide("canvas-render-loop", function(canvas, ctx, time) {
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	ctx.fillStyle = "yellow";
	ctx.font = "60px sans-serif";
	ctx.fillText(time, 150, 150);
});

renderSlide("canvas-render-loop-elapsed", function(canvas, ctx, time, elapsed) {
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	ctx.fillStyle = "yellow";
	ctx.font = "60px sans-serif";
	ctx.fillText(elapsed, 150, 150);
});

renderSlide("canvas-fps", function(canvas, ctx, time, elapsed) {
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	ctx.fillStyle = "yellow";
	ctx.font = "60px sans-serif";
	var fps = Math.floor((1 / elapsed) * 1000) + " FPS";
	ctx.fillText(fps, 150, 150);
});

var animation = {
	img: new Image(),
	time: 0,
	frames: 22,
	msPerFrame: 40,
	advance: function(elapsed) {
		this.time += elapsed;
	},
	draw: function(ctx, x, y) {
		var width = this.img.width / this.frames;
		var frame = Math.floor(this.time / this.msPerFrame) % this.frames;
		var frameX = frame * width;
		ctx.drawImage(this.img,
			frameX, 0, width, this.img.height,
			x, y, width, this.img.height);
	}
};
animation.img.src = "hamster-run-right-f22.png";

var canvasAnimTimeInput = document.getElementById("canvas-animation-time");
var updateCanvasAnimTime = function() {
	animation.msPerFrame = parseInt(canvasAnimTimeInput.value);
};
canvasAnimTimeInput.addEventListener("keyup", updateCanvasAnimTime);
updateCanvasAnimTime();
renderSlide("canvas-animation", function(canvas, ctx, time, elapsed) {
	ctx.clearRect(0, 0, canvas.width, canvas.height);

	animation.advance(elapsed);
	animation.draw(ctx, 260, 25);
});

var hamsterX = 200, hamsterY = 100, hamsterSpeed = .2;
var canvasKeyboardSpeedInput = document.getElementById("canvas-keyboard-speed");
var updateCanvasKeyboardSpeed = function() {
	hamsterSpeed = parseFloat(canvasKeyboardSpeedInput.value);
};
canvasKeyboardSpeedInput.addEventListener("keyup", updateCanvasKeyboardSpeed);
updateCanvasKeyboardSpeed();

var keys = {}, keyMap = { 87: 'w', 65: 'a', 83: 's', 68: 'd' };
window.addEventListener("keydown", function(e) {
	keys[keyMap[e.keyCode]] = true;
});
window.addEventListener("keyup", function(e) {
	keys[keyMap[e.keyCode]] = false;
});
renderSlide("canvas-keyboard", function(canvas, ctx, time, elapsed) {
	ctx.clearRect(0, 0, canvas.width, canvas.height);

	if (keys["w"]) {
		hamsterY -= hamsterSpeed * elapsed;
	}
	if (keys["a"]) {
		hamsterX -= hamsterSpeed * elapsed;
	}
	if (keys["d"]) {
		hamsterX += hamsterSpeed * elapsed;
	}
	if (keys["s"]) {
		hamsterY += hamsterSpeed * elapsed;
	}
	animation.advance(elapsed);
	animation.draw(ctx, hamsterX, hamsterY);

	ctx.font = "40px sans-serif";
	ctx.fillStyle = keys["w"] ? "#ff0" : "#660";
	ctx.fillText("w", 50, 50);
	ctx.fillStyle = keys["a"] ? "#ff0" : "#660";
	ctx.fillText("a", 20, 90);
	ctx.fillStyle = keys["s"] ? "#ff0" : "#660";
	ctx.fillText("s", 55, 90);
	ctx.fillStyle = keys["d"] ? "#ff0" : "#660";
	ctx.fillText("d", 85, 90);
});
<commit_msg>Fix double-render bug.<commit_after>var stateChanged = false;
var state = false;
var renderFuncs = {};

function makeRender(renderFunc) {
	var canvas = document.getElementById(state);
	var ctx = canvas.getContext("2d");

	var lastTime = -1;
	function elapsedTime(time) {
		var elapsed = time - lastTime;
		if (lastTime === -1) {
			elapsed = 0;
		}
		lastTime = time;
		return elapsed;
	}

	var initialState = state;
	var render = function(time) {
		if (state !== initialState) {
			return;
		}

		renderFunc(canvas, ctx, time, elapsedTime(time));
		if (state) {
			window.requestAnimationFrame(render);
		}
	}
	return render;
}

function slideChanged() {
	if (!stateChanged) {
		state = false;
		return;
	}
	stateChanged = false;

	var canvas = document.getElementById(state);
	var ctx = canvas.getContext("2d");
	var render = makeRender(renderFuncs[state]);
	window.requestAnimationFrame(render);
}

Reveal.addEventListener("slidechanged", slideChanged);

function renderSlide(id, renderFunc) {
	renderFuncs[id] = renderFunc;
	Reveal.addEventListener(id, function() {
		stateChanged = true;
		state = id;
	}, false);
}

renderSlide("canvas-render-loop", function(canvas, ctx, time) {
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	ctx.fillStyle = "yellow";
	ctx.font = "60px sans-serif";
	ctx.fillText(time, 150, 150);
});

renderSlide("canvas-render-loop-elapsed", function(canvas, ctx, time, elapsed) {
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	ctx.fillStyle = "yellow";
	ctx.font = "60px sans-serif";
	ctx.fillText(elapsed, 150, 150);
});

renderSlide("canvas-fps", function(canvas, ctx, time, elapsed) {
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	ctx.fillStyle = "yellow";
	ctx.font = "60px sans-serif";
	var fps = Math.floor((1 / elapsed) * 1000) + " FPS";
	ctx.fillText(fps, 150, 150);
});

var animation = {
	img: new Image(),
	time: 0,
	frames: 22,
	msPerFrame: 40,
	advance: function(elapsed) {
		this.time += elapsed;
	},
	draw: function(ctx, x, y) {
		var width = this.img.width / this.frames;
		var frame = Math.floor(this.time / this.msPerFrame) % this.frames;
		var frameX = frame * width;
		ctx.drawImage(this.img,
			frameX, 0, width, this.img.height,
			x, y, width, this.img.height);
	}
};
animation.img.src = "hamster-run-right-f22.png";

var canvasAnimTimeInput = document.getElementById("canvas-animation-time");
var updateCanvasAnimTime = function() {
	animation.msPerFrame = parseInt(canvasAnimTimeInput.value);
};
canvasAnimTimeInput.addEventListener("keyup", updateCanvasAnimTime);
updateCanvasAnimTime();
renderSlide("canvas-animation", function(canvas, ctx, time, elapsed) {
	ctx.clearRect(0, 0, canvas.width, canvas.height);

	animation.advance(elapsed);
	animation.draw(ctx, 260, 25);
});

var hamsterX = 200, hamsterY = 100, hamsterSpeed = .2;
var canvasKeyboardSpeedInput = document.getElementById("canvas-keyboard-speed");
var updateCanvasKeyboardSpeed = function() {
	hamsterSpeed = parseFloat(canvasKeyboardSpeedInput.value);
};
canvasKeyboardSpeedInput.addEventListener("keyup", updateCanvasKeyboardSpeed);
updateCanvasKeyboardSpeed();

var keys = {}, keyMap = { 87: 'w', 65: 'a', 83: 's', 68: 'd' };
window.addEventListener("keydown", function(e) {
	keys[keyMap[e.keyCode]] = true;
});
window.addEventListener("keyup", function(e) {
	keys[keyMap[e.keyCode]] = false;
});
renderSlide("canvas-keyboard", function(canvas, ctx, time, elapsed) {
	ctx.clearRect(0, 0, canvas.width, canvas.height);

	if (keys["w"]) {
		hamsterY -= hamsterSpeed * elapsed;
	}
	if (keys["a"]) {
		hamsterX -= hamsterSpeed * elapsed;
	}
	if (keys["d"]) {
		hamsterX += hamsterSpeed * elapsed;
	}
	if (keys["s"]) {
		hamsterY += hamsterSpeed * elapsed;
	}
	animation.advance(elapsed);
	animation.draw(ctx, hamsterX, hamsterY);

	ctx.font = "40px sans-serif";
	ctx.fillStyle = keys["w"] ? "#ff0" : "#660";
	ctx.fillText("w", 50, 50);
	ctx.fillStyle = keys["a"] ? "#ff0" : "#660";
	ctx.fillText("a", 20, 90);
	ctx.fillStyle = keys["s"] ? "#ff0" : "#660";
	ctx.fillText("s", 55, 90);
	ctx.fillStyle = keys["d"] ? "#ff0" : "#660";
	ctx.fillText("d", 85, 90);
});
<|endoftext|>


<commit_before>var util = require('util');
var path = require('path');
var fs = require('fs');
var fse = require('fs-extra2');
var cp = require('child_process');
var os = require('os');
var BOOTSTRAP_PATH = path.join(__dirname, 'bootstrap.js');
//默认设置为`~`，防止Linux在开机启动时Node无法获取homedir
var homedir = (typeof os.homedir == 'function' ? os.homedir() :
process.env[process.platform == 'win32' ? 'USERPROFILE' : 'HOME']) || '~';

var version = process.version.substring(1).split('.');
var supportMaxHeaderSize = (version[0] == 10 && version[1] >= 15) || (version[0] == 11 && version[1] > 5) || version[0] > 11;
var DATA_DIR = path.join(homedir, '.startingAppData');
fse.ensureDirSync(DATA_DIR);
/* eslint-disable no-console */
function isRunning(pid, callback) {

  pid ? cp.exec(util.format(process.platform === 'win32' ?
    'tasklist /fi "PID eq %s" | findstr /i "node.exe"'
    : 'ps -f -p %s | grep "node"', pid),
    function (err, stdout, stderr) {
      callback(!err && !!stdout.toString().trim());
    }) : callback(false);
}

exports.isRunning = isRunning;

function run(main, options, callback) {
  options.debugMode = true;
  callback = callback || noop;
  var child = execCmd(main, options, {
    stdio: [ 0, 1, 'pipe']
  });
  var timer;
  var done;
  var execCallback = function() {
    !done && callback(null, options);
    done = true;
  };
  var delay = function(stop) {
    clearTimeout(timer);
    timer = !stop && setTimeout(execCallback, 1000);
  };
  var err = '';

  child.stderr.setEncoding('utf8');
  child.stderr.on('data', function(data) {
    delay();
    err += data;
  });
  child.stderr.on('end', function(data) {
    execCallback();
    err && console.error(err);
  });
  delay();
}

exports.run = run;

function start(main, options, callback, version, isRestart) {
  var config = readConfig(main);
  if (config && !isRestart && config.version != version) {
    return restart(main, options, callback, version);
  }
  callback = callback || noop;
  isRunning(config.pid, function (isrunning) {
    if (isrunning) {
      return callback(true, config.options || {});
    }

    isRunning(config._pid, function (isrunning) {
      if (isrunning) {
        return callback(true, config.options || {});
      }

      var errorFile = path.join(DATA_DIR, 'error.' + encodeURIComponent(main));
      try {
        if (fs.existsSync(errorFile)) {
          fs.unlinkSync(errorFile);
        }
      } catch(e) {
        return callback(e, options);
      }

      var child = execCmd(main, options, {
        detached: true,
        stdio: ['ignore', 'ignore', fs.openSync(errorFile, 'a+')]
      });

      config._pid = child.pid;
      config.main = main;
      config.version = version;
      try {
        writeConfig(main, config);
      } catch(e) {
        return callback(e, options);
      }

      var startTime = Date.now();
      (function execCallback() {
        var error;
        try {
          error = fs.readFileSync(errorFile, {encoding: 'utf8'});
          fs.unlinkSync(errorFile);
        } catch(e) {}

        if (error) {
          callback(null, options);
          console.error(error);
          return;
        }

        if (Date.now() - startTime < 3000) {
          return setTimeout(execCallback, 600);
        }

        delete config._pid;
        config.pid = child.pid;
        config.options = options;
        writeConfig(main, config);
        child.unref();
        callback(null, options);
      })();
    });
  });
}

exports.start = start;

function restart(main, options, callback, version) {
  stop(main, function (_, _main, opts) {
    setTimeout(function () {
      if (!options.clearPreOptions) {
        var noGlobalPlugins = opts.noGlobalPlugins;
        options = util._extend(opts, options);
        options.noGlobalPlugins = noGlobalPlugins || options.noGlobalPlugins;
      }
      start(main, options, callback, version, true);
    }, 1000);
  });
}

exports.restart = restart;

function stop(main, callback) {
  var config = readConfig(main);
  var pid = config.pid;
  var _pid = config._pid;
  main = config.main;
  callback = callback || noop;
  isRunning(pid, function (isrunning) {
    try {
      pid && process.kill(pid);
      removeConfig(main);
    } catch (err) {
      isrunning = isrunning && err;
    }

    if (isrunning) {
      callback(isrunning, main, config.options || {});
    } else {
      isRunning(_pid, function (isrunning) {
        try {
          _pid && process.kill(_pid);
          removeConfig(main);
        } catch (err) {
          isrunning = isrunning && err;
        }
        callback(isrunning, main, config.options || {});
      });
    }
  });
}

exports.stop = stop;

function getHashIndex(main) {
  var lastIndex = main.length - 1;
  if (main[lastIndex] == '#') {
    lastIndex = main.lastIndexOf('#', lastIndex - 1);
  } else {
    lastIndex = -1;
  }
  return lastIndex;
}

function getRunningPath(main) {
  var index = getHashIndex(main);
  main = encodeURIComponent(index == -1 ? '#' : main.substring(index));
  return path.join(DATA_DIR, main);
}

function showDebugHelp(value) {
  var host = '127.0.0.1';
  var port = 9229;
  if (/^([^\s]+:)?(\d+)$/.test(value)) {
    if (RegExp.$1) {
      host = RegExp.$1.slice(0, -1);
    }
    port = RegExp.$2;
  }
  console.log('Debugger listening on ' + host + ':' + port);
  console.log('For help see https://nodejs.org/en/docs/inspector');
}

function execCmd(main, data, options) {
  var lastIndex = getHashIndex(main);
  if (lastIndex != -1) {
    main = main.substring(0, lastIndex);
  }
  var args = [BOOTSTRAP_PATH, 'run', main];
  if (data.inspectBrk) {
    args.unshift('--inspect-brk' + (data.inspectBrk === true ? '' : '=' + data.inspectBrk));
    showDebugHelp(data.inspectBrk);
  } else if (data.inspect) {
    showDebugHelp(data.inspect);
    args.unshift('--inspect' + (data.inspect === true ? '' : '=' + data.inspect));
  }
  supportMaxHeaderSize && args.unshift('--max-http-header-size=51200');
  if (data) {
    args.push('--data');
    args.push(encodeURIComponent(JSON.stringify(data)));
  }

  return cp.spawn('node', args, options);
}

function readConfig(main) {
  try {
    return fse.readJsonSync(getRunningPath(main));
  } catch(e) {}

  var running = path.join(DATA_DIR, encodeURIComponent(main));
  try {
    return fse.readJsonSync(running);
  } catch(e) {}

  return {};
}

function writeConfig(main, config) {
  fse.outputJsonSync(getRunningPath(main), config || {});
}

function removeConfig(main) {
  var running = getRunningPath(main);
  fs.existsSync(running) && fse.removeSync(running);
  running = path.join(DATA_DIR, encodeURIComponent(main));
  fs.existsSync(running) && fse.removeSync(running);
}

function noop() {}

<commit_msg>refactor: run command<commit_after>var util = require('util');
var path = require('path');
var fs = require('fs');
var fse = require('fs-extra2');
var cp = require('child_process');
var os = require('os');
var BOOTSTRAP_PATH = path.join(__dirname, 'bootstrap.js');
//默认设置为`~`，防止Linux在开机启动时Node无法获取homedir
var homedir = (typeof os.homedir == 'function' ? os.homedir() :
process.env[process.platform == 'win32' ? 'USERPROFILE' : 'HOME']) || '~';

var version = process.version.substring(1).split('.');
var supportMaxHeaderSize = (version[0] == 10 && version[1] >= 15) || (version[0] == 11 && version[1] > 5) || version[0] > 11;
var DATA_DIR = path.join(homedir, '.startingAppData');
fse.ensureDirSync(DATA_DIR);
/* eslint-disable no-console */
function isRunning(pid, callback) {

  pid ? cp.exec(util.format(process.platform === 'win32' ?
    'tasklist /fi "PID eq %s" | findstr /i "node.exe"'
    : 'ps -f -p %s | grep "node"', pid),
    function (err, stdout, stderr) {
      callback(!err && !!stdout.toString().trim());
    }) : callback(false);
}

exports.isRunning = isRunning;

function run(main, options, callback) {
  options.debugMode = true;
  callback = callback || noop;
  var child = execCmd(main, options, {
    stdio: [ 0, 1, 2]
  });
  var execCallback = function() {
    timer && callback(null, options);
    timer = null;
  };
  var timer = setTimeout(execCallback, 1200);
  var handleExit = function() {
    clearTimeout(timer);
    timer = null;
  };
  child.on('exit', handleExit);
  child.on('close', handleExit);
}

exports.run = run;

function start(main, options, callback, version, isRestart) {
  var config = readConfig(main);
  if (config && !isRestart && config.version != version) {
    return restart(main, options, callback, version);
  }
  callback = callback || noop;
  isRunning(config.pid, function (isrunning) {
    if (isrunning) {
      return callback(true, config.options || {});
    }

    isRunning(config._pid, function (isrunning) {
      if (isrunning) {
        return callback(true, config.options || {});
      }

      var errorFile = path.join(DATA_DIR, 'error.' + encodeURIComponent(main));
      try {
        if (fs.existsSync(errorFile)) {
          fs.unlinkSync(errorFile);
        }
      } catch(e) {
        return callback(e, options);
      }

      var child = execCmd(main, options, {
        detached: true,
        stdio: ['ignore', 'ignore', fs.openSync(errorFile, 'a+')]
      });

      config._pid = child.pid;
      config.main = main;
      config.version = version;
      try {
        writeConfig(main, config);
      } catch(e) {
        return callback(e, options);
      }

      var startTime = Date.now();
      (function execCallback() {
        var error;
        try {
          error = fs.readFileSync(errorFile, {encoding: 'utf8'});
          fs.unlinkSync(errorFile);
        } catch(e) {}

        if (error) {
          callback(null, options);
          console.error(error);
          return;
        }

        if (Date.now() - startTime < 3000) {
          return setTimeout(execCallback, 600);
        }

        delete config._pid;
        config.pid = child.pid;
        config.options = options;
        writeConfig(main, config);
        child.unref();
        callback(null, options);
      })();
    });
  });
}

exports.start = start;

function restart(main, options, callback, version) {
  stop(main, function (_, _main, opts) {
    setTimeout(function () {
      if (!options.clearPreOptions) {
        var noGlobalPlugins = opts.noGlobalPlugins;
        options = util._extend(opts, options);
        options.noGlobalPlugins = noGlobalPlugins || options.noGlobalPlugins;
      }
      start(main, options, callback, version, true);
    }, 1000);
  });
}

exports.restart = restart;

function stop(main, callback) {
  var config = readConfig(main);
  var pid = config.pid;
  var _pid = config._pid;
  main = config.main;
  callback = callback || noop;
  isRunning(pid, function (isrunning) {
    try {
      pid && process.kill(pid);
      removeConfig(main);
    } catch (err) {
      isrunning = isrunning && err;
    }

    if (isrunning) {
      callback(isrunning, main, config.options || {});
    } else {
      isRunning(_pid, function (isrunning) {
        try {
          _pid && process.kill(_pid);
          removeConfig(main);
        } catch (err) {
          isrunning = isrunning && err;
        }
        callback(isrunning, main, config.options || {});
      });
    }
  });
}

exports.stop = stop;

function getHashIndex(main) {
  var lastIndex = main.length - 1;
  if (main[lastIndex] == '#') {
    lastIndex = main.lastIndexOf('#', lastIndex - 1);
  } else {
    lastIndex = -1;
  }
  return lastIndex;
}

function getRunningPath(main) {
  var index = getHashIndex(main);
  main = encodeURIComponent(index == -1 ? '#' : main.substring(index));
  return path.join(DATA_DIR, main);
}

function execCmd(main, data, options) {
  var lastIndex = getHashIndex(main);
  if (lastIndex != -1) {
    main = main.substring(0, lastIndex);
  }
  var args = [BOOTSTRAP_PATH, 'run', main];
  if (data.inspectBrk) {
    args.unshift('--inspect-brk' + (data.inspectBrk === true ? '' : '=' + data.inspectBrk));
  } else if (data.inspect) {
    args.unshift('--inspect' + (data.inspect === true ? '' : '=' + data.inspect));
  }
  supportMaxHeaderSize && args.unshift('--max-http-header-size=51200');
  if (data) {
    args.push('--data');
    args.push(encodeURIComponent(JSON.stringify(data)));
  }

  return cp.spawn('node', args, options);
}

function readConfig(main) {
  try {
    return fse.readJsonSync(getRunningPath(main));
  } catch(e) {}

  var running = path.join(DATA_DIR, encodeURIComponent(main));
  try {
    return fse.readJsonSync(running);
  } catch(e) {}

  return {};
}

function writeConfig(main, config) {
  fse.outputJsonSync(getRunningPath(main), config || {});
}

function removeConfig(main) {
  var running = getRunningPath(main);
  fs.existsSync(running) && fse.removeSync(running);
  running = path.join(DATA_DIR, encodeURIComponent(main));
  fs.existsSync(running) && fse.removeSync(running);
}

function noop() {}
<|endoftext|>


<commit_before>import Storage from '../common/storage'
import { setToken } from './HttpUtils'
import HttpUtils from './HttpUtils'
import { USERS } from './Urls'
import store from '../redux/store'
import { fetchProfileSuccess } from '../redux/modules/user'

export default async function login (account, password, school_id) {
  const params = {
    account,
    password,
    school_id
  }

  const response = await HttpUtils.post(USERS.login, params)
  if (response.status !== 0) {
    const msg = response.msg || '登录失败，请检查账号或者密码是否正确'
    throw new Error(msg)
  }

  await Storage.set('user', {
    ...params
  })

  const {
    uid,
    token,
    timestamp
  } = response.data

  setToken({
    uid,
    token,
    timestamp
  })

  store.dispatch(fetchProfileSuccess(response.data))

  return response
}
<commit_msg>feat(relogin): add relogin to avoid token expired<commit_after>import Storage from '../common/storage'
import { setApiBaseUrl, setToken } from './HttpUtils'
import HttpUtils from './HttpUtils'
import { USERS } from './Urls'
import store from '../redux/store'
import { fetchProfileSuccess } from '../redux/modules/user'
import schools from './schools'

let reLoginInterval = null

const login = async (account, password, school_id) => {
  const params = {
    account,
    password,
    school_id
  }

  const response = await HttpUtils.post(USERS.login, params)
  if (response.status !== 0) {
    const msg = response.msg || '登录失败，请检查账号或者密码是否正确'
    throw new Error(msg)
  }

  await Storage.set('user', {
    ...params
  })

  const {
    uid,
    token,
    timestamp
  } = response.data

  setToken({
    uid,
    token,
    timestamp
  })

  store.dispatch(fetchProfileSuccess(response.data))

  if (!reLoginInterval) {
    autoReLogin()
  }

  return response
}

export default login

export function autoReLogin () {
  reLoginInterval = setInterval(async () => {
    const user = await Storage.get('user', {})
    if (!user.account || !user.password) {
      return
    }

    setApiBaseUrl(schools[user.school_id].host)

    try {
      login(user.account, user.password, user.school_id)
    } catch (e) {
      console.log(e)
    }
  }, 3600 * 1000)
}<|endoftext|>


<commit_before>var readline = require("readline");

function Cli(handler) {
  this.handler = handler;
}

Cli.prototype.run = function() {
  var self = this;
  var rli = readline.createInterface(process.stdin, process.stdout);
  rli.setPrompt("> ");

  rli.on("line", function(line) {
    var args = line.split(/\s+/), cmd = args.shift();

    if (self.handler[cmd]) {
      self.handler[cmd].call(rli, args, function(err) {
        rli.prompt();
      });
    } else if (cmd.length > 0) {
      console.log("cmd not found.");
      rli.prompt();
    } else {
      rli.prompt();
    }
    rli.prompt();
  }).on("close", function() {
    console.log("");
    process.stdin.destroy();
  });

  rli.prompt();
};

module.exports = Cli;
<commit_msg>エラー時は例外投げる様に追加<commit_after>var readline = require("readline");

function Cli(handler) {
  this.handler = handler;
}

Cli.prototype.run = function() {
  var self = this;
  var rli = readline.createInterface(process.stdin, process.stdout);
  rli.setPrompt("> ");

  rli.on("line", function(line) {
    var args = line.split(/\s+/), cmd = args.shift();

    if (self.handler[cmd]) {
      self.handler[cmd].call(rli, args, function(err) {
        if (err) {
          throw err;
        }

        rli.prompt();
      });
    } else if (cmd.length > 0) {
      console.log("cmd not found.");
      rli.prompt();
    } else {
      rli.prompt();
    }
    rli.prompt();
  }).on("close", function() {
    console.log("");
    process.stdin.destroy();
  });

  rli.prompt();
};

module.exports = Cli;
<|endoftext|>


<commit_before>var _ = require('lodash'),
  async = require('async'),
  cli = null,
  Config = require('./config'),
  config = Config(),
  Service = require('./service'),
  Installer = require('./installer'),
  S = require('string'),
  temp = require('temp');

// handle a user interacting with the
// command-line, update config appropriately,
// output human readable messages.
function Cli(opts) {
  _.extend(this, {
    logger: require('./logger'),
    Interview: require('./interview'),
    service: Service,
    rimraf: require('rimraf'),
    yargs: require('yargs'), // overridable parsed arguments.
    takenFlags: [],
    commands: {
      'install': "ndm install\task user about environment variables and install service.",
      'init': "ndm init\tcreate the ndm service.json.",
      'generate': "ndm generate\tgenerate OS-specific daemon wrappers.",
      'remove': "ndm remove\tremove OS-specific daemon wrappers.",
      'update': "ndm update\tupdate service.json with new services.",
      'start': "ndm start [<service]\tstart all, or a specific service.",
      'stop': "ndm stop [<service>]\tstop all, or a specific service.",
      'restart': "ndm restart[<service]\trestart all, or a specific service.",
      'list': "ndm list\tlist all services availble in service.json",
      'interview': "ndm interview\task the user to fill in missing fields in service.json",
      'run-script': "ndm run-script [<script-name>][<service>]\trun script with env and args from service.json",
      'list-scripts': "ndm list-scripts\tlist all the scripts provided by ndm services"
    }
  }, opts);
};

// actually run the cli, the cli is also
// included and used for logging.
Cli.prototype.run = function() {
  var _this = this,
    argv = null;

  this.generateArgs();

  argv = this.yargs.argv;

  if (argv._.length === 0 || argv.help) {
    this.logger.log(this.yargs.help());
  } else if (!this.commands[argv._[0]]){
    this.logger.error('command ' + argv._[0] + ' not found')
    this.logger.log(this.yargs.help());
  } else {
    // make the aliases actually work.
    argv = this.yargs.normalize().argv;

    this.updateConfigWithArgs(argv); // update config singleton.

    try { // execute the command, passing along args.
      this[argv._[0]].apply(this, argv._.slice(1));
    } catch (e) {
      this.logger.error(e.message);
    }
  }
};

// updates the configuration singleton
// with any CLI args passed in.
Cli.prototype.updateConfigWithArgs = function(argv) {
  _.keys(config).forEach(function(key) {
    if (argv[key]) {
      config[key] = argv[key];
    }
  });

  // write the changes.
  Config(config);
};

// generate kwargs CLI parser from config.
Cli.prototype.generateArgs = function() {
  var _this = this,
    descriptions = config.descriptions(),
    options = {};

  // populate config flags.
  _.forIn(config, function(value, key) {
    if (descriptions[key]) {
      options[_this._getFlag(key.toLowerCase())] = {
        alias: S(key).dasherize().s,
        describe: descriptions[key],
        default: config[key]
      };
    }
  });

  _this.yargs.options(options);

  // now add the usage info.
  this.generateUsage();
};

Cli.prototype.generateUsage = function() {
  var usage = 'Deploy service daemons directly from npm packages\n\nUsage:\n';

  _.forIn(this.commands, function(value, key) {
    usage += "\n" + value;
  });

  this.yargs.usage(usage);
};

// iterate over the key, and find a flag
// that hasn't yet been used.
Cli.prototype._getFlag = function(candidates) {
  for (var i = 0; i < candidates.length; i++) {
    var char = candidates.charAt(i);
    if (this.takenFlags.indexOf(char) === -1) {
      this.takenFlags.push(char);
      return char;
    }
  }
};

// initialize ndm directory.
Cli.prototype.init = function() {
  this.logger.log("setting up ndm directory:");
  (new Installer()).init();
};

// update service.json with new
// services in package.json.
Cli.prototype.update = function() {
  this.logger.log("updating service.json:");
  (new Installer()).update();
};

// generate service-daemons from
// service.json
Cli.prototype.generate = function(serviceName) {
  var _this = this;

  this.logger.log('generating service wrappers:')

  this.service.allServices(serviceName).forEach(function(service) {
    service.generateScript();
    _this.logger.warn("  generated " + service.scriptPath());
  });

  this.service.printRunMessage();
};

// remove service-daemons
Cli.prototype.remove = function(serviceName) {
  var _this = this;

  // stop services before removing their wrappers.
  this.stop(serviceName, function() {
    // now remove the wrapper for each service.
    _this.service.allServices(serviceName).forEach(function(service) {
      service.removeScript();
      _this.logger.warn("  removed " + service.scriptPath());
    });
  });
};

// start|stop|restart services.
Cli.prototype.start = function(serviceName, cb) {
  this.logger.log('starting services:');
  this._runCommand('start', serviceName, cb);
};

Cli.prototype.stop = function(serviceName, cb) {
  this.logger.log('stopping services:');
  this._runCommand('stop', serviceName, cb);
};

Cli.prototype.restart = function(serviceName, cb) {
  this.logger.log('restarting services');
  this._runCommand('restart', serviceName, cb);
};

Cli.prototype._runCommand = function(command, serviceName, cb) {
  var _this = this;

  async.each(this.service.allServices(serviceName), function(service, done) {
    service.runCommand(command, function(err) {
      _this.logger.warn("  " + command + " " + service.scriptPath());
      done();
    });
  }, cb);
};

// run a script from package.json's scripts block, in the
// context of service.json's args and envs.
Cli.prototype['run-script'] = function(scriptName) {
  var _this = this,
    filter = Config().filter,
    scriptsExecuted = 0;

  scriptsExecuted = _.filter(this.service.allServices(filter), function(service) {
    if (!service.hasScript(scriptName)) return false;

    service.runScript(scriptName, function(err) {
      if (err) _this.logger.error(err);
    });

    return true;
  }).length;

  if (!scriptsExecuted) this['list-scripts']();
};

// list all available services.
Cli.prototype.list = function() {
  var _this = this;

  this.service.allServices().forEach(function(service) {
    _this.logger.log(service.name + ":\t" + service.description);
  });
};

// list all available scripts.
Cli.prototype['list-scripts'] = function(serviceName) {
  var _this = this;

  this.service.allServices(serviceName).forEach(function(service) {
    _this.logger.log(service.name + ':\t' + service.description);
    service.listScripts();
  });
};

// interactive Q/A session with service.json.
Cli.prototype.interview = function(serviceName) {
  var interview = new this.Interview(
    serviceJsonPath = Service._serviceJsonPath(serviceName)
  );
  interview.run(function() {});
};

// Ask a user questions about the service they're
// installing, and install it.
Cli.prototype.install = function(serviceName) {
  var _this = this,
    tmpServiceJsonPath = temp.path({suffix: '.json'}),
    srcServiceJsonPath = Service._serviceJsonPath(serviceName),
    interview = new this.Interview({
      serviceJsonPath: srcServiceJsonPath,
      tmpServiceJsonPath: tmpServiceJsonPath
    });

  interview.run(function() {
    _this.generate(serviceName); // generate the service wrappers.
    _this.rimraf.sync(tmpServiceJsonPath); // remove the temporary service.json.
  });
};

// export CLI as a singleton, to that
// it can easily be imported for logging.
module.exports = function(opts) {
  if (!opts && cli) return cli;
  else {
    cli = new Cli(opts);
    return cli;
  }
};
<commit_msg>list now uses allServices(serviceNameFilter)<commit_after>var _ = require('lodash'),
  async = require('async'),
  cli = null,
  Config = require('./config'),
  config = Config(),
  Service = require('./service'),
  Installer = require('./installer'),
  S = require('string'),
  temp = require('temp');

// handle a user interacting with the
// command-line, update config appropriately,
// output human readable messages.
function Cli(opts) {
  _.extend(this, {
    logger: require('./logger'),
    Interview: require('./interview'),
    service: Service,
    rimraf: require('rimraf'),
    yargs: require('yargs'), // overridable parsed arguments.
    takenFlags: [],
    commands: {
      'install': "ndm install\task user about environment variables and install service.",
      'init': "ndm init\tcreate the ndm service.json.",
      'generate': "ndm generate\tgenerate OS-specific daemon wrappers.",
      'remove': "ndm remove\tremove OS-specific daemon wrappers.",
      'update': "ndm update\tupdate service.json with new services.",
      'start': "ndm start [<service]\tstart all, or a specific service.",
      'stop': "ndm stop [<service>]\tstop all, or a specific service.",
      'restart': "ndm restart[<service]\trestart all, or a specific service.",
      'list': "ndm list\tlist all services availble in service.json",
      'interview': "ndm interview\task the user to fill in missing fields in service.json",
      'run-script': "ndm run-script [<script-name>][<service>]\trun script with env and args from service.json",
      'list-scripts': "ndm list-scripts\tlist all the scripts provided by ndm services"
    }
  }, opts);
};

// actually run the cli, the cli is also
// included and used for logging.
Cli.prototype.run = function() {
  var _this = this,
    argv = null;

  this.generateArgs();

  argv = this.yargs.argv;

  if (argv._.length === 0 || argv.help) {
    this.logger.log(this.yargs.help());
  } else if (!this.commands[argv._[0]]){
    this.logger.error('command ' + argv._[0] + ' not found')
    this.logger.log(this.yargs.help());
  } else {
    // make the aliases actually work.
    argv = this.yargs.normalize().argv;

    this.updateConfigWithArgs(argv); // update config singleton.

    try { // execute the command, passing along args.
      this[argv._[0]].apply(this, argv._.slice(1));
    } catch (e) {
      this.logger.error(e.message);
    }
  }
};

// updates the configuration singleton
// with any CLI args passed in.
Cli.prototype.updateConfigWithArgs = function(argv) {
  _.keys(config).forEach(function(key) {
    if (argv[key]) {
      config[key] = argv[key];
    }
  });

  // write the changes.
  Config(config);
};

// generate kwargs CLI parser from config.
Cli.prototype.generateArgs = function() {
  var _this = this,
    descriptions = config.descriptions(),
    options = {};

  // populate config flags.
  _.forIn(config, function(value, key) {
    if (descriptions[key]) {
      options[_this._getFlag(key.toLowerCase())] = {
        alias: S(key).dasherize().s,
        describe: descriptions[key],
        default: config[key]
      };
    }
  });

  _this.yargs.options(options);

  // now add the usage info.
  this.generateUsage();
};

Cli.prototype.generateUsage = function() {
  var usage = 'Deploy service daemons directly from npm packages\n\nUsage:\n';

  _.forIn(this.commands, function(value, key) {
    usage += "\n" + value;
  });

  this.yargs.usage(usage);
};

// iterate over the key, and find a flag
// that hasn't yet been used.
Cli.prototype._getFlag = function(candidates) {
  for (var i = 0; i < candidates.length; i++) {
    var char = candidates.charAt(i);
    if (this.takenFlags.indexOf(char) === -1) {
      this.takenFlags.push(char);
      return char;
    }
  }
};

// initialize ndm directory.
Cli.prototype.init = function() {
  this.logger.log("setting up ndm directory:");
  (new Installer()).init();
};

// update service.json with new
// services in package.json.
Cli.prototype.update = function() {
  this.logger.log("updating service.json:");
  (new Installer()).update();
};

// generate service-daemons from
// service.json
Cli.prototype.generate = function(serviceName) {
  var _this = this;

  this.logger.log('generating service wrappers:')

  this.service.allServices(serviceName).forEach(function(service) {
    service.generateScript();
    _this.logger.warn("  generated " + service.scriptPath());
  });

  this.service.printRunMessage();
};

// remove service-daemons
Cli.prototype.remove = function(serviceName) {
  var _this = this;

  // stop services before removing their wrappers.
  this.stop(serviceName, function() {
    // now remove the wrapper for each service.
    _this.service.allServices(serviceName).forEach(function(service) {
      service.removeScript();
      _this.logger.warn("  removed " + service.scriptPath());
    });
  });
};

// start|stop|restart services.
Cli.prototype.start = function(serviceName, cb) {
  this.logger.log('starting services:');
  this._runCommand('start', serviceName, cb);
};

Cli.prototype.stop = function(serviceName, cb) {
  this.logger.log('stopping services:');
  this._runCommand('stop', serviceName, cb);
};

Cli.prototype.restart = function(serviceName, cb) {
  this.logger.log('restarting services');
  this._runCommand('restart', serviceName, cb);
};

Cli.prototype._runCommand = function(command, serviceName, cb) {
  var _this = this;

  async.each(this.service.allServices(serviceName), function(service, done) {
    service.runCommand(command, function(err) {
      _this.logger.warn("  " + command + " " + service.scriptPath());
      done();
    });
  }, cb);
};

// run a script from package.json's scripts block, in the
// context of service.json's args and envs.
Cli.prototype['run-script'] = function(scriptName) {
  var _this = this,
    filter = Config().filter,
    scriptsExecuted = 0;

  scriptsExecuted = _.filter(this.service.allServices(filter), function(service) {
    if (!service.hasScript(scriptName)) return false;

    service.runScript(scriptName, function(err) {
      if (err) _this.logger.error(err);
    });

    return true;
  }).length;

  if (!scriptsExecuted) this['list-scripts']();
};

// list all available services.
Cli.prototype.list = function(serviceName) {
  var _this = this;

  this.service.allServices(serviceName).forEach(function(service) {
    _this.logger.log(service.name + ":\t" + service.description);
  });
};

// list all available scripts.
Cli.prototype['list-scripts'] = function(serviceName) {
  var _this = this;

  this.service.allServices(serviceName).forEach(function(service) {
    _this.logger.log(service.name + ':\t' + service.description);
    service.listScripts();
  });
};

// interactive Q/A session with service.json.
Cli.prototype.interview = function(serviceName) {
  var interview = new this.Interview(
    serviceJsonPath = Service._serviceJsonPath(serviceName)
  );
  interview.run(function() {});
};

// Ask a user questions about the service they're
// installing, and install it.
Cli.prototype.install = function(serviceName) {
  var _this = this,
    tmpServiceJsonPath = temp.path({suffix: '.json'}),
    srcServiceJsonPath = Service._serviceJsonPath(serviceName),
    interview = new this.Interview({
      serviceJsonPath: srcServiceJsonPath,
      tmpServiceJsonPath: tmpServiceJsonPath
    });

  interview.run(function() {
    _this.generate(serviceName); // generate the service wrappers.
    _this.rimraf.sync(tmpServiceJsonPath); // remove the temporary service.json.
  });
};

// export CLI as a singleton, to that
// it can easily be imported for logging.
module.exports = function(opts) {
  if (!opts && cli) return cli;
  else {
    cli = new Cli(opts);
    return cli;
  }
};
<|endoftext|>


<commit_before>'use strict';

var pendingpointsListUrl = 'http://3pp.io:4343/get/pendingpoints';
var assertionUrl = 'http://3pp.io:4343/persona';
var postCommentUrl = 'http://3pp.io:4343/post/comment';

$(document).ready(function(){
    var loggedInUser = null;

    function logout(){
        loggedInUser = null;
        var response = $.post(assertionUrl + '/logout');
        response.always(showPersona);
    }

    function verify(assertion){
        var response = $.ajax(assertionUrl + '/verify', {data: {assertion: assertion}, type: 'POST', xhrFields: {withCredentials: true}});
        response.done(login);
        response.fail(function(){
            navigator.id.logout();
        });
    }

    function login(data){
        showCommentForms(data.email);
    }

    if(navigator.id){
        navigator.id.watch({
            loggedInUser: loggedInUser,
            onlogin: verify,
            onlogout: logout
        });
    }

    function showPersona(){
		$('.leaveComment').html('
To comment, please sign in — you can use an existing email address.

');
		$('.signinButton').click(function(){ navigator.id.request(); }).css('cursor', 'pointer');
	}

	function showCommentForms(email){
        $('#pendingpoints .comments').each(function(){
            var pointId = $(this).attr('id').substr(9);
            $(this).find('.leaveComment').html('
Commenting as ' + email + ' (Not your email address?)
<form class="commentForm"><fieldset>
<textarea name="comment" required class="input-xxlarge"></textarea>

<button class="btn btn-primary" id="postComment">Send</button>
</fieldset></form>')
            .find('.commentForm').submit(function(e){
                var response = $.ajax(postCommentUrl, {data: {comment: $('#comments-' + pointId + ' .leaveComment textarea').val(), pointId: pointId}, type: 'POST', xhrFields: {withCredentials: true}});
                response.done(showConfirmation);
                response.fail(showError);
                e.preventDefault();
            });
        });

		$('.signoutButton').click(function(){ navigator.id.logout(); });
	}

	function showConfirmation(){
		$('#pendingpoints').before('
<button type="button" class="close" data-dismiss="alert">×</button>Your comment has been submitted. Thank you!
');
        showOverview();
	}

	function showError(){
		$('#pendingpoints').before('
<button type="button" class="close" data-dismiss="alert">×</button>Something went wrong saving your comment, did you fill in all the fields?
');
	}

    function showOverview(){
        var response = $.get(pendingpointsListUrl, 'json');
        response.done(function(points){
            $('#pendingpoints').text('');
            points.forEach(function(point){
                point.tosdr.point = point.tosdr.point.replace(/[^A-Za-z]/g, '');
                point.id = point.id.replace(/[^A-Za-z0-9_-]/g, '');
                point.title = $('
').text(point.title).html();
                var badge = '',
                    icon = 'question-sign',
                    sign = '?';
                if (point.tosdr.point == 'good') {
                    badge = 'badge-success';
                    icon = 'thumbs-up';
                    sign = '+';
                } else if (point.tosdr.point == 'bad') {
                    badge = 'badge-warning';
                    icon = 'thumbs-down';
                    sign = '-';
                } else if (point.tosdr.point == 'blocker') {
                    badge = 'badge-important';
                    icon = 'remove';
                    sign = '×';
                } else if (point.tosdr.point == 'neutral') {
                    badge = 'badge-neutral';
                    icon = 'asterisk';
                    sign = '⋅';
                }
                $('#pendingpoints').append('

' + sign + ' ' + point.title + '

');
                $.each(point.services, function(index, service){
                    $('#pendingpoints .' + point.id + ' .services').append('
' + service.replace(/[^A-Za-z0-9_-]/g, '') + '
 ');
                });

                $.each(point.comments, function(index, comment){
                    $('#pendingpoints .comments-' + point.id + ' .leaveComment').before('

');
                    $('#currentAddition').text(comment.content).prepend('').removeAttr('id');
                });
            });
            showPersona();
        });

        response.fail(function(response, textStatus, errorThrown){
            console.log(textStatus, errorThrown);
            $('#pendingpoints').text('Unfortunately, something went wrong fetching the pending discussion points.');
        });
    }

    showOverview();
});
<commit_msg>fix pendingpoint API URL<commit_after>'use strict';

var pendingpointsListUrl = 'https://3pp.io:4343/get/pendingpoints';
var assertionUrl = 'https://3pp.io:4343/persona';
var postCommentUrl = 'https://3pp.io:4343/post/comment';

$(document).ready(function(){
    var loggedInUser = null;

    function logout(){
        loggedInUser = null;
        var response = $.post(assertionUrl + '/logout');
        response.always(showPersona);
    }

    function verify(assertion){
        var response = $.ajax(assertionUrl + '/verify', {data: {assertion: assertion}, type: 'POST', xhrFields: {withCredentials: true}});
        response.done(login);
        response.fail(function(){
            navigator.id.logout();
        });
    }

    function login(data){
        showCommentForms(data.email);
    }

    if(navigator.id){
        navigator.id.watch({
            loggedInUser: loggedInUser,
            onlogin: verify,
            onlogout: logout
        });
    }

    function showPersona(){
		$('.leaveComment').html('
To comment, please sign in — you can use an existing email address.

');
		$('.signinButton').click(function(){ navigator.id.request(); }).css('cursor', 'pointer');
	}

	function showCommentForms(email){
        $('#pendingpoints .comments').each(function(){
            var pointId = $(this).attr('id').substr(9);
            $(this).find('.leaveComment').html('
Commenting as ' + email + ' (Not your email address?)
<form class="commentForm"><fieldset>
<textarea name="comment" required class="input-xxlarge"></textarea>

<button class="btn btn-primary" id="postComment">Send</button>
</fieldset></form>')
            .find('.commentForm').submit(function(e){
                var response = $.ajax(postCommentUrl, {data: {comment: $('#comments-' + pointId + ' .leaveComment textarea').val(), pointId: pointId}, type: 'POST', xhrFields: {withCredentials: true}});
                response.done(showConfirmation);
                response.fail(showError);
                e.preventDefault();
            });
        });

		$('.signoutButton').click(function(){ navigator.id.logout(); });
	}

	function showConfirmation(){
		$('#pendingpoints').before('
<button type="button" class="close" data-dismiss="alert">×</button>Your comment has been submitted. Thank you!
');
        showOverview();
	}

	function showError(){
		$('#pendingpoints').before('
<button type="button" class="close" data-dismiss="alert">×</button>Something went wrong saving your comment, did you fill in all the fields?
');
	}

    function showOverview(){
        var response = $.get(pendingpointsListUrl, 'json');
        response.done(function(points){
            $('#pendingpoints').text('');
            points.forEach(function(point){
                point.tosdr.point = point.tosdr.point.replace(/[^A-Za-z]/g, '');
                point.id = point.id.replace(/[^A-Za-z0-9_-]/g, '');
                point.title = $('
').text(point.title).html();
                var badge = '',
                    icon = 'question-sign',
                    sign = '?';
                if (point.tosdr.point == 'good') {
                    badge = 'badge-success';
                    icon = 'thumbs-up';
                    sign = '+';
                } else if (point.tosdr.point == 'bad') {
                    badge = 'badge-warning';
                    icon = 'thumbs-down';
                    sign = '-';
                } else if (point.tosdr.point == 'blocker') {
                    badge = 'badge-important';
                    icon = 'remove';
                    sign = '×';
                } else if (point.tosdr.point == 'neutral') {
                    badge = 'badge-neutral';
                    icon = 'asterisk';
                    sign = '⋅';
                }
                $('#pendingpoints').append('

' + sign + ' ' + point.title + '

');
                $.each(point.services, function(index, service){
                    $('#pendingpoints .' + point.id + ' .services').append('
' + service.replace(/[^A-Za-z0-9_-]/g, '') + '
 ');
                });

                $.each(point.comments, function(index, comment){
                    $('#pendingpoints .comments-' + point.id + ' .leaveComment').before('

');
                    $('#currentAddition').text(comment.content).prepend('').removeAttr('id');
                });
            });
            showPersona();
        });

        response.fail(function(response, textStatus, errorThrown){
            console.log(textStatus, errorThrown);
            $('#pendingpoints').text('Unfortunately, something went wrong fetching the pending discussion points.');
        });
    }

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


<commit_before>// User menu toggle
$("#dropdown-menu a").on("click", function(event) {
    $(this).parent().toggleClass("open");
});
$("body").on("click", function(event) {
    if(!$("#dropdown-menu").is(event.target) && $("#dropdown-menu").has(event.target).length === 0) {
        $("#dropdown-menu").removeClass("open");
    }
});

// Enable/Disable
$("#flip-status").on("click", (e) => {
    e.preventDefault();
    const btnStatus = $("#flip-status");
    const status = $("#status");
    const text = btnStatus.text().trim();
    const token = encodeURIComponent($("#token").html());

    switch(text) {
        case "Enable":
            btnStatus.html(" Enabling...");
            $.getJSON("api.php?enable&token=" + token, (data) => {
                if(data.status === "enabled") {
                    btnStatus.html(" Disable");
                    status.html(" Active");
                }
            });
            break;
        case "Disable":
            btnStatus.html(" Disabling...");
            $.getJSON("api.php?disable&token=" + token, (data) => {
                if(data.status === "disabled") {
                    btnStatus.html(" Enable");
                    status.html(" Offline");
                }
            });
            break;
    }
});

var piholeVersion = $("#piholeVersion").html();
var webVersion = $("#webVersion").html();

// Credit for following function: https://gist.github.com/alexey-bass/1115557
function versionCompare(left, right) {
    if (typeof left + typeof right != 'stringstring')
        return false;

    var a = left.split('.')
        ,   b = right.split('.')
        ,   i = 0, len = Math.max(a.length, b.length);

    for (; i < len; i++) {
        if ((a[i] && !b[i] && parseInt(a[i]) > 0) || (parseInt(a[i]) > parseInt(b[i]))) {
            return 1;
        } else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i]))) {
            return -1;
        }
    }

    return 0;
}

// Update check
$.getJSON("https://api.github.com/repos/pi-hole/pi-hole/releases/latest", function(json) {
    // Skip if on dev
    if(piholeVersion !== "vDev" && versionCompare(piholeVersion, json.tag_name.slice(1)) < 0) {
        // Alert user
        $("#piholeVersion").html($("#piholeVersion").text() + '(Update available!)');
        $("#alPiholeUpdate").show();
    }
});
$.getJSON("https://api.github.com/repos/pi-hole/AdminLTE/releases/latest", function(json) {
    // Skip if on dev
    if(webVersion !== "vDev" && versionCompare(webVersion, json.tag_name.slice(1)) < 0) {
        // Alert user
        $("#webVersion").html($("#webVersion").text() + '(Update available!)');
        $("#alWebUpdate").show();
    }
});

/*
 * Make sure that Pi-hole is updated to at least v2.7, since that is needed to use the sudo
 * features of the interface. Skip if on dev
 */
if(piholeVersion !== "vDev" && versionCompare(piholeVersion, "v2.7") < 0)
    alert("Pi-hole needs to be updated to at least v2.7 before you can use features such as whitelisting/blacklisting from this web interface!")

// Session timer
var sessionvalidity = parseInt(document.getElementById("sessiontimercounter").textContent);
var start = new Date;

function updateSessionTimer()
{
    start = new Date;
    start.setSeconds(start.getSeconds() + sessionvalidity);
}

if(sessionvalidity > 0)
{
    // setSeconds will correctly handle wrap-around cases
    updateSessionTimer();

    setInterval(function() {
        var current = new Date;
        var totalseconds = (start - current) / 1000;

        // var hours = Math.floor(totalseconds / 3600);
        // totalseconds = totalseconds % 3600;

        var minutes = Math.floor(totalseconds / 60);
        if(minutes < 10){ minutes = "0" + minutes; }

        var seconds = Math.floor(totalseconds % 60);
        if(seconds < 10){ seconds = "0" + seconds; }

        if(totalseconds > 0)
            document.getElementById("sessiontimercounter").textContent = minutes + ":" + seconds;
        else
            document.getElementById("sessiontimercounter").textContent = "-- : --";
    }, 1000);
}
else
{
    document.getElementById("sessiontimer").style.display = "none";
}
<commit_msg>Hide "exact match" button on queryads.php page if version is 2.9.5 or lower<commit_after>// User menu toggle
$("#dropdown-menu a").on("click", function(event) {
    $(this).parent().toggleClass("open");
});
$("body").on("click", function(event) {
    if(!$("#dropdown-menu").is(event.target) && $("#dropdown-menu").has(event.target).length === 0) {
        $("#dropdown-menu").removeClass("open");
    }
});

// Enable/Disable
$("#flip-status").on("click", (e) => {
    e.preventDefault();
    const btnStatus = $("#flip-status");
    const status = $("#status");
    const text = btnStatus.text().trim();
    const token = encodeURIComponent($("#token").html());

    switch(text) {
        case "Enable":
            btnStatus.html(" Enabling...");
            $.getJSON("api.php?enable&token=" + token, (data) => {
                if(data.status === "enabled") {
                    btnStatus.html(" Disable");
                    status.html(" Active");
                }
            });
            break;
        case "Disable":
            btnStatus.html(" Disabling...");
            $.getJSON("api.php?disable&token=" + token, (data) => {
                if(data.status === "disabled") {
                    btnStatus.html(" Enable");
                    status.html(" Offline");
                }
            });
            break;
    }
});

var piholeVersion = $("#piholeVersion").html();
var webVersion = $("#webVersion").html();

// Credit for following function: https://gist.github.com/alexey-bass/1115557
function versionCompare(left, right) {
    if (typeof left + typeof right != 'stringstring')
        return false;

    var a = left.split('.')
        ,   b = right.split('.')
        ,   i = 0, len = Math.max(a.length, b.length);

    for (; i < len; i++) {
        if ((a[i] && !b[i] && parseInt(a[i]) > 0) || (parseInt(a[i]) > parseInt(b[i]))) {
            return 1;
        } else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i]))) {
            return -1;
        }
    }

    return 0;
}

// Update check
$.getJSON("https://api.github.com/repos/pi-hole/pi-hole/releases/latest", function(json) {
    // Skip if on dev
    if(piholeVersion !== "vDev" && versionCompare(piholeVersion, json.tag_name.slice(1)) < 0) {
        // Alert user
        $("#piholeVersion").html($("#piholeVersion").text() + '(Update available!)');
        $("#alPiholeUpdate").show();
    }
});
$.getJSON("https://api.github.com/repos/pi-hole/AdminLTE/releases/latest", function(json) {
    // Skip if on dev
    if(webVersion !== "vDev" && versionCompare(webVersion, json.tag_name.slice(1)) < 0) {
        // Alert user
        $("#webVersion").html($("#webVersion").text() + '(Update available!)');
        $("#alWebUpdate").show();
    }
});

/*
 * Make sure that Pi-hole is updated to at least v2.7, since that is needed to use the sudo
 * features of the interface. Skip if on dev
 */
if(piholeVersion !== "vDev" && versionCompare(piholeVersion, "v2.7") < 0)
    alert("Pi-hole needs to be updated to at least v2.7 before you can use features such as whitelisting/blacklisting from this web interface!")

// Session timer
var sessionvalidity = parseInt(document.getElementById("sessiontimercounter").textContent);
var start = new Date;

function updateSessionTimer()
{
    start = new Date;
    start.setSeconds(start.getSeconds() + sessionvalidity);
}

if(sessionvalidity > 0)
{
    // setSeconds will correctly handle wrap-around cases
    updateSessionTimer();

    setInterval(function() {
        var current = new Date;
        var totalseconds = (start - current) / 1000;

        // var hours = Math.floor(totalseconds / 3600);
        // totalseconds = totalseconds % 3600;

        var minutes = Math.floor(totalseconds / 60);
        if(minutes < 10){ minutes = "0" + minutes; }

        var seconds = Math.floor(totalseconds % 60);
        if(seconds < 10){ seconds = "0" + seconds; }

        if(totalseconds > 0)
            document.getElementById("sessiontimercounter").textContent = minutes + ":" + seconds;
        else
            document.getElementById("sessiontimercounter").textContent = "-- : --";
    }, 1000);
}
else
{
    document.getElementById("sessiontimer").style.display = "none";
}

// Hide "exact match" button on queryads.php page if version is 2.9.5 or lower
alert(versionCompare(piholeVersion, "v2.9.5"));
if(versionCompare(piholeVersion, "v2.9.5") < 1)
{
	$("#btnSearchExact").hide();
}
<|endoftext|>


<commit_before>"use strict"

let path = require("path")
let opts = require("commander")
let fs = require("fs")
let _ = require('lodash')

let postabular = require("..")

let pkg = fs.readFileSync(`${__dirname}/../package.json`)

let list = function(val) { return val.split(',') }

opts
    .version(JSON.parse(pkg).version)
    .option('-u, --use <plugins>', 'use plugins', list, [])
    .option('-l, --local <plugins>', 'use local plugins', list, [])
    .option('-c, --config <config>', 'use config')
    .option('-o, --output <filename>', 'output file name')
    .parse(process.argv)

if (!process.argv.slice(2).length) {
    opts.help()
}

let plugins = []
plugins = plugins.concat(opts.use.map(plugin => { return require(plugin) }))
plugins = plugins.concat(opts.local.map(plugin => { return require(path.join(process.cwd(), plugin)) }))

let os = fs.createReadStream(opts.args[0]).pipe(postabular(plugins))

if (opts.output) {
    os.pipe(fs.createWriteStream(opts.output))
} else {
    os.pipe(process.stdout)
}
<commit_msg>allow pipe input from stdin<commit_after>"use strict"

let path = require("path")
let opts = require("commander")
let fs = require("fs")
let _ = require('lodash')

let postabular = require("..")

let pkg = fs.readFileSync(`${__dirname}/../package.json`)

let list = function(val) { return val.split(',') }

opts
    .version(JSON.parse(pkg).version)
    .option('-u, --use <plugins>', 'use plugins', list, [])
    .option('-l, --local <plugins>', 'use local plugins', list, [])
    .option('-c, --config <config>', 'use config')
    .option('-o, --output <filename>', 'output file name')
    .parse(process.argv)

if (!process.argv.slice(2).length) {
    opts.help()
}

let plugins = []
plugins = plugins.concat(opts.use.map(plugin => { return require(plugin) }))
plugins = plugins.concat(opts.local.map(plugin => { return require(path.join(process.cwd(), plugin)) }))

let os = undefined
if (opts.args[0]) {
    os = fs.createReadStream(opts.args[0]).pipe(postabular(plugins))
} else {
    os = process.stdin.pipe(postabular(plugins))
}

if (opts.output) {
    os.pipe(fs.createWriteStream(opts.output))
} else {
    os.pipe(process.stdout)
}
<|endoftext|>