<commit_before>'use strict';

viewModel.WindRoseComparison = new Object();
var wrb = viewModel.WindRoseComparison;

// WIND ROSE COMPARISON
wrb.sectorDerajatComparison = ko.observable(0);
wrb.dataWindroseComparison = ko.observableArray([]);
wrb.project = ko.observable();
wrb.dateStart = ko.observable();
wrb.dateEnd = ko.observable();
var listOfChartComparison = [];
var listOfButtonComparison = {};
/*wrb.showHideLegendComparison = function (index) {
    var idName = "btn" + index;
    listOfButtonComparison[idName] = !listOfButtonComparison[idName];
    if (listOfButtonComparison[idName] == false) {
        $("#" + idName).css({ 'background': '#8f8f8f', 'border-color': '#8f8f8f' });
    } else {
        $("#" + idName).css({ 'background': colorFieldsWR[index], 'border-color': colorFieldsWR[index] });
    }
    $.each(listOfChartComparison, function (idx, idChart) {
       if($(idChart).data("kendoChart").options.series.length - 1 >= index) {
          $(idChart).data("kendoChart").options.series[index].visible = listOfButtonComparison[idName];
          $(idChart).data("kendoChart").refresh();
        }
    });
}*/

wrb.getPDF = function(selector){
    app.loading(true);
    var project = $("#projectList").data("kendoDropDownList").value();
    var dateStart = $('#dateStart').data('kendoDatePicker').value();
    var dateEnd = $('#dateEnd').data('kendoDatePicker').value();

    kendo.drawing.drawDOM($(selector)).then(function(group){
        group.options.set("pdf", {
            paperSize: "auto",
            margin: {
                left   : "5mm",
                top    : "5mm",
                right  : "5mm",
                bottom : "5mm"
            },
        });
      kendo.drawing.pdf.saveAs(group, project+"WindRoseComparison"+kendo.toString(dateStart, "dd/MM/yyyy")+"to"+kendo.toString(dateEnd, "dd/MM/yyyy")+".pdf");
        setTimeout(function(){
            app.loading(false);
        },2000)
    });
}

wrb.InitTurbineListCompare = function () {
    if (wrb.dataWindroseComparison().Data.length > 1) {
        $("#checkAllCompare").html('' +
            '<input type="checkbox" id="showHideAllCompare" checked onclick="wrb.showHideAllCompare(this)" >' +
            '' +
            'Select All' +
            '');
    } else {
        $("#checkAllCompare").html("");
    }

    $("#turbine-list-compare").html("");
    $.each(wrb.dataWindroseComparison().Data, function (idx, val) {
        $("#turbine-list-compare").append('
' +
            '<button class="btn btn-default btn-sm turbine-chk" type="button" onclick="wrb.showHideCompare(' + val.idxseries + ')" style="border-color:' + val.color + ';background-color:' + val.color + '"></button>' +
            '<input class="chk-option-windcomp" type="checkbox" name="' + val.name + '" checked id="chk-windrose-comparison' + val.idxseries + '" hidden>' +
            '<button class="btn btn-default btn-sm turbine-btn wbtn" onclick="wrb.showHideCompare(' + val.idxseries + ')" type="button">' + val.name + '</button>' +
            '
');
    });
}

wrb.showHideAllCompare = function (e) {

    if (e.checked == true) {
        $('.fa-check-windcomp').css("visibility", 'visible');
        $.each(wrb.dataWindroseComparison().Data, function (i, val) {
            if($("#WRChartComparison").data("kendoChart").options.series[i] != undefined){
                $("#WRChartComparison").data("kendoChart").options.series[i].visible = true;
            }
        });
        $('#labelShowHideCompare b').text('Select All');
    } else {
        $.each(wrb.dataWindroseComparison().Data, function (i, val) {
            if($("#WRChartComparison").data("kendoChart").options.series[i] != undefined){
                $("#WRChartComparison").data("kendoChart").options.series[i].visible = false;
            }
        });
        $('.fa-check-windcomp').css("visibility", 'hidden');
        $('#labelShowHideCompare b').text('Select All');
    }
    $('.chk-option-windcomp').not(e).prop('checked', e.checked);

    $("#WRChartComparison").data("kendoChart").redraw();
}

wrb.showHideCompare = function (idx) {
    var stat = false;

    $('#chk-windrose-comparison' + idx).trigger('click');
    var chart = $("#WRChartComparison").data("kendoChart");
    var leTur = $('input[id*=chk-windrose-comparison][type=checkbox]').length

    if ($('input[id*=chk-windrose-comparison][type=checkbox]:checked').length == $('input[id*=chk-windrose-comparison][type=checkbox]').length) {
        $('#showHideAllCompare').prop('checked', true);
    } else {
        $('#showHideAllCompare').prop('checked', false);
    }

    if ($('#chk-windrose-comparison' + idx).is(':checked')) {
        $('#icon-windrose-comparison' + idx).css("visibility", "visible");
    } else {
        $('#icon-windrose-comparison' + idx).css("visibility", "hidden");
    }

    if ($('#chk-windrose-comparison' + idx).is(':checked')) {
        $("#WRChartComparison").data("kendoChart").options.series[idx].visible = true
    } else {
        $("#WRChartComparison").data("kendoChart").options.series[idx].visible = false
    }
    $("#WRChartComparison").data("kendoChart").redraw();
}

wrb.initChartWRC = function () {
    listOfChartComparison = [];
    var dataSeries = wrb.dataWindroseComparison().Data;
    // var categories = wrb.dataWindroseComparison().Categories;
    var nilaiMax = wrb.dataWindroseComparison().MaxValue;

    var majorUnit = 10;
    if(nilaiMax < 40) {
        majorUnit = 5;
    }

    $("#WRChartComparison").kendoChart({
        theme: "flat",
        title: {
            text: "Wind Rose Comparison",
            font: '16px Source Sans Pro, Lato , Open Sans , Helvetica Neue, Arial, sans-serif',
            visible: false
        },
        chartArea : {
            height : 350
        },
        legend: {
            position: "bottom",
            visible: false,
            labels : {
                font: '12px Source Sans Pro, Lato , Open Sans , Helvetica Neue, Arial, sans-serif',
                // template: kendo.template($("#legendItemTemplate").html()),
            }
        },
        series: dataSeries,
        xAxis: {
        	majorUnit: 30,
        	startAngle: 90,
        	reverse: true,
        	labels: {
                font: '12px Source Sans Pro, Lato , Open Sans , Helvetica Neue, Arial, sans-serif'
            },
        },
        yAxis: {
        	labels: {
        		template: kendo.template("#= kendo.toString(value, 'n0') #%"),
                font: '12px Source Sans Pro, Lato , Open Sans , Helvetica Neue, Arial, sans-serif'
            },
        },

        tooltip: {
            visible: true,
            template: "#= series.name # : #= dataItem.DirectionDesc #"+String.fromCharCode(176)+
            			" (#= kendo.toString(dataItem.Contribution, 'n2') #%) for #= kendo.toString(dataItem.Hours, 'n2') # Hours",
            background: "rgb(255,255,255, 0.9)",
            color: "#58666e",
            font: 'Source Sans Pro, Lato , Open Sans , Helvetica Neue, Arial, sans-serif',
            border: {
                color: "#eee",
                width: "2px",
            },
        }
    });

    setTimeout(function(){
        var chart = $("#WRChartComparison").data("kendoChart");
        var viewModel = kendo.observable({
          series: chart.options.series,
          markerColor: function(e) {
            return e.get("visible") ? e.color : "grey";
          }
        });

        kendo.bind($("#legendWindrose"), viewModel);
    },300);

    wrb.InitTurbineListCompare();
    // $('#WRChartComparison').data('kendoChart').options.chartArea.width = $('#WRChartComparison').height() + ($('#WRChartComparison').height()/4);
    // $('#WRChartComparison').data('kendoChart').refresh();
}

wrb.WindRoseComparison = function(){
    var isValid = fa.LoadData();
    if(isValid) {
        pm.showFilter();
        if(pm.isFirstWindRoseComparison() === true){
            app.loading(true);
            setTimeout(function () {
                // var breakDownVal = $("#nosectionComparison").data("kendoDropDownList").value();
                var breakDownVal = "36";
                var secDer = 360 / breakDownVal;
                // wrb.sectorDerajatComparison(secDer);
                var param = {
                    period: fa.period,
                    dateStart: fa.dateStart,
                    dateEnd: fa.dateEnd,
                    turbine: fa.turbine(),
                    project: fa.project,
                    breakDown: breakDownVal,
                };
                toolkit.ajaxPost(viewModel.appName + "analyticwindrose/getwindrosedata", param, function (res) {
                    if (!app.isFine(res)) {
                        app.loading(false);
                        return;
                    }
                    if (res.data != null) {
                        var tempData = _.sortBy(res.data.Data, 'name');
                        res.data.Data = tempData;
                        res.data.Data.forEach(function(val, idx){
                            res.data.Data[idx].idxseries = idx;
                        });
                        wrb.dataWindroseComparison(res.data);
                        wrb.initChartWRC();
                    }

                    app.loading(false);
                    pm.isFirstWindRoseComparison(false);

                })
            }, 300);
            var metDate = 'Data Available (MET) from: ' + availDateList.availabledatestartmet + ' until: ' + availDateList.availabledateendmet + ''
            var scadaDate = ' | (SCADA) from: ' + availDateList.availabledatestartscada + ' until: ' + availDateList.availabledateendscada + ''
            $('#availabledatestart').html(metDate);
            $('#availabledateend').html(scadaDate);

            var project = $('#projectList').data("kendoDropDownList").value();
            var dateStart = $('#dateStart').data('kendoDatePicker').value();
            var dateEnd = $('#dateEnd').data('kendoDatePicker').value();
            wrb.project(project);
            wrb.dateStart(moment(new Date(dateStart)).format("DD-MMM-YYYY"));
            wrb.dateEnd(moment(new Date(dateEnd)).format("DD-MMM-YYYY"));

        }else{
            var metDate = 'Data Available (MET) from: ' + availDateList.availabledatestartmet + ' until: ' + availDateList.availabledateendmet + ''
            var scadaDate = ' | (SCADA) from: ' + availDateList.availabledatestartscada + ' until: ' + availDateList.availabledateendscada + ''
            $('#availabledatestart').html(metDate);
            $('#availabledateend').html(scadaDate);

            var project = $('#projectList').data("kendoDropDownList").value();
            var dateStart = $('#dateStart').data('kendoDatePicker').value();
            var dateEnd = $('#dateEnd').data('kendoDatePicker').value();
            wrb.project(project);
            wrb.dateStart(moment(new Date(dateStart)).format("DD-MMM-YYYY"));
            wrb.dateEnd(moment(new Date(dateEnd)).format("DD-MMM-YYYY"));

            setTimeout(function(){
                $.each(listOfChartComparison, function(idx, elem){
                    $(elem).data("kendoChart").refresh();
                });
                app.loading(false);
            }, 300);
        }
    }
}<commit_msg>fix windrose comparison<commit_after>'use strict';

viewModel.WindRoseComparison = new Object();
var wrb = viewModel.WindRoseComparison;

// WIND ROSE COMPARISON
wrb.sectorDerajatComparison = ko.observable(0);
wrb.dataWindroseComparison = ko.observableArray([]);
wrb.project = ko.observable();
wrb.dateStart = ko.observable();
wrb.dateEnd = ko.observable();
var listOfChartComparison = [];
var listOfButtonComparison = {};
/*wrb.showHideLegendComparison = function (index) {
    var idName = "btn" + index;
    listOfButtonComparison[idName] = !listOfButtonComparison[idName];
    if (listOfButtonComparison[idName] == false) {
        $("#" + idName).css({ 'background': '#8f8f8f', 'border-color': '#8f8f8f' });
    } else {
        $("#" + idName).css({ 'background': colorFieldsWR[index], 'border-color': colorFieldsWR[index] });
    }
    $.each(listOfChartComparison, function (idx, idChart) {
       if($(idChart).data("kendoChart").options.series.length - 1 >= index) {
          $(idChart).data("kendoChart").options.series[index].visible = listOfButtonComparison[idName];
          $(idChart).data("kendoChart").refresh();
        }
    });
}*/

wrb.getPDF = function(selector){
    app.loading(true);
    var project = $("#projectList").data("kendoDropDownList").value();
    var dateStart = $('#dateStart').data('kendoDatePicker').value();
    var dateEnd = $('#dateEnd').data('kendoDatePicker').value();

    kendo.drawing.drawDOM($(selector)).then(function(group){
        group.options.set("pdf", {
            paperSize: "auto",
            margin: {
                left   : "5mm",
                top    : "5mm",
                right  : "5mm",
                bottom : "5mm"
            },
        });
      kendo.drawing.pdf.saveAs(group, project+"WindRoseComparison"+kendo.toString(dateStart, "dd/MM/yyyy")+"to"+kendo.toString(dateEnd, "dd/MM/yyyy")+".pdf");
        setTimeout(function(){
            app.loading(false);
        },2000)
    });
}

wrb.InitTurbineListCompare = function () {
    if (wrb.dataWindroseComparison().Data.length > 1) {
        $("#checkAllCompare").html('' +
            '<input type="checkbox" id="showHideAllCompare" checked onclick="wrb.showHideAllCompare(this)" >' +
            '' +
            'Select All' +
            '');
    } else {
        $("#checkAllCompare").html("");
    }

    $("#turbine-list-compare").html("");
    $.each(wrb.dataWindroseComparison().Data, function (idx, val) {
        $("#turbine-list-compare").append('
' +
            '<button class="btn btn-default btn-sm turbine-chk" type="button" onclick="wrb.showHideCompare(' + val.idxseries + ')" style="border-color:' + val.color + ';background-color:' + val.color + '"></button>' +
            '<input class="chk-option-windcomp" type="checkbox" name="' + val.name + '" checked id="chk-windrose-comparison' + val.idxseries + '" hidden>' +
            '<button class="btn btn-default btn-sm turbine-btn wbtn" onclick="wrb.showHideCompare(' + val.idxseries + ')" type="button">' + val.name + '</button>' +
            '
');
    });
}

wrb.showHideAllCompare = function (e) {

    if (e.checked == true) {
        $('.fa-check-windcomp').css("visibility", 'visible');
        $.each(wrb.dataWindroseComparison().Data, function (i, val) {
            if($("#WRChartComparison").data("kendoChart").options.series[i] != undefined){
                $("#WRChartComparison").data("kendoChart").options.series[i].visible = true;
            }
        });
        $('#labelShowHideCompare b').text('Select All');
    } else {
        $.each(wrb.dataWindroseComparison().Data, function (i, val) {
            if($("#WRChartComparison").data("kendoChart").options.series[i] != undefined){
                $("#WRChartComparison").data("kendoChart").options.series[i].visible = false;
            }
        });
        $('.fa-check-windcomp').css("visibility", 'hidden');
        $('#labelShowHideCompare b').text('Select All');
    }
    $('.chk-option-windcomp').not(e).prop('checked', e.checked);

    $("#WRChartComparison").data("kendoChart").redraw();
}

wrb.showHideCompare = function (idx) {
    var stat = false;

    $('#chk-windrose-comparison' + idx).trigger('click');
    var chart = $("#WRChartComparison").data("kendoChart");
    var leTur = $('input[id*=chk-windrose-comparison][type=checkbox]').length

    if ($('input[id*=chk-windrose-comparison][type=checkbox]:checked').length == $('input[id*=chk-windrose-comparison][type=checkbox]').length) {
        $('#showHideAllCompare').prop('checked', true);
    } else {
        $('#showHideAllCompare').prop('checked', false);
    }

    if ($('#chk-windrose-comparison' + idx).is(':checked')) {
        $('#icon-windrose-comparison' + idx).css("visibility", "visible");
    } else {
        $('#icon-windrose-comparison' + idx).css("visibility", "hidden");
    }

    if ($('#chk-windrose-comparison' + idx).is(':checked')) {
        $("#WRChartComparison").data("kendoChart").options.series[idx].visible = true
    } else {
        $("#WRChartComparison").data("kendoChart").options.series[idx].visible = false
    }
    $("#WRChartComparison").data("kendoChart").redraw();
}

wrb.initChartWRC = function () {
    listOfChartComparison = [];
    var dataSeries = wrb.dataWindroseComparison().Data;
    // var categories = wrb.dataWindroseComparison().Categories;
    var nilaiMax = wrb.dataWindroseComparison().MaxValue;

    var majorUnit = 10;
    if(nilaiMax < 40) {
        majorUnit = 5;
    }

    $("#WRChartComparison").html("");
    $("#WRChartComparison").kendoChart({
        theme: "flat",
        title: {
            text: "Wind Rose Comparison",
            font: '16px Source Sans Pro, Lato , Open Sans , Helvetica Neue, Arial, sans-serif',
            visible: false
        },
        chartArea : {
            height : 350
        },
        legend: {
            position: "bottom",
            visible: false,
            labels : {
                font: '12px Source Sans Pro, Lato , Open Sans , Helvetica Neue, Arial, sans-serif',
                // template: kendo.template($("#legendItemTemplate").html()),
            }
        },
        series: dataSeries,
        xAxis: {
        	majorUnit: 30,
        	startAngle: 90,
        	reverse: true,
        	labels: {
                font: '12px Source Sans Pro, Lato , Open Sans , Helvetica Neue, Arial, sans-serif'
            },
        },
        yAxis: {
        	labels: {
        		template: kendo.template("#= kendo.toString(value, 'n0') #%"),
                font: '12px Source Sans Pro, Lato , Open Sans , Helvetica Neue, Arial, sans-serif'
            },
        },

        tooltip: {
            visible: true,
            template: "#= series.name # : #= dataItem.DirectionDesc #"+String.fromCharCode(176)+
            			" (#= kendo.toString(dataItem.Contribution, 'n2') #%) for #= kendo.toString(dataItem.Hours, 'n2') # Hours",
            background: "rgb(255,255,255, 0.9)",
            color: "#58666e",
            font: 'Source Sans Pro, Lato , Open Sans , Helvetica Neue, Arial, sans-serif',
            border: {
                color: "#eee",
                width: "2px",
            },
        }
    });

    setTimeout(function(){
        var chart = $("#WRChartComparison").data("kendoChart");
        var viewModel = kendo.observable({
          series: chart.options.series,
          markerColor: function(e) {
            return e.get("visible") ? e.color : "grey";
          }
        });

        kendo.bind($("#legendWindrose"), viewModel);
    },300);

    wrb.InitTurbineListCompare();
    // $('#WRChartComparison').data('kendoChart').options.chartArea.width = $('#WRChartComparison').height() + ($('#WRChartComparison').height()/4);
    // $('#WRChartComparison').data('kendoChart').refresh();
}

wrb.WindRoseComparison = function(){
    var isValid = fa.LoadData();
    if(isValid) {
        pm.showFilter();
        if(pm.isFirstWindRoseComparison() === true){
            app.loading(true);
            setTimeout(function () {
                // var breakDownVal = $("#nosectionComparison").data("kendoDropDownList").value();
                var breakDownVal = "36";
                var secDer = 360 / breakDownVal;
                // wrb.sectorDerajatComparison(secDer);
                var param = {
                    period: fa.period,
                    dateStart: fa.dateStart,
                    dateEnd: fa.dateEnd,
                    turbine: fa.turbine(),
                    project: fa.project,
                    breakDown: breakDownVal,
                };
                toolkit.ajaxPost(viewModel.appName + "analyticwindrose/getwindrosedata", param, function (res) {
                    if (!app.isFine(res)) {
                        app.loading(false);
                        return;
                    }
                    if (res.data != null) {
                        var tempData = _.sortBy(res.data.Data, 'name');
                        res.data.Data = tempData;
                        res.data.Data.forEach(function(val, idx){
                            res.data.Data[idx].idxseries = idx;
                        });
                        wrb.dataWindroseComparison(res.data);
                        wrb.initChartWRC();
                    }

                    app.loading(false);
                    pm.isFirstWindRoseComparison(false);

                })
            }, 300);
            var metDate = 'Data Available (MET) from: ' + availDateList.availabledatestartmet + ' until: ' + availDateList.availabledateendmet + ''
            var scadaDate = ' | (SCADA) from: ' + availDateList.availabledatestartscada + ' until: ' + availDateList.availabledateendscada + ''
            $('#availabledatestart').html(metDate);
            $('#availabledateend').html(scadaDate);

            var project = $('#projectList').data("kendoDropDownList").value();
            var dateStart = $('#dateStart').data('kendoDatePicker').value();
            var dateEnd = $('#dateEnd').data('kendoDatePicker').value();
            wrb.project(project);
            wrb.dateStart(moment(new Date(dateStart)).format("DD-MMM-YYYY"));
            wrb.dateEnd(moment(new Date(dateEnd)).format("DD-MMM-YYYY"));

        }else{
            var metDate = 'Data Available (MET) from: ' + availDateList.availabledatestartmet + ' until: ' + availDateList.availabledateendmet + ''
            var scadaDate = ' | (SCADA) from: ' + availDateList.availabledatestartscada + ' until: ' + availDateList.availabledateendscada + ''
            $('#availabledatestart').html(metDate);
            $('#availabledateend').html(scadaDate);

            var project = $('#projectList').data("kendoDropDownList").value();
            var dateStart = $('#dateStart').data('kendoDatePicker').value();
            var dateEnd = $('#dateEnd').data('kendoDatePicker').value();
            wrb.project(project);
            wrb.dateStart(moment(new Date(dateStart)).format("DD-MMM-YYYY"));
            wrb.dateEnd(moment(new Date(dateEnd)).format("DD-MMM-YYYY"));

            setTimeout(function(){
                $.each(listOfChartComparison, function(idx, elem){
                    $(elem).data("kendoChart").refresh();
                });
                app.loading(false);
            }, 300);
        }
    }
}<|endoftext|>


<commit_before>var es = require('../')
  , it = require('it-is').style('colour')
  , d = require('ubelt')
  , join = require('path').join
  , fs = require('fs')
  , Stream = require('stream').Stream

exports ['pipeable'] = function (test) {
  var readme = join(__filename)
    , expected = fs.readFileSync(readme, 'utf-8').split('\n')
    , cs = es.split()
    , actual = []
    , ended = false

  var a = new Stream ()

  a.write = function (l) {
    actual.push(l.trim())
  }
  a.end = function () {

      ended = true
      expected.forEach(function (v,k) {
        //String.split will append an empty string ''
        //if the string ends in a split pattern.
        //es.split doesn't which was breaking this test.
        //clearly, appending the empty string is correct.
        //tests are passing though. which is the current job.
        if(v)
          it(actual[k]).like(v)
      })
      console.log('88888888888888888888888!!!')
      test.done()
    }
  a.writable = true

  fs.createReadStream(readme, {flags: 'r'}).pipe(cs)
  cs.pipe(a)

}

<commit_msg>tidy up a little<commit_after>var es = require('../')
  , it = require('it-is').style('colour')
  , d = require('ubelt')
  , join = require('path').join
  , fs = require('fs')
  , Stream = require('stream').Stream

exports ['es.split() works like String#split'] = function (test) {
  var readme = join(__filename)
    , expected = fs.readFileSync(readme, 'utf-8').split('\n')
    , cs = es.split()
    , actual = []
    , ended = false

  var a = new Stream ()

  a.write = function (l) {
    actual.push(l.trim())
  }
  a.end = function () {

      ended = true
      expected.forEach(function (v,k) {
        //String.split will append an empty string ''
        //if the string ends in a split pattern.
        //es.split doesn't which was breaking this test.
        //clearly, appending the empty string is correct.
        //tests are passing though. which is the current job.
        if(v)
          it(actual[k]).like(v)
      })
      test.done()
  }
  a.writable = true

  fs.createReadStream(readme, {flags: 'r'}).pipe(cs)
  cs.pipe(a)

}

<|endoftext|>


<commit_before>angular
.module('webui.ctrls.download', [
  "ui.bootstrap",
  'webui.services.utils', 'webui.services.rpc', 'webui.services.alerts',
  'webui.services.settings', 'webui.services.modals'
])
.controller('DownloadCtrl', [
  '$scope', '$rpc', '$utils', '$alerts', '$modals',
  '$fileSettings', '$activeInclude', '$waitingExclude',
  // for document title
  '$window',
function(
  scope, rpc, utils, alerts, modals,
  fsettings, activeInclude, waitingExclude, window
) {

  var re_slashes = /\\/g;
  var slash = "/";
  var allStopped = [];

  scope.active = [], scope.waiting = [], scope.stopped = [];
  scope.gstats = {};
  scope.hideLinkedMetadata = true;

  // pause the download
  // d: the download ctx
  scope.pause = function(d) {
    rpc.once('forcePause', [d.gid]);
  }

  // resume the download
  // d: the download ctx
  scope.resume = function(d) {
    rpc.once('unpause', [d.gid]);
  }

  // remove the download,
  // put it in stopped list if active,
  // otherwise permanantly remove it
  // d: the download ctx
  scope.remove = function(d, cb, noConfirm) {
    if (!noConfirm && !confirm("Remove %s and associated meta-data?".replace("%s", d.name))) {
      return;
    }
    var method = 'remove';

    if (scope.getType(d) == 'stopped')
      method = 'removeDownloadResult';

    if (d.followedFrom) {
      scope.remove(d.followedFrom, function() {}, true);
      d.followedFrom = null;
    }
    rpc.once(method, [d.gid], cb);

    var lists = [scope.active, scope.waiting, scope.stopped], ind = -1, i;
    for (var i = 0; i < lists.length; ++i) {
      var list = lists[i];
      var idx = list.indexOf(d);
      if (idx < 0) {
        continue;
      }
      list.splice(idx, 1);
      return;
    }
  }

  scope.restart = function(d) {
    var uris =
      _.chain(d.files).map(function(f) { return f.uris })
      .filter(function(uris) { return uris.length })
      .map(function(uris) {
        return _.chain(uris)
          .map(function(u) { return u.uri })
          .uniq().value();
      }).value();

    if (uris.length > 0) {
      scope.remove(d, function() {
        rpc.once('addUri', uris, angular.noop, true);
      });
    }
  }

  // start filling in the model of active,
  // waiting and stopped download
  rpc.subscribe('tellActive', [], function(data) {
    scope.$apply(function() {
      utils.mergeMap(data[0], scope.active, scope.getCtx);
    });
  });

  rpc.subscribe('tellWaiting', [0, 1000], function(data) {
    scope.$apply(function() {
      utils.mergeMap(data[0], scope.waiting, scope.getCtx);
    });
  });

  rpc.subscribe('tellStopped', [0, 1000], function(data) {
    scope.$apply(function() {
      if (!scope.hideLinkedMetadata) {
        utils.mergeMap(data[0], scope.stopped, scope.getCtx);
        return;
      }
      utils.mergeMap(data[0], allStopped, scope.getCtx);
      var gids = {};
      _.forEach(allStopped, function(e) {
        gids[e.gid] = e;
      });
      _.forEach(scope.active, function(e) {
        gids[e.gid] = e;
      });
      _.forEach(scope.waiting, function(e) {
        gids[e.gid] = e;
      });
      scope.stopped = _.filter(allStopped, function(e) {
        if (!e.metadata || !e.followedBy || !(e.followedBy in gids)) {
          return true;
        }
        var linked = gids[e.followedBy];
        linked.followedFrom = e;
        return false;
      });
    });
  });

  rpc.subscribe('getGlobalStat', [], function(data) {
    scope.$apply(function() {
      scope.gstats = data[0];
      window.document.title = utils.getTitle(scope.gstats);
    });
  });

  rpc.once('getVersion', [], function(data) {
    scope.$apply(function() {
      scope.miscellaneous = data[0];
    });
  });

  // total number of downloads, updates dynamically as downloads are
  // stored in scope
  scope.totalDownloads = 0;

  // download search filter
  scope.downloadFilter = "";
  scope.downloadFilterCommitted = "";

  scope.onDownloadFilter = function() {
    if (scope.downloadFilterTimer) {
      clearTimeout(scope.downloadFilterTimer);
    }
    scope.downloadFilterTimer = setTimeout(function() {
      delete scope.downloadFilterTimer;
      if (scope.downloadFilterCommitted !== scope.downloadFilter) {
        scope.downloadFilterCommitted = scope.downloadFilter;
        scope.$digest();
      }
    }, 500);
  };

  scope.filterDownloads = function(downloads) {
    if (!scope.downloadFilterCommitted) {
      return downloads;
    }
    var filter = scope.downloadFilterCommitted.
      replace(/[{}()\[\]\\^$.?]/g, "\\$&").
      replace(/\*/g, ".*").
      replace(/\./g, ".");
    filter = new RegExp(filter, "i");
    return _.filter(downloads, function(d) {
      if (filter.test(d.name)) return true;
      return _.filter(d.files, function(f) {
        return filter.test(f.relpath);
      }).length;
    });
  };

  scope.clearFilter = function() {
    scope.downloadFilter = scope.downloadFilterCommitted = "";
  };

  scope.toggleStateFilters = function() {
    scope.filterSpeed = !scope.filterSpeed;
    scope.filterActive = !scope.filterActive;
    scope.filterWaiting = !scope.filterWaiting;
    scope.filterComplete = !scope.filterComplete;
    scope.filterError = !scope.filterError;
    scope.filterPaused = !scope.filterPaused;
    scope.filterRemoved = !scope.filterRemoved;
    scope.persistFilters();
  };

  scope.resetFilters = function() {
    scope.filterSpeed =
      scope.filterActive =
      scope.filterWaiting =
      scope.filterComplete =
      scope.filterError =
      scope.filterPaused =
      scope.filterRemoved =
      true;
    scope.clearFilter();
    scope.persistFilters();
  };

  scope.persistFilters = function() {
    var o = JSON.stringify({
      s: scope.filterSpeed,
      a: scope.filterActive,
      w: scope.filterWaiting,
      c: scope.filterComplete,
      e: scope.filterError,
      p: scope.filterPaused,
      r: scope.filterRemoved
    });
    utils.setCookie("aria2filters", o);
  };

  scope.loadFilters = function() {
    var o = JSON.parse(utils.getCookie("aria2filters"));
    if (!o) {
      scope.resetFilters();
      return;
    }
    scope.filterSpeed = !!o.s;
    scope.filterActive = !!o.a;
    scope.filterWaiting = !!o.w;
    scope.filterComplete = !!o.c;
    scope.filterError = !!o.e;
    scope.filterPaused = !!o.p;
    scope.filterRemoved = !!o.r;
  };

  scope.loadFilters();

  // max downloads shown in one page
  scope.pageSize = 32;

  // current displayed page
  scope.currentPage = 1;

  // total amount of downloads returned by aria2
  scope.totalAria2Downloads = function() {
    return scope.active.length
      + scope.waiting.length
      + scope.stopped.length;
  }

  // actual downloads used by the view
  scope.getDownloads = function() {
    var downloads = [];
    if (scope.filterActive) {
      if (!scope.filterSpeed) {
        downloads = _.filter(scope.active, function (e) {
          return !+e.uploadSpeed && !+e.downloadSpeed;
        });
      }
      else {
        downloads = scope.active;
      }
    }
    else if (scope.filterSpeed) {
      downloads = _.filter(scope.active, function (e) {
        return +e.uploadSpeed || +e.downloadSpeed;
      });
    }
    if (scope.filterWaiting) {
      downloads = downloads.concat(_.filter(scope.waiting, function (e) {
        return e.status == "waiting";
      }));
    }
    if (scope.filterPaused) {
      downloads = downloads.concat(_.filter(scope.waiting, function (e) {
        return e.status == "paused";
      }));
    }
    if (scope.filterError) {
      downloads = downloads.concat(_.filter(scope.stopped, function (e) {
        return e.status == "error";
      }));
    }
    if (scope.filterComplete) {
      downloads = downloads.concat(_.filter(scope.stopped, function (e) {
        return e.status == "complete";
      }));
    }
    if (scope.filterRemoved) {
      downloads = downloads.concat(_.filter(scope.stopped, function (e) {
        return e.status == "removed";
      }));
    }

    downloads = scope.filterDownloads(downloads);

    scope.totalDownloads = downloads.length;

    downloads = downloads.slice( (scope.currentPage - 1) * scope.pageSize );
    downloads.splice( scope.pageSize );

    return downloads;
  }

  // convert the donwload form aria2 to once used by the view,
  // minor additions of some fields and checks
  scope.getCtx = function(d, ctx) {
    if (!ctx) {
      ctx = {
        dir: d.dir,
        status: d.status,
        gid: d.gid,
        followedBy: (d.followedBy && d.followedBy.length == 1
          ? d.followedBy[0] : null),
        followedFrom: null,
        numPieces: d.numPieces,
        connections: d.connections,
        bitfield: d.bitfield,
        totalLength: d.totalLength,
        fmtTotalLength: utils.fmtsize(d.totalLength),
        completedLength: d.completedLength,
        fmtCompletedLength: utils.fmtsize(d.completedLength),
        uploadLength: d.uploadLength,
        fmtUploadLength: utils.fmtsize(d.uploadLength),
        pieceLength: d.pieceLength,
        fmtPieceLength: utils.fmtsize(d.pieceLength),
        downloadSpeed: d.downloadSpeed,
        fmtDownloadSpeed: utils.fmtspeed(d.downloadSpeed),
        uploadSpeed: d.uploadSpeed,
        fmtUploadSpeed: utils.fmtspeed(d.uploadSpeed),
        collapsed: true,
        files: []
      };
    }
    else {
      ctx.dir = d.dir;
      ctx.status = d.status;
      ctx.gid = d.gid;
      ctx.followedBy = (d.followedBy && d.followedBy.length == 1
        ? d.followedBy[0] : null);
      ctx.followedFrom = null;
      ctx.numPieces = d.numPieces;
      ctx.connections = d.connections;
      ctx.bitfield = d.bitfield;
      if (ctx.totalLength !== d.totalLength) {
        ctx.totalLength = d.totalLength;
        ctx.fmtTotalLength = utils.fmtsize(d.totalLength);
      }
      if (ctx.completedLength !== d.completedLength) {
        ctx.completedLength = d.completedLength;
        ctx.fmtCompletedLength = utils.fmtsize(d.completedLength);
      }
      if (ctx.uploadLength !== d.uploadength) {
        ctx.uploadLength = d.uploadlength;
        ctx.fmtUploadLength = utils.fmtsize(d.uploadLength);
      }
      if (ctx.pieceLength !== d.pieceLength) {
        ctx.pieceLength = d.pieceLength;
        ctx.fmtPieceLength = utils.fmtsize(d.pieceLength);
      }
      if (ctx.downloadSpeed !== d.downloadSpeed) {
        ctx.downloadSpeed =  d.downloadSpeed;
        ctx.fmtDownloadSpeed = utils.fmtspeed(d.downloadSpeed);
      }
      if (ctx.uploadSpeed !== d.uploadSpeed) {
        ctx.uploadSpeed =  d.uploadSpeed;
        ctx.fmtUploadSpeed = utils.fmtspeed(d.uploadSpeed);
      }
    }

    var dlName;
    var files = d.files;
    if (files) {
      var cfiles = ctx.files;
      for (var i = 0; i < files.length; ++i) {
        var cfile = cfiles[i] || (cfiles[i] = {});
        var file = files[i];
        if (file.path !== cfile.path) {
          cfile.path = file.path;
          cfile.length = file.length;
          cfile.fmtLength = utils.fmtsize(file.length);
          cfile.relpath = file.path.replace(re_slashes, slash);
          if (!cfile.relpath) {
            cfile.relpath = (file.uris && file.uris[0] && file.uris[0].uri) || "Unknown";
          }
          else if (!cfile.relpath.startsWith("[")) { // METADATA
            cfile.relpath = cfile.relpath.substr(ctx.dir.length + 1);
          }
        }
      }
      cfiles.length = files.length;
      if (cfiles.length) {
        dlName = cfiles[0].relpath;
      }
    }
    else {
      delete ctx.files;
    }

    var btName;
    if (d.bittorrent) {
      btName = d.bittorrent.info && d.bittorrent.info.name;
      ctx.bittorrent = true;
    }
    else {
      delete ctx.bittorrent;
    }

    ctx.name = btName || dlName || "Unknown";
    ctx.metadata = ctx.name.startsWith("[METADATA]");
    if (ctx.metadata) {
      ctx.name = ctx.name.substr(10);
    }

    return ctx;
  };

  scope.canRestart = function(d) {
    if (['active', 'paused'].indexOf(d.status) == -1
        && !d.bittorrent)
      return true;

    return false;
  };

  scope.hasStatus = function hasStatus(d, status) {
    if (_.isArray(status)) {
      if (status.length == 0) return false;
      return hasStatus(d, status[0]) || hasStatus(d, status.slice(1));
    }
    else {
      return d.status == status;
    }
  };

  // get time left for the download with
  // current download speed,
  // could be smarter by different heuristics
  scope.getEta = function(d) {
    return (d.totalLength-d.completedLength) / d.downloadSpeed;
  }

  scope.getProgressClass = function(d) {
    switch (d.status) {
      case "paused":
        return "progress-info";
      case "error":
        return "progress-danger";
      case "removed":
        return "progress-warning";
      case "active":
        return "progress-active";
      case "complete":
        return "progress-success";
      default:
        return "";
    }
  };

  // gets the progress in percentages
  scope.getProgress = function(d) {
    var percentage = (d.completedLength / d.totalLength)*100 || 0;
    percentage = percentage.toFixed(2);
    if(!percentage) percentage = 0;

    return percentage;
  };

  // gets the type for the download as classified by the aria2 rpc calls
  scope.getType = function(d) {
    var type = d.status;
    if (type == "paused") type = "waiting";
    if (["error", "removed", "complete"].indexOf(type) != -1)
      type = "stopped";
    return type;
  };

  scope.showSettings = function(d) {
    var type = scope.getType(d)
      , settings = {};

    rpc.once('getOption', [d.gid], function(data) {
      var vals = data[0];

      for (var i in fsettings) {
        if (type == 'active' && activeInclude.indexOf(i) == -1) continue;

        if (type == 'waiting' && waitingExclude.indexOf(i) != -1) continue;

        settings[i] = fsettings[i];
        settings[i].val = vals[i] || settings[i].val;
      }

      modals.invoke('settings', settings, scope.name + ' settings', function(settings) {
        var sets = {};
        for (var i in settings) { sets[i] = settings[i].val };

        rpc.once('changeOption', [d.gid, sets]);
      });
    });

    return false;
  }

}]);
<commit_msg>Do not remove stuff from lists...<commit_after>angular
.module('webui.ctrls.download', [
  "ui.bootstrap",
  'webui.services.utils', 'webui.services.rpc', 'webui.services.alerts',
  'webui.services.settings', 'webui.services.modals'
])
.controller('DownloadCtrl', [
  '$scope', '$rpc', '$utils', '$alerts', '$modals',
  '$fileSettings', '$activeInclude', '$waitingExclude',
  // for document title
  '$window',
function(
  scope, rpc, utils, alerts, modals,
  fsettings, activeInclude, waitingExclude, window
) {

  var re_slashes = /\\/g;
  var slash = "/";
  var allStopped = [];

  scope.active = [], scope.waiting = [], scope.stopped = [];
  scope.gstats = {};
  scope.hideLinkedMetadata = true;

  // pause the download
  // d: the download ctx
  scope.pause = function(d) {
    rpc.once('forcePause', [d.gid]);
  }

  // resume the download
  // d: the download ctx
  scope.resume = function(d) {
    rpc.once('unpause', [d.gid]);
  }

  // remove the download,
  // put it in stopped list if active,
  // otherwise permanantly remove it
  // d: the download ctx
  scope.remove = function(d, cb, noConfirm) {
    if (!noConfirm && !confirm("Remove %s and associated meta-data?".replace("%s", d.name))) {
      return;
    }
    var method = 'remove';

    if (scope.getType(d) == 'stopped')
      method = 'removeDownloadResult';

    if (d.followedFrom) {
      scope.remove(d.followedFrom, function() {}, true);
      d.followedFrom = null;
    }
    rpc.once(method, [d.gid], cb);
  }

  scope.restart = function(d) {
    var uris =
      _.chain(d.files).map(function(f) { return f.uris })
      .filter(function(uris) { return uris.length })
      .map(function(uris) {
        return _.chain(uris)
          .map(function(u) { return u.uri })
          .uniq().value();
      }).value();

    if (uris.length > 0) {
      scope.remove(d, function() {
        rpc.once('addUri', uris, angular.noop, true);
      });
    }
  }

  // start filling in the model of active,
  // waiting and stopped download
  rpc.subscribe('tellActive', [], function(data) {
    scope.$apply(function() {
      utils.mergeMap(data[0], scope.active, scope.getCtx);
    });
  });

  rpc.subscribe('tellWaiting', [0, 1000], function(data) {
    scope.$apply(function() {
      utils.mergeMap(data[0], scope.waiting, scope.getCtx);
    });
  });

  rpc.subscribe('tellStopped', [0, 1000], function(data) {
    scope.$apply(function() {
      if (!scope.hideLinkedMetadata) {
        utils.mergeMap(data[0], scope.stopped, scope.getCtx);
        return;
      }
      utils.mergeMap(data[0], allStopped, scope.getCtx);
      var gids = {};
      _.forEach(allStopped, function(e) {
        gids[e.gid] = e;
      });
      _.forEach(scope.active, function(e) {
        gids[e.gid] = e;
      });
      _.forEach(scope.waiting, function(e) {
        gids[e.gid] = e;
      });
      scope.stopped = _.filter(allStopped, function(e) {
        if (!e.metadata || !e.followedBy || !(e.followedBy in gids)) {
          return true;
        }
        var linked = gids[e.followedBy];
        linked.followedFrom = e;
        return false;
      });
    });
  });

  rpc.subscribe('getGlobalStat', [], function(data) {
    scope.$apply(function() {
      scope.gstats = data[0];
      window.document.title = utils.getTitle(scope.gstats);
    });
  });

  rpc.once('getVersion', [], function(data) {
    scope.$apply(function() {
      scope.miscellaneous = data[0];
    });
  });

  // total number of downloads, updates dynamically as downloads are
  // stored in scope
  scope.totalDownloads = 0;

  // download search filter
  scope.downloadFilter = "";
  scope.downloadFilterCommitted = "";

  scope.onDownloadFilter = function() {
    if (scope.downloadFilterTimer) {
      clearTimeout(scope.downloadFilterTimer);
    }
    scope.downloadFilterTimer = setTimeout(function() {
      delete scope.downloadFilterTimer;
      if (scope.downloadFilterCommitted !== scope.downloadFilter) {
        scope.downloadFilterCommitted = scope.downloadFilter;
        scope.$digest();
      }
    }, 500);
  };

  scope.filterDownloads = function(downloads) {
    if (!scope.downloadFilterCommitted) {
      return downloads;
    }
    var filter = scope.downloadFilterCommitted.
      replace(/[{}()\[\]\\^$.?]/g, "\\$&").
      replace(/\*/g, ".*").
      replace(/\./g, ".");
    filter = new RegExp(filter, "i");
    return _.filter(downloads, function(d) {
      if (filter.test(d.name)) return true;
      return _.filter(d.files, function(f) {
        return filter.test(f.relpath);
      }).length;
    });
  };

  scope.clearFilter = function() {
    scope.downloadFilter = scope.downloadFilterCommitted = "";
  };

  scope.toggleStateFilters = function() {
    scope.filterSpeed = !scope.filterSpeed;
    scope.filterActive = !scope.filterActive;
    scope.filterWaiting = !scope.filterWaiting;
    scope.filterComplete = !scope.filterComplete;
    scope.filterError = !scope.filterError;
    scope.filterPaused = !scope.filterPaused;
    scope.filterRemoved = !scope.filterRemoved;
    scope.persistFilters();
  };

  scope.resetFilters = function() {
    scope.filterSpeed =
      scope.filterActive =
      scope.filterWaiting =
      scope.filterComplete =
      scope.filterError =
      scope.filterPaused =
      scope.filterRemoved =
      true;
    scope.clearFilter();
    scope.persistFilters();
  };

  scope.persistFilters = function() {
    var o = JSON.stringify({
      s: scope.filterSpeed,
      a: scope.filterActive,
      w: scope.filterWaiting,
      c: scope.filterComplete,
      e: scope.filterError,
      p: scope.filterPaused,
      r: scope.filterRemoved
    });
    utils.setCookie("aria2filters", o);
  };

  scope.loadFilters = function() {
    var o = JSON.parse(utils.getCookie("aria2filters"));
    if (!o) {
      scope.resetFilters();
      return;
    }
    scope.filterSpeed = !!o.s;
    scope.filterActive = !!o.a;
    scope.filterWaiting = !!o.w;
    scope.filterComplete = !!o.c;
    scope.filterError = !!o.e;
    scope.filterPaused = !!o.p;
    scope.filterRemoved = !!o.r;
  };

  scope.loadFilters();

  // max downloads shown in one page
  scope.pageSize = 32;

  // current displayed page
  scope.currentPage = 1;

  // total amount of downloads returned by aria2
  scope.totalAria2Downloads = function() {
    return scope.active.length
      + scope.waiting.length
      + scope.stopped.length;
  }

  // actual downloads used by the view
  scope.getDownloads = function() {
    var downloads = [];
    if (scope.filterActive) {
      if (!scope.filterSpeed) {
        downloads = _.filter(scope.active, function (e) {
          return !+e.uploadSpeed && !+e.downloadSpeed;
        });
      }
      else {
        downloads = scope.active;
      }
    }
    else if (scope.filterSpeed) {
      downloads = _.filter(scope.active, function (e) {
        return +e.uploadSpeed || +e.downloadSpeed;
      });
    }
    if (scope.filterWaiting) {
      downloads = downloads.concat(_.filter(scope.waiting, function (e) {
        return e.status == "waiting";
      }));
    }
    if (scope.filterPaused) {
      downloads = downloads.concat(_.filter(scope.waiting, function (e) {
        return e.status == "paused";
      }));
    }
    if (scope.filterError) {
      downloads = downloads.concat(_.filter(scope.stopped, function (e) {
        return e.status == "error";
      }));
    }
    if (scope.filterComplete) {
      downloads = downloads.concat(_.filter(scope.stopped, function (e) {
        return e.status == "complete";
      }));
    }
    if (scope.filterRemoved) {
      downloads = downloads.concat(_.filter(scope.stopped, function (e) {
        return e.status == "removed";
      }));
    }

    downloads = scope.filterDownloads(downloads);

    scope.totalDownloads = downloads.length;

    downloads = downloads.slice( (scope.currentPage - 1) * scope.pageSize );
    downloads.splice( scope.pageSize );

    return downloads;
  }

  // convert the donwload form aria2 to once used by the view,
  // minor additions of some fields and checks
  scope.getCtx = function(d, ctx) {
    if (!ctx) {
      ctx = {
        dir: d.dir,
        status: d.status,
        gid: d.gid,
        followedBy: (d.followedBy && d.followedBy.length == 1
          ? d.followedBy[0] : null),
        followedFrom: null,
        numPieces: d.numPieces,
        connections: d.connections,
        bitfield: d.bitfield,
        totalLength: d.totalLength,
        fmtTotalLength: utils.fmtsize(d.totalLength),
        completedLength: d.completedLength,
        fmtCompletedLength: utils.fmtsize(d.completedLength),
        uploadLength: d.uploadLength,
        fmtUploadLength: utils.fmtsize(d.uploadLength),
        pieceLength: d.pieceLength,
        fmtPieceLength: utils.fmtsize(d.pieceLength),
        downloadSpeed: d.downloadSpeed,
        fmtDownloadSpeed: utils.fmtspeed(d.downloadSpeed),
        uploadSpeed: d.uploadSpeed,
        fmtUploadSpeed: utils.fmtspeed(d.uploadSpeed),
        collapsed: true,
        files: []
      };
    }
    else {
      ctx.dir = d.dir;
      ctx.status = d.status;
      ctx.gid = d.gid;
      ctx.followedBy = (d.followedBy && d.followedBy.length == 1
        ? d.followedBy[0] : null);
      ctx.followedFrom = null;
      ctx.numPieces = d.numPieces;
      ctx.connections = d.connections;
      ctx.bitfield = d.bitfield;
      if (ctx.totalLength !== d.totalLength) {
        ctx.totalLength = d.totalLength;
        ctx.fmtTotalLength = utils.fmtsize(d.totalLength);
      }
      if (ctx.completedLength !== d.completedLength) {
        ctx.completedLength = d.completedLength;
        ctx.fmtCompletedLength = utils.fmtsize(d.completedLength);
      }
      if (ctx.uploadLength !== d.uploadength) {
        ctx.uploadLength = d.uploadlength;
        ctx.fmtUploadLength = utils.fmtsize(d.uploadLength);
      }
      if (ctx.pieceLength !== d.pieceLength) {
        ctx.pieceLength = d.pieceLength;
        ctx.fmtPieceLength = utils.fmtsize(d.pieceLength);
      }
      if (ctx.downloadSpeed !== d.downloadSpeed) {
        ctx.downloadSpeed =  d.downloadSpeed;
        ctx.fmtDownloadSpeed = utils.fmtspeed(d.downloadSpeed);
      }
      if (ctx.uploadSpeed !== d.uploadSpeed) {
        ctx.uploadSpeed =  d.uploadSpeed;
        ctx.fmtUploadSpeed = utils.fmtspeed(d.uploadSpeed);
      }
    }

    var dlName;
    var files = d.files;
    if (files) {
      var cfiles = ctx.files;
      for (var i = 0; i < files.length; ++i) {
        var cfile = cfiles[i] || (cfiles[i] = {});
        var file = files[i];
        if (file.path !== cfile.path) {
          cfile.path = file.path;
          cfile.length = file.length;
          cfile.fmtLength = utils.fmtsize(file.length);
          cfile.relpath = file.path.replace(re_slashes, slash);
          if (!cfile.relpath) {
            cfile.relpath = (file.uris && file.uris[0] && file.uris[0].uri) || "Unknown";
          }
          else if (!cfile.relpath.startsWith("[")) { // METADATA
            cfile.relpath = cfile.relpath.substr(ctx.dir.length + 1);
          }
        }
      }
      cfiles.length = files.length;
      if (cfiles.length) {
        dlName = cfiles[0].relpath;
      }
    }
    else {
      delete ctx.files;
    }

    var btName;
    if (d.bittorrent) {
      btName = d.bittorrent.info && d.bittorrent.info.name;
      ctx.bittorrent = true;
    }
    else {
      delete ctx.bittorrent;
    }

    ctx.name = btName || dlName || "Unknown";
    ctx.metadata = ctx.name.startsWith("[METADATA]");
    if (ctx.metadata) {
      ctx.name = ctx.name.substr(10);
    }

    return ctx;
  };

  scope.canRestart = function(d) {
    if (['active', 'paused'].indexOf(d.status) == -1
        && !d.bittorrent)
      return true;

    return false;
  };

  scope.hasStatus = function hasStatus(d, status) {
    if (_.isArray(status)) {
      if (status.length == 0) return false;
      return hasStatus(d, status[0]) || hasStatus(d, status.slice(1));
    }
    else {
      return d.status == status;
    }
  };

  // get time left for the download with
  // current download speed,
  // could be smarter by different heuristics
  scope.getEta = function(d) {
    return (d.totalLength-d.completedLength) / d.downloadSpeed;
  }

  scope.getProgressClass = function(d) {
    switch (d.status) {
      case "paused":
        return "progress-info";
      case "error":
        return "progress-danger";
      case "removed":
        return "progress-warning";
      case "active":
        return "progress-active";
      case "complete":
        return "progress-success";
      default:
        return "";
    }
  };

  // gets the progress in percentages
  scope.getProgress = function(d) {
    var percentage = (d.completedLength / d.totalLength)*100 || 0;
    percentage = percentage.toFixed(2);
    if(!percentage) percentage = 0;

    return percentage;
  };

  // gets the type for the download as classified by the aria2 rpc calls
  scope.getType = function(d) {
    var type = d.status;
    if (type == "paused") type = "waiting";
    if (["error", "removed", "complete"].indexOf(type) != -1)
      type = "stopped";
    return type;
  };

  scope.showSettings = function(d) {
    var type = scope.getType(d)
      , settings = {};

    rpc.once('getOption', [d.gid], function(data) {
      var vals = data[0];

      for (var i in fsettings) {
        if (type == 'active' && activeInclude.indexOf(i) == -1) continue;

        if (type == 'waiting' && waitingExclude.indexOf(i) != -1) continue;

        settings[i] = fsettings[i];
        settings[i].val = vals[i] || settings[i].val;
      }

      modals.invoke('settings', settings, scope.name + ' settings', function(settings) {
        var sets = {};
        for (var i in settings) { sets[i] = settings[i].val };

        rpc.once('changeOption', [d.gid, sets]);
      });
    });

    return false;
  }

}]);
<|endoftext|>


<commit_before>const LifeforcePlugin = require("../utils/LifeforcePlugin.js");

let messageQueue = [];
let readySend = false;
let readyTimer = null;

class TelegramBot extends LifeforcePlugin {
  constructor(restifyserver, logger, name) {
    super(restifyserver, logger, name);
    this.apiMap = [
      {
        path: "/api/telegram/log",
        type: "post",
        handler: handleTelegramLog
      }
    ];

    const TGBot = require("node-telegram-bot-api");
    this.bot = new TGBot(this.config.telegram.token, { polling: true });

    this.bot.on("message", msg => {
      const chatId = msg.chat.id;

      // send a message to the chat acknowledging receipt of their message
      this.bot.sendMessage(
        chatId,
        "Hello! Here is the information about your chat message: \n" +
          JSON.stringify(msg.chat)
      );
    });

    if (this.config.telegram.logger) {
      var messageSend = function(message) {
        messageQueue.push(message);
      };

      logger.registerCallback(messageSend.bind(this));

      readyTimer = setInterval(() => {
        if (messageQueue.length > 0) {
          this.bot.sendMessage(
            this.config.telegram.chatid,
            messageQueue.join("\n")
          );
          messageQueue = [];
        }
      }, 10000);
    }
  }
}

function handleTelegramLog(req, res, next) {
  if (req.body) {
    var payload = req.body + "";

    // send a message to the chat acknowledging receipt of their message
    this.bot.sendMessage(this.config.telegram.chatid, payload);
    res.send(200, { error: false });
  } else {
    res.send(400);
  }
}

module.exports = TelegramBot;
<commit_msg>Allow file upload via telegram<commit_after>const LifeforcePlugin = require("../utils/LifeforcePlugin.js");
const fs = require("fs");

let messageQueue = [];
let readySend = false;
let readyTimer = null;

class TelegramBot extends LifeforcePlugin {
  constructor(restifyserver, logger, name) {
    super(restifyserver, logger, name);
    this.apiMap = [
      {
        path: "/api/telegram/log",
        type: "post",
        handler: handleTelegramLog
      }
    ];

    const TGBot = require("node-telegram-bot-api");
    this.bot = new TGBot(this.config.telegram.token, { polling: true });

    this.bot.on("message", msg => {
      const chatId = msg.chat.id;

      let response =
        "Hello! Here is the information about your chat message: \n" +
        JSON.stringify(msg.chat);

      if (msg.document) {
        response = "You sent a file: " + JSON.stringify(msg.document);
        const filepath = this.config.telegram.filepath;

        this.bot
          .downloadFile(msg.document.file_id, filepath)
          .then(newpath => {
            let savepath = filepath + "/" + msg.document.file_name;

            // Take the file from newpath and move it into a better location
            fs.rename(newpath, savepath, done => {
              this.bot.sendMessage(chatId, "Saved " + msg.document.file_id);
            });
          })
          .catch(err => {
            this.bot.sendMessage(
              chatId,
              "Unable to save " + msg.document.file_id + ", err: " + err.message
            );
          });
      }

      // send a message to the chat acknowledging receipt of their message
      this.bot.sendMessage(chatId, response);
    });

    if (this.config.telegram.logger) {
      var messageSend = function(message) {
        messageQueue.push(message);
      };

      logger.registerCallback(messageSend.bind(this));

      readyTimer = setInterval(() => {
        if (messageQueue.length > 0) {
          this.bot.sendMessage(
            this.config.telegram.chatid,
            messageQueue.join("\n")
          );
          messageQueue = [];
        }
      }, 10000);
    }
  }
}

function handleTelegramLog(req, res, next) {
  if (req.body) {
    var payload = req.body + "";

    // send a message to the chat acknowledging receipt of their message
    this.bot.sendMessage(this.config.telegram.chatid, payload);
    res.send(200, { error: false });
  } else {
    res.send(400);
  }
}

module.exports = TelegramBot;
<|endoftext|>


<commit_before>/**   For testing asynchronous json downloading, here we have a tiny http server.
 *
 *    For every request (regardless f path) it responds with the json [0,1,2,3,4,5,6,7,8,9]
 *    but writing out each number one at a time.
 *
 *    You can start this server up and visit it in a browser to see the numbers stream in.
 */

require('color');

function startServer( port, grunt ) {

   "use strict";

   var JSON_MIME_TYPE = "application/octet-stream";
   var verboseLog = grunt? grunt.verbose.ok : console.log,
       errorLog = grunt? grunt.log.error : console.error;

   function echoBackBody(req, res) {
      req.pipe(res);
   }

   function echoBackHeadersAsBodyJson(req, res) {
      res.end(JSON.stringify(req.headers));
   }

   function echoBackHeadersAsHeaders(req, res) {
      for( var name in req.headers ) {
         res.set(name, req.headers[name]);
      }
      res.end('{"see":"headers", "for":"content"}');
   }

   function replyWithTenSlowNumbers(_req, res) {
      sendJsonHeaders(res);

      var NUMBER_INTERVAL = 250;
      var MAX_NUMBER = 9;

      verboseLog(
         'slow number server: will write numbers 0 ..' +
         String(MAX_NUMBER).blue +
         ' out as a json array at a rate of one per',
         String(NUMBER_INTERVAL).blue + 'ms'
      );

      res.write('[\n');

      var curNumber = 0;

      var inervalId = setInterval(function () {

         res.write(String(curNumber));

         if (curNumber == MAX_NUMBER) {

            res.end(']');
            clearInterval(inervalId);
            verboseLog('slow number server: finished writing out');
         } else {
            res.write(',\n');
            curNumber++;
         }

      }, NUMBER_INTERVAL);
   }

   function replyWithInvalidJson(req, res) {
      res.end('{{');
   }

   function serve404Json(req, res) {
      // our little REST endpoint with errors:
      res.status(404).send(JSON.stringify(
          {
               "found":"false",
               "errorMessage":"was not found"
          }
      ));
   }

   function replyWithStaticJson(req, res) {
      sendJsonHeaders(res);

      if( !req.url ) {
         throw new Error('no url given');
      }

      var filename = 'test/json/' + req.params.name + '.json';

      verboseLog('will respond with contents of file ' + filename);

      require('fs').createReadStream(filename)
         .on('error', function(err){
             errorLog('could not read static file ' + filename +
                      ' ' + err);
         }).pipe(res);

   }

   function sendJsonHeaders(res) {

      res.setHeader("Content-Type", JSON_MIME_TYPE);
      res.writeHead(200);
   }

   function twoHundredItems(_req, res) {

      var TIME_BETWEEN_RECORDS = 40;
      // 80 records but only every other one has a URL:
      var NUMBER_OF_RECORDS = 200;

      res.write('{"data": [');

      var i = 0;

      var inervalId = setInterval(function () {

            res.write(JSON.stringify({
               "id": i,
               "url": "http://localhost:4444/item/" + i,
               // let's get some entropy in here for gzip:
               "number1": Math.random(),
               "number2": Math.random(),
               "number3": Math.random(),
               "number4": Math.random()
            }));

         if (i == NUMBER_OF_RECORDS) {

            res.end(']}');

            clearInterval(inervalId);

            console.log('db server: finished writing to stream');
         } else {
            res.write(',');
         }

         i++;

      }, TIME_BETWEEN_RECORDS);
   }

   function replyWithTenSlowNumbersGzipped(req, serverResponse){

      // request out non-gzipped stream and re-serve gzipped
      require('http').get({
                        host: 'localhost',
                        path: '/twoHundredItems',
                        port: port })
      .on('response', function(clientResponse){

         var zlib = require('zlib');

         //res.writeHead(200, { 'content-encoding': 'gzip' });

         serverResponse.setHeader("content-type", JSON_MIME_TYPE);
         serverResponse.setHeader("content-encoding", 'gzip');
         serverResponse.writeHead(200);

         clientResponse.pipe(zlib.createGzip({
            flush: zlib.Z_SYNC_FLUSH
         })).pipe(serverResponse);

       });
   }

   function makeApp() {
      var express = require('express'),
          app = express();

      app.get(    '/echoBackBody',              function(req, res){ res.end("POST/PUT/PATCH here, don't GET")});
      app.post(   '/echoBackBody',              echoBackBody);
      app.put(    '/echoBackBody',              echoBackBody);
      app.patch(  '/echoBackBody',              echoBackBody);
      app.get(    '/echoBackHeadersAsBodyJson', echoBackHeadersAsBodyJson);
      app.get(    '/echoBackHeadersAsHeaders',  echoBackHeadersAsHeaders);
      app.get(    '/static/json/:name.json',    replyWithStaticJson);
      app.get(    '/tenSlowNumbers',            replyWithTenSlowNumbers);
      app.get(    '/twoHundredItems',           twoHundredItems);
      app.get(    '/gzippedTwoHundredItems',    replyWithTenSlowNumbersGzipped);
      app.get(    '/invalidJson',               replyWithInvalidJson);
      app.get(    '/404json',                   serve404Json);

      return app;
   }

   makeApp().listen(port);

   verboseLog('streaming source server started on port'.green, String(port).blue);
}

function exportApi(){

   var server;

   module.exports.start = function(port, grunt){
      server = startServer(port, grunt);
   };
   module.exports.stop = function(){
      server.close();
   };
}

exportApi();
<commit_msg>rename function<commit_after>/**   For testing asynchronous json downloading, here we have a tiny http server.
 *
 *    For every request (regardless f path) it responds with the json [0,1,2,3,4,5,6,7,8,9]
 *    but writing out each number one at a time.
 *
 *    You can start this server up and visit it in a browser to see the numbers stream in.
 */

require('color');

function startServer( port, grunt ) {

   "use strict";

   var JSON_MIME_TYPE = "application/octet-stream";
   var verboseLog = grunt? grunt.verbose.ok : console.log,
       errorLog = grunt? grunt.log.error : console.error;

   function echoBackBody(req, res) {
      req.pipe(res);
   }

   function echoBackHeadersAsBodyJson(req, res) {
      res.end(JSON.stringify(req.headers));
   }

   function echoBackHeadersAsHeaders(req, res) {
      for( var name in req.headers ) {
         res.set(name, req.headers[name]);
      }
      res.end('{"see":"headers", "for":"content"}');
   }

   function replyWithTenSlowNumbers(_req, res) {
      sendJsonOkHeaders(res);

      var NUMBER_INTERVAL = 250;
      var MAX_NUMBER = 9;

      verboseLog(
         'slow number server: will write numbers 0 ..' +
         String(MAX_NUMBER).blue +
         ' out as a json array at a rate of one per',
         String(NUMBER_INTERVAL).blue + 'ms'
      );

      res.write('[\n');

      var curNumber = 0;

      var inervalId = setInterval(function () {

         res.write(String(curNumber));

         if (curNumber == MAX_NUMBER) {

            res.end(']');
            clearInterval(inervalId);
            verboseLog('slow number server: finished writing out');
         } else {
            res.write(',\n');
            curNumber++;
         }

      }, NUMBER_INTERVAL);
   }

   function replyWithInvalidJson(req, res) {
      res.end('{{');
   }

   function serve404Json(req, res) {
      // our little REST endpoint with errors:
      res.status(404).send(JSON.stringify(
          {
               "found":"false",
               "errorMessage":"was not found"
          }
      ));
   }

   function replyWithStaticJson(req, res) {
      sendJsonOkHeaders(res);

      if( !req.url ) {
         throw new Error('no url given');
      }

      var filename = 'test/json/' + req.params.name + '.json';

      verboseLog('will respond with contents of file ' + filename);

      require('fs').createReadStream(filename)
         .on('error', function(err){
             errorLog('could not read static file ' + filename +
                      ' ' + err);
         }).pipe(res);

   }

   function sendJsonOkHeaders(res) {

      res.setHeader("Content-Type", JSON_MIME_TYPE);
      res.writeHead(200);
   }

   function twoHundredItems(_req, res) {

      var TIME_BETWEEN_RECORDS = 40;
      // 80 records but only every other one has a URL:
      var NUMBER_OF_RECORDS = 200;

      res.write('{"data": [');

      var i = 0;

      var inervalId = setInterval(function () {

            res.write(JSON.stringify({
               "id": i,
               "url": "http://localhost:4444/item/" + i,
               // let's get some entropy in here for gzip:
               "number1": Math.random(),
               "number2": Math.random(),
               "number3": Math.random(),
               "number4": Math.random()
            }));

         if (i == NUMBER_OF_RECORDS) {

            res.end(']}');

            clearInterval(inervalId);

            console.log('db server: finished writing to stream');
         } else {
            res.write(',');
         }

         i++;

      }, TIME_BETWEEN_RECORDS);
   }

   function replyWithTenSlowNumbersGzipped(req, serverResponse){

      // request out non-gzipped stream and re-serve gzipped
      require('http').get({
                        host: 'localhost',
                        path: '/twoHundredItems',
                        port: port })
      .on('response', function(clientResponse){

         var zlib = require('zlib');

         //res.writeHead(200, { 'content-encoding': 'gzip' });

         serverResponse.setHeader("content-type", JSON_MIME_TYPE);
         serverResponse.setHeader("content-encoding", 'gzip');
         serverResponse.writeHead(200);

         clientResponse.pipe(zlib.createGzip({
            flush: zlib.Z_SYNC_FLUSH
         })).pipe(serverResponse);

       });
   }

   function makeApp() {
      var express = require('express'),
          app = express();

      app.get(    '/echoBackBody',              function(req, res){ res.end("POST/PUT/PATCH here, don't GET")});
      app.post(   '/echoBackBody',              echoBackBody);
      app.put(    '/echoBackBody',              echoBackBody);
      app.patch(  '/echoBackBody',              echoBackBody);
      app.get(    '/echoBackHeadersAsBodyJson', echoBackHeadersAsBodyJson);
      app.get(    '/echoBackHeadersAsHeaders',  echoBackHeadersAsHeaders);
      app.get(    '/static/json/:name.json',    replyWithStaticJson);
      app.get(    '/tenSlowNumbers',            replyWithTenSlowNumbers);
      app.get(    '/twoHundredItems',           twoHundredItems);
      app.get(    '/gzippedTwoHundredItems',    replyWithTenSlowNumbersGzipped);
      app.get(    '/invalidJson',               replyWithInvalidJson);
      app.get(    '/404json',                   serve404Json);

      return app;
   }

   makeApp().listen(port);

   verboseLog('streaming source server started on port'.green, String(port).blue);
}

function exportApi(){

   var server;

   module.exports.start = function(port, grunt){
      server = startServer(port, grunt);
   };
   module.exports.stop = function(){
      server.close();
   };
}

exportApi();
<|endoftext|>


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

    module("New");

    // Get the number of "own" properties in an object.
    var getPropSize = function (obj) {
        var size = 0,
            key;
        for (key in obj) {
            if ({}.hasOwnProperty.call(obj, key)) size++;
        }
        return size;
    };

    test("new syringe", 2, function () {
        var syr = Syringe.create();
        equal('object', typeof syr, 'object should be returned.');
        equal(getPropSize(syr.get()), 0, 'object should be empty.');
    });

    test("new syringe with props map", 2, function () {
        var syr = Syringe.create({
            'data': 'somedata'
        });
        equal(typeof syr, 'object', 'object should be returned.');
        equal(getPropSize(syr.get()), 1, 'object should have one property.');
    });

    test("new syringe with verified props map", 1, function () {
        var syr = Syringe.create({
            'data': 'somedata'
        });
        equal(syr.get('data'), 'somedata', 'data should be set correctly.');
    });

    test("new syringe with verified deep props map", 1, function () {
        var syr = Syringe.create({
            'data': {
                'first': {
                    'second': 'done'
                }
            }
        });
        equal(syr.get('data.first.second'), 'done', 'deep data should be set correctly.');
    });

    module("Add");

    test("add a new shallow property", 1, function () {
        var syr = Syringe.create();
        syr.add('data', 'ok')
        equal(syr.get('data'), 'ok', 'shallow data should be set correctly.');
    });

    test("add a new deep property", 1, function () {
        var syr = Syringe.create();
        syr.add('data.first.second', 'done')
        equal(syr.get('data.first.second'), 'done', 'deep data should be set correctly.');
    });

    test("add a new property map with deep and shallow items", 2, function () {
        var syr = Syringe.create();
        syr.add({
            'data': {
                'first': {
                    'second': 'done'
                }
            },
            'data2': {}
        })
        equal(syr.get('data.first.second'), 'done', 'deep data should be set correctly.');
        equal(typeof syr.get('data2'), 'object', 'object should be returned.');
    });

    test("add deep method with binding", 1, function () {
        var syr = Syringe.create();
        syr.add({
            'first': {
                'second': 'done'
            }
        });
        syr.add('func', function (data, msg) {
            return msg + ' - ' + data;
        }, ['first.second']);
        equal(syr.exec('func', ['hello world']), 'hello world - done', 'data from bound method should be returned.');
    });

    test("add deep constructor function with binding", 1, function () {
        var syr = Syringe.create();
        syr.add({
            'first': {
                'second': 'done'
            }
        });

        var Func = function (data, msg) {
            this.data = data;
            this.msg = msg;
        }

        Func.prototype.say = function () {
            return this.msg + ' - ' + this.data;
        };

        syr.add('Func', Func, ['first.second']);

        var F = (syr.get('Func'));
        var f = new F('hello world');

        equal('hello world - done', f.say(), 'data from bound method should be returned.');
    });

    module("Set");

    test("set shallow props of non-existant item", 1, function () {
        var syr = Syringe.create();
        raises(function () {
            syr.set('data', 'ok');
        },
        Error, "must throw error to pass.");
    });

    test("set shallow props of existing item", 1, function () {
        var syr = Syringe.create();
        syr.add('data', false);
        syr.set('data', 'ok');
        equal(syr.get('data'), 'ok', 'shallow data should be set correctly.');
    });

    test("set deep props", 1, function () {
        var syr = Syringe.create({
            'data': {
                'first': {
                    'second': 'done'
                }
            }
        });
        syr.set('data.first.second', 'ok');
        equal(syr.get('data.first.second'), 'ok', 'deep data should be set correctly.');
    });

    module("Remove");

    test("remove shallow props", 2, function () {
        var syr = Syringe.create({
            'data': {
                'first': {
                    'second': 'done'
                },
                'other': {}
            },
            'data2': {}
        });
        syr.remove('data');
        strictEqual(syr.get('data'), false, 'targeted data should be removed.');
        equal(typeof syr.get('data2'), 'object', 'siblings of targeted data should not be removed.');
    });

    test("remove deep props", 3, function () {
        var syr = Syringe.create({
            'data': {
                'first': {
                    'second': 'done'
                },
                'other': {}
            },
            'data2': {}
        });
        syr.remove('data.first');
        strictEqual(syr.get('data.first'), false, 'targeted deep data should be removed.');
        equal(typeof syr.get('data.other'), 'object', 'siblings of targeted data should not be removed.');
        equal(typeof syr.get('data2'), 'object', 'siblings of ancestor of targeted data should not be removed.');
    });

    module("Bind");

    test("bind an anonymous method, no context", 1, function () {
        var syr = Syringe.create({
            'data': 'done'
        });
        var f = syr.bind(['data'], function (data, args) {
            return 'process is ' + data;
        });
        equal(f(), 'process is done', 'bound function returns injected data.');
    });

    test("bind an anonymous method, with context", 1, function () {
        var syr = Syringe.create({
            'data': 'done'
        });

        var f = syr.bind(['data'], function (data, args) {
            return this.example + ' process is ' + data;
        }, {
            'example': 'test'
        });

        equal(f(), 'test process is done', 'bound function returns injected data.');
    });

    test("bind a named method, with no context", 1, function () {
        var syr = Syringe.create({
            'data': 'done'
        });

        syr.bind('f', ['data'], function (data, args) {
            return 'process is ' + data;
        });

        equal(f(), 'process is done', 'bound function returns injected data.');
    });

    test("bind a named method, with context", 1, function () {
        var syr = Syringe.create({
            'data': 'done'
        });

        var obj = {
            'first': {}
        };
        syr.bind('x.f', ['data'], function (data, args) {
            return 'process is ' + data;
        }, obj.first);

        equal(obj.first.x.f(), 'process is done', 'bound function returns injected data.');
    });

    module("Exec");

    test("directly execute a registry method", 1, function () {
        var syr = Syringe.create({
            'f': function (arg1, arg2) {
                return this.example + ' ' + arg1 + ' is ' + arg2
            }
        });

        equal(syr.exec('f', ['process', 'done'], {
            'example': 'test'
        }), 'test process is done', 'executed function returns data.');
    });

    module("Wrap");

    test("wrap an existing method", 1, function () {
        var syr = Syringe.create({
            'data': 'foo',
            'data2': 'bar'
        });

        var f = syr.bind(['data', 'data2'], function (d, d2, arg, arg2) {
            return d + ' ' + d2 + ' ' + arg + ' ' + arg2;
        });

        var f2 = function (fn, arg) {
            return fn(arg, this.example);
        };

        f2 = syr.wrap(f, f2, {
            'example': 'test'
        });

        equal(f2('woo'), 'foo bar woo test', 'wrapped function returns injected data and expected arguments.');
    });

    module("Copy");

    test("copy an existing method", 1, function () {
        var syr = Syringe.create({
            'data': 'foo',
            'data2': 'bar'
        });

        var f = syr.bind(['data'], function (d) {
            return d;
        });

        var f2 = function (d) {
            return d;
        };

        f2 = syr.copy(['data2'], f);

        equal(f2('woo'), 'bar', 'bound function returns injected data.');
    });

});<commit_msg>Async test added for .fetch()<commit_after>$(document).ready(function () {

    module("New");

    // Get the number of "own" properties in an object.
    var getPropSize = function (obj) {
        var size = 0,
            key;
        for (key in obj) {
            if ({}.hasOwnProperty.call(obj, key)) size++;
        }
        return size;
    };

    test("new syringe", 2, function () {
        var syr = Syringe.create();
        equal('object', typeof syr, 'object should be returned.');
        equal(getPropSize(syr.get()), 0, 'object should be empty.');
    });

    test("new syringe with props map", 2, function () {
        var syr = Syringe.create({
            'data': 'somedata'
        });
        equal(typeof syr, 'object', 'object should be returned.');
        equal(getPropSize(syr.get()), 1, 'object should have one property.');
    });

    test("new syringe with verified props map", 1, function () {
        var syr = Syringe.create({
            'data': 'somedata'
        });
        equal(syr.get('data'), 'somedata', 'data should be set correctly.');
    });

    test("new syringe with verified deep props map", 1, function () {
        var syr = Syringe.create({
            'data': {
                'first': {
                    'second': 'done'
                }
            }
        });
        equal(syr.get('data.first.second'), 'done', 'deep data should be set correctly.');
    });

    module("Add");

    test("add a new shallow property", 1, function () {
        var syr = Syringe.create();
        syr.add('data', 'ok')
        equal(syr.get('data'), 'ok', 'shallow data should be set correctly.');
    });

    test("add a new deep property", 1, function () {
        var syr = Syringe.create();
        syr.add('data.first.second', 'done')
        equal(syr.get('data.first.second'), 'done', 'deep data should be set correctly.');
    });

    test("add a new property map with deep and shallow items", 2, function () {
        var syr = Syringe.create();
        syr.add({
            'data': {
                'first': {
                    'second': 'done'
                }
            },
            'data2': {}
        })
        equal(syr.get('data.first.second'), 'done', 'deep data should be set correctly.');
        equal(typeof syr.get('data2'), 'object', 'object should be returned.');
    });

    test("add deep method with binding", 1, function () {
        var syr = Syringe.create();
        syr.add({
            'first': {
                'second': 'done'
            }
        });
        syr.add('func', function (data, msg) {
            return msg + ' - ' + data;
        }, ['first.second']);
        equal(syr.exec('func', ['hello world']), 'hello world - done', 'data from bound method should be returned.');
    });

    test("add deep constructor function with binding", 1, function () {
        var syr = Syringe.create();
        syr.add({
            'first': {
                'second': 'done'
            }
        });

        var Func = function (data, msg) {
            this.data = data;
            this.msg = msg;
        }

        Func.prototype.say = function () {
            return this.msg + ' - ' + this.data;
        };

        syr.add('Func', Func, ['first.second']);

        var F = (syr.get('Func'));
        var f = new F('hello world');

        equal('hello world - done', f.say(), 'data from bound method should be returned.');
    });

    module("Set");

    test("set shallow props of non-existant item", 1, function () {
        var syr = Syringe.create();
        raises(function () {
            syr.set('data', 'ok');
        },
        Error, "must throw error to pass.");
    });

    test("set shallow props of existing item", 1, function () {
        var syr = Syringe.create();
        syr.add('data', false);
        syr.set('data', 'ok');
        equal(syr.get('data'), 'ok', 'shallow data should be set correctly.');
    });

    test("set deep props", 1, function () {
        var syr = Syringe.create({
            'data': {
                'first': {
                    'second': 'done'
                }
            }
        });
        syr.set('data.first.second', 'ok');
        equal(syr.get('data.first.second'), 'ok', 'deep data should be set correctly.');
    });

    module("Remove");

    test("remove shallow props", 2, function () {
        var syr = Syringe.create({
            'data': {
                'first': {
                    'second': 'done'
                },
                'other': {}
            },
            'data2': {}
        });
        syr.remove('data');
        strictEqual(syr.get('data'), false, 'targeted data should be removed.');
        equal(typeof syr.get('data2'), 'object', 'siblings of targeted data should not be removed.');
    });

    test("remove deep props", 3, function () {
        var syr = Syringe.create({
            'data': {
                'first': {
                    'second': 'done'
                },
                'other': {}
            },
            'data2': {}
        });
        syr.remove('data.first');
        strictEqual(syr.get('data.first'), false, 'targeted deep data should be removed.');
        equal(typeof syr.get('data.other'), 'object', 'siblings of targeted data should not be removed.');
        equal(typeof syr.get('data2'), 'object', 'siblings of ancestor of targeted data should not be removed.');
    });

    module("Bind");

    test("bind an anonymous method, no context", 1, function () {
        var syr = Syringe.create({
            'data': 'done'
        });
        var f = syr.bind(['data'], function (data, args) {
            return 'process is ' + data;
        });
        equal(f(), 'process is done', 'bound function returns injected data.');
    });

    test("bind an anonymous method, with context", 1, function () {
        var syr = Syringe.create({
            'data': 'done'
        });

        var f = syr.bind(['data'], function (data, args) {
            return this.example + ' process is ' + data;
        }, {
            'example': 'test'
        });

        equal(f(), 'test process is done', 'bound function returns injected data.');
    });

    test("bind a named method, with no context", 1, function () {
        var syr = Syringe.create({
            'data': 'done'
        });

        syr.bind('f', ['data'], function (data, args) {
            return 'process is ' + data;
        });

        equal(f(), 'process is done', 'bound function returns injected data.');
    });

    test("bind a named method, with context", 1, function () {
        var syr = Syringe.create({
            'data': 'done'
        });

        var obj = {
            'first': {}
        };
        syr.bind('x.f', ['data'], function (data, args) {
            return 'process is ' + data;
        }, obj.first);

        equal(obj.first.x.f(), 'process is done', 'bound function returns injected data.');
    });

    module("Exec");

    test("directly execute a registry method", 1, function () {
        var syr = Syringe.create({
            'f': function (arg1, arg2) {
                return this.example + ' ' + arg1 + ' is ' + arg2
            }
        });

        equal(syr.exec('f', ['process', 'done'], {
            'example': 'test'
        }), 'test process is done', 'executed function returns data.');
    });

    module("Wrap");

    test("wrap an existing method", 1, function () {
        var syr = Syringe.create({
            'data': 'foo',
            'data2': 'bar'
        });

        var f = syr.bind(['data', 'data2'], function (d, d2, arg, arg2) {
            return d + ' ' + d2 + ' ' + arg + ' ' + arg2;
        });

        var f2 = function (fn, arg) {
            return fn(arg, this.example);
        };

        f2 = syr.wrap(f, f2, {
            'example': 'test'
        });

        equal(f2('woo'), 'foo bar woo test', 'wrapped function returns injected data and expected arguments.');
    });

    module("Copy");

    test("copy an existing method", 1, function () {
        var syr = Syringe.create({
            'data': 'foo',
            'data2': 'bar'
        });

        var f = syr.bind(['data'], function (d) {
            return d;
        });

        var f2 = function (d) {
            return d;
        };

        f2 = syr.copy(['data2'], f);

        equal(f2('woo'), 'bar', 'bound function returns injected data.');
    });

    module("Fetch");

    asyncTest("fetch and bind an item", 1, function() {

        var syr = Syringe.create();

        syr.fetch({
            'us': {
                'path': 'http://underscorejs.org/underscore-min.js',
                'bind': '_'
            }
        }, function () {
            ok((typeof this.get('us') === 'function'), "asynch data fetched and bound correctly" );
            start();
        });
    });

});<|endoftext|>