<commit_before>function fn_list_endpoints()
{
    var group = $.trim($("input[name='hostgroup_search']").val());
    var qs = $.trim($("input[name='endpoint_search']").val());
    if (group.length > 0 && qs.length > 0) {
        alert('Hostgroup 與 Endpoint 只能選一個');
    } else if (group.length > 0) {
        $(".loading").show();
        $.getJSON("/api/groups", {q: group}, function(ret){
                    $(".loading").hide();
                    if (!ret.ok) {
                        alert(ret.msg);
                        return;
                    }
                    var hosts = ret.data;

                    var tbody_hosts = $("#tbody-endpoints");
                    tbody_hosts.html("");
                    for (var hidx in hosts) {
                        var h = hosts[hidx];
                        var line_html = '
'
                        + '<input type="checkbox" data-fullname="'+ h +'"></input>	'
                        + '' + h + '	'
                        + '
';
                        tbody_hosts.append($(line_html));
                    }
                    fn_check_all_hosts();
        }).error(function(req, ret, errorThrown){
            $(".loading").hide();
            alert(req.statusText)
        })
    } else if (qs.length > 0) {
        var tags = $.trim($("input[name='tag_search']").val());
        var limit = $("#endpoint-limit").val();
        $(".loading").show();
        $.getJSON("/api/endpoints", {q: qs, tags: tags, limit:limit, _r:Math.random()}, function(ret){
                    $(".loading").hide();
                    if (!ret.ok) {
                        alert(ret.msg);
                        return;
                    }
                    var hosts = ret.data;

                    // display_endpoints
                    var tbody_hosts = $("#tbody-endpoints");
                    tbody_hosts.html("");
                    for (var hidx in hosts) {
                        var h = hosts[hidx];
                        var line_html = '
'
                        + '<input type="checkbox" data-fullname="'+ h +'"></input>	'
                        + '' + h + '	'
                        + '
';
                        tbody_hosts.append($(line_html));
                    }
                    fn_check_all_hosts();
        }).error(function(req, ret, errorThrown){
            $(".loading").hide();
            alert(req.statusText)
        })
    }
}

function fn_list_counters(){
    var qs = $.trim($("#counter-search").val());
    var hosts = new Array();
    $("#tbody-endpoints input:checked").each(function(i, o){
        var name = $(o).attr("data-fullname");
        hosts.push(name);
    });
    if (hosts.length === 0){
        alert("先选定一些endpoints");
        return false;
    }

    var limit = $("#counter-limit").val();
    $(".loading").show();
    $.ajax({
        method: "POST",
        url: "/api/counters",
        dataType: "json",
        data: {"endpoints": JSON.stringify(hosts), "q": qs, "limit": limit, "_r": Math.random()},
        success:function(ret){
            $(".loading").hide();
            if(ret.ok){
                var items = ret.data;
                // display counters
                var tbody_items = $("#tbody-counters");
                tbody_items.html("");

                for (var i in items) {
                    var c = items[i];
                    var display_counter_type = "计数器";
                    if(c[1] == "GAUGE") {
                        display_counter_type = "原始值";
                    }
                    var line_html = '
'
                    + '<input type="checkbox" class="input shiftCheckbox" data-fullkey="'+c[0]+'"></input>	'
                    + '' + c[0] + '	'
                    + ''+ display_counter_type +'	'
                    + ''+ c[2] +'s	'
                    + '
'
                    tbody_items.append($(line_html));
                    tbody_items.find('.shiftCheckbox').shiftcheckbox();
                }
            }else{
                alert("搜索失败：" + ret.msg);
                return false;
            }
        }
    });
}

function filter_endpoint()
{
    var filter_text = $("#endpoint-filter").val().toLowerCase();
    var targets = $("#tbody-endpoints tr");
    if(!filter_text){
        targets.each(function(i, obj){
            $(obj).show();
        });
    }else{
        var filter_pattern = new RegExp(filter_text, "i");

        targets.each(function(i, obj){
            var checkbox = $($(obj).find("input[type='checkbox']")[0]);
            var name = checkbox.attr("data-fullname");
            if(filter_pattern.exec(name) == null){
                $(obj).hide();
            }else{
                $(obj).show();
            }
            if($(obj).is(":visible")){
                checkbox.prop("checked", true);
            }else{
                checkbox.prop("checked", false);
            }
        });
    }
};

function filter_counter()
{
    var filter_text = $("#counter-filter").val().toLowerCase();
    var targets = $("#tbody-counters tr");
    if(!filter_text){
        targets.each(function(i, obj){
            $(obj).show();
        });
    }else{
        var filter_pattern = new RegExp(filter_text, "i");
        targets.each(function(i, obj){
            var checkbox = $($(obj).find("input[type='checkbox']")[0]);
            var name = checkbox.attr("data-fullkey");
            if(filter_pattern.exec(name) == null){
                $(obj).hide();
            }else{
                $(obj).show();
            }
            if($(obj).is(":visible")){
                checkbox.prop("checked", true);
            }else{
                checkbox.prop("checked", false);
            }
        });
    }
};

function fn_show_chart(counter)
{
    var checked_hosts = new Array();
    $("#tbody-endpoints input:checked").each(function(i, o){
        if($(o).is(":visible")){
            var hostfullname = $(o).attr("data-fullname");
            checked_hosts.push(hostfullname);
        }
    });
    if(checked_hosts.length === 0){
        alert("先选endpoint：）");
        return false;
    }

    checked_items = new Array();
    checked_items.push(counter);
    var w = window.open();
    $.ajax({
        url: "/chart",
        dataType: "json",
        method: "POST",
        data: {"endpoints": checked_hosts, "counters": checked_items, "graph_type": "h", "_r": Math.random()},
        success: function(ret) {
            if (ret.ok) {
                setTimeout(function(){w.location='/chart/big?id='+ret.id;}, 0);
            } else {
                alert("请求出错了");
            }
        },
        error: function(){
            alert("请求出错了");
        }
    });
    return false;
}

function fn_show_all(graph_type)
{
    var checked_hosts = new Array();
    $("#tbody-endpoints input:checked").each(function(i, o){
        if($(o).is(":visible")){
            var hostfullname = $(o).attr("data-fullname");
            checked_hosts.push(hostfullname);
        }
    });
    if(checked_hosts.length === 0){
        alert("先选endpoint：）");
        return false;
    }

    var checked_items = new Array();
    $("#tbody-counters input:checked").each(function(i, o){
        if($(o).is(":visible")){
            var key_ = $(o).attr("data-fullkey");
            checked_items.push(key_);
        }
    });
    if (checked_items.length === 0){
        alert("请选择counter");
        return false;
    }

    var w = window.open();
    $.ajax({
        url: "/chart",
        dataType: "json",
        method: "POST",
        data: {"endpoints": checked_hosts, "counters": checked_items, "graph_type": graph_type, "_r": Math.random()},
        success: function(ret) {
            if (ret.ok) {
                setTimeout(function(){w.location="/charts?id="+ret.id+"&graph_type="+graph_type;}, 0);
            }else {
                alert("请求出错了");
            }
        },
        error: function(){
            alert("请求出错了");
        }
    });
    return false;
}

function fn_check_all_items()
{
    var box = $("#check_all_counters");
    if(box.prop("checked")){
        $("#tbody-counters").find("input:checkbox").each(function(i, o){
            $(o).prop("checked", true);
        });
    }else{
        $("#tbody-counters").find("input:checkbox").each(function(i, o){
            $(o).prop("checked", false);
        });
    }
}

function fn_check_all_hosts()
{
    var box = $("#check_all_endpoints");
    if(box.prop("checked")){
        $("#tbody-endpoints").find("input:checkbox").each(function(i, o){
            $(o).prop("checked", true);
        });
    }else{
        $("#tbody-endpoints").find("input:checkbox").each(function(i, o){
            $(o).prop("checked", false);
        });
    }
}

function fn_filter_group()
{
    var filter_text = $("#group-filter").val().toLowerCase();
    var group_objs = $(".group");
    if(!filter_text){
        group_objs.each(function(i, obj){
            $(obj).show();
        });
    }else if (filter_text.length <= 2) {
    }else{
        group_objs.each(function(i, obj){
            var groupname = $($(obj).children("a")[0]).attr("data-gname");
            if(groupname.toLowerCase().indexOf(filter_text) === -1){
                $(obj).hide();
            }else{
                $(obj).show();
            }
        });
        fn_collapse_in_groups();
    }
};

function fn_collapse_in_groups()
{
    $(".accordion-body").each(function(i, obj){
        if(!$(obj).hasClass("in")){
            $(obj).collapse("show");
        }
    });
};
<commit_msg>[OWL-295] refine alert message<commit_after>function fn_list_endpoints()
{
    var group = $.trim($("input[name='hostgroup_search']").val());
    var qs = $.trim($("input[name='endpoint_search']").val());
    if (group.length > 0 && qs.length > 0) {
        alert('Hostgroup 與 Endpoint 只能擇一輸入');
    } else if (group.length > 0) {
        $(".loading").show();
        $.getJSON("/api/groups", {q: group}, function(ret){
                    $(".loading").hide();
                    if (!ret.ok) {
                        alert(ret.msg);
                        return;
                    }
                    var hosts = ret.data;

                    var tbody_hosts = $("#tbody-endpoints");
                    tbody_hosts.html("");
                    for (var hidx in hosts) {
                        var h = hosts[hidx];
                        var line_html = '
'
                        + '<input type="checkbox" data-fullname="'+ h +'"></input>	'
                        + '' + h + '	'
                        + '
';
                        tbody_hosts.append($(line_html));
                    }
                    fn_check_all_hosts();
        }).error(function(req, ret, errorThrown){
            $(".loading").hide();
            alert(req.statusText)
        })
    } else if (qs.length > 0) {
        var tags = $.trim($("input[name='tag_search']").val());
        var limit = $("#endpoint-limit").val();
        $(".loading").show();
        $.getJSON("/api/endpoints", {q: qs, tags: tags, limit:limit, _r:Math.random()}, function(ret){
                    $(".loading").hide();
                    if (!ret.ok) {
                        alert(ret.msg);
                        return;
                    }
                    var hosts = ret.data;

                    // display_endpoints
                    var tbody_hosts = $("#tbody-endpoints");
                    tbody_hosts.html("");
                    for (var hidx in hosts) {
                        var h = hosts[hidx];
                        var line_html = '
'
                        + '<input type="checkbox" data-fullname="'+ h +'"></input>	'
                        + '' + h + '	'
                        + '
';
                        tbody_hosts.append($(line_html));
                    }
                    fn_check_all_hosts();
        }).error(function(req, ret, errorThrown){
            $(".loading").hide();
            alert(req.statusText)
        })
    }
}

function fn_list_counters(){
    var qs = $.trim($("#counter-search").val());
    var hosts = new Array();
    $("#tbody-endpoints input:checked").each(function(i, o){
        var name = $(o).attr("data-fullname");
        hosts.push(name);
    });
    if (hosts.length === 0){
        alert("先选定一些endpoints");
        return false;
    }

    var limit = $("#counter-limit").val();
    $(".loading").show();
    $.ajax({
        method: "POST",
        url: "/api/counters",
        dataType: "json",
        data: {"endpoints": JSON.stringify(hosts), "q": qs, "limit": limit, "_r": Math.random()},
        success:function(ret){
            $(".loading").hide();
            if(ret.ok){
                var items = ret.data;
                // display counters
                var tbody_items = $("#tbody-counters");
                tbody_items.html("");

                for (var i in items) {
                    var c = items[i];
                    var display_counter_type = "计数器";
                    if(c[1] == "GAUGE") {
                        display_counter_type = "原始值";
                    }
                    var line_html = '
'
                    + '<input type="checkbox" class="input shiftCheckbox" data-fullkey="'+c[0]+'"></input>	'
                    + '' + c[0] + '	'
                    + ''+ display_counter_type +'	'
                    + ''+ c[2] +'s	'
                    + '
'
                    tbody_items.append($(line_html));
                    tbody_items.find('.shiftCheckbox').shiftcheckbox();
                }
            }else{
                alert("搜索失败：" + ret.msg);
                return false;
            }
        }
    });
}

function filter_endpoint()
{
    var filter_text = $("#endpoint-filter").val().toLowerCase();
    var targets = $("#tbody-endpoints tr");
    if(!filter_text){
        targets.each(function(i, obj){
            $(obj).show();
        });
    }else{
        var filter_pattern = new RegExp(filter_text, "i");

        targets.each(function(i, obj){
            var checkbox = $($(obj).find("input[type='checkbox']")[0]);
            var name = checkbox.attr("data-fullname");
            if(filter_pattern.exec(name) == null){
                $(obj).hide();
            }else{
                $(obj).show();
            }
            if($(obj).is(":visible")){
                checkbox.prop("checked", true);
            }else{
                checkbox.prop("checked", false);
            }
        });
    }
};

function filter_counter()
{
    var filter_text = $("#counter-filter").val().toLowerCase();
    var targets = $("#tbody-counters tr");
    if(!filter_text){
        targets.each(function(i, obj){
            $(obj).show();
        });
    }else{
        var filter_pattern = new RegExp(filter_text, "i");
        targets.each(function(i, obj){
            var checkbox = $($(obj).find("input[type='checkbox']")[0]);
            var name = checkbox.attr("data-fullkey");
            if(filter_pattern.exec(name) == null){
                $(obj).hide();
            }else{
                $(obj).show();
            }
            if($(obj).is(":visible")){
                checkbox.prop("checked", true);
            }else{
                checkbox.prop("checked", false);
            }
        });
    }
};

function fn_show_chart(counter)
{
    var checked_hosts = new Array();
    $("#tbody-endpoints input:checked").each(function(i, o){
        if($(o).is(":visible")){
            var hostfullname = $(o).attr("data-fullname");
            checked_hosts.push(hostfullname);
        }
    });
    if(checked_hosts.length === 0){
        alert("先选endpoint：）");
        return false;
    }

    checked_items = new Array();
    checked_items.push(counter);
    var w = window.open();
    $.ajax({
        url: "/chart",
        dataType: "json",
        method: "POST",
        data: {"endpoints": checked_hosts, "counters": checked_items, "graph_type": "h", "_r": Math.random()},
        success: function(ret) {
            if (ret.ok) {
                setTimeout(function(){w.location='/chart/big?id='+ret.id;}, 0);
            } else {
                alert("请求出错了");
            }
        },
        error: function(){
            alert("请求出错了");
        }
    });
    return false;
}

function fn_show_all(graph_type)
{
    var checked_hosts = new Array();
    $("#tbody-endpoints input:checked").each(function(i, o){
        if($(o).is(":visible")){
            var hostfullname = $(o).attr("data-fullname");
            checked_hosts.push(hostfullname);
        }
    });
    if(checked_hosts.length === 0){
        alert("先选endpoint：）");
        return false;
    }

    var checked_items = new Array();
    $("#tbody-counters input:checked").each(function(i, o){
        if($(o).is(":visible")){
            var key_ = $(o).attr("data-fullkey");
            checked_items.push(key_);
        }
    });
    if (checked_items.length === 0){
        alert("请选择counter");
        return false;
    }

    var w = window.open();
    $.ajax({
        url: "/chart",
        dataType: "json",
        method: "POST",
        data: {"endpoints": checked_hosts, "counters": checked_items, "graph_type": graph_type, "_r": Math.random()},
        success: function(ret) {
            if (ret.ok) {
                setTimeout(function(){w.location="/charts?id="+ret.id+"&graph_type="+graph_type;}, 0);
            }else {
                alert("请求出错了");
            }
        },
        error: function(){
            alert("请求出错了");
        }
    });
    return false;
}

function fn_check_all_items()
{
    var box = $("#check_all_counters");
    if(box.prop("checked")){
        $("#tbody-counters").find("input:checkbox").each(function(i, o){
            $(o).prop("checked", true);
        });
    }else{
        $("#tbody-counters").find("input:checkbox").each(function(i, o){
            $(o).prop("checked", false);
        });
    }
}

function fn_check_all_hosts()
{
    var box = $("#check_all_endpoints");
    if(box.prop("checked")){
        $("#tbody-endpoints").find("input:checkbox").each(function(i, o){
            $(o).prop("checked", true);
        });
    }else{
        $("#tbody-endpoints").find("input:checkbox").each(function(i, o){
            $(o).prop("checked", false);
        });
    }
}

function fn_filter_group()
{
    var filter_text = $("#group-filter").val().toLowerCase();
    var group_objs = $(".group");
    if(!filter_text){
        group_objs.each(function(i, obj){
            $(obj).show();
        });
    }else if (filter_text.length <= 2) {
    }else{
        group_objs.each(function(i, obj){
            var groupname = $($(obj).children("a")[0]).attr("data-gname");
            if(groupname.toLowerCase().indexOf(filter_text) === -1){
                $(obj).hide();
            }else{
                $(obj).show();
            }
        });
        fn_collapse_in_groups();
    }
};

function fn_collapse_in_groups()
{
    $(".accordion-body").each(function(i, obj){
        if(!$(obj).hasClass("in")){
            $(obj).collapse("show");
        }
    });
};
<|endoftext|>


<commit_before>var FS = require('fs')
var Path = require('path')

var myfiles = ['install.js', 'package.json', 'readme.md', 'license.md', '.git']

function run() {
  var lifecycle = process.env.npm_lifecycle_event
  var ntm = process.cwd()
  var node_modules = Path.join(ntm, '..')
  if (Path.basename(node_modules) !== 'node_modules')
    return // Allows npm link; will do nothing
  var parent = Path.join(node_modules, '..')

  var package
  try {
    package = require(Path.join(parent, 'package'))
  }
  catch (err) {
    return // Allows npm link; will do nothing
  }

  var opts = package.ntm || {}

  var danger = !!opts.danger
  var linkedDirPath
  if (danger) {
    linkedDirPath = ntm
  }
  else if (opts.alias) {
    linkedDirPath = Path.join(node_modules, opts.alias)
  }
  else {
    // Use default path ntm/mod
    linkedDirPath = Path.join(ntm, 'mod')
  }

  if (danger) {
    // Remove all symlinks
    var filesToDelete = FS.readdirSync(ntm)
    filesToDelete.forEach(function(file) {
      var stat = FS.lstatSync(file)
      if (stat.isSymbolicLink()) {
        FS.unlinkSync(file)
      }
    })
  }
  else {
    // Delete existing dir
    try {
      FS.unlinkSync(linkedDirPath)
    }
    catch (err) {}
  }

  if (lifecycle === 'preuninstall') return true

  // Get source dir
  var sourcePath
  sourcePath = Path.join(parent, opts.src || 'mod')

  if (danger) {
    // Get contents and symlink each
    var files = FS.readdirSync(sourcePath)
    files.forEach(function(file) {
      var realfile = Path.join(sourcePath, file)
      var basename = Path.basename(file)
      console.log(('file'), file)
      console.log(('basename'), basename)
      // Deny overwriting ntm files.
      if (!myfiles.some(function(myfile) { return basename === myfile })) {
        // Symlink
        var stat = FS.statSync(realfile)
        if (stat.isFile()) {
          FS.symlinkSync(file, Path.join(ntm, Path.basename(file)), 'junction')
        }
        else {
          FS.symlinkSync(file, Path.join(ntm, Path.basename(file)), 'junction')
        }
      }
    })
  }
  else {
    // Symlink
    FS.symlinkSync(sourcePath, linkedDirPath, 'junction')
  }

  console.log('calling install script') // DEBUG
  console.log(1) // DEBUG
  var installFile = opts.install || 'install'
  console.log('installFile', installFile) // DEBUG
  require(Path.join(sourcePath, installFile))
  console.log(2) // DEBUG

  return true
}

module.exports = run

if (require.main === module) {
  try {
    run()
  }
  catch (err) {
    console.error(err.stack)
    process.exit(1)
  }
}
<commit_msg>better file checking; removed debug stuff<commit_after>var FS = require('fs')
var Path = require('path')

var myfiles = ['install.js', 'package.json', 'readme.md', 'license.md', '.git']

function run() {
  var lifecycle = process.env.npm_lifecycle_event
  var ntm = process.cwd()
  var node_modules = Path.join(ntm, '..')
  if (Path.basename(node_modules) !== 'node_modules')
    return // Allows npm link; will do nothing
  var parent = Path.join(node_modules, '..')

  var package
  try {
    package = require(Path.join(parent, 'package'))
  }
  catch (err) {
    return // Allows npm link; will do nothing
  }

  var opts = package.ntm || {}

  var danger = !!opts.danger
  var linkedDirPath
  if (danger) {
    linkedDirPath = ntm
  }
  else if (opts.alias) {
    linkedDirPath = Path.join(node_modules, opts.alias)
  }
  else {
    // Use default path ntm/mod
    linkedDirPath = Path.join(ntm, 'mod')
  }

  if (danger) {
    // Remove all symlinks
    var filesToDelete = FS.readdirSync(ntm)
    filesToDelete.forEach(function(file) {
      var stat = FS.lstatSync(file)
      if (stat.isSymbolicLink()) {
        FS.unlinkSync(file)
      }
    })
  }
  else {
    // Delete existing dir
    try {
      FS.unlinkSync(linkedDirPath)
    }
    catch (err) {}
  }

  if (lifecycle === 'preuninstall') return true

  // Get source dir
  var sourcePath
  sourcePath = Path.join(parent, opts.src || 'mod')
  try {
    FS.statSync(sourcePath)
  }
  catch (err) {
    // Abort
    return
  }

  if (danger) {
    // Get contents and symlink each
    var files = FS.readdirSync(sourcePath)
    files.forEach(function(file) {
      var realfile = Path.join(sourcePath, file)
      var basename = Path.basename(file)
      console.log(('file'), file)
      console.log(('basename'), basename)
      // Deny overwriting ntm files.
      if (!myfiles.some(function(myfile) { return basename === myfile })) {
        // Symlink
        var stat = FS.statSync(realfile)
        if (stat.isFile()) {
          FS.symlinkSync(file, Path.join(ntm, Path.basename(file)), 'junction')
        }
        else {
          FS.symlinkSync(file, Path.join(ntm, Path.basename(file)), 'junction')
        }
      }
    })
  }
  else {
    // Symlink
    FS.symlinkSync(sourcePath, linkedDirPath, 'junction')
  }

  var installFile = opts.install || 'install'
  installFile = Path.join(sourcePath, installFile)
  var installFileExists
  try {
    require.resolve(installFile)
    installFileExists = true
  }
  catch (err) {
  }
  if (installFileExists) {
    require(installFile)
  }

  return true
}

module.exports = run

if (require.main === module) {
  try {
    run()
  }
  catch (err) {
    console.error(err.stack)
    process.exit(1)
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
var clc = require('cli-color');
var maxNickLen = 12;
var fileBuf = 4096; // how many characters of file to remember
var path = require('path');
var myNick = "FruitieX";
var hilight_re = new RegExp(".*" + myNick + ".*", 'i');
var ansi_escape_re = /\x1b[^m]*m/;
var readline = require('readline');
var fs = require('fs');

var rlv = require('readline-vim');

var separatorColor = clc.xterm(239);
var numColor = clc.xterm(232).bgXterm(255);
var chanColor = clc.xterm(255).bgXterm(239);
var chanInsertColor = clc.xterm(232).bgXterm(255);
var myNickColor = 1;
var hilightColor = 3;

var openChan = function(filePath) {
	var out, rli, vim;
	var completions = [];

	var outFileName = filePath + '/out';
	var inFileName = filePath + '/in';
	var file = fs.readFileSync(outFileName, 'utf8');
	var inFile = fs.openSync(inFileName, 'a');

	var basename = path.basename(filePath);
	var num_s = basename.substring(0, basename.indexOf('_'));
	var num = "";
	if(num_s !== "") {
		num_s = ' ' + num_s + ' ';
		num = numColor(num_s);
	}
	var chan_s = ' ' + basename.substring(basename.indexOf('_') + 1, basename.length) + ' ';
	var chan = chanColor(chan_s);
	var chan_insert = chanInsertColor(chan_s);

	// prompt printing function, assumes cursor is inside the prompt where node
	// readline expects it to be. cursor position is restored after printing the
	// prompt unless cursorAfterPrompt is given, then it's put after the input line
	var printPrompt = function(cursorAfterPrompt) {
		if(!cursorAfterPrompt)
			process.stdout.write('\033[s'); // store cursor position

		process.stdout.write('\r'); // move cursor to beginning of line

		// for multiline inputs move cursor up to prompt before printing
		var inputLength = num_s.length + chan_s.length + rli.cursor + 1;
		if(inputLength + 1 > process.stdout.columns)
			process.stdout.write('\033[' + Math.floor(inputLength / process.stdout.columns) + 'A');

		if(vim.isnormal)
			process.stdout.write(num + chan + ' ');
		else
			process.stdout.write(num + chan_insert + ' ');
		process.stdout.write(rli.line);

		if(!cursorAfterPrompt)
			process.stdout.write('\033[u'); // restore cursor position
	};

	var clearPrompt = function() {
		// move cursor to beginning of prompt first
		var inputLength = num_s.length + chan_s.length + rli.line.length;
		process.stdout.write('\033[' + Math.floor(process.stdout.rows - inputLength / process.stdout.columns + 1) + ';0H');
		// clear channel name + prompt
		process.stdout.write(Array(num_s.length + chan_s.length + 1 + rli.line.length + 1).join(' '));
	};

	// prints line at current terminal cursor position
	var printLine = function(line) {
		var hilight = false;

		// remove timestamps
		line = line.replace(/^([^ ]+ ){2}/, '');
		line = line.split(' ');
		// remove <> around nick
		var nick = line[0].substring(1, line[0].length - 1);

		if(completions.indexOf(nick) === -1) {
			completions.push(nick);
		}

		if(nick === myNick) {
			clrnick = myNickColor;
		} else {
			// nick color, avoids dark colors
			var clrnick = 0;
			for(var i = 0; i < nick.length; i++) {
				clrnick += nick.charCodeAt(i);
			}
			clrnick = Math.pow(clrnick, 2) + clrnick * 2;
			clrnick = clrnick % 255;
			switch(clrnick) {
				case 18: case 22: case 23: case 24:
					clrnick += 3; break;
				case 52: case 53: case 54: case 55: case 56: case 57: case 88: case 89:
					clrnick += 6; break;
				case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239:
					clrnick += 8; break;
				case 0: case 8: case 19: case 22:
					clrnick++; break;
			}
		}

		// limit nicklen
		nick = nick.substr(0, maxNickLen);
		// align nicks
		process.stdout.write(Array(maxNickLen - nick.length + 1).join(' '));
		process.stdout.write(clc.xterm(clrnick)(nick) + separatorColor(':'));
		process.stdout.write(' ');

		// remove nick from line array, now contains space separated text message
		line.shift();
		line = line.join(' ');
		line = line.replace(ansi_escape_re, '');

		// 'fix' encoding of people not using utf-8...
		line = line.replace(/�/g, 'å');
		line = line.replace(/�/g, 'ä');
		line = line.replace(/�/g, 'ö');

		if(line.match(hilight_re))
			hilight = true;

		var textColor = 15;
		if (nick === myNick)
			textColor = myNickColor;
		else if (hilight)
			textColor = hilightColor;

		// separator takes up 1 char + whitespace
		var availWidth = process.stdout.columns - maxNickLen - 2;

		var wrappedChars = 0;
		var i = 0;

		// terminal too small? don't print anything
		if(availWidth <= 5)
			return;

		while(i * availWidth - wrappedChars < line.length) {
			var start = i * availWidth - wrappedChars;
			var curLine = line.substr(start, availWidth);
			// remove leading space on next line
			curLine.replace(/^\s+/, '');
			// line wrap at word boundary only if there is whitespace on this line
			if(start + availWidth < line.length && curLine.lastIndexOf(' ') !== -1) {
				curLine = curLine.slice(0, curLine.lastIndexOf(' '));
				// remove whitespace
				wrappedChars--;
			}

			wrappedChars += availWidth - curLine.length;

			// empty space on line wrap
			if (i > 0)
				process.stdout.write(Array(process.stdout.columns - availWidth + 1).join(' '));

			process.stdout.write(clc.xterm(textColor)(curLine));
			process.stdout.write('\n');
			i++;
		}
		/*
		process.stdout.write(num_s + chan_s + ' ');
		setTimeout(printPrompt);
		*/
	};

	// log entire file
	var redraw = function() {
		// clear prompt, then move cursor to beginning of prompt
		clearPrompt();
		var inputLength = num_s.length + chan_s.length + rli.line.length;
		process.stdout.write('\033[' + Math.floor(process.stdout.rows - inputLength / process.stdout.columns + 1) + ';0H');

		var start = 0;
		while (start < file.length) {
			var end = file.indexOf('\n', start);
			if(end === -1)
				break;

			//process.stdout.write('\n');
			printLine(file.substring(start, end));
			start = end + 1;
		}
		// make room for the prompt and redraw it
		// TODO: why should this be rli.cursor
		var inputLength = num_s.length + chan_s.length + 1 + rli.cursor;
		for(var i = 0; i < Math.floor(inputLength / process.stdout.columns); i++) {
			process.stdout.write('\n');
		}
	};

	// limit file to size fileBuf
	if(file.length >= fileBuf) {
		file = file.substr(file.length - fileBuf, file.length);
		file = file.substr(file.indexOf("\n") + 1, file.length);
	}

	// 'ctrl-c' = quitting
	process.stdin.on('data', function(key) {
		if(key == '\u0003') process.exit();
	});
	process.stdin.on('data', function(key) {
		var keyHex = key.toString('hex');

		// previous channel (alt + h)
		if(keyHex === '1b68') process.exit(10);
		// next channel (alt + l)
		else if(keyHex === '1b6c') process.exit(11);
		// jump to this channel (alt + 1-9)
		else if(keyHex.substring(0, 3) === '1b3' &&
			keyHex.substring(3) !== '0') process.exit(keyHex.substring(3));

		// DEBUG: uncomment this line to find the keycodes
		//console.log(key.toString('hex'));
	});

	var rli = readline.createInterface({
		input: process.stdin,
		output: process.stdout,
		completer: function(line) {
			var word = line.substring(line.lastIndexOf(" ") + 1);
			var hits = completions.filter(function(c) { return c.indexOf(word) == 0 })
			for(var i = hits.length - 1; i >= 0; i--) {
				if(hits[i] === '!' || hits[i] === '***') {
					hits.splice(i, 1);
				} else if (word === line) { // typing at beginning of line
					hits[i] += ': ';
				} else {
					hits[i] += ', ';
				}
			}
			return [hits, word]
		}
	});

	// watch file for changes
	Tail = require('tail').Tail;
	out = new Tail(outFileName);
	out.on('line', function(data) {
		file += data + '\n';
		// limit file to size fileBuf
		if(file.length >= fileBuf) {
			file = file.substr(file.length - fileBuf, file.length);
			file = file.substr(file.indexOf("\n") + 1, file.length);
		}

		process.stdout.write('\033[s'); // store cursor position

		// clear prompt, then move cursor to beginning of prompt
		clearPrompt();
		var inputLength = num_s.length + chan_s.length + rli.line.length;
		process.stdout.write('\033[' + Math.floor(process.stdout.rows - inputLength / process.stdout.columns + 1) + ';0H');

		// print line
		printLine(data);

		// make room for prompt
		// TODO: WHY should we use rli.cursor not rli.line.length here?
		// otherwise we get too many newlines when cursor is positioned on an input
		// line != the last one. IDGI.
		inputLength = num_s.length + chan_s.length + rli.cursor;
		for(var i = 0; i < Math.floor(inputLength / process.stdout.columns); i++) {
			process.stdout.write('\n');
		}

		printPrompt(true); // print prompt again, but let us handle cursor restoring
		process.stdout.write('\033[u'); // restore cursor position
	});

	process.stdout.on('resize', function() {
		var cursorPos = rli.cursor;
		process.stdout.write('\u001B[2J\u001B[0;0f'); // clear terminal
		redraw();

		// hack: print our prompt after node readline prints its prompt ;)
		setTimeout(function() {
			printPrompt(true);
			var inputLength = num_s.length + chan_s.length + 1 + rli.line.length;
			// move cursor to beginning of first prompt line
			process.stdout.write('\033[' + Math.floor(process.stdout.rows - inputLength / process.stdout.columns + 1) + ';0H');

			// move cursor back to where it was:
			// down
			if((num_s.length + chan_s.length + 1 + cursorPos) >= process.stdout.columns)
				process.stdout.write('\033[' + Math.floor((num_s.length + chan_s.length + 1 + cursorPos) / process.stdout.columns) + 'B');
			// right
			if((num_s.length + chan_s.length + 1 + cursorPos) % process.stdout.columns)
				process.stdout.write('\033[' + (num_s.length + chan_s.length + 1 + cursorPos) % process.stdout.columns + 'C');
		});
	});

	rli.setPrompt(num_s + chan_s + ' ');
	rli.on('line', function(cmd) {
		redraw();
		printPrompt(true);

		var msg_s = cmd;
		if(msg_s.substring(0, 3) === '/bl') {
			msg_s = "/privmsg *backlog " + chan_s + msg_s.substring(4);
		}

		var msg = new Buffer(msg_s + '\n', 'utf8');
		fs.writeSync(inFile, msg, 0, msg.length, null);
	});

	vim = rlv(rli, function() {
		printPrompt();
	});
	vim.threshold = 500;
	var map = vim.map;
	map.insert('jj', 'esc');

	// clear terminal and print file contents at launch
	process.stdout.write('\u001B[2J\u001B[0;0f');
	redraw();
	printPrompt(true);

	// start in insert mode
	vim.forceInsert();
};

openChan(process.argv[2]);
<commit_msg>clear entire terminal at redraw()<commit_after>#!/usr/bin/env node
var clc = require('cli-color');
var maxNickLen = 12;
var fileBuf = 4096; // how many characters of file to remember
var path = require('path');
var myNick = "FruitieX";
var hilight_re = new RegExp(".*" + myNick + ".*", 'i');
var ansi_escape_re = /\x1b[^m]*m/;
var readline = require('readline');
var fs = require('fs');

var rlv = require('readline-vim');

var separatorColor = clc.xterm(239);
var numColor = clc.xterm(232).bgXterm(255);
var chanColor = clc.xterm(255).bgXterm(239);
var chanInsertColor = clc.xterm(232).bgXterm(255);
var myNickColor = 1;
var hilightColor = 3;

var openChan = function(filePath) {
	var out, rli, vim;
	var completions = [];

	var outFileName = filePath + '/out';
	var inFileName = filePath + '/in';
	var file = fs.readFileSync(outFileName, 'utf8');
	var inFile = fs.openSync(inFileName, 'a');

	var basename = path.basename(filePath);
	var num_s = basename.substring(0, basename.indexOf('_'));
	var num = "";
	if(num_s !== "") {
		num_s = ' ' + num_s + ' ';
		num = numColor(num_s);
	}
	var chan_s = ' ' + basename.substring(basename.indexOf('_') + 1, basename.length) + ' ';
	var chan = chanColor(chan_s);
	var chan_insert = chanInsertColor(chan_s);

	// prompt printing function, assumes cursor is inside the prompt where node
	// readline expects it to be. cursor position is restored after printing the
	// prompt unless cursorAfterPrompt is given, then it's put after the input line
	var printPrompt = function(cursorAfterPrompt) {
		if(!cursorAfterPrompt)
			process.stdout.write('\033[s'); // store cursor position

		process.stdout.write('\r'); // move cursor to beginning of line

		// for multiline inputs move cursor up to prompt before printing
		var inputLength = num_s.length + chan_s.length + rli.cursor + 1;
		if(inputLength + 1 > process.stdout.columns)
			process.stdout.write('\033[' + Math.floor(inputLength / process.stdout.columns) + 'A');

		if(vim.isnormal)
			process.stdout.write(num + chan + ' ');
		else
			process.stdout.write(num + chan_insert + ' ');
		process.stdout.write(rli.line);

		if(!cursorAfterPrompt)
			process.stdout.write('\033[u'); // restore cursor position
	};

	var clearPrompt = function() {
		// move cursor to beginning of prompt first
		var inputLength = num_s.length + chan_s.length + rli.line.length;
		process.stdout.write('\033[' + Math.floor(process.stdout.rows - inputLength / process.stdout.columns + 1) + ';0H');
		// clear channel name + prompt
		process.stdout.write(Array(num_s.length + chan_s.length + 1 + rli.line.length + 1).join(' '));
	};

	// prints line at current terminal cursor position
	var printLine = function(line) {
		var hilight = false;

		// remove timestamps
		line = line.replace(/^([^ ]+ ){2}/, '');
		line = line.split(' ');
		// remove <> around nick
		var nick = line[0].substring(1, line[0].length - 1);

		if(completions.indexOf(nick) === -1) {
			completions.push(nick);
		}

		if(nick === myNick) {
			clrnick = myNickColor;
		} else {
			// nick color, avoids dark colors
			var clrnick = 0;
			for(var i = 0; i < nick.length; i++) {
				clrnick += nick.charCodeAt(i);
			}
			clrnick = Math.pow(clrnick, 2) + clrnick * 2;
			clrnick = clrnick % 255;
			switch(clrnick) {
				case 18: case 22: case 23: case 24:
					clrnick += 3; break;
				case 52: case 53: case 54: case 55: case 56: case 57: case 88: case 89:
					clrnick += 6; break;
				case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239:
					clrnick += 8; break;
				case 0: case 8: case 19: case 22:
					clrnick++; break;
			}
		}

		// limit nicklen
		nick = nick.substr(0, maxNickLen);
		// align nicks
		process.stdout.write(Array(maxNickLen - nick.length + 1).join(' '));
		process.stdout.write(clc.xterm(clrnick)(nick) + separatorColor(':'));
		process.stdout.write(' ');

		// remove nick from line array, now contains space separated text message
		line.shift();
		line = line.join(' ');
		line = line.replace(ansi_escape_re, '');

		// 'fix' encoding of people not using utf-8...
		line = line.replace(/�/g, 'å');
		line = line.replace(/�/g, 'ä');
		line = line.replace(/�/g, 'ö');

		if(line.match(hilight_re))
			hilight = true;

		var textColor = 15;
		if (nick === myNick)
			textColor = myNickColor;
		else if (hilight)
			textColor = hilightColor;

		// separator takes up 1 char + whitespace
		var availWidth = process.stdout.columns - maxNickLen - 2;

		var wrappedChars = 0;
		var i = 0;

		// terminal too small? don't print anything
		if(availWidth <= 5)
			return;

		while(i * availWidth - wrappedChars < line.length) {
			var start = i * availWidth - wrappedChars;
			var curLine = line.substr(start, availWidth);
			// remove leading space on next line
			curLine.replace(/^\s+/, '');
			// line wrap at word boundary only if there is whitespace on this line
			if(start + availWidth < line.length && curLine.lastIndexOf(' ') !== -1) {
				curLine = curLine.slice(0, curLine.lastIndexOf(' '));
				// remove whitespace
				wrappedChars--;
			}

			wrappedChars += availWidth - curLine.length;

			// empty space on line wrap
			if (i > 0)
				process.stdout.write(Array(process.stdout.columns - availWidth + 1).join(' '));

			process.stdout.write(clc.xterm(textColor)(curLine));
			process.stdout.write('\n');
			i++;
		}
		/*
		process.stdout.write(num_s + chan_s + ' ');
		setTimeout(printPrompt);
		*/
	};

	// log entire file
	var redraw = function() {
		process.stdout.write('\u001B[2J\u001B[0;0f'); // clear terminal
		var inputLength = num_s.length + chan_s.length + rli.line.length;
		process.stdout.write('\033[' + Math.floor(process.stdout.rows - inputLength / process.stdout.columns + 1) + ';0H');

		var start = 0;
		while (start < file.length) {
			var end = file.indexOf('\n', start);
			if(end === -1)
				break;

			//process.stdout.write('\n');
			printLine(file.substring(start, end));
			start = end + 1;
		}
		// make room for the prompt and redraw it
		// TODO: why should this be rli.cursor
		var inputLength = num_s.length + chan_s.length + 1 + rli.cursor;
		for(var i = 0; i < Math.floor(inputLength / process.stdout.columns); i++) {
			process.stdout.write('\n');
		}
	};

	// limit file to size fileBuf
	if(file.length >= fileBuf) {
		file = file.substr(file.length - fileBuf, file.length);
		file = file.substr(file.indexOf("\n") + 1, file.length);
	}

	// 'ctrl-c' = quitting
	process.stdin.on('data', function(key) {
		if(key == '\u0003') process.exit();
	});
	process.stdin.on('data', function(key) {
		var keyHex = key.toString('hex');

		// previous channel (alt + h)
		if(keyHex === '1b68') process.exit(10);
		// next channel (alt + l)
		else if(keyHex === '1b6c') process.exit(11);
		// jump to this channel (alt + 1-9)
		else if(keyHex.substring(0, 3) === '1b3' &&
			keyHex.substring(3) !== '0') process.exit(keyHex.substring(3));

		// DEBUG: uncomment this line to find the keycodes
		//console.log(key.toString('hex'));
	});

	var rli = readline.createInterface({
		input: process.stdin,
		output: process.stdout,
		completer: function(line) {
			var word = line.substring(line.lastIndexOf(" ") + 1);
			var hits = completions.filter(function(c) { return c.indexOf(word) == 0 })
			for(var i = hits.length - 1; i >= 0; i--) {
				if(hits[i] === '!' || hits[i] === '***') {
					hits.splice(i, 1);
				} else if (word === line) { // typing at beginning of line
					hits[i] += ': ';
				} else {
					hits[i] += ', ';
				}
			}
			return [hits, word]
		}
	});

	// watch file for changes
	Tail = require('tail').Tail;
	out = new Tail(outFileName);
	out.on('line', function(data) {
		file += data + '\n';
		// limit file to size fileBuf
		if(file.length >= fileBuf) {
			file = file.substr(file.length - fileBuf, file.length);
			file = file.substr(file.indexOf("\n") + 1, file.length);
		}

		process.stdout.write('\033[s'); // store cursor position

		// clear prompt, then move cursor to beginning of prompt
		clearPrompt();
		var inputLength = num_s.length + chan_s.length + rli.line.length;
		process.stdout.write('\033[' + Math.floor(process.stdout.rows - inputLength / process.stdout.columns + 1) + ';0H');

		// print line
		printLine(data);

		// make room for prompt
		// TODO: WHY should we use rli.cursor not rli.line.length here?
		// otherwise we get too many newlines when cursor is positioned on an input
		// line != the last one. IDGI.
		inputLength = num_s.length + chan_s.length + rli.cursor;
		for(var i = 0; i < Math.floor(inputLength / process.stdout.columns); i++) {
			process.stdout.write('\n');
		}

		printPrompt(true); // print prompt again, but let us handle cursor restoring
		process.stdout.write('\033[u'); // restore cursor position
	});

	process.stdout.on('resize', function() {
		var cursorPos = rli.cursor;
		process.stdout.write('\u001B[2J\u001B[0;0f'); // clear terminal
		redraw();

		// hack: print our prompt after node readline prints its prompt ;)
		setTimeout(function() {
			printPrompt(true);
			var inputLength = num_s.length + chan_s.length + 1 + rli.line.length;
			// move cursor to beginning of first prompt line
			process.stdout.write('\033[' + Math.floor(process.stdout.rows - inputLength / process.stdout.columns + 1) + ';0H');

			// move cursor back to where it was:
			// down
			if((num_s.length + chan_s.length + 1 + cursorPos) >= process.stdout.columns)
				process.stdout.write('\033[' + Math.floor((num_s.length + chan_s.length + 1 + cursorPos) / process.stdout.columns) + 'B');
			// right
			if((num_s.length + chan_s.length + 1 + cursorPos) % process.stdout.columns)
				process.stdout.write('\033[' + (num_s.length + chan_s.length + 1 + cursorPos) % process.stdout.columns + 'C');
		});
	});

	rli.setPrompt(num_s + chan_s + ' ');
	rli.on('line', function(cmd) {
		redraw();
		printPrompt(true);

		var msg_s = cmd;
		if(msg_s.substring(0, 3) === '/bl') {
			msg_s = "/privmsg *backlog " + chan_s + msg_s.substring(4);
		}

		var msg = new Buffer(msg_s + '\n', 'utf8');
		fs.writeSync(inFile, msg, 0, msg.length, null);
	});

	vim = rlv(rli, function() {
		printPrompt();
	});
	vim.threshold = 500;
	var map = vim.map;
	map.insert('jj', 'esc');

	// clear terminal and print file contents at launch
	process.stdout.write('\u001B[2J\u001B[0;0f');
	redraw();
	printPrompt(true);

	// start in insert mode
	vim.forceInsert();
};

openChan(process.argv[2]);
<|endoftext|>


<commit_before><commit_msg>Add more info logging<commit_after><|endoftext|>


<commit_before>import App from './components/App';
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import configureStore from './store';

const store = configureStore();

function setup() {
  class Root extends React.Component {
    render() {
      return (
        <Provider store={store}>
          <App />
        </Provider>
      );
    }
  }

  return Root;
}

module.exports = setup;
<commit_msg>clean up setup.js<commit_after>import App from './components/App';
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import configureStore from './store';

const store = configureStore();

function setup() {
  class Root extends Component {
    render() {
      return (
        <Provider store={store}>
          <App />
        </Provider>
      );
    }
  }

  return Root;
}

module.exports = setup;
<|endoftext|>


<commit_before><commit_msg>positioning tweak<commit_after><|endoftext|>


<commit_before><commit_msg>add z carriage angle, spindle mt<commit_after><|endoftext|>


<commit_before>import { parseQuery } from '../utils';

describe('parseQuery function', () => {
  test('Returns siteId', () => {
    const query = {
      siteId: 'FRONTA2CLMLh6f2Wa',
    };

    expect(parseQuery(query).siteId).toBe('FRONTA2CLMLh6f2Wa');
  });

  test('Returns initialUrl', () => {
    const query = {
      initialUrl: 'https://frontity.com/',
    };

    expect(parseQuery(query).initialUrl).toBe('https://frontity.com/');
  });

  test('Returns perPage', () => {
    const query = {
      perPage: '10',
    };

    expect(parseQuery(query).perPage).toBe('10');
  });

  test('Returns the right env', () => {
    const query = {};

    expect(parseQuery(query).env).toBe('pre');

    query.env = 'pre';

    expect(parseQuery(query).env).toBe('pre');

    query.env = 'prod';

    expect(parseQuery(query).env).toBe('prod');
  });

  test('Returns the right device', () => {
    const query = {};

    expect(parseQuery(query).device).toBe('mobile');

    query.device = 'mobile';

    expect(parseQuery(query).device).toBe('mobile');

    query.device = 'tablet';

    expect(parseQuery(query).device).toBe('tablet');
  });

  test('Query has no type/listType/singleType, nor id/listId/singleId nor page', () => {
    const query = {};
    const result = parseQuery(query);

    expect(result.type).toBe('latest');
    expect(result.id).toBe('post');
    expect(result.page).toBe(1);
  });

  test('Query has singleType and singleId', () => {
    const query = {
      singleType: 'post',
      singleId: '60',
    };
    const result = parseQuery(query);

    expect(result.type).toBe('post');
    expect(result.id).toBe(60);
    expect(result.page).toBeUndefined();
  });

  test('Query has listType and listId', () => {
    const query = {
      listType: 'category',
      listId: '3',
    };
    const result = parseQuery(query);

    expect(result.type).toBe('category');
    expect(result.id).toBe(3);
    expect(result.page).toBe(1);
  });

  test('Query has listType, listId, and page', () => {
    const query = {
      listType: 'category',
      listId: '3',
      page: '2',
    };
    const result = parseQuery(query);

    expect(result.type).toBe('category');
    expect(result.id).toBe(3);
    expect(result.page).toBe(2);
  });

  test('Query has type and id', () => {
    const query = {
      type: 'post',
      id: 60,
    };
    const result = parseQuery(query);

    expect(result.type).toBe('post');
    expect(result.id).toBe(60);
    expect(result.page).toBeUndefined();
  });

  test('Query has type, id and page', () => {
    const query = {
      type: 'latest',
      id: 'post',
      page: '2',
    };
    const result = parseQuery(query);

    expect(result.type).toBe('latest');
    expect(result.id).toBe('post');
    expect(result.page).toBe(2);
  });
});
<commit_msg>Add a couple of test to utils<commit_after>import { parseQuery } from '../utils';

describe('parseQuery function', () => {
  test('Returns siteId', () => {
    const query = {
      siteId: 'FRONTA2CLMLh6f2Wa',
    };

    expect(parseQuery(query).siteId).toBe('FRONTA2CLMLh6f2Wa');
  });

  test('Returns initialUrl', () => {
    const query = {
      initialUrl: 'https://frontity.com/',
    };

    expect(parseQuery(query).initialUrl).toBe('https://frontity.com/');
  });

  test('Returns incomplete initialUrl', () => {
    const query = {
      initialUrl: '/some-post/with-stuff',
    };

    expect(parseQuery(query).initialUrl).toBe('/some-post/with-stuff');
  });

  test('Returns perPage', () => {
    const query = {
      perPage: '11',
    };

    expect(parseQuery(query).perPage).toBe('11');
  });

  test('Returns the right env', () => {
    const query = {};

    expect(parseQuery(query).env).toBe('pre');

    query.env = 'pre';

    expect(parseQuery(query).env).toBe('pre');

    query.env = 'prod';

    expect(parseQuery(query).env).toBe('prod');
  });

  test('Returns the right device', () => {
    const query = {};

    expect(parseQuery(query).device).toBe('mobile');

    query.device = 'mobile';

    expect(parseQuery(query).device).toBe('mobile');

    query.device = 'tablet';

    expect(parseQuery(query).device).toBe('tablet');
  });

  test('Query has no type/listType/singleType, nor id/listId/singleId nor page', () => {
    const query = {};
    const result = parseQuery(query);

    expect(result.type).toBe('latest');
    expect(result.id).toBe('post');
    expect(result.page).toBe(1);
  });

  test('Query has singleType and singleId', () => {
    const query = {
      singleType: 'post',
      singleId: '60',
    };
    const result = parseQuery(query);

    expect(result.type).toBe('post');
    expect(result.id).toBe(60);
    expect(result.page).toBeUndefined();
  });

  test('Query has listType and listId', () => {
    const query = {
      listType: 'category',
      listId: '3',
    };
    const result = parseQuery(query);

    expect(result.type).toBe('category');
    expect(result.id).toBe(3);
    expect(result.page).toBe(1);
  });

  test('Query has listType, listId, and page', () => {
    const query = {
      listType: 'category',
      listId: '3',
      page: '2',
    };
    const result = parseQuery(query);

    expect(result.type).toBe('category');
    expect(result.id).toBe(3);
    expect(result.page).toBe(2);
  });

  test('Query has type and id', () => {
    const query = {
      type: 'post',
      id: 60,
    };
    const result = parseQuery(query);

    expect(result.type).toBe('post');
    expect(result.id).toBe(60);
    expect(result.page).toBeUndefined();
  });

  test('Query has type, id and page', () => {
    const query = {
      type: 'latest',
      id: 'post',
      page: '2',
    };
    const result = parseQuery(query);

    expect(result.type).toBe('latest');
    expect(result.id).toBe('post');
    expect(result.page).toBe(2);
  });
});
<|endoftext|>


<commit_before>/*
    --------------- jSQL ---------------
    a SQL like database using javascript
    website: http://jsql.us
    licence: MIT Licence
    version: 0.2.0 dev

    description: using jSQL to process the data easily.
*/

(function() {
    var push             = Array.prototype.push,
        slice            = Array.prototype.slice,
        unshift          = Array.prototype.unshift,
        toString         = Object.prototype.toString,
        hasOwnProperty   = Object.prototype.hasOwnProperty;

    var nativeForEach      = Array.prototype.forEach,
        nativeMap          = Array.prototype.map,
        nativeReduce       = Array.prototype.reduce,
        nativeReduceRight  = Array.prototype.reduceRight,
        nativeFilter       = Array.prototype.filter,
        nativeEvery        = Array.prototype.every,
        nativeSome         = Array.prototype.some,
        nativeIndexOf      = Array.prototype.indexOf,
        nativeLastIndexOf  = Array.prototype.lastIndexOf,
        nativeIsArray      = Array.isArray,
        nativeKeys         = Object.keys,
        nativeBind         = Function.prototype.bind;

    var _jSQL, jSQL, _DB = {};

    if(typeof(this.jSQL) !== 'undefined') {
        _jSQL = this.jSQL;
    }

    jSQL = function() {
        this.init.apply(this,arguments);
    };

    jSQL.prototype = {
        init: function() {
            this._jSQL = _jSQL;
            this._DB = _DB;
            this._currentDB = null;
            this._buffer = null;
        },

        /**
        * create a new database
        * @param dbname [String]
        * @param db [Object]
        */

        create: function(dbname, db) {
            var indexList;

            if(this._DB.hasOwnProperty(dbname)) {
                throw('DB Already Exist.');
            }

            if(this._isArray(db)) {
                indexList = this._listSlice(arguments, '2:');
                this._appendKey(db, indexList);
            }

            if(this._isObject(db)) {
                db = this._objectToArray(db);
            }

            this._DB[dbname] = db;
            return this;
        },

        /**
        * select an exist database as current
        * @param dbname [String]
        */

        use: function(dbname) {
            if(!this._DB.hasOwnProperty(dbname)) {
                throw('Database Not Exist.');
            }

            this._currentDB = this._DB[dbname];
            return this;
        },

        /**
        * drop an exist database
        * @param dbname [String]
        */

        drop: function(dbname) {
            if(this._DB.hasOwnProperty(dbname)) {
                delete this._DB[dbname];
            }
        },

        dbs: function() {
            return this._keys(this._DB);
        },

        /**
        * select object from currentDB
        * @param key
            '*':     return all
            'a':     return base value which key is 'a'
            'a.b.c': return deep value a->b->c
        *
        */

        select: function(key) {
            if(!this._currentDB) {
                throw('Please Select Database First.');
            }

            this._buffer = this._currentDB; //reset the _buffer

            if(key==='*') {
                return this;
            }

            this.where(function(data) {
                return typeof(this._deep(data, key)) !== 'undefined';
            });

            return this;
        },

        /**
        * get the count of current result set
        */

        count: function() {
            return this._buffer.length;
        },

        /**
        * calculate the total of spec key
        * @param key
            '*':     return first package key count
            'a':     return base value which key is 'a'
            'a.b.c': return deep value a->b->c
        *
        * @use [jSQL instance].total('a.b.c')
        */

        total: function(scope) {
            var rs = 0, _tmp;

            for(var _key in this._currentDB) {
                if(this._currentDB.hasOwnProperty(_key)) {
                    _tmp = scope === '*' ?
                        this._currentDB[_key] :
                        typeof(scope) === 'function' ?
                            scope.call(this, this._currentDB[_key], _key) === true ? true : undefined :
                            this._deep(this._currentDB[_key], scope);

                    if(typeof(_tmp) !== 'undefined') {
                        rs++;
                    }
                }
            }

            return rs;
        },

        /**
        * sort the current result set
        * @param field [String]
        * @param callback [function]
        * @param order [String <asc | desc>]
        */

        orderby: function(field, callback, order) {
            var _array = this._buffer;
            var _this = this;

            if(typeof(callback) !== 'function') {
                callback = [order, order = callback][0];
            }

            _array.sort(function(a, b) {
                a = _this._deep(a, field);
                b = _this._deep(b, field);
                if(callback) {
                    a = callback(a);
                    b = callback(b);
                }
                return  order && order.toLowerCase() === 'asc' ? a - b : b - a;
            });

            this._buffer = _array;
            return this;
        },

        where: function(fn) {
            var _tmp = [], _swap;
            this._buffer = this._buffer || this._currentDB;

            for(var i in this._buffer) {
                if(this._buffer.hasOwnProperty(i)) {
                    if(typeof(fn) === 'function') {
                        _swap = fn.call(this, this._buffer[i], i);
                    }

                    if(this._isArray(fn)) {
                        _swap = false;

                        for(var f in fn) {
                            if(fn.hasOwnProperty(f)) {
                                if(fn[f].call(this, this._buffer[i], i)) {
                                    _swap = true;
                                }
                            }
                        }
                    }

                    if(_swap) {
                        _tmp[i] = this._buffer[i];
                    }
                }
            }
            this._buffer = _tmp;
            return this;
        },

        iterate: function(fn) {
            var _swap;
            this._buffer = this._buffer || this._currentDB;

            for(var i in this._buffer) {
                if(this._buffer.hasOwnProperty(i)) {
                    _swap = fn.call(this, this._buffer[i]);

                    if(_swap) {
                        this.update(i, _swap);
                    }
                }
            }
            return this;
        },

        /**
        * return the current result set
        * @return [Object]
        */

        findAll: function() {
            return this._clone(this._arrayToObject(this._buffer));
        },

        /**
        * return a specified item of current result set
        * if the key doesn't given, it'll return the first item
        * @return [Object]
        */

        find: function(key) {
            if(!key) {
                for(var i in this._buffer) {
                    if(key) {
                        break;
                    }

                    if(this._buffer.hasOwnProperty(i)) {
                        key = i;
                    }
                }
            }

            return this._clone(this._buffer[key]);
        },

        /**
        * return the current result set as array list
        * @return [Object]
        */

        listAll: function() {
            return this._clone(this._buffer);
        },

        /**
        * update the current result set
        * @param key [String]
        * @param data [Object]
        */

        update: function(key, data) {
            if(!this._currentDB) {
                throw('Please Select Database First.');
            }

            if(this._currentDB.hasOwnProperty(key)) {
                this._currentDB[key] = data;
            }
        },

        /**
        * limit the current result set
        * @param start [Number]
        * @param end [Number]
        */

        limit: function(start, end) {
            var _tmp = this._buffer;
            var limit;

            if(!end) {
                start = [0, end = start][0];
            }

            limit = start + ':' + (start + end);

            this._buffer = this._listSlice(_tmp, limit);
            return this;
        },

        keys: function() {
            return this._keys(this.findAll());
        },

        /**
        * private methods
        */

        _deep: function(data, scope) {
            var _tmp = data, scope = scope.split('.');
            for(var i = 0; i < scope.length; i++) {
                _tmp = _tmp[scope[i]];
            }
            return _tmp;
        },

        _isArray: nativeIsArray || function(obj) {
            return toString.call(obj) === '[object Array]';
        },

        _isObject: function(obj) {
            return obj === Object(obj);
        },

        _clone: function(obj) {
            var _tmp = {};

            if (!this._isObject(obj)) return obj;
            if(this._isArray(obj)) return obj.slice();

            for(var i in obj) {
                if(obj.hasOwnProperty(i)) {
                    _tmp[i] = obj[i];
                }
            }
            return _tmp;
        },

        _objectToArray: function(object) {
            var array = [], object = this._clone(object);

            for(var i in object) {
                if(object.hasOwnProperty(i)) {
                    object[i].jSQL_Key = i;
                    array.push(object[i]);
                }
            }

            return array;
        },

        _arrayToObject: function(array, key) {
            var object = {};

            for(var i = 0; i < array.length; i++) {
                object[array[i][key || 'jSQL_Key']] = array[i];
                delete object[array[i][key || 'jSQL_Key']][key || 'jSQL_Key'];
            };

            return object;
        },

        _each: function(list, fn) {
            if(nativeForEach) {
                list.forEach(fn);
                return;
            }

            for(var i = 0; i < list.length; i++) {
                fn(list[i], i, list);
            }
        },

        _keygen: function(object, indexList) {
            var that = this;
            var baseRef = [].slice.call(arguments, 1);
            var key = '';

            if(that._isArray(indexList)) {
                baseRef = indexList;
            }

            that._each(baseRef, function(o, i, r) {
                key += object[o];
            });

            return key;
        },

        _listSlice: function(list, range) {
            var start, end;

            list = [].slice.call(list);
            range = range.split(':');
            start = range[0] || 0;
            end = range.length > 1 ? range[1] || list.length : list.length;
            return [].slice.call(list, start, end);
        },

        _appendKey: function(list, indexList) {
            var that = this;
            that._each(list, function(o, i, r) {
                o['jSQL_Key'] = that._keygen(o, indexList);
            });
        },

        _keys: nativeKeys || (function() {
            var hasDontEnumBug = true,
                dontEnums = [
                    'toString',
                    'toLocaleString',
                    'valueOf',
                    'hasOwnProperty',
                    'isPrototypeOf',
                    'propertyIsEnumerable',
                    'constructor'
                ],
                dontEnumsLength = dontEnums.length;

            for (var key in {'toString': null}) {
                hasDontEnumBug = false;
            }

            return function keys(object) {
                if ((typeof object != 'object' && typeof object != 'function') || object === null) {
                    throw new TypeError('Object.keys called on a non-object');
                }

                var keys = [];
                for (var name in object) {
                    if (object.hasOwnProperty(name)) {
                        keys.push(name);
                    }
                }

                if (hasDontEnumBug) {
                    for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
                        var dontEnum = dontEnums[i];
                        if (object.hasOwnProperty(dontEnum)) {
                            keys.push(dontEnum);
                        }
                    }
                }
                return keys;
            };
        })()
    };

    jSQL = new jSQL();

    typeof(module) !== 'undefined' && module.exports ? module.exports = jSQL : this.jSQL = jSQL;
})();
<commit_msg>update jSQL clone.<commit_after>/*
    --------------- jSQL ---------------
    a SQL like database using javascript
    website: http://jsql.us
    licence: MIT Licence
    version: 0.2.0 dev

    description: using jSQL to process the data easily.
*/

(function() {
    var push             = Array.prototype.push,
        slice            = Array.prototype.slice,
        unshift          = Array.prototype.unshift,
        toString         = Object.prototype.toString,
        hasOwnProperty   = Object.prototype.hasOwnProperty;

    var nativeForEach      = Array.prototype.forEach,
        nativeMap          = Array.prototype.map,
        nativeReduce       = Array.prototype.reduce,
        nativeReduceRight  = Array.prototype.reduceRight,
        nativeFilter       = Array.prototype.filter,
        nativeEvery        = Array.prototype.every,
        nativeSome         = Array.prototype.some,
        nativeIndexOf      = Array.prototype.indexOf,
        nativeLastIndexOf  = Array.prototype.lastIndexOf,
        nativeIsArray      = Array.isArray,
        nativeKeys         = Object.keys,
        nativeBind         = Function.prototype.bind;

    var _jSQL, jSQL, _DB = {};

    if(typeof(this.jSQL) !== 'undefined') {
        _jSQL = this.jSQL;
    }

    jSQL = function() {
        this.init.apply(this,arguments);
    };

    jSQL.prototype = {
        init: function() {
            this._jSQL = _jSQL;
            this._DB = _DB;
            this._currentDB = null;
            this._buffer = null;
        },

        /**
        * create a new database
        * @param dbname [String]
        * @param db [Object]
        */

        create: function(dbname, db) {
            var indexList;

            if(this._DB.hasOwnProperty(dbname)) {
                throw('DB Already Exist.');
            }

            if(this._isArray(db)) {
                indexList = this._listSlice(arguments, '2:');
                this._appendKey(db, indexList);
            }

            if(this._isObject(db)) {
                db = this._objectToArray(db);
            }

            this._DB[dbname] = db;
            return this;
        },

        /**
        * select an exist database as current
        * @param dbname [String]
        */

        use: function(dbname) {
            if(!this._DB.hasOwnProperty(dbname)) {
                throw('Database Not Exist.');
            }

            this._currentDB = this._DB[dbname];
            return this;
        },

        /**
        * drop an exist database
        * @param dbname [String]
        */

        drop: function(dbname) {
            if(this._DB.hasOwnProperty(dbname)) {
                delete this._DB[dbname];
            }
        },

        dbs: function() {
            return this._keys(this._DB);
        },

        /**
        * select object from currentDB
        * @param key
            '*':     return all
            'a':     return base value which key is 'a'
            'a.b.c': return deep value a->b->c
        *
        */

        select: function(key) {
            if(!this._currentDB) {
                throw('Please Select Database First.');
            }

            this._buffer = this._currentDB; //reset the _buffer

            if(key==='*') {
                return this;
            }

            this.where(function(data) {
                return typeof(this._deep(data, key)) !== 'undefined';
            });

            return this;
        },

        /**
        * get the count of current result set
        */

        count: function() {
            return this._buffer.length;
        },

        /**
        * calculate the total of spec key
        * @param key
            '*':     return first package key count
            'a':     return base value which key is 'a'
            'a.b.c': return deep value a->b->c
        *
        * @use [jSQL instance].total('a.b.c')
        */

        total: function(scope) {
            var rs = 0, _tmp;

            for(var _key in this._currentDB) {
                if(this._currentDB.hasOwnProperty(_key)) {
                    _tmp = scope === '*' ?
                        this._currentDB[_key] :
                        typeof(scope) === 'function' ?
                            scope.call(this, this._currentDB[_key], _key) === true ? true : undefined :
                            this._deep(this._currentDB[_key], scope);

                    if(typeof(_tmp) !== 'undefined') {
                        rs++;
                    }
                }
            }

            return rs;
        },

        /**
        * sort the current result set
        * @param field [String]
        * @param callback [function]
        * @param order [String <asc | desc>]
        */

        orderby: function(field, callback, order) {
            var _array = this._buffer;
            var _this = this;

            if(typeof(callback) !== 'function') {
                callback = [order, order = callback][0];
            }

            _array.sort(function(a, b) {
                a = _this._deep(a, field);
                b = _this._deep(b, field);
                if(callback) {
                    a = callback(a);
                    b = callback(b);
                }
                return  order && order.toLowerCase() === 'asc' ? a - b : b - a;
            });

            this._buffer = _array;
            return this;
        },

        where: function(fn) {
            var _tmp = [], _swap;
            this._buffer = this._buffer || this._currentDB;

            for(var i in this._buffer) {
                if(this._buffer.hasOwnProperty(i)) {
                    if(typeof(fn) === 'function') {
                        _swap = fn.call(this, this._buffer[i], i);
                    }

                    if(this._isArray(fn)) {
                        _swap = false;

                        for(var f in fn) {
                            if(fn.hasOwnProperty(f)) {
                                if(fn[f].call(this, this._buffer[i], i)) {
                                    _swap = true;
                                }
                            }
                        }
                    }

                    if(_swap) {
                        _tmp[i] = this._buffer[i];
                    }
                }
            }
            this._buffer = _tmp;
            return this;
        },

        iterate: function(fn) {
            var _swap;
            this._buffer = this._buffer || this._currentDB;

            for(var i in this._buffer) {
                if(this._buffer.hasOwnProperty(i)) {
                    _swap = fn.call(this, this._buffer[i]);

                    if(_swap) {
                        this.update(i, _swap);
                    }
                }
            }
            return this;
        },

        /**
        * return the current result set
        * @return [Object]
        */

        findAll: function() {
            return this._clone(this._arrayToObject(this._buffer));
        },

        /**
        * return a specified item of current result set
        * if the key doesn't given, it'll return the first item
        * @return [Object]
        */

        find: function(key) {
            if(!key) {
                for(var i in this._buffer) {
                    if(key) {
                        break;
                    }

                    if(this._buffer.hasOwnProperty(i)) {
                        key = i;
                    }
                }
            }

            return this._clone(this._buffer[key]);
        },

        /**
        * return the current result set as array list
        * @return [Object]
        */

        listAll: function() {
            return this._clone(this._buffer);
        },

        /**
        * update the current result set
        * @param key [String]
        * @param data [Object]
        */

        update: function(key, data) {
            if(!this._currentDB) {
                throw('Please Select Database First.');
            }

            if(this._currentDB.hasOwnProperty(key)) {
                this._currentDB[key] = data;
            }
        },

        /**
        * limit the current result set
        * @param start [Number]
        * @param end [Number]
        */

        limit: function(start, end) {
            var _tmp = this._buffer;
            var limit;

            if(!end) {
                start = [0, end = start][0];
            }

            limit = start + ':' + (start + end);

            this._buffer = this._listSlice(_tmp, limit);
            return this;
        },

        keys: function() {
            return this._keys(this.findAll());
        },

        /**
        * private methods
        */

        _deep: function(data, scope) {
            var _tmp = data, scope = scope.split('.');
            for(var i = 0; i < scope.length; i++) {
                _tmp = _tmp[scope[i]];
            }
            return _tmp;
        },

        _isArray: nativeIsArray || function(obj) {
            return toString.call(obj) === '[object Array]';
        },

        _isObject: function(obj) {
            return obj === Object(obj);
        },

        _clone: function (obj) {
            if(obj == null || typeof(obj) != 'object') {
                return obj;
            }

            var temp = new obj.constructor();
            for(var key in obj) {
                temp[key] = arguments.callee(obj[key]);
            }

            return temp;
        },

        _objectToArray: function(object) {
            var array = [], object = this._clone(object);

            for(var i in object) {
                if(object.hasOwnProperty(i)) {
                    object[i].jSQL_Key = i;
                    array.push(object[i]);
                }
            }

            return array;
        },

        _arrayToObject: function(array, key) {
            var object = {};

            for(var i = 0; i < array.length; i++) {
                object[array[i][key || 'jSQL_Key']] = array[i];
                delete object[array[i][key || 'jSQL_Key']][key || 'jSQL_Key'];
            };

            return object;
        },

        _each: function(list, fn) {
            if(nativeForEach) {
                list.forEach(fn);
                return;
            }

            for(var i = 0; i < list.length; i++) {
                fn(list[i], i, list);
            }
        },

        _keygen: function(object, indexList) {
            var that = this;
            var baseRef = [].slice.call(arguments, 1);
            var key = '';

            if(that._isArray(indexList)) {
                baseRef = indexList;
            }

            that._each(baseRef, function(o, i, r) {
                key += object[o];
            });

            return key;
        },

        _listSlice: function(list, range) {
            var start, end;

            list = [].slice.call(list);
            range = range.split(':');
            start = range[0] || 0;
            end = range.length > 1 ? range[1] || list.length : list.length;
            return [].slice.call(list, start, end);
        },

        _appendKey: function(list, indexList) {
            var that = this;
            that._each(list, function(o, i, r) {
                o['jSQL_Key'] = that._keygen(o, indexList);
            });
        },

        _keys: nativeKeys || (function() {
            var hasDontEnumBug = true,
                dontEnums = [
                    'toString',
                    'toLocaleString',
                    'valueOf',
                    'hasOwnProperty',
                    'isPrototypeOf',
                    'propertyIsEnumerable',
                    'constructor'
                ],
                dontEnumsLength = dontEnums.length;

            for (var key in {'toString': null}) {
                hasDontEnumBug = false;
            }

            return function keys(object) {
                if ((typeof object != 'object' && typeof object != 'function') || object === null) {
                    throw new TypeError('Object.keys called on a non-object');
                }

                var keys = [];
                for (var name in object) {
                    if (object.hasOwnProperty(name)) {
                        keys.push(name);
                    }
                }

                if (hasDontEnumBug) {
                    for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
                        var dontEnum = dontEnums[i];
                        if (object.hasOwnProperty(dontEnum)) {
                            keys.push(dontEnum);
                        }
                    }
                }
                return keys;
            };
        })()
    };

    jSQL = new jSQL();

    typeof(module) !== 'undefined' && module.exports ? module.exports = jSQL : this.jSQL = jSQL;
})();
<|endoftext|>


<commit_before>"use strict";

const addGroup = function() {
    let newName = $('#new-group-name').val()
    const newChoices = $('#new-group-choices')
        .val()
        .split(/[\r\n]+/)
        .map(function(element) {
            return element.trim()
        })
        .filter(function(element) {
            return element
        })

    if (!newName) {
        console.log('Name required')
        return
    }
    newName = createGroupName(newName)
    if (newChoices.length == 0) {
        console.log('Non-empty lines required')
        return
    }

    const newGroup = {}
    newGroup.name = newName
    newGroup.proportional = false
    newGroup.choices = []
    newChoices.forEach(function(choice) {
        newGroup.choices.push({
            name: choice,
            points: 0
        })
    })
    group = newGroup
    updateGroup().then(populateGroups).then(function() {
        $('#groups').val(newName)
    }).then(reloadGroup).then(setColors).then(drawGroup)
}

const createGroupName = function(name) {
    while ($('#groups').find('option').filter(function() {
            return $(this).val() == name
        }).length > 0) {
        name += '_'
    }
    return name
}

const editGroup = function() {
    const data = $('#group-display table')
    const newGroup = {}
    const oldName = group.name

    let newName = data.find('thead').find('td').eq(0).text().trim()
    if (newName == '') {
        newName = oldName
    }

    $('#groups option').filter(function() {
        return $(this).val() == oldName
    }).remove()
    newGroup.name = createGroupName(newName)

    newGroup.choices = []
    data.find('tbody tr').each(function(id, line) {
        const name = $(line).find('td').eq(0).text().trim()

        const pointsFromField = Number($(line).find('td').eq(1).text())
        const points = isNaN(pointsFromField) ? 0 : pointsFromField

        const disabled = $(line).find('td').eq(2).find('input').prop('checked')
        if (name.trim() != '') {
            newGroup.choices.push({
                name: name,
                points,
                points,
                disabled: disabled
            })
        }
    })

    if (newGroup.choices.length == 0) {
        newGroup.choices = group.choices
    }

    newGroup.last = group.last

    newGroup.proportional = group.proportional

    group = newGroup

    updateGroup().then(function() {
        if (oldName != group.name) {
            deleteGroup(oldName)
        }
    }).then(populateGroups).then(function() {
        $('#groups').val(newGroup.name)
    }).then(reloadGroup).then(updateGroupDisplay).then(drawGroup)
}

const updateGroupDisplay = function() {
    const validateWithEnter = function(event) {
        if (event.which == 13) {
            event.preventDefault()
            $(this).blur()
        }
    }

    $('#group-display').text('')
    if (group === undefined) {
        return
    }
    const table = $('
')
    table.append(`<thead><tr></tr><tr><td>${i18n.__('Name')}</td><td>${i18n.__('Points')}</td><td>${i18n.__('Disabled')}</td></tr></thead><tbody></tbody>`)
    const title = $('', {
        contenteditable: true,
        colspan: 3,
        text: group.name,
        blur: editGroup,
        keydown: validateWithEnter
    })
    table.find('thead').find('tr').eq(0).append(title)
    $('#group-display').append(table)
    for (let i = 0; i < group.choices.length; i++) {
        const choice = group.choices[i]

        const name = $('', {
            contenteditable: true,
            blur: editGroup,
            keydown: validateWithEnter
        })
        name.append(choice.name)

        const points = $('', {
            contenteditable: true,
            blur: editGroup,
            keydown: validateWithEnter
        })
        points.append(choice.points)

        const disabled = $('')
        disabled.append($('<input>', {
            type: 'checkbox',
            checked: choice.disabled,
            change: editGroup
        }))

        const row = $('
')
        row.append(name)
        row.append(points)
        row.append(disabled)
        table.find('tbody').append(row)
    }

    const row = $('
')
    row.append($('', {
        contenteditable: true,
        blur: editGroup,
        keydown: validateWithEnter
    }))
    row.append('-	<input type="checkbox" disabled>	')
    table.find('tbody').append(row)
}

const reset = function() {
    group.choices.forEach(function(choice) {
        choice.points = 0
    })
    group.last = undefined
}
<commit_msg>Protect against XSS<commit_after>"use strict";

const addGroup = function() {
    let newName = $('#new-group-name').val().replace(/


<commit_before>var jurl = function (b) {
	var that = this;
	var url = initialParse(b);

	that.addUrlParameter = function(key, index){
		key=trim(key);
		if(!isBlank(key)){
			if(isBlank(index) && isNaN(index)){
				url.urlParameters.push(key);
			} else {
				if(index < url.urlParameters.length){
					url.urlParameters.splice(index, 0, key);
				}
			}
		}
		return that;
	};

	that.setQueryParameter = function(key, value){
		key=trim(key);
		if(!isBlank(key)){
			url.queryParameters[key] = "";
			if(!isBlank(value)){
				url.queryParameters[key] = value;
			}
		}
		return that;
	};

	that.setHashParameter = function(hash){
		hash=trim(hash);
		if(isBlank(hash)){
			url.hashParameter = null;
		}
		url.hashParameter=trim(hash);

		return that;
	};

	that.getQueryParameter = function(key){
		key=trim(key);
		if(isBlank(key) ||!url.queryParameters.hasOwnProperty(key)){
			return null;
		}
		return url.queryParameters[key];
	};

	that.getParameterIndex = function(key){
		key=trim(key);
		var p;
		for(p = 0; p < url.urlParameters.length; p+=1){
			if(url.urlParameters[p] === key){
				return p;
			}
		}
		return null;
	};

	that.getHost = function(key){
		return url.base;
	};

	that.removeUrlParameter = function(key){
		key=trim(key);
		if(url.urlParameters.indexOf(key) > -1){
			url.urlParameters.splice(url.urlParameters.indexOf(key), 1);
		}
		return that;
	};

	that.removeQueryParameter = function(key){
		key=trim(key);
		if(url.queryParameters.hasOwnProperty(key)){
			delete url.queryParameters[key];
		}
		return that;
	};

	that.build = function () {
		var urlString = url.base;
		if (url.urlParameters.length > 0) {
			urlString += "/" + url.urlParameters.join("/");
		}

		var params = [], p;
		for (p in url.queryParameters) {
			if (url.queryParameters.hasOwnProperty(p)) {
				var paramString = p;
				var value = url.queryParameters[p];
				if (!isBlank(value)) {
					paramString += "=" + value;
				}
				params.push(paramString);
			}
		}
		if(params.length > 0){
			urlString += "?" + params.join("&");
		}

		if(!isBlank(url.hashParameter)){
			urlString += "#" + url.hashParameter;
		}

		return urlString;
	};

	function isBlank (string) {
		return (string === null) || (typeof (string) === "undefined")  || (trim(string) === "");
	}

	function trim(string){
		if((string === null) || (typeof (string) === "undefined")) {
			return string;
		}
		string = string + "";
		return string.replace(/(^\s*)|(\s*$)/g, "");
	}

	function initialParse (b) {
		var urlRegex = /^((((file|gopher|news|nntp|telnet|http|ftp|https|ftps|sftp):\/\/)?(www\.)?([a-zA-Z0-9\-\.]+(\.[a-zA-Z]{2,3})?(:[a-zA-Z0-9]*)?))(\/[a-zA-Z0-9\-_\/]*)?)(\?([a-zA-Z0-9\-_&=%]*))?(#([a-zA-Z0-9\-_&=\/]*))?$/;
		var match = urlRegex.exec(b);
		if(match < 3){
			return "";
		}
		return {
			base: match[2],
			urlParameters: parseUrlParams(match[9]),
			queryParameters: parseQueryParams(match[11]),
			hashParameter: match[13]
		}
	}

	function parseQueryParams (queryString) {
		if (isBlank(queryString)) {
			return {};
		}
		var params = {};
		var splitParams = queryString.split("&");
		var s;
		for (s=0; s < splitParams.length; s+=1) {
			var keyValueSplit = splitParams[s].split("=");
			params[keyValueSplit[0]] = "";
			if (keyValueSplit.length > 1) {
				params[keyValueSplit[0]] = keyValueSplit[1];
			}
		}
		return params;
	}

	function parseUrlParams (urlString) {
		if (isBlank(urlString)) {
			return [];
		}
		var params = [];
		var splitParams = urlString.split("/");
		var s;
		for (s=0; s < splitParams.length; s+=1) {
			if(!isBlank(splitParams[s])){
				params.push(splitParams[s]);
			}
		}
		return params;
	}

	return that;
};

(function( $ ) {
  $.fn.jurl = function() {
	if(this.attr("href")){
	  	return new jurl(this.attr("href"));
	}
	throw "Not href attribute on element: " + input;

  };
})( jQuery );
<commit_msg>Added "." as permitted char in query param value<commit_after>var jurl = function (b) {
	var that = this;
	var url = initialParse(b);

	that.addUrlParameter = function(key, index){
		key=trim(key);
		if(!isBlank(key)){
			if(isBlank(index) && isNaN(index)){
				url.urlParameters.push(key);
			} else {
				if(index < url.urlParameters.length){
					url.urlParameters.splice(index, 0, key);
				}
			}
		}
		return that;
	};

	that.setQueryParameter = function(key, value){
		key=trim(key);
		if(!isBlank(key)){
			url.queryParameters[key] = "";
			if(!isBlank(value)){
				url.queryParameters[key] = value;
			}
		}
		return that;
	};

	that.setHashParameter = function(hash){
		hash=trim(hash);
		if(isBlank(hash)){
			url.hashParameter = null;
		}
		url.hashParameter=trim(hash);

		return that;
	};

	that.getQueryParameter = function(key){
		key=trim(key);
		if(isBlank(key) ||!url.queryParameters.hasOwnProperty(key)){
			return null;
		}
		return url.queryParameters[key];
	};

	that.getParameterIndex = function(key){
		key=trim(key);
		var p;
		for(p = 0; p < url.urlParameters.length; p+=1){
			if(url.urlParameters[p] === key){
				return p;
			}
		}
		return null;
	};

	that.getHost = function(key){
		return url.base;
	};

	that.removeUrlParameter = function(key){
		key=trim(key);
		if(url.urlParameters.indexOf(key) > -1){
			url.urlParameters.splice(url.urlParameters.indexOf(key), 1);
		}
		return that;
	};

	that.removeQueryParameter = function(key){
		key=trim(key);
		if(url.queryParameters.hasOwnProperty(key)){
			delete url.queryParameters[key];
		}
		return that;
	};

	that.build = function () {
		var urlString = url.base;
		if (url.urlParameters.length > 0) {
			urlString += "/" + url.urlParameters.join("/");
		}

		var params = [], p;
		for (p in url.queryParameters) {
			if (url.queryParameters.hasOwnProperty(p)) {
				var paramString = p;
				var value = url.queryParameters[p];
				if (!isBlank(value)) {
					paramString += "=" + value;
				}
				params.push(paramString);
			}
		}
		if(params.length > 0){
			urlString += "?" + params.join("&");
		}

		if(!isBlank(url.hashParameter)){
			urlString += "#" + url.hashParameter;
		}

		return urlString;
	};

	function isBlank (string) {
		return (string === null) || (typeof (string) === "undefined")  || (trim(string) === "");
	}

	function trim(string){
		if((string === null) || (typeof (string) === "undefined")) {
			return string;
		}
		string = string + "";
		return string.replace(/(^\s*)|(\s*$)/g, "");
	}

	function initialParse (b) {
		var urlRegex = /^((((file|gopher|news|nntp|telnet|http|ftp|https|ftps|sftp):\/\/)?(www\.)?([a-zA-Z0-9\-\.]+(\.[a-zA-Z]{2,3})?(:[a-zA-Z0-9]*)?))(\/[a-zA-Z0-9\-_\/]*)?)(\?([a-zA-Z0-9\-_&=%.]*))?(#([a-zA-Z0-9\-_&=\/]*))?$/;
		var match = urlRegex.exec(b);
		if(match < 3){
			return "";
		}
		return {
			base: match[2],
			urlParameters: parseUrlParams(match[9]),
			queryParameters: parseQueryParams(match[11]),
			hashParameter: match[13]
		}
	}

	function parseQueryParams (queryString) {
		if (isBlank(queryString)) {
			return {};
		}
		var params = {};
		var splitParams = queryString.split("&");
		var s;
		for (s=0; s < splitParams.length; s+=1) {
			var keyValueSplit = splitParams[s].split("=");
			params[keyValueSplit[0]] = "";
			if (keyValueSplit.length > 1) {
				params[keyValueSplit[0]] = keyValueSplit[1];
			}
		}
		return params;
	}

	function parseUrlParams (urlString) {
		if (isBlank(urlString)) {
			return [];
		}
		var params = [];
		var splitParams = urlString.split("/");
		var s;
		for (s=0; s < splitParams.length; s+=1) {
			if(!isBlank(splitParams[s])){
				params.push(splitParams[s]);
			}
		}
		return params;
	}

	return that;
};

(function( $ ) {
  $.fn.jurl = function() {
	if(this.attr("href")){
	  	return new jurl(this.attr("href"));
	}
	throw "Not href attribute on element: " + input;

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