<commit_before>/* global window, document, coge*/
var coge = window.coge = (function(ns) {
    ns.utils = {
        ascending: function(a, b) {
            return a  b ? 1 : 0;
        },

        descending: function(a, b) {
            return a > b ? -1 : a < b ? 1 : 0;
        },

        open: function(link) {
            window.open(link, "_self");
        },

        shuffle: function(array) {
            var copy = array.slice(0), n = array.length, tmp, i;

            while (n) {
                i = Math.floor(Math.random() * n--);
                tmp = copy[n];
                copy[n] = copy[i];
                copy[i] = tmp;
            };

            return copy;
        },

        post: function(action, params) {
            var key,
                input,
                form = document.createElement("form");

            form.method = "post";
            form.action = action;
            form.setAttribute("target", "_blank");
            form.setAttribute("enctype", "multipart/form-data");

            for(key in params) {
                if (params.hasOwnProperty(key)) {
                    input = document.createElement("textarea");
                    input.name = key;
                    input.value = params[key];
                    form.appendChild(input);
                }
            }

            form.submit("action");
        },

        toPrettyDuration: function(seconds) {
            var fields = [
                [parseInt((seconds / 86400).toFixed(1), 10), " day"],
                [parseInt((seconds / 3600).toFixed(1), 10) % 24, " hour"],
                [parseInt(((seconds / 60) % 60).toFixed(1), 10), " minute"],
                [(seconds % 60).toFixed(2), " second"]
            ];

            return fields.filter(function(item) { return item[0] !== 0; })
                .map(function(item) {
                    var word = (item[0] > 1) ? item[1] + "s" : item[1];
                    return item[0] + word;
                })
                .join(", ");
        },

        log10: function(value) {
            return Math.log(value) / Math.log(10);
        },

        ucfirst: function(string) {
            return string.charAt(0).toUpperCase() + string.slice(1);
        },

        getURLParameters: function () { // returns object of query params
            var match,
                pl     = /\+/g,  // Regex for replacing addition symbol with a space
                search = /([^&=]+)=?([^&]*)/g,
                decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
                query  = window.location.search.substring(1);

            var urlParams = {};
            while (match = search.exec(query))
               urlParams[decode(match[1])] = decode(match[2]);
            return urlParams;
        },

        toQueryString: function(obj, prefix) { // accepts an object of query params
		    var str = [];
		    for (var p in obj) {
		        if (obj.hasOwnProperty(p)) {
		        	var k = prefix ? prefix + "[" + p + "]" : p, v = obj[p];
		        	str.push(typeof v == "object" ?
		        			serialize(v, k) :
		        			encodeURIComponent(k) + "=" + encodeURIComponent(v));
		        }
		    }
		    return str.join("&");
    	},

    	objToString: function(obj) {
    	    var str = '
';
    	    for (var p in obj) {
    	        if (obj.hasOwnProperty(p)) {
    	            str += '     ' + p + ': ' + obj[p] + '
';
    	        }
    	    }
    	    return str;
    	}
    };

    return ns;
})(coge || {});
<commit_msg>Non-functional change<commit_after>/* global window, document, coge*/
var coge = window.coge = (function(ns) {
    ns.utils = {
        ascending: function(a, b) {
            return a  b ? 1 : 0;
        },

        descending: function(a, b) {
            return a > b ? -1 : a < b ? 1 : 0;
        },

        open: function(link) {
            window.open(link, "_self");
        },

        shuffle: function(array) {
            var copy = array.slice(0), n = array.length, tmp, i;

            while (n) {
                i = Math.floor(Math.random() * n--);
                tmp = copy[n];
                copy[n] = copy[i];
                copy[i] = tmp;
            };

            return copy;
        },

        post: function(action, params) {
            var key,
                input,
                form = document.createElement("form");

            form.method = "post";
            form.action = action;
            form.setAttribute("target", "_blank");
            form.setAttribute("enctype", "multipart/form-data");

            for(key in params) {
                if (params.hasOwnProperty(key)) {
                    input = document.createElement("textarea");
                    input.name = key;
                    input.value = params[key];
                    form.appendChild(input);
                }
            }

            form.submit("action");
        },

        toPrettyDuration: function(seconds) {
            var fields = [
                [parseInt((seconds / 86400).toFixed(1), 10), " day"],
                [parseInt((seconds / 3600).toFixed(1), 10) % 24, " hour"],
                [parseInt(((seconds / 60) % 60).toFixed(1), 10), " minute"],
                [(seconds % 60).toFixed(2), " second"]
            ];

            return fields.filter(function(item) { return item[0] !== 0; })
                .map(function(item) {
                    var word = (item[0] > 1) ? item[1] + "s" : item[1];
                    return item[0] + word;
                })
                .join(", ");
        },

        log10: function(value) {
            return Math.log(value) / Math.log(10);
        },

        ucfirst: function(string) {
            return string.charAt(0).toUpperCase() + string.slice(1);
        },

        getURLParameters: function () { // returns object of query params
            var match,
                pl     = /\+/g,  // Regex for replacing addition symbol with a space
                search = /([^&=]+)=?([^&]*)/g,
                decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
                query  = window.location.search.substring(1);

            var urlParams = {};
            while (match = search.exec(query))
               urlParams[decode(match[1])] = decode(match[2]);
            return urlParams;
        },

        toQueryString: function(obj, prefix) { // accepts an object of query params
		    var str = [];
		    for (var p in obj) {
		        if (obj.hasOwnProperty(p)) {
		        	var k = prefix ? prefix + "[" + p + "]" : p, v = obj[p];
		        	str.push(typeof v == "object" ?
		        			serialize(v, k) :
		        			encodeURIComponent(k) + "=" + encodeURIComponent(v));
		        }
		    }
		    return str.join("&");
    	},

    	objToString: function(obj) {
    	    var str = '
';
    	    for (var p in obj) {
    	        if (obj.hasOwnProperty(p)) {
    	            str += '     ' + p + ': ' + obj[p] + '
';
    	        }
    	    }
    	    return str;
    	},
    };

    return ns;
})(coge || {});
<|endoftext|>


<commit_before>$(function(){
  var timerID;
  timelineAllLoad();
  timerID = setInterval('timelineDifferenceLoad()', 15000);

  $('#gorgon-submit').click( function() {
    var Body = $('#gorgon-textarea-body').val();
    var Csrf = $(this).attr('data-post-csrftoken');
    var baseUrl = $(this).attr('data-post-baseurl');
    $.ajax({
      url: baseUrl + '/timeline/post',
      type: 'POST',
      data: 'CSRFtoken=' + Csrf + '&body=' + Body,
      dataType: 'json',
      success: function(data) {
        if(data.status=='success'){
          $('#gorgon-textarea-body').val('');
          timelineAllLoad();
        }else{
          alert(data.message);
        }
      }
    });
  });

  $('#gorgon-loadmore').click( function() {
    $('#timeline-list-loader').show();
    $('#gorgon-loadmore').hide();
    timelineLoadmore();
    $('#timeline-list-loader').hide();
    $('#gorgon-loadmore').show();
  });

});

function timelineAllLoad() {
  var baseUrl = $('#gorgon-submit').attr('data-post-baseurl');
  if (gorgon)
  {
    $.getJSON( baseUrl + '/timeline/get', gorgon, renderJSON);
  }
  else
  {
    $.getJSON( baseUrl + '/timeline/get?mode=all', renderJSON);
  }
}

function renderJSON(json) {
  var textdata = new Array();
  $('.comment-textarea').each(function(){
    var elementId = $(this).attr('data-timeline-id');
    var textValue = $(this).val();
    textdata[elementId] = textValue;
  });
  var baseUrl = $('#gorgon-submit').attr('data-post-baseurl');
  var commentCSRF = $('#gorgon-submit').attr('data-post-csrftoken');
  $('#timeline-list').empty();
  $('#timelineTemplate').tmpl(json.data).appendTo('#timeline-list');
  if(json.data[0])
  {
    $('#timeline-list').attr('data-last-id', json.data[0].id);
  }
  var max = json.data.length - 1;
  if (json.data[max])
  {
    $('#timeline-list').attr('data-loadmore-id', json.data[max].id);
  }
  if(json.data)
  for(i=0;i<json.data.length;i++)
  {
    if(json.data[i].reply)
    {
      $('#timelineCommentTemplate').tmpl(json.data[i].reply).appendTo('#commentlist-' +json.data[i].id);
    }
    if(!textdata[json.data[i].id])
    {
      textdata[json.data[i].id] = '';
    }
    $('#comment-textarea-' + json.data[i].id).val(textdata[json.data[i].id]);
  }
  $('button.timeline-comment-button').timelineComment();
  $('.timeline-now-comment-button').timelineCommentSmtToggle();
  $('a[rel^="timelineDelete"]').timelineDelete({callback: "timelineAllLoad()",});
  $('#timeline-list-loader').hide();
  $('#timeline-list').show();
}

function timelineDifferenceLoad() {
  var baseUrl = $('#timeline-list').attr('data-post-baseurl');
  var lastId = $('#timeline-list').attr('data-last-id');
  var commentCSRF = $('#gorgon-submit').attr('data-post-csrftoken');
  $.getJSON( baseUrl + '/timeline/get?list=check&lastId=' + lastId, gorgon, function (json) {
    if (json.data[0])
    {
      $('#timeline-list').attr('data-last-id', json.data[0].id);
    }
    $('#timeline-list').prepend($('#timelineTemplate').tmpl(json.data));
    for(i=0;i<json.data.length;i++)
    {
      if(json.data[i].reply)
      {
        $('#timelineCommentTemplate').tmpl(json.data[i].reply).appendTo('#comment-list-' + json.data[i].id);
      }
    }
    $('.timeline-now-comment-button').timelineCommentSmtToggle();
  });
}

function timelineLoadmore() {
  var baseUrl = $('#timeline-list').attr('data-post-baseurl');
  var loadmoreId = $('#timeline-list').attr('data-loadmore-id');
  var commentCSRF = $('#gorgon-submit').attr('data-post-csrftoken');

  $('#timeline-list-loader').css('display', 'show');
  $('#gorgon-loadmore').css('display', 'none');

  $.getJSON( baseUrl + '/timeline/get?list=more&moreId=' + loadmoreId, gorgon, function (json) {
    $('#timeline-list-loader').css('dispaly', 'show');
    $('#gorgon-loadmore').css('display', 'none');
    var max = json.data.length - 1;
    if (max < 0)
    {
      max = 0;
    }
    if (json.data[max])
    {
      $('#timeline-list').attr('data-loadmore-id', json.data[max].id);
    }
    $timelineData = $('#timelineTemplate').tmpl(json.data);
    $('#timeline-list').after($timelineData);
    for(i=0;i<json.data.length;i++)
    {
      if(json.data[i].reply)
      {
        $('#timelineCommentTemplate').tmpl(json.data[i].reply).appendTo('#comment-list-' + json.data[i].id);
      }
    }
    $('button.timeline-comment-button').timelineComment();
    $('.timeline-now-comment-button').timelineCommentSmtToggle();
    $('#timeline-list-loader').css('display', 'none');
    $('#gorgon-loadmore').css('display', 'show');
  });
}

function timelineLoad() {
  var baseUrl = window.location.pathname;
  $.getJSON( baseUrl + '/list',function(json){
    $('#timeline-list').empty();
    $('#timelineTemplate').tmpl(json.data).appendTo('#timeline-list');
    for(i=0;i<json.data.length;i++){
      if(json.data[i].reply)
      {
        $('#timelineCommentTemplate').tmpl(json.data[i].reply).appendTo('#commentlist-' + json.data[i].id);
      }
    }
    $('.comment-list').append('<form><textarea class="comment-textarea">コメントする</textarea><button class="comment-button button" >投稿</button></form>');

    // $("a[rel^='prettyPopin']").timelinePopin();
    // $("a[rel^='timelineDelete']").timelineDelete();
  });
}

function convertTag(str) {
  str = str.replace(/&/g,'&');
  str = str.replace(/"/g,'"');
  str = str.replace(/'/g,''');
  str = str.replace(/</g,'<');
  str = str.replace(/>/g,'>');
  return str;
}
<commit_msg>fixed over load (limit 20)<commit_after>$(function(){
  var timerID;
  timelineAllLoad();
  timerID = setInterval('timelineDifferenceLoad()', 15000);

  $('#gorgon-submit').click( function() {
    var Body = $('#gorgon-textarea-body').val();
    var Csrf = $(this).attr('data-post-csrftoken');
    var baseUrl = $(this).attr('data-post-baseurl');
    $.ajax({
      url: baseUrl + '/timeline/post',
      type: 'POST',
      data: 'CSRFtoken=' + Csrf + '&body=' + Body,
      dataType: 'json',
      success: function(data) {
        if(data.status=='success'){
          $('#gorgon-textarea-body').val('');
          timelineAllLoad();
        }else{
          alert(data.message);
        }
      }
    });
  });

  $('#gorgon-loadmore').click( function() {
    $('#timeline-list-loader').show();
    $('#gorgon-loadmore').hide();
    timelineLoadmore();
    $('#timeline-list-loader').hide();
    $('#gorgon-loadmore').show();
  });

});

function timelineAllLoad() {
  var baseUrl = $('#gorgon-submit').attr('data-post-baseurl');
  if (gorgon)
  {
    $.getJSON( baseUrl + '/timeline/get', gorgon, renderJSON);
  }
  else
  {
    $.getJSON( baseUrl + '/timeline/get?mode=all&limit=20', renderJSON);
  }
}

function renderJSON(json) {
  var textdata = new Array();
  $('.comment-textarea').each(function(){
    var elementId = $(this).attr('data-timeline-id');
    var textValue = $(this).val();
    textdata[elementId] = textValue;
  });
  var baseUrl = $('#gorgon-submit').attr('data-post-baseurl');
  var commentCSRF = $('#gorgon-submit').attr('data-post-csrftoken');
  $('#timeline-list').empty();
  $('#timelineTemplate').tmpl(json.data).appendTo('#timeline-list');
  if(json.data[0])
  {
    $('#timeline-list').attr('data-last-id', json.data[0].id);
  }
  var max = json.data.length - 1;
  if (json.data[max])
  {
    $('#timeline-list').attr('data-loadmore-id', json.data[max].id);
  }
  if(json.data)
  for(i=0;i<json.data.length;i++)
  {
    if(json.data[i].reply)
    {
      $('#timelineCommentTemplate').tmpl(json.data[i].reply).appendTo('#commentlist-' +json.data[i].id);
    }
    if(!textdata[json.data[i].id])
    {
      textdata[json.data[i].id] = '';
    }
    $('#comment-textarea-' + json.data[i].id).val(textdata[json.data[i].id]);
  }
  $('button.timeline-comment-button').timelineComment();
  $('.timeline-now-comment-button').timelineCommentSmtToggle();
  $('a[rel^="timelineDelete"]').timelineDelete({callback: "timelineAllLoad()",});
  $('#timeline-list-loader').hide();
  $('#timeline-list').show();
}

function timelineDifferenceLoad() {
  var baseUrl = $('#timeline-list').attr('data-post-baseurl');
  var lastId = $('#timeline-list').attr('data-last-id');
  var commentCSRF = $('#gorgon-submit').attr('data-post-csrftoken');
  $.getJSON( baseUrl + '/timeline/get?list=check&limit=20&lastId=' + lastId, gorgon, function (json) {
    if (json.data[0])
    {
      $('#timeline-list').attr('data-last-id', json.data[0].id);
    }
    $('#timeline-list').prepend($('#timelineTemplate').tmpl(json.data));
    for(i=0;i<json.data.length;i++)
    {
      if(json.data[i].reply)
      {
        $('#timelineCommentTemplate').tmpl(json.data[i].reply).appendTo('#comment-list-' + json.data[i].id);
      }
    }
    $('.timeline-now-comment-button').timelineCommentSmtToggle();
  });
}

function timelineLoadmore() {
  var baseUrl = $('#timeline-list').attr('data-post-baseurl');
  var loadmoreId = $('#timeline-list').attr('data-loadmore-id');
  var commentCSRF = $('#gorgon-submit').attr('data-post-csrftoken');

  $('#timeline-list-loader').css('display', 'show');
  $('#gorgon-loadmore').css('display', 'none');

  $.getJSON( baseUrl + '/timeline/get?list=more&limit=20&moreId=' + loadmoreId, gorgon, function (json) {
    $('#timeline-list-loader').css('dispaly', 'show');
    $('#gorgon-loadmore').css('display', 'none');
    var max = json.data.length - 1;
    if (max < 0)
    {
      max = 0;
    }
    if (json.data[max])
    {
      $('#timeline-list').attr('data-loadmore-id', json.data[max].id);
    }
    $timelineData = $('#timelineTemplate').tmpl(json.data);
    $('#timeline-list').after($timelineData);
    for(i=0;i<json.data.length;i++)
    {
      if(json.data[i].reply)
      {
        $('#timelineCommentTemplate').tmpl(json.data[i].reply).appendTo('#comment-list-' + json.data[i].id);
      }
    }
    $('button.timeline-comment-button').timelineComment();
    $('.timeline-now-comment-button').timelineCommentSmtToggle();
    $('#timeline-list-loader').css('display', 'none');
    $('#gorgon-loadmore').css('display', 'show');
  });
}

function timelineLoad() {
  var baseUrl = window.location.pathname;
  $.getJSON( baseUrl + '/list',function(json){
    $('#timeline-list').empty();
    $('#timelineTemplate').tmpl(json.data).appendTo('#timeline-list');
    for(i=0;i<json.data.length;i++){
      if(json.data[i].reply)
      {
        $('#timelineCommentTemplate').tmpl(json.data[i].reply).appendTo('#commentlist-' + json.data[i].id);
      }
    }
    $('.comment-list').append('<form><textarea class="comment-textarea">コメントする</textarea><button class="comment-button button" >投稿</button></form>');

    // $("a[rel^='prettyPopin']").timelinePopin();
    // $("a[rel^='timelineDelete']").timelineDelete();
  });
}

function convertTag(str) {
  str = str.replace(/&/g,'&');
  str = str.replace(/"/g,'"');
  str = str.replace(/'/g,''');
  str = str.replace(/</g,'<');
  str = str.replace(/>/g,'>');
  return str;
}
<|endoftext|>


<commit_before>$(function () {
    var logLevelInputFactory = function (item) {
        return '' + item.name + '' +
            '<select class="config-item" data-field="' + item.name + '">' +
            '<option value="">None</option>' +
            '<option value="error">Error</option>' +
            '<option value="warning">Warning</option>' +
            '<option value="info">Info</option>' +
            '<option value="debug">Debug</option>' +
            '</select>' +
            '' + item.help + '';
    };

    var inputFieldFactory = function (item) {
        return '' + item.name + '' +
            '<input type="text" class="pure-input-1 config-item" id="' + item.name + '" name="' + item.name + '" data-field="' + item.name + '">' +
            '' + item.help + '';
    };

    var passwordFieldFactory = function (item) {
        return '' + item.name + '' +
            '<input type="password" class="pure-input-1 config-item" id="' + item.name + '" name="' + item.name + '" data-field="' + item.name + '">' +
            '' + item.help + '';
    };

    var checkboxFactory = function (item) {
        return '' +
            '<input class="config-item" type="checkbox" value="' + item.name + '" data-field="' + item.name + '" name="' + item.name + '"> ' +
            item.name +
            '' + item.help + '' +
            '';
    };

    var legendFactory = function (item) {
        return '<legend>' + item.name + '</legend>';
    };

    var protocolInputField = function (item) {
        return '

';
    };

    var inputApply = function (item_id, data) {
        $('.config-item[data-field="' + item_id + '"]').val(data);
    };

    var checkboxApply = function (item_id, data) {
        $('.config-item[data-field="' + item_id + '"]').prop("checked", data);
    };

    var protocolApply = function (item_id, data) {

        var fillProtocolData = function (protos) {
            $("#rfProtocols").empty();
            protos.forEach(function (value) {
                var elem = '' +
                    '<input class="config-item protocols-item" id="proto_check_' + value + '" type="checkbox" value="' + value + '" data-field="' + item_id + '" name="' + item_id + '">' +
                    ' Protocol ' + value +
                    '';
                $("#rfProtocols").append(elem);
                registerConfigUi('#proto_check_' + value);
            });
            if (data.length > 0) {
                data.forEach(function (value) {
                    $('#proto_check_' + value).prop('checked', true);
                });
            } else {
                $(".protocols-item").each(function (_, value) {
                    $(value).prop("checked", true);
                });
            }
        };

        $.ajax({
                   url: "/protocols",
                   type: "GET",
                   contentType: 'application/json',
                   success: fillProtocolData
               });
    };

    var inputGet = function (item_id) {
        return $('.config-item[data-field="' + item_id + '"]').val();
    };

    var passwordGet = function (item_id) {
        var pwd = $('.config-item[data-field="' + item_id + '"]').val();
        if (pwd.length < 8) {
            alert("Password must have at least 8 characters");
            return undefined;
        }
        return pwd;
    };

    var inputGetInt = function (item_id) {
        return parseInt(inputGet(item_id));
    };

    var checkboxGet = function (item_id) {
        return $('.config-item[data-field="' + item_id + '"]').prop("checked");
    };

    var protocolGet = function (item_id) {
        var checked = $('.protocols-item:checked');
        if ($('.protocols-item').length === checked.length) {
            return [];
        }
        return $.map(checked, function (x) {
            return $(x).val();
        });
    };

    function ConfigItem(name, factory, apply, fetch, help) {
        this.name = name;
        this.factory = factory;
        this.apply = apply;
        this.fetch = fetch;
        this.help = help;
    }

    function GroupItem(name, factory) {
        this.name = name;
        this.factory = factory;
    }

    var CONFIG_ITEMS = [
        new GroupItem("General Config", legendFactory),
        new ConfigItem("deviceName", inputFieldFactory, inputApply, inputGet, "The general name of the device"),
        new ConfigItem("configPassword", passwordFieldFactory, inputApply, passwordGet, "The admin password for the web UI (min. 8 characters)"),

        new GroupItem("MQTT Config", legendFactory),
        new ConfigItem("mqttBroker", inputFieldFactory, inputApply, inputGet, "MQTT Broker host"),
        new ConfigItem("mqttBrokerPort", inputFieldFactory, inputApply, inputGetInt, "MQTT Broker port"),
        new ConfigItem("mqttUser", inputFieldFactory, inputApply, inputGet, "MQTT username (optional)"),
        new ConfigItem("mqttPassword", passwordFieldFactory, inputApply, inputGet, "MQTT password (optional)"),
        new ConfigItem("mqttRetain", checkboxFactory, checkboxApply, checkboxGet, "Retain MQTT messages"),

        new GroupItem("MQTT Topic Config", legendFactory),
        new ConfigItem("mqttReceiveTopic", inputFieldFactory, inputApply, inputGet, "Topic to publish received signal"),
        new ConfigItem("mqttSendTopic", inputFieldFactory, inputApply, inputGet, "Topic to get signals to send from"),

        new GroupItem("433MHz RF Config", legendFactory),
        new ConfigItem("rfEchoMessages", checkboxFactory, checkboxApply, checkboxGet, "Echo sent rf messages back"),
        new ConfigItem("rfReceiverPin", inputFieldFactory, inputApply, inputGetInt, "The GPIO pin used for the rf receiver"),
        new ConfigItem("rfReceiverPinPullUp", checkboxFactory, checkboxApply, checkboxGet, "Activate pullup on rf receiver pin (required for 5V protection with reverse diode)"),
        new ConfigItem("rfTransmitterPin", inputFieldFactory, inputApply, inputGetInt, "The GPIO pin used for the RF transmitter"),

        new GroupItem("Enabled RF protocols", legendFactory),
        new ConfigItem("rfProtocols", protocolInputField, protocolApply, protocolGet, ""),

        new GroupItem("Log Config", legendFactory),
        new ConfigItem("serialLogLevel", logLevelInputFactory, inputApply, inputGet, "Level for serial logging"),
        new ConfigItem("webLogLevel", logLevelInputFactory, inputApply, inputGet, "Level for logging to the web UI"),
        new ConfigItem("syslogLevel", logLevelInputFactory, inputApply, inputGet, "Level for syslog logging"),
        new ConfigItem("syslogHost", inputFieldFactory, inputApply, inputGet, "Syslog server (optional)"),
        new ConfigItem("syslogPort", inputFieldFactory, inputApply, inputGetInt, "Syslog port (optional)"),

        new GroupItem("Status LED", legendFactory),
        new ConfigItem("ledPin", inputFieldFactory, inputApply, inputGetInt, "The GPIO pin used for the status LED"),
        new ConfigItem("ledActiveHigh", checkboxFactory, checkboxApply, checkboxGet, "The way how the LED is connected to the pin (false for built-in led)")
    ];

    var ui_map = {};
    CONFIG_ITEMS.forEach(function (value) {
        if (value.fetch !== undefined) {
            ui_map[value.name] = value;
        }
    });

    var closeWebSocket = function () {
    };

    var openWebSocket = function () {
        var container = $('#log-container');
        var pre = container.find('pre');

        var webSocket = new WebSocket("ws://" + location.hostname + ":81");
        var tm;

        closeWebSocket = function () {
            clearTimeout(tm);
            webSocket.close();
        };

        var ping = function () {
            tm = setTimeout(function () {
                webSocket.send("__PING__");

                tm = setTimeout(function () {
                    webSocket.close();
                    openWebSocket();
                }, 2000);
            }, 5000);
        };

        webSocket.onmessage = function (e) {
            var message = e.data;

            if (message === "__PONG__") {
                clearTimeout(tm);
                ping();
                return;
            }

            pre.append(message);
            if (message === '\n' || (typeof message === "string" && message.indexOf('\n') !== -1)) {
                container.scrollTop(pre.get(0).scrollHeight);
            }
        };

        webSocket.onerror = function (ev) {
            webSocket.close();
            setTimeout(function () {
                openWebSocket();
            });
        };

        webSocket.onopen = function (ev) {
            ping();
        };
    };

    var last_cfg = {};
    var changes = {};

    function throttle(callback, limit) {
        var wait = false;
        return function (args) {
            if (!wait) {
                callback.apply(this, arguments);
                wait = true;
                setTimeout(function () {
                    wait = false;
                }, limit);
            }
        };
    }

    var registerConfigUi = function (item) {
        var _item = $(item);
        _item.change(function () {
            var name = _item.data("field");
            var new_data = ui_map[name].fetch(name);
            if (new_data !== undefined && JSON.stringify(last_cfg[name]) !== JSON.stringify(new_data)) {
                changes[name] = new_data;
            }
        });
    };

    var loadConfig = function () {
        var applyConfig = function (data) {
            $.each(data, function (key, value) {
                ui_map[key].apply(key, value);
            });
            changes = {};
        };

        $.ajax({
                   url: '/config',
                   type: 'GET',
                   contentType: 'application/json',
                   success: applyConfig
               });
    };

    var SystemCommandActions = {
        restart: function () {
            var _body = $("body");
            _body.empty();
            _body.append("
Device will reboot!

Try to reconnect in 15 seconds.
");
            setTimeout(function () {
                window.location.reload(true);
            }, 15000);
        },
        reset_wifi: function () {
            var _body = $("body");
            _body.empty();
            _body.append("
Devices WIFI settings where cleared!

Please reconfigure it.
");
        },
        reset_config: function () {
            var _body = $("body");
            _body.empty();
            _body.append("
Devices Config was reset - reboot device!
" +
                "
You might have to reconfigure the wifi!
" +
                "
Reload page in 10 seconds...
");
            setTimeout(function () {
                window.location.reload(true);
            }, 10000);
        }
    };

    var sendCommand = throttle(
        function (params) {
            $.ajax({
                       url: '/system',
                       type: 'POST',
                       data: JSON.stringify(params),
                       contentType: 'application/json',
                       success: function () {
                           SystemCommandActions[params.command]();
                       }
                   }
            );
        },
        1000
    );

    var initUi = function () {
        var settings = "";
        CONFIG_ITEMS.forEach(function (item) {
            settings += item.factory(item);
        });
        $("#settings").prepend(settings);
        CONFIG_ITEMS.forEach(function (item) {
            registerConfigUi('.config-item[data-field="' + item.name + '"]');
        });
        loadConfig();
        loadDebug();
    };

    var applyDebug = function (data) {
        $.each(data, function (key, value) {
            $('.debug-item[data-dbg-field="' + key + '"]').prop("checked", value);
        });
    };

    var loadDebug = function () {
        $.ajax({
                   url: "/debug",
                   type: "GET",
                   contentType: 'application/json',
                   success: applyDebug
               });
    };

    var submitDebug = function (item) {
        var data = {};
        data[item.name] = item.checked;

        $.ajax({
                   url: '/debug',
                   type: "PUT",
                   data: JSON.stringify(data),
                   contentType: 'application/json',
                   success: applyDebug
               });
    };

    var loadFwVersion = function () {
        $.ajax({
                   url: "/firmware",
                   type: "GET",
                   contentType: 'application/json',
                   success: function (data) {
                       $('#current-fw-version').append('Current version: ' + data.version);
                   }
               });
    };

    $('.system-btn').click(function () {
        sendCommand({command: $(this).data('command')});
    });

    $('#settings-form').submit(function (e) {
        e.preventDefault();

        $.ajax({
                   url: "/config",
                   type: 'PUT',
                   contentType: 'application/json',
                   data: JSON.stringify(changes),
                   success: loadConfig

               });

        return false;
    });

    $('#cfg-form-reset').click(function (e) {
        e.preventDefault();
        loadConfig();
        return false;
    });

    $(".debug-item").forEach(function (item) {
        $(item).change(function (e) {
            submitDebug(this);
          });
    });

    $('#dbg-form-reset').click(function (e) {
        e.preventDefault();
        loadDebug();
        return false;
    });

    // Clear log
    $('#btn-clear-log').click(function (e) {
        $('#log-container').find('pre').empty();
    });

    $('#update-form').submit(function () {
        closeWebSocket();
    });

    initUi();
    loadFwVersion();
    openWebSocket();
});
<commit_msg>Frontend: remove #dbg-form-reset click event<commit_after>$(function () {
    var logLevelInputFactory = function (item) {
        return '' + item.name + '' +
            '<select class="config-item" data-field="' + item.name + '">' +
            '<option value="">None</option>' +
            '<option value="error">Error</option>' +
            '<option value="warning">Warning</option>' +
            '<option value="info">Info</option>' +
            '<option value="debug">Debug</option>' +
            '</select>' +
            '' + item.help + '';
    };

    var inputFieldFactory = function (item) {
        return '' + item.name + '' +
            '<input type="text" class="pure-input-1 config-item" id="' + item.name + '" name="' + item.name + '" data-field="' + item.name + '">' +
            '' + item.help + '';
    };

    var passwordFieldFactory = function (item) {
        return '' + item.name + '' +
            '<input type="password" class="pure-input-1 config-item" id="' + item.name + '" name="' + item.name + '" data-field="' + item.name + '">' +
            '' + item.help + '';
    };

    var checkboxFactory = function (item) {
        return '' +
            '<input class="config-item" type="checkbox" value="' + item.name + '" data-field="' + item.name + '" name="' + item.name + '"> ' +
            item.name +
            '' + item.help + '' +
            '';
    };

    var legendFactory = function (item) {
        return '<legend>' + item.name + '</legend>';
    };

    var protocolInputField = function (item) {
        return '

';
    };

    var inputApply = function (item_id, data) {
        $('.config-item[data-field="' + item_id + '"]').val(data);
    };

    var checkboxApply = function (item_id, data) {
        $('.config-item[data-field="' + item_id + '"]').prop("checked", data);
    };

    var protocolApply = function (item_id, data) {

        var fillProtocolData = function (protos) {
            $("#rfProtocols").empty();
            protos.forEach(function (value) {
                var elem = '' +
                    '<input class="config-item protocols-item" id="proto_check_' + value + '" type="checkbox" value="' + value + '" data-field="' + item_id + '" name="' + item_id + '">' +
                    ' Protocol ' + value +
                    '';
                $("#rfProtocols").append(elem);
                registerConfigUi('#proto_check_' + value);
            });
            if (data.length > 0) {
                data.forEach(function (value) {
                    $('#proto_check_' + value).prop('checked', true);
                });
            } else {
                $(".protocols-item").each(function (_, value) {
                    $(value).prop("checked", true);
                });
            }
        };

        $.ajax({
                   url: "/protocols",
                   type: "GET",
                   contentType: 'application/json',
                   success: fillProtocolData
               });
    };

    var inputGet = function (item_id) {
        return $('.config-item[data-field="' + item_id + '"]').val();
    };

    var passwordGet = function (item_id) {
        var pwd = $('.config-item[data-field="' + item_id + '"]').val();
        if (pwd.length < 8) {
            alert("Password must have at least 8 characters");
            return undefined;
        }
        return pwd;
    };

    var inputGetInt = function (item_id) {
        return parseInt(inputGet(item_id));
    };

    var checkboxGet = function (item_id) {
        return $('.config-item[data-field="' + item_id + '"]').prop("checked");
    };

    var protocolGet = function (item_id) {
        var checked = $('.protocols-item:checked');
        if ($('.protocols-item').length === checked.length) {
            return [];
        }
        return $.map(checked, function (x) {
            return $(x).val();
        });
    };

    function ConfigItem(name, factory, apply, fetch, help) {
        this.name = name;
        this.factory = factory;
        this.apply = apply;
        this.fetch = fetch;
        this.help = help;
    }

    function GroupItem(name, factory) {
        this.name = name;
        this.factory = factory;
    }

    var CONFIG_ITEMS = [
        new GroupItem("General Config", legendFactory),
        new ConfigItem("deviceName", inputFieldFactory, inputApply, inputGet, "The general name of the device"),
        new ConfigItem("configPassword", passwordFieldFactory, inputApply, passwordGet, "The admin password for the web UI (min. 8 characters)"),

        new GroupItem("MQTT Config", legendFactory),
        new ConfigItem("mqttBroker", inputFieldFactory, inputApply, inputGet, "MQTT Broker host"),
        new ConfigItem("mqttBrokerPort", inputFieldFactory, inputApply, inputGetInt, "MQTT Broker port"),
        new ConfigItem("mqttUser", inputFieldFactory, inputApply, inputGet, "MQTT username (optional)"),
        new ConfigItem("mqttPassword", passwordFieldFactory, inputApply, inputGet, "MQTT password (optional)"),
        new ConfigItem("mqttRetain", checkboxFactory, checkboxApply, checkboxGet, "Retain MQTT messages"),

        new GroupItem("MQTT Topic Config", legendFactory),
        new ConfigItem("mqttReceiveTopic", inputFieldFactory, inputApply, inputGet, "Topic to publish received signal"),
        new ConfigItem("mqttSendTopic", inputFieldFactory, inputApply, inputGet, "Topic to get signals to send from"),

        new GroupItem("433MHz RF Config", legendFactory),
        new ConfigItem("rfEchoMessages", checkboxFactory, checkboxApply, checkboxGet, "Echo sent rf messages back"),
        new ConfigItem("rfReceiverPin", inputFieldFactory, inputApply, inputGetInt, "The GPIO pin used for the rf receiver"),
        new ConfigItem("rfReceiverPinPullUp", checkboxFactory, checkboxApply, checkboxGet, "Activate pullup on rf receiver pin (required for 5V protection with reverse diode)"),
        new ConfigItem("rfTransmitterPin", inputFieldFactory, inputApply, inputGetInt, "The GPIO pin used for the RF transmitter"),

        new GroupItem("Enabled RF protocols", legendFactory),
        new ConfigItem("rfProtocols", protocolInputField, protocolApply, protocolGet, ""),

        new GroupItem("Log Config", legendFactory),
        new ConfigItem("serialLogLevel", logLevelInputFactory, inputApply, inputGet, "Level for serial logging"),
        new ConfigItem("webLogLevel", logLevelInputFactory, inputApply, inputGet, "Level for logging to the web UI"),
        new ConfigItem("syslogLevel", logLevelInputFactory, inputApply, inputGet, "Level for syslog logging"),
        new ConfigItem("syslogHost", inputFieldFactory, inputApply, inputGet, "Syslog server (optional)"),
        new ConfigItem("syslogPort", inputFieldFactory, inputApply, inputGetInt, "Syslog port (optional)"),

        new GroupItem("Status LED", legendFactory),
        new ConfigItem("ledPin", inputFieldFactory, inputApply, inputGetInt, "The GPIO pin used for the status LED"),
        new ConfigItem("ledActiveHigh", checkboxFactory, checkboxApply, checkboxGet, "The way how the LED is connected to the pin (false for built-in led)")
    ];

    var ui_map = {};
    CONFIG_ITEMS.forEach(function (value) {
        if (value.fetch !== undefined) {
            ui_map[value.name] = value;
        }
    });

    var closeWebSocket = function () {
    };

    var openWebSocket = function () {
        var container = $('#log-container');
        var pre = container.find('pre');

        var webSocket = new WebSocket("ws://" + location.hostname + ":81");
        var tm;

        closeWebSocket = function () {
            clearTimeout(tm);
            webSocket.close();
        };

        var ping = function () {
            tm = setTimeout(function () {
                webSocket.send("__PING__");

                tm = setTimeout(function () {
                    webSocket.close();
                    openWebSocket();
                }, 2000);
            }, 5000);
        };

        webSocket.onmessage = function (e) {
            var message = e.data;

            if (message === "__PONG__") {
                clearTimeout(tm);
                ping();
                return;
            }

            pre.append(message);
            if (message === '\n' || (typeof message === "string" && message.indexOf('\n') !== -1)) {
                container.scrollTop(pre.get(0).scrollHeight);
            }
        };

        webSocket.onerror = function (ev) {
            webSocket.close();
            setTimeout(function () {
                openWebSocket();
            });
        };

        webSocket.onopen = function (ev) {
            ping();
        };
    };

    var last_cfg = {};
    var changes = {};

    function throttle(callback, limit) {
        var wait = false;
        return function (args) {
            if (!wait) {
                callback.apply(this, arguments);
                wait = true;
                setTimeout(function () {
                    wait = false;
                }, limit);
            }
        };
    }

    var registerConfigUi = function (item) {
        var _item = $(item);
        _item.change(function () {
            var name = _item.data("field");
            var new_data = ui_map[name].fetch(name);
            if (new_data !== undefined && JSON.stringify(last_cfg[name]) !== JSON.stringify(new_data)) {
                changes[name] = new_data;
            }
        });
    };

    var loadConfig = function () {
        var applyConfig = function (data) {
            $.each(data, function (key, value) {
                ui_map[key].apply(key, value);
            });
            changes = {};
        };

        $.ajax({
                   url: '/config',
                   type: 'GET',
                   contentType: 'application/json',
                   success: applyConfig
               });
    };

    var SystemCommandActions = {
        restart: function () {
            var _body = $("body");
            _body.empty();
            _body.append("
Device will reboot!

Try to reconnect in 15 seconds.
");
            setTimeout(function () {
                window.location.reload(true);
            }, 15000);
        },
        reset_wifi: function () {
            var _body = $("body");
            _body.empty();
            _body.append("
Devices WIFI settings where cleared!

Please reconfigure it.
");
        },
        reset_config: function () {
            var _body = $("body");
            _body.empty();
            _body.append("
Devices Config was reset - reboot device!
" +
                "
You might have to reconfigure the wifi!
" +
                "
Reload page in 10 seconds...
");
            setTimeout(function () {
                window.location.reload(true);
            }, 10000);
        }
    };

    var sendCommand = throttle(
        function (params) {
            $.ajax({
                       url: '/system',
                       type: 'POST',
                       data: JSON.stringify(params),
                       contentType: 'application/json',
                       success: function () {
                           SystemCommandActions[params.command]();
                       }
                   }
            );
        },
        1000
    );

    var initUi = function () {
        var settings = "";
        CONFIG_ITEMS.forEach(function (item) {
            settings += item.factory(item);
        });
        $("#settings").prepend(settings);
        CONFIG_ITEMS.forEach(function (item) {
            registerConfigUi('.config-item[data-field="' + item.name + '"]');
        });
        loadConfig();
        loadDebug();
    };

    var applyDebug = function (data) {
        $.each(data, function (key, value) {
            $('.debug-item[data-dbg-field="' + key + '"]').prop("checked", value);
        });
    };

    var loadDebug = function () {
        $.ajax({
                   url: "/debug",
                   type: "GET",
                   contentType: 'application/json',
                   success: applyDebug
               });
    };

    var submitDebug = function (item) {
        var data = {};
        data[item.name] = item.checked;

        $.ajax({
                   url: '/debug',
                   type: "PUT",
                   data: JSON.stringify(data),
                   contentType: 'application/json',
                   success: applyDebug
               });
    };

    var loadFwVersion = function () {
        $.ajax({
                   url: "/firmware",
                   type: "GET",
                   contentType: 'application/json',
                   success: function (data) {
                       $('#current-fw-version').append('Current version: ' + data.version);
                   }
               });
    };

    $('.system-btn').click(function () {
        sendCommand({command: $(this).data('command')});
    });

    $('#settings-form').submit(function (e) {
        e.preventDefault();

        $.ajax({
                   url: "/config",
                   type: 'PUT',
                   contentType: 'application/json',
                   data: JSON.stringify(changes),
                   success: loadConfig

               });

        return false;
    });

    $('#cfg-form-reset').click(function (e) {
        e.preventDefault();
        loadConfig();
        return false;
    });

    $(".debug-item").forEach(function (item) {
        $(item).change(function (e) {
            submitDebug(this);
          });
    });

    // Clear log
    $('#btn-clear-log').click(function (e) {
        $('#log-container').find('pre').empty();
    });

    $('#update-form').submit(function () {
        closeWebSocket();
    });

    initUi();
    loadFwVersion();
    openWebSocket();
});
<|endoftext|>


<commit_before>import fetch from 'isomorphic-fetch';

const btoa = require('btoa');

const NS_S = 'svn:';
const NS_D = 'DAV:';

const NS_SVN_DAV = "http://subversion.tigris.org/xmlns/dav/";
const NS_SVN_DAV_DEPTH = NS_SVN_DAV + "svn/depth";
const NS_SVN_DAV_MERGINFO = NS_SVN_DAV + "svn/mergeinfo";
const NS_SVN_DAV_LOG_REVPROPS = NS_SVN_DAV + "svn/log-revprops";
const NS_SVN_DAV_ATOMIC_REVPROPS = NS_SVN_DAV + "svn/atomic-revprops";
const NS_SVN_DAV_PARTIAL_REPLAY = NS_SVN_DAV + "svn/partial-replay";

const DAVFeatures = {
  '1': {},
  '2': {},
  'version-control': {},
  'checkout': {},
  'working-resource': {},
  'merge': {},
  'baseline': {},
  'activity': {},
  'version-controlled-collection': {},
  'http://subversion.tigris.org/xmlns/dav/svn/inherited-props': {},
  'http://subversion.tigris.org/xmlns/dav/svn/inline-props': {},
  'http://subversion.tigris.org/xmlns/dav/svn/reverse-file-revs': {},
  'http://subversion.tigris.org/xmlns/dav/svn/ephemeral-txnprops': {},
  'http://subversion.tigris.org/xmlns/dav/svn/replay-rev-resource': {},
  [NS_SVN_DAV_MERGINFO]: {},
  [NS_SVN_DAV_PARTIAL_REPLAY]: {},
  [NS_SVN_DAV_DEPTH]: {},
  [NS_SVN_DAV_LOG_REVPROPS]: {},
  [NS_SVN_DAV_ATOMIC_REVPROPS]: {}
};

const SVNHeaders = [
  'SVN-Repository-UUID',
  'SVN-Repository-Root',
  'SVN-Rev-Root-Stub', // /svn/delivery_notes/!svn/rvr
  'SVN-Rev-Stub', // /svn/delivery_notes/!svn/rev
  'SVN-Txn-Root-Stub', // /svn/delivery_notes/!svn/vtxr
  'SVN-Txn-Stub', // /svn/delivery_notes/!svn/txn

  'SVN-Me-Resource', // /svn/delivery_notes/!svn/me
  'SVN-Rev-Root-Stub', // /svn/delivery_notes/!svn/rvr

  'SVN-Youngest-Rev', // Integer
  'SVN-Allow-Bulk-Updates', // Prefer
  'SVN-Supported-Posts', // create-txn create-txn-with-props
  'SVN-Repository-MergeInfo' // yes
];

const SVN = {
  toString() {
      return "svn";
    },
    get basicAuthorization() {
      return 'Basic ' + btoa(this.credentials.user + ':' + this.credentials.password);
    },
    get vccDefault() {
      return [this.attributes['SVN-Repository-Root'], "!svn/vcc/default"].join('/');
    },
    davHeader() {
      return [NS_SVN_DAV_DEPTH, NS_SVN_DAV_MERGINFO, NS_SVN_DAV_LOG_REVPROPS].join(',');
    },
    propfind(url, properties, depth = 1) {
      var xml = '<?xml version="1.0" encoding="UTF-8" ?>\n';
      xml += '<D:propfind xmlns:D="DAV:">\n';

      if (properties) {
        for (var p in properties) {
          xml += '<D:prop><' + p + ' xmlns=\"' + properties[p] + '\"/></D:prop>';
        }
      } else {
        xml += '<D:allprop/>\n';
      }

      xml += '</D:propfind>\n';

      return fetch(url, {
        method: 'PROPFIND',
        body: xml,
        headers: {
          "Authorization": svn.basicAuthorization,
          "DAV": this.davHeader(),
          "Depth": depth,
          "Content-type": "text/xml; charset=UTF-8"
        }
      });
    },
    report(url, start, end) {
      if (end - start > 1000) start = end - 1000;

      var xml = '<?xml version="1.0" encoding="UTF-8" ?>\n';
      xml += '<S:log-report xmlns:S="svn:">\n';
      xml += '<S:start-revision>' + start + '</S:start-revision>\n';
      xml += '<S:end-revision>' + end + '</S:end-revision>\n';

      const properties = ['svn:author', 'svn:date', 'svn:log'];
      properties.forEach((item) => {
        xml += '<S:revprop>' + item + '</S:revprop>';
      });
      xml += '<S:path/>\n';
      xml += '</S:log-report>\n';

      return fetch(url, {
        method: 'REPORT',
        body: xml,
        headers: {
          "Authorization": svn.basicAuthorization,
          "DAV": this.davHeader(),
          "Content-type": "text/xml; charset=UTF-8"
        }
      });
    }
};

/*
OPTIONS /svn/delivery_notes HTTP/1.1
Host: subversion.assembla.com
Authorization: Basic YXJsYWM3NzpzdGFydDEyMw==
User-Agent: SVN/1.9.2 (x86_64-apple-darwin15.0.0) serf/1.3.8
Content-Type: text/xml
Connection: keep-alive
Accept-Encoding: gzip
DAV: http://subversion.tigris.org/xmlns/dav/svn/depth
DAV: http://subversion.tigris.org/xmlns/dav/svn/mergeinfo
DAV: http://subversion.tigris.org/xmlns/dav/svn/log-revprops
Content-Length: 131

<?xml version="1.0" encoding="utf-8"?><D:options xmlns:D="DAV:"><D:activity-collection-set></D:activity-collection-set></D:options>
*/

export function init(url, options) {

  const attributes = {};
  const davFeatures = new Set();
  const allowedMethods = new Set();

  const svn = Object.create(SVN, {
    credentials: {
      get() {
        return options.credentials;
      }
    },
    attributes: {
      get() {
        return attributes;
      }
    },
    davFeatures: {
      get() {
        return davFeatures;
      }
    },
    allowedMethods: {
      get() {
        return allowedMethods;
      }
    }
  });

  return fetch(url, {
    method: 'OPTIONS',
    body: '<?xml version="1.0" encoding="utf-8"?><D:options xmlns:D="DAV:"><D:activity-collection-set></D:activity-collection-set></D:options>',
    headers: {
      "Authorization": svn.basicAuthorization,
      "DAV": svn.davHeader(),
      "Content-type": "text/xml"
    }
  }).then(function (response) {
    const headers = response.headers._headers;

    //console.log(`${JSON.stringify(headers)}`);
    //console.log(`RAW headers: ${JSON.stringify(headers.raw)}`);

    SVNHeaders.forEach(h => {
      if (headers[h]) {
        attributes[h] = headers[h];
        console.log(`${h}: ${headers[h]}`);
      }
    });

    headerIntoSet(headers.dav, davFeatures);
    headerIntoSet(headers.allow, allowedMethods);

    attributes['SVN-Youngest-Rev'] = parseInt(headers['SVN-Youngest-Rev'], 10);

    //console.log(`rev: ${headers['SVN-Youngest-Rev']}`);
    //console.log(`Attributes: ${JSON.stringify(attributes)}`);
    return svn;
  });
}

function headerIntoSet(header, target) {
  if (header) {
    header.forEach(h => h.split(/\s*,\s*/).forEach(e => target.add(e)));
  }
}
<commit_msg>fix: use this in member function<commit_after>import fetch from 'isomorphic-fetch';

const btoa = require('btoa');

const NS_S = 'svn:';
const NS_D = 'DAV:';

const NS_SVN_DAV = "http://subversion.tigris.org/xmlns/dav/";
const NS_SVN_DAV_DEPTH = NS_SVN_DAV + "svn/depth";
const NS_SVN_DAV_MERGINFO = NS_SVN_DAV + "svn/mergeinfo";
const NS_SVN_DAV_LOG_REVPROPS = NS_SVN_DAV + "svn/log-revprops";
const NS_SVN_DAV_ATOMIC_REVPROPS = NS_SVN_DAV + "svn/atomic-revprops";
const NS_SVN_DAV_PARTIAL_REPLAY = NS_SVN_DAV + "svn/partial-replay";

const DAVFeatures = {
  '1': {},
  '2': {},
  'version-control': {},
  'checkout': {},
  'working-resource': {},
  'merge': {},
  'baseline': {},
  'activity': {},
  'version-controlled-collection': {},
  'http://subversion.tigris.org/xmlns/dav/svn/inherited-props': {},
  'http://subversion.tigris.org/xmlns/dav/svn/inline-props': {},
  'http://subversion.tigris.org/xmlns/dav/svn/reverse-file-revs': {},
  'http://subversion.tigris.org/xmlns/dav/svn/ephemeral-txnprops': {},
  'http://subversion.tigris.org/xmlns/dav/svn/replay-rev-resource': {},
  [NS_SVN_DAV_MERGINFO]: {},
  [NS_SVN_DAV_PARTIAL_REPLAY]: {},
  [NS_SVN_DAV_DEPTH]: {},
  [NS_SVN_DAV_LOG_REVPROPS]: {},
  [NS_SVN_DAV_ATOMIC_REVPROPS]: {}
};

const SVNHeaders = [
  'SVN-Repository-UUID',
  'SVN-Repository-Root',
  'SVN-Rev-Root-Stub', // /svn/delivery_notes/!svn/rvr
  'SVN-Rev-Stub', // /svn/delivery_notes/!svn/rev
  'SVN-Txn-Root-Stub', // /svn/delivery_notes/!svn/vtxr
  'SVN-Txn-Stub', // /svn/delivery_notes/!svn/txn

  'SVN-Me-Resource', // /svn/delivery_notes/!svn/me
  'SVN-Rev-Root-Stub', // /svn/delivery_notes/!svn/rvr

  'SVN-Youngest-Rev', // Integer
  'SVN-Allow-Bulk-Updates', // Prefer
  'SVN-Supported-Posts', // create-txn create-txn-with-props
  'SVN-Repository-MergeInfo' // yes
];

const SVN = {
  toString() {
      return "svn";
    },
    get basicAuthorization() {
      return 'Basic ' + btoa(this.credentials.user + ':' + this.credentials.password);
    },
    get vccDefault() {
      return [this.attributes['SVN-Repository-Root'], "!svn/vcc/default"].join('/');
    },
    get davHeader() {
      return [NS_SVN_DAV_DEPTH, NS_SVN_DAV_MERGINFO, NS_SVN_DAV_LOG_REVPROPS].join(',');
    },
    propfind(url, properties, depth = 1) {
      var xml = '<?xml version="1.0" encoding="UTF-8" ?>\n';
      xml += '<D:propfind xmlns:D="DAV:">\n';

      if (properties) {
        for (var p in properties) {
          xml += '<D:prop><' + p + ' xmlns=\"' + properties[p] + '\"/></D:prop>';
        }
      } else {
        xml += '<D:allprop/>\n';
      }

      xml += '</D:propfind>\n';

      return fetch(url, {
        method: 'PROPFIND',
        body: xml,
        headers: {
          "Authorization": this.basicAuthorization,
          "DAV": this.davHeader,
          "Depth": depth,
          "Content-type": "text/xml; charset=UTF-8"
        }
      });
    },
    report(url, start, end) {
      if (end - start > 1000) start = end - 1000;

      let xml = '<?xml version="1.0" encoding="UTF-8" ?>\n';
      xml += '<S:log-report xmlns:S="svn:">\n';
      xml += '<S:start-revision>' + start + '</S:start-revision>\n';
      xml += '<S:end-revision>' + end + '</S:end-revision>\n';
      xml += ['svn:author', 'svn:date', 'svn:log'].map(item => {
        '<S:revprop>' + item + '</S:revprop>';
      }).join('');

      xml += '<S:path/>\n';
      xml += '</S:log-report>\n';

      return fetch(url, {
        method: 'REPORT',
        body: xml,
        headers: {
          "Authorization": this.basicAuthorization,
          "DAV": this.davHeader,
          "Content-type": "text/xml; charset=UTF-8"
        }
      });
    }
};

/*
OPTIONS /svn/delivery_notes HTTP/1.1
Host: subversion.assembla.com
Authorization: Basic YXJsYWM3NzpzdGFydDEyMw==
User-Agent: SVN/1.9.2 (x86_64-apple-darwin15.0.0) serf/1.3.8
Content-Type: text/xml
Connection: keep-alive
Accept-Encoding: gzip
DAV: http://subversion.tigris.org/xmlns/dav/svn/depth
DAV: http://subversion.tigris.org/xmlns/dav/svn/mergeinfo
DAV: http://subversion.tigris.org/xmlns/dav/svn/log-revprops
Content-Length: 131

<?xml version="1.0" encoding="utf-8"?><D:options xmlns:D="DAV:"><D:activity-collection-set></D:activity-collection-set></D:options>
*/

export function init(url, options) {

  const attributes = {};
  const davFeatures = new Set();
  const allowedMethods = new Set();

  const svn = Object.create(SVN, {
    credentials: {
      get() {
        return options.credentials;
      }
    },
    attributes: {
      get() {
        return attributes;
      }
    },
    davFeatures: {
      get() {
        return davFeatures;
      }
    },
    allowedMethods: {
      get() {
        return allowedMethods;
      }
    }
  });

  return fetch(url, {
    method: 'OPTIONS',
    body: '<?xml version="1.0" encoding="utf-8"?><D:options xmlns:D="DAV:"><D:activity-collection-set></D:activity-collection-set></D:options>',
    headers: {
      "Authorization": svn.basicAuthorization,
      "DAV": svn.davHeader,
      "Content-type": "text/xml"
    }
  }).then(function (response) {
    const headers = response.headers._headers;

    //console.log(`${JSON.stringify(headers)}`);
    //console.log(`RAW headers: ${JSON.stringify(headers.raw)}`);

    SVNHeaders.forEach(h => {
      if (headers[h]) {
        attributes[h] = headers[h];
        console.log(`${h}: ${headers[h]}`);
      }
    });

    headerIntoSet(headers.dav, davFeatures);
    headerIntoSet(headers.allow, allowedMethods);

    attributes['SVN-Youngest-Rev'] = parseInt(headers['SVN-Youngest-Rev'], 10);

    //console.log(`rev: ${headers['SVN-Youngest-Rev']}`);
    //console.log(`Attributes: ${JSON.stringify(attributes)}`);
    return svn;
  });
}

function headerIntoSet(header, target) {
  if (header) {
    header.forEach(h => h.split(/\s*,\s*/).forEach(e => target.add(e)));
  }
}
<|endoftext|>


<commit_before>/**
 * Date functions and classes
 */

import { leadingZeroes } from './format';
import { MONTHS_SHORT } from './const';

export function yearMonthDifference(ym1, ym2) {
    return 12 * (ym2[0] - ym1[0]) + ym2[1] - ym1[1];
}

function leapYear(year) {
    return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
}

const WEEK_DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

function pmod(number, denominator) {
    return ((number % denominator) + denominator) % denominator;
}

export function monthDays(month, year) {
    if (month === 2) {
        if (leapYear(year)) {
            return 29;
        }

        return 28;
    }

    if (month < 8) {
        return 30 + (month % 2);
    }

    return 30 + ((month + 1) % 2);
}

// year-month-date class
export class YMD {
    constructor(value) {
        const values = YMD.getValues(value);

        this.valid = values !== null && values.year &&
            values.month >= 1 && values.month <= 12 &&
            values.date >= 1 && values.date <= monthDays(values.month, values.year);

        if (this.valid) {
            this.year = values.year;
            this.month = values.month;
            this.date = values.date;
        }
        else {
            this.year = null;
            this.month = null;
            this.date = null;
        }
    }
    static getYear(parts) {
        if (parts.length === 3) {
            if (parts[2] < 100) {
                return parts[2] + 2000;
            }

            return parts[2];
        }

        return new Date().getFullYear();
    }
    static getValuesFromSlashString(value) {
        // parse string initialiser
        const parts = value.split('/').map(item => parseInt(item, 10));

        const year = YMD.getYear(parts);
        const month = parts[1];
        const date = parts[0];

        return { year, month, date };
    }
    static getValuesFromISOString(value) {
        const parts = value.split('-').map(item => parseInt(item, 10));

        const year = parts[0];
        const month = parts[1];
        const date = parts[2];

        return { year, month, date };
    }
    static getValuesFromString(value) {
        if (value.match(/^[0-9]{1,2}\/[0-9]{1,2}(\/[0-9]{2,4})?$/)) {
            return YMD.getValuesFromSlashString(value);
        }

        if (value.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/)) {
            return YMD.getValuesFromISOString(value);
        }

        return null;
    }
    static getValues(value) {
        // process a constructor object / string into year/month/date values
        if (typeof value === 'string') {
            return YMD.getValuesFromString(value);
        }

        if (typeof value === 'object') {
            const year = value[0];
            const month = value[1];
            const date = value[2];

            return { year, month, date };
        }

        if (typeof value === 'undefined' || typeof value === 'number') {
            const dateTime = value
                ? new Date(value)
                : new Date();

            const year = dateTime.getFullYear();
            const month = dateTime.getMonth() + 1;
            const date = dateTime.getDate();

            return { year, month, date };
        }

        return null;
    }
    formatNumbers() {
        return [
            leadingZeroes(this.year, 4),
            leadingZeroes(this.month, 2),
            leadingZeroes(this.date, 2)
        ];
    }
    format() {
        return this
            .formatNumbers()
            .reverse()
            .join('/');
    }
    formatISO() {
        return this
            .formatNumbers()
            .join('-');
    }
    valueOf() {
        return this.timestamp();
    }
    toString() {
        // this format gets passed to API POST / PUT requests (for e.g. updating)
        return {
            year: this.year,
            month: this.month,
            date: this.date
        };
    }
    timestamp() {
        return Math.floor(new Date(this.year, this.month - 1, this.date).getTime() / 1000);
    }

}

class TimeTick {
    constructor() {
        this.tick = 86400;
        this.major = 7;
    }
    start(obj) {
        const year = obj.getFullYear();
        const month = obj.getMonth();
        const date = obj.getDate();

        const day = obj.getDay();

        return {
            time: new Date(year, month, date, 0, 0, 0, 0).getTime() + 86400,
            index: day
        };
    }
    next(key, time) {
        const nextTime = time - this.tick * 1000;

        const major = key % this.major === 0
            ? 2
            : 0;

        const label = major
            ? this.label(time)
            : null;

        return { time, nextTime, major, label };
    }
    label(time) {
        const obj = new Date(time);

        const date = obj.getDate();
        const month = MONTHS_SHORT[obj.getMonth()];

        return `${date} ${month}`;
    }
    genTicks(t0, t1) {
        const ticks = [];
        const start = this.start(new Date(t1));
        for (let key = start.index, time = start.time; time >= t0; key--) {
            const next = this.next(key, time);
            const tick = {
                time: next.time / 1000,
                major: next.major
            };
            if (next.label) {
                tick.label = next.label;
            }
            ticks.push(tick);
            time = next.nextTime;

            if (next.extra) {
                // extra tick
                const extraTick = {
                    time: next.extra.time / 1000,
                    major: next.extra.major
                };
                if (next.extra.label) {
                    extraTick.label = next.extra.label;
                }
                ticks.push(extraTick);
            }
        }

        return ticks;
    }
}
class TimeTickDayWeek extends TimeTick {
}
class TimeTickHourDay extends TimeTick {
    constructor() {
        super();
        this.tick = 3600 * 3;
        this.major = 8;
    }
    start(dateTime) {
        const year = dateTime.getFullYear();
        const month = dateTime.getMonth();
        const date = dateTime.getDate();
        const hour = Math.ceil(dateTime.getHours() / 3) * 3;

        return {
            time: new Date(year, month, date, hour, 0, 0, 0).getTime(),
            index: hour / 3
        };
    }
    label(time) {
        return WEEK_DAYS[new Date(time).getDay()];
    }
}
class TimeTickMinuteHour extends TimeTick {
    constructor() {
        super();
        this.tick = 1800000;
        this.major = 3; // every x *hours*
        this.startMinute = 60 * this.tick / 3600000;
    }
    start(dateTime) {
        const year = dateTime.getFullYear();
        const month = dateTime.getMonth();
        const date = dateTime.getDate();
        const hour = dateTime.getHours();
        const minute = Math.floor(dateTime.getMinutes() / this.startMinute) * this.startMinute;

        return {
            time: new Date(year, month, date, hour, minute, 0, 0).getTime(),
            index: Math.round(hour * 2 + minute / this.startMinute)
        };
    }
    getMajor(key) {
        if (pmod(key, 2) === 0) {
            if (pmod(key, 48) === 0) {
                return 2;
            }

            return 1;
        }

        return 0;
    }
    next(key, time) {
        const major = this.getMajor(key);
        const label = pmod(key, 2) === 0
            ? this.label(time)
            : null;

        const nextTime = time - this.tick;

        return { time, nextTime, major, label };
    }
    label(time) {
        const dateTime = new Date(time);
        const hour = dateTime.getHours();

        if (hour === 0) {
            return WEEK_DAYS[dateTime.getDay()];
        }

        const hourText = (hour + 11) % 12 + 1;
        const amPm = hour < 12
            ? 'am'
            : 'pm';

        return `${hourText}${amPm}`;
    }
}
class TimeTickSecondMinute extends TimeTick {
    constructor() {
        super();
        this.tick = 30;
        this.major = 60;
    }
    getIndex(dateTime) {
        return dateTime.getSeconds();
    }
    start(dateTime) {
        const time = Math.floor(dateTime.getTime() / 1000 / this.tick) * 1000 * this.tick;
        const index = this.getIndex(dateTime) % this.major;

        return { time, index };
    }
    next(key, time) {
        const nextTime = time - this.tick * 1000;

        const major = this.getIndex(new Date(time)) % this.major === 0
            ? 2
            : 0;

        const label = major
            ? this.label(time)
            : null;

        return { time, nextTime, major, label };
    }
    label(time) {
        const dateTime = new Date(time);

        let hour = dateTime.getHours();
        if (hour < 10) {
            hour = `0${hour}`;
        }

        let minute = dateTime.getMinutes();
        if (minute < 10) {
            minute = `0${minute}`;
        }

        return `${hour}:${minute}`;
    }
}
class TimeTickSecondMinute2 extends TimeTickSecondMinute {
    constructor() {
        super();
        this.tick = 60;
        this.major = 600;
    }
    getIndex(dateTime) {
        const seconds = dateTime.getSeconds();
        const minutes = dateTime.getMinutes();

        return (seconds + minutes * 60);
    }
}
class TimeTickWeekMonth extends TimeTick {
    constructor() {
        super();
        this.tick = 86400 * 7;
        this.major = 4;
    }
    start(dateTime) {
        const day = dateTime.getDay();
        const startDate = new Date(dateTime.getTime() - day * 86400 * 1000);

        const year = startDate.getFullYear();
        const month = startDate.getMonth();
        const date = startDate.getDate();

        const time = new Date(year, month, date, 0, 0, 0, 0).getTime();
        const startMonth = new Date(year, month, 1, 0, 0, 0, 0).getTime();

        const index = Math.floor((time - startMonth) / 86400 / 7 / 1000);

        return { time, index };
    }
    next(key, time) {
        const nextTime = time - this.tick * 1000;
        let extra = null;
        const dateTime = new Date(time);
        const date = dateTime.getDate();
        if (date <= 7) {
            // get the exact start of the month
            const year = dateTime.getFullYear();
            const month = dateTime.getMonth();
            const monthStart = new Date(year, month, 1, 0, 0, 0, 0);
            const label = MONTHS_SHORT[monthStart.getMonth()];

            extra = { time: monthStart.getTime(), major: 2, label };
        }

        return { time, nextTime, major: 0, extra };
    }
}
class TimeTickMonthYear extends TimeTick {
    constructor() {
        super();
        this.major = 12;
    }
    start(obj) {
        const year = obj.getFullYear();
        const month = obj.getMonth();

        const time = new Date(year, month, 1).getTime();
        const index = month;

        return { time, index };
    }
    next(key, time) {
        const dateTime = new Date(time);
        const month = dateTime.getMonth();

        const major = month === 0
            ? 2
            : 0;

        const yearBreak = major
            ? 1
            : 0;

        const year = dateTime.getFullYear() - yearBreak;

        const nextTime = new Date(year, (month + 11) % 12, 1).getTime();

        const label = major
            ? this.label(dateTime)
            : null;

        return { time, major, nextTime, label };
    }
    label(dateTime) {
        return dateTime.getFullYear().toString();
    }
}

export function getTimeSeriesTicker(range) {
    // determine the tick processor to use
    if (range < 600) {
        return new TimeTickSecondMinute();
    }
    if (range < 3600) {
        return new TimeTickSecondMinute2();
    }
    if (range < 86400 * 0.6) {
        return new TimeTickMinuteHour();
    }
    if (range < 86400 * 8) {
        return new TimeTickHourDay();
    }
    if (range < 86400 * 35) {
        return new TimeTickDayWeek();
    }
    if (range < 86400 * 35 * 12) {
        return new TimeTickWeekMonth();
    }

    return new TimeTickMonthYear();
}

/**
 * Gets an appropriate range of ticks based on the time range provided
 * @param {integer} begin UNIX timestamp (secs)
 * @param {integer} end UNIX timestamp (secs)
 * @return {array} range of ticks
 */
export function timeSeriesTicks(begin, end) {
    const range = end - begin;

    const ticker = getTimeSeriesTicker(range);

    return ticker.genTicks(begin * 1000, end * 1000);
}

<commit_msg>Display year-halves in time series<commit_after>/**
 * Date functions and classes
 */

import { leadingZeroes } from './format';
import { MONTHS_SHORT } from './const';

export function yearMonthDifference(ym1, ym2) {
    return 12 * (ym2[0] - ym1[0]) + ym2[1] - ym1[1];
}

function leapYear(year) {
    return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
}

const WEEK_DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

function pmod(number, denominator) {
    return ((number % denominator) + denominator) % denominator;
}

export function monthDays(month, year) {
    if (month === 2) {
        if (leapYear(year)) {
            return 29;
        }

        return 28;
    }

    if (month < 8) {
        return 30 + (month % 2);
    }

    return 30 + ((month + 1) % 2);
}

// year-month-date class
export class YMD {
    constructor(value) {
        const values = YMD.getValues(value);

        this.valid = values !== null && values.year &&
            values.month >= 1 && values.month <= 12 &&
            values.date >= 1 && values.date <= monthDays(values.month, values.year);

        if (this.valid) {
            this.year = values.year;
            this.month = values.month;
            this.date = values.date;
        }
        else {
            this.year = null;
            this.month = null;
            this.date = null;
        }
    }
    static getYear(parts) {
        if (parts.length === 3) {
            if (parts[2] < 100) {
                return parts[2] + 2000;
            }

            return parts[2];
        }

        return new Date().getFullYear();
    }
    static getValuesFromSlashString(value) {
        // parse string initialiser
        const parts = value.split('/').map(item => parseInt(item, 10));

        const year = YMD.getYear(parts);
        const month = parts[1];
        const date = parts[0];

        return { year, month, date };
    }
    static getValuesFromISOString(value) {
        const parts = value.split('-').map(item => parseInt(item, 10));

        const year = parts[0];
        const month = parts[1];
        const date = parts[2];

        return { year, month, date };
    }
    static getValuesFromString(value) {
        if (value.match(/^[0-9]{1,2}\/[0-9]{1,2}(\/[0-9]{2,4})?$/)) {
            return YMD.getValuesFromSlashString(value);
        }

        if (value.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/)) {
            return YMD.getValuesFromISOString(value);
        }

        return null;
    }
    static getValues(value) {
        // process a constructor object / string into year/month/date values
        if (typeof value === 'string') {
            return YMD.getValuesFromString(value);
        }

        if (typeof value === 'object') {
            const year = value[0];
            const month = value[1];
            const date = value[2];

            return { year, month, date };
        }

        if (typeof value === 'undefined' || typeof value === 'number') {
            const dateTime = value
                ? new Date(value)
                : new Date();

            const year = dateTime.getFullYear();
            const month = dateTime.getMonth() + 1;
            const date = dateTime.getDate();

            return { year, month, date };
        }

        return null;
    }
    formatNumbers() {
        return [
            leadingZeroes(this.year, 4),
            leadingZeroes(this.month, 2),
            leadingZeroes(this.date, 2)
        ];
    }
    format() {
        return this
            .formatNumbers()
            .reverse()
            .join('/');
    }
    formatISO() {
        return this
            .formatNumbers()
            .join('-');
    }
    valueOf() {
        return this.timestamp();
    }
    toString() {
        // this format gets passed to API POST / PUT requests (for e.g. updating)
        return {
            year: this.year,
            month: this.month,
            date: this.date
        };
    }
    timestamp() {
        return Math.floor(new Date(this.year, this.month - 1, this.date).getTime() / 1000);
    }

}

class TimeTick {
    constructor() {
        this.tick = 86400;
        this.major = 7;
    }
    start(obj) {
        const year = obj.getFullYear();
        const month = obj.getMonth();
        const date = obj.getDate();

        const day = obj.getDay();

        return {
            time: new Date(year, month, date, 0, 0, 0, 0).getTime() + 86400,
            index: day
        };
    }
    next(key, time) {
        const nextTime = time - this.tick * 1000;

        const major = key % this.major === 0
            ? 2
            : 0;

        const label = major
            ? this.label(time)
            : null;

        return { time, nextTime, major, label };
    }
    label(time) {
        const obj = new Date(time);

        const date = obj.getDate();
        const month = MONTHS_SHORT[obj.getMonth()];

        return `${date} ${month}`;
    }
    genTicks(t0, t1) {
        const ticks = [];
        const start = this.start(new Date(t1));
        for (let key = start.index, time = start.time; time >= t0; key--) {
            const next = this.next(key, time);
            const tick = {
                time: next.time / 1000,
                major: next.major
            };
            if (next.label) {
                tick.label = next.label;
            }
            ticks.push(tick);
            time = next.nextTime;

            if (next.extra) {
                // extra tick
                const extraTick = {
                    time: next.extra.time / 1000,
                    major: next.extra.major
                };
                if (next.extra.label) {
                    extraTick.label = next.extra.label;
                }
                ticks.push(extraTick);
            }
        }

        return ticks;
    }
}
class TimeTickDayWeek extends TimeTick {
}
class TimeTickHourDay extends TimeTick {
    constructor() {
        super();
        this.tick = 3600 * 3;
        this.major = 8;
    }
    start(dateTime) {
        const year = dateTime.getFullYear();
        const month = dateTime.getMonth();
        const date = dateTime.getDate();
        const hour = Math.ceil(dateTime.getHours() / 3) * 3;

        return {
            time: new Date(year, month, date, hour, 0, 0, 0).getTime(),
            index: hour / 3
        };
    }
    label(time) {
        return WEEK_DAYS[new Date(time).getDay()];
    }
}
class TimeTickMinuteHour extends TimeTick {
    constructor() {
        super();
        this.tick = 1800000;
        this.major = 3; // every x *hours*
        this.startMinute = 60 * this.tick / 3600000;
    }
    start(dateTime) {
        const year = dateTime.getFullYear();
        const month = dateTime.getMonth();
        const date = dateTime.getDate();
        const hour = dateTime.getHours();
        const minute = Math.floor(dateTime.getMinutes() / this.startMinute) * this.startMinute;

        return {
            time: new Date(year, month, date, hour, minute, 0, 0).getTime(),
            index: Math.round(hour * 2 + minute / this.startMinute)
        };
    }
    getMajor(key) {
        if (pmod(key, 2) === 0) {
            if (pmod(key, 48) === 0) {
                return 2;
            }

            return 1;
        }

        return 0;
    }
    next(key, time) {
        const major = this.getMajor(key);
        const label = pmod(key, 2) === 0
            ? this.label(time)
            : null;

        const nextTime = time - this.tick;

        return { time, nextTime, major, label };
    }
    label(time) {
        const dateTime = new Date(time);
        const hour = dateTime.getHours();

        if (hour === 0) {
            return WEEK_DAYS[dateTime.getDay()];
        }

        const hourText = (hour + 11) % 12 + 1;
        const amPm = hour < 12
            ? 'am'
            : 'pm';

        return `${hourText}${amPm}`;
    }
}
class TimeTickSecondMinute extends TimeTick {
    constructor() {
        super();
        this.tick = 30;
        this.major = 60;
    }
    getIndex(dateTime) {
        return dateTime.getSeconds();
    }
    start(dateTime) {
        const time = Math.floor(dateTime.getTime() / 1000 / this.tick) * 1000 * this.tick;
        const index = this.getIndex(dateTime) % this.major;

        return { time, index };
    }
    next(key, time) {
        const nextTime = time - this.tick * 1000;

        const major = this.getIndex(new Date(time)) % this.major === 0
            ? 2
            : 0;

        const label = major
            ? this.label(time)
            : null;

        return { time, nextTime, major, label };
    }
    label(time) {
        const dateTime = new Date(time);

        let hour = dateTime.getHours();
        if (hour < 10) {
            hour = `0${hour}`;
        }

        let minute = dateTime.getMinutes();
        if (minute < 10) {
            minute = `0${minute}`;
        }

        return `${hour}:${minute}`;
    }
}
class TimeTickSecondMinute2 extends TimeTickSecondMinute {
    constructor() {
        super();
        this.tick = 60;
        this.major = 600;
    }
    getIndex(dateTime) {
        const seconds = dateTime.getSeconds();
        const minutes = dateTime.getMinutes();

        return (seconds + minutes * 60);
    }
}
class TimeTickWeekMonth extends TimeTick {
    constructor() {
        super();
        this.tick = 86400 * 7;
        this.major = 4;
    }
    start(dateTime) {
        const day = dateTime.getDay();
        const startDate = new Date(dateTime.getTime() - day * 86400 * 1000);

        const year = startDate.getFullYear();
        const month = startDate.getMonth();
        const date = startDate.getDate();

        const time = new Date(year, month, date, 0, 0, 0, 0).getTime();
        const startMonth = new Date(year, month, 1, 0, 0, 0, 0).getTime();

        const index = Math.floor((time - startMonth) / 86400 / 7 / 1000);

        return { time, index };
    }
    next(key, time) {
        const nextTime = time - this.tick * 1000;
        let extra = null;
        const dateTime = new Date(time);
        const date = dateTime.getDate();
        if (date <= 7) {
            // get the exact start of the month
            const year = dateTime.getFullYear();
            const month = dateTime.getMonth();
            const monthStart = new Date(year, month, 1, 0, 0, 0, 0);
            const label = MONTHS_SHORT[monthStart.getMonth()];

            extra = { time: monthStart.getTime(), major: 2, label };
        }

        return { time, nextTime, major: 0, extra };
    }
}
class TimeTickMonthYear extends TimeTick {
    constructor() {
        super();
        this.major = 12;
    }
    start(obj) {
        const year = obj.getFullYear();
        const month = obj.getMonth();

        const time = new Date(year, month, 1).getTime();
        const index = month;

        return { time, index };
    }
    label(month, dateTime) {
        if (month === 0) {
            return dateTime.getFullYear().toString();
        }

        if (month === 6) {
            return 'H2';
        }

        return null;
    }
    getMajor(month) {
        if (!month) {
            // start of year
            return 2;
        }

        return Math.floor((month % 7) / 6);
    }
    next(key, time) {
        const dateTime = new Date(time);
        const month = dateTime.getMonth();

        const major = this.getMajor(month);

        const yearBreak = month === 0;
        const year = dateTime.getFullYear() - yearBreak;

        const nextTime = new Date(year, (month + 11) % 12, 1).getTime();

        const label = this.label(month, dateTime);

        return { time, major, nextTime, label };
    }
}

export function getTimeSeriesTicker(range) {
    // determine the tick processor to use
    if (range < 600) {
        return new TimeTickSecondMinute();
    }
    if (range < 3600) {
        return new TimeTickSecondMinute2();
    }
    if (range < 86400 * 0.6) {
        return new TimeTickMinuteHour();
    }
    if (range < 86400 * 8) {
        return new TimeTickHourDay();
    }
    if (range < 86400 * 35) {
        return new TimeTickDayWeek();
    }
    if (range < 86400 * 35 * 12) {
        return new TimeTickWeekMonth();
    }

    return new TimeTickMonthYear();
}

/**
 * Gets an appropriate range of ticks based on the time range provided
 * @param {integer} begin UNIX timestamp (secs)
 * @param {integer} end UNIX timestamp (secs)
 * @return {array} range of ticks
 */
export function timeSeriesTicks(begin, end) {
    const range = end - begin;

    const ticker = getTimeSeriesTicker(range);

    return ticker.genTicks(begin * 1000, end * 1000);
}

<|endoftext|>


<commit_before>var child_process = require('child_process');

module.exports = function(grunt) {
  'use strict';

  require('load-grunt-tasks')(grunt);

  var pkg = require('./package.json');
  var protractorConfig = grunt.option('protractorConfig') || 'camunda-webapp-test/ci.conf.js';

  var config = pkg.gruntConfig || {};

  config.grunt = grunt;
  config.pkg = pkg;
  config.protractorConfig = protractorConfig;

  var browserifyConf = { };

  require('./grunt/config/browserify')(config, browserifyConf);
  require('./ui/admin/grunt/config/browserify')(config, browserifyConf);
  require('./ui/tasklist/grunt/config/browserify')(config, browserifyConf);
  require('./ui/cockpit/grunt/config/browserify')(config, browserifyConf);

  var copyConf = require('./grunt/config/copy');
  require('./ui/admin/grunt/config/copy')(config, copyConf);
  require('./ui/cockpit/grunt/config/copy')(config, copyConf);
  require('./ui/tasklist/grunt/config/copy')(config, copyConf);

  var lessConf = { };
  require('./grunt/config/less')(config, lessConf, {
    appName: 'admin',
    sourceDir: pkg.gruntConfig.adminSourceDir,
    buildTarget: pkg.gruntConfig.adminBuildTarget,
  });
  require('./grunt/config/less')(config, lessConf, {
    appName: 'cockpit',
    sourceDir: pkg.gruntConfig.cockpitSourceDir,
    buildTarget: pkg.gruntConfig.cockpitBuildTarget,
  });
  require('./grunt/config/less')(config, lessConf, {
    appName: 'tasklist',
    sourceDir: pkg.gruntConfig.tasklistSourceDir,
    buildTarget: pkg.gruntConfig.tasklistBuildTarget,
  });

  var localesConf = { };
  require('./grunt/config/localescompile')(config, localesConf, {
    appName: 'tasklist',
    sourceDir: pkg.gruntConfig.tasklistSourceDir,
    buildTarget: pkg.gruntConfig.tasklistBuildTarget,
  });

  var watchConf = {
    commons_styles: {
      options: {
        liverreload: false,
      },
      files: ['node_modules/camunda-commons-ui/{lib,resources}/**/*.less'],
      tasks: ['less']
    }
  };
  require('./ui/tasklist/grunt/config/watch')(config, watchConf);
  require('./ui/cockpit/grunt/config/watch')(config, watchConf);
  require('./ui/admin/grunt/config/watch')(config, watchConf);

  var uglifyConf = {};
  require('./grunt/config/uglify')(config, uglifyConf);
  require('./ui/admin/grunt/config/uglify')(config, uglifyConf);
  require('./ui/tasklist/grunt/config/uglify')(config, uglifyConf);
  require('./ui/cockpit/grunt/config/uglify')(config, uglifyConf);

  grunt.initConfig({
    buildMode:        'dev',

    pkg:              pkg,

    browserify:       browserifyConf,

    copy:             copyConf,

    less:             lessConf,

    localescompile:   localesConf,

    uglify:           uglifyConf,

    clean:            require('./grunt/config/clean')(config),

    watch:            watchConf,

    protractor:       require('./grunt/config/protractor')(config)
  });

  require('camunda-commons-ui/grunt/tasks/localescompile')(grunt);

  grunt.registerTask('build', function(mode, app) {

    if(typeof app !== 'undefined') {
      console.log(' ------------  will build ' + app + ' -------------');
      var objs = [browserifyConf, copyConf, lessConf, localesConf, watchConf, uglifyConf];
      for(var i = 0; i < objs.length; i++) {
        var obj = objs[i];
        for (var key in obj) {
          if (obj.hasOwnProperty(key) && key.toLowerCase().indexOf(app) === -1 && key !== 'options' &&
                                         key.toLowerCase().indexOf('webapp') === -1 &&
                                         key.toLowerCase().indexOf('sdk') === -1) {
              delete obj[key];
          }
        }
      }
    }

    grunt.config.data.buildMode = mode || 'prod';

    var tasksToRun = [
      'clean',
      'browserify',
      'copy',
      'less'
    ];

    if(typeof app === 'undefined' || app === 'tasklist') {
      tasksToRun.push('localescompile');
    }

    if(grunt.config.data.buildMode === 'prod') {
      tasksToRun.push('uglify');
    }

    grunt.task.run(tasksToRun);

  });

  grunt.registerTask('auto-build', function(app) {
    if(app) {
      grunt.task.run([
        'build:dev:' + app,
        'watch'
      ]);
    } else {
      grunt.task.run([
        'build:dev',
        'watch'
      ]);
    }
  });

  grunt.registerTask('ensureSelenium', function() {
    // async task
    var done = this.async();

    child_process.execFile('node', [__dirname + '/node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager', '--chrome', 'update'], function(err) {
      done();
    });
  });

  grunt.registerTask('default', ['build']);

  grunt.registerTask('test-e2e', ['ensureSelenium', 'protractor:e2e']);
};
<commit_msg>fix(tests): use correct path to ci.conf<commit_after>var child_process = require('child_process');

module.exports = function(grunt) {
  'use strict';

  require('load-grunt-tasks')(grunt);

  var pkg = require('./package.json');
  var protractorConfig = grunt.option('protractorConfig') || 'ui/common/tests/ci.conf.js';

  var config = pkg.gruntConfig || {};

  config.grunt = grunt;
  config.pkg = pkg;
  config.protractorConfig = protractorConfig;

  var browserifyConf = { };

  require('./grunt/config/browserify')(config, browserifyConf);
  require('./ui/admin/grunt/config/browserify')(config, browserifyConf);
  require('./ui/tasklist/grunt/config/browserify')(config, browserifyConf);
  require('./ui/cockpit/grunt/config/browserify')(config, browserifyConf);

  var copyConf = require('./grunt/config/copy');
  require('./ui/admin/grunt/config/copy')(config, copyConf);
  require('./ui/cockpit/grunt/config/copy')(config, copyConf);
  require('./ui/tasklist/grunt/config/copy')(config, copyConf);

  var lessConf = { };
  require('./grunt/config/less')(config, lessConf, {
    appName: 'admin',
    sourceDir: pkg.gruntConfig.adminSourceDir,
    buildTarget: pkg.gruntConfig.adminBuildTarget,
  });
  require('./grunt/config/less')(config, lessConf, {
    appName: 'cockpit',
    sourceDir: pkg.gruntConfig.cockpitSourceDir,
    buildTarget: pkg.gruntConfig.cockpitBuildTarget,
  });
  require('./grunt/config/less')(config, lessConf, {
    appName: 'tasklist',
    sourceDir: pkg.gruntConfig.tasklistSourceDir,
    buildTarget: pkg.gruntConfig.tasklistBuildTarget,
  });

  var localesConf = { };
  require('./grunt/config/localescompile')(config, localesConf, {
    appName: 'tasklist',
    sourceDir: pkg.gruntConfig.tasklistSourceDir,
    buildTarget: pkg.gruntConfig.tasklistBuildTarget,
  });

  var watchConf = {
    commons_styles: {
      options: {
        liverreload: false,
      },
      files: ['node_modules/camunda-commons-ui/{lib,resources}/**/*.less'],
      tasks: ['less']
    }
  };
  require('./ui/tasklist/grunt/config/watch')(config, watchConf);
  require('./ui/cockpit/grunt/config/watch')(config, watchConf);
  require('./ui/admin/grunt/config/watch')(config, watchConf);

  var uglifyConf = {};
  require('./grunt/config/uglify')(config, uglifyConf);
  require('./ui/admin/grunt/config/uglify')(config, uglifyConf);
  require('./ui/tasklist/grunt/config/uglify')(config, uglifyConf);
  require('./ui/cockpit/grunt/config/uglify')(config, uglifyConf);

  grunt.initConfig({
    buildMode:        'dev',

    pkg:              pkg,

    browserify:       browserifyConf,

    copy:             copyConf,

    less:             lessConf,

    localescompile:   localesConf,

    uglify:           uglifyConf,

    clean:            require('./grunt/config/clean')(config),

    watch:            watchConf,

    protractor:       require('./grunt/config/protractor')(config)
  });

  require('camunda-commons-ui/grunt/tasks/localescompile')(grunt);

  grunt.registerTask('build', function(mode, app) {

    if(typeof app !== 'undefined') {
      console.log(' ------------  will build ' + app + ' -------------');
      var objs = [browserifyConf, copyConf, lessConf, localesConf, watchConf, uglifyConf];
      for(var i = 0; i < objs.length; i++) {
        var obj = objs[i];
        for (var key in obj) {
          if (obj.hasOwnProperty(key) && key.toLowerCase().indexOf(app) === -1 && key !== 'options' &&
                                         key.toLowerCase().indexOf('webapp') === -1 &&
                                         key.toLowerCase().indexOf('sdk') === -1) {
              delete obj[key];
          }
        }
      }
    }

    grunt.config.data.buildMode = mode || 'prod';

    var tasksToRun = [
      'clean',
      'browserify',
      'copy',
      'less'
    ];

    if(typeof app === 'undefined' || app === 'tasklist') {
      tasksToRun.push('localescompile');
    }

    if(grunt.config.data.buildMode === 'prod') {
      tasksToRun.push('uglify');
    }

    grunt.task.run(tasksToRun);

  });

  grunt.registerTask('auto-build', function(app) {
    if(app) {
      grunt.task.run([
        'build:dev:' + app,
        'watch'
      ]);
    } else {
      grunt.task.run([
        'build:dev',
        'watch'
      ]);
    }
  });

  grunt.registerTask('ensureSelenium', function() {
    // async task
    var done = this.async();

    child_process.execFile('node', [__dirname + '/node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager', '--chrome', 'update'], function(err) {
      done();
    });
  });

  grunt.registerTask('default', ['build']);

  grunt.registerTask('test-e2e', ['ensureSelenium', 'protractor:e2e']);
};
<|endoftext|>