/*
Copyright (c) 2018, Adrian Dusa
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, in whole or in part, are permitted provided that the
following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * The names of its contributors may NOT be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ADRIAN DUSA BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

navigator.browserType = (function(){
    var N = navigator.appName, ua = navigator.userAgent, tem;
    var M = ua.match(/(opera|chrome|safari|firefox|msie|trident)\/?\s*(\.?\d+(\.\d+)*)/i);
    if (M && (tem= ua.match(/version\/([\.\d]+)/i))!= null) {M[2]=tem[1];}
    M = M? M[1] : N;
    return M;
})();
$.extend($.fn.disableTextSelection = function() {
    return this
         .attr('unselectable', 'on')
         .css('user-select', 'none')
         .on('selectstart', false);
});
function addDiv(parent, child, settings) {
    var div1 = document.getElementById(parent + "_main");
    var div2 = document.createElement("div");
    div2.id = parent + "_" + child + ((settings.border)?"_border":"");
    div1.appendChild(div2);
    $("#" + div2.id).css({
        position: "absolute",
        left:     (settings.left   + "px"),
        top:      (settings.top    + "px"),
        width:    (settings.width  + "px"),
        height:   (settings.height + "px")
    });
    if (settings.border) {
        var div3 = document.getElementById(parent + "_" + child + "_border");
        var div4 = document.createElement("div");
        div4.id = parent + "_" + child;
        div3.appendChild(div4);
        $("#" + parent + "_" + child).css({
            "max-height": (settings.height + "px"),
            "overflow-x": "hidden",
            "overflow-y": ((child == "path")?"hidden":"auto")
        });
        if (child != "path" && child != "direxp") {
            $("#" + parent + "_" + child + "_border").addClass("border");
        }
    }
}
Raphael.fn.counter = function(options) {
    if (options.fontsize == void 0) {
        options.fontsize = 14;
    }
    if (options.width == void 0) {
        options.width = 18;
    }
    var cntr = new Array();
    cntr.active = true;
    cntr.value = options.startval;
    var txtanchor = "middle";
    cntr.textlabel = this.text(options.x, options.y, "")
        .attr({"text-anchor": txtanchor, "font-size": options.fontsize + "px"});
    cntr.textvalue = this.text(options.x, options.y, "" + options.startval)
        .attr({"text-anchor": txtanchor, "font-size": options.fontsize + "px"});
    cntr.downsign = this.path([
        ["M", options.x - 12 - options.width / 2, options.y - options.textheight/4],
        ["l", 12, 0],
        ["l", -6, 12],
        ["z"]
    ]).attr({fill: "#eeeeee", "stroke-width": 1.2, stroke: "#a0a0a0"});
    cntr.upsign = this.path([
        ["M", options.x + options.width / 2, options.y + options.textheight/4],
        ["l", 12, 0],
        ["l", -6, -12],
        ["z"]
    ]).attr({fill: "#eeeeee", "stroke-width": 1.2, stroke: "#a0a0a0"});
    cntr.down = this.rect(options.x - 22, options.y - 6, 15, 15)
        .attr({fill: "#fff", opacity: 0, stroke: "#000", "stroke-width": 1, cursor: "pointer"})
        .click(function() {
            if (cntr.value > options.startval) {
                cntr.value -= 1;
                cntr.textvalue.attr({"text": ("" + cntr.value)});
            }
        });
    cntr.up = this.rect(options.x + 8, options.y - 8, 15, 15)
        .attr({fill: "#fff", opacity: 0, stroke: "#000", "stroke-width": 1, cursor: "pointer"})
        .click(function() {
            if (cntr.value < options.maxval) {
                cntr.value += 1;
                cntr.textvalue.attr({"text": ("" + cntr.value)});
            }
        });
    cntr.hideIt = function() {
        cntr.upsign.hide();
        cntr.downsign.hide();
        cntr.up.hide();
        cntr.down.hide();
        cntr.textvalue.hide();
        cntr.textlabel.hide();
    }
    cntr.showIt = function() {
        cntr.upsign.show();
        cntr.downsign.show();
        cntr.up.show();
        cntr.down.show();
        cntr.textvalue.show();
        cntr.textlabel.show();
    }
    cntr.label = function(options) {
        if (options.anchor == void 0) {
            options.anchor = "end";
        }
        if (options.label != void 0) {
            cntr.textlabel.attr({"text": "" + options.label, "text-anchor": options.anchor});
        }
        if (options.x != void 0 && options.y != void 0) {
            cntr.textlabel.transform("t" + options.x + "," + options.y);
        }
    }
    return(cntr);
}
Raphael.fn.checkBox = function(x, y, isChecked, label, pos, dim, fontsize) {
    if (dim == void 0) {
        dim = 12;
    }
    if (pos == void 0) {
        pos = 3;
    }
    if (fontsize == void 0) {
        fontsize = 14;
    }
    var cb = new Array();
    cb.active = true;
    cb.label = new Array(1);
    var txtanchor = "start";
    var xpos = x;
    var ypos = y;
    if (pos == 1) { 
        xpos -= 8;
        ypos += dim / 2;
        txtanchor = "end";
    }
    else if (pos == 2) { 
        xpos += dim / 2;
        ypos -= dim;
        txtanchor = "middle";
    }
    else if (pos == 3) { 
        xpos += 20;
        ypos += dim / 2;
    }
    else { 
        xpos += dim / 2;
        ypos += 5;
        txtanchor = "middle";
    }
    cb.label[0] = this.text(xpos, ypos, label)
        .attr({"text-anchor": txtanchor, "font-size": (fontsize + "px")});
    cb.box = this.rect(x, y, dim, dim)
        .attr({fill: isChecked?"#97bd6c":"#eeeeee","stroke-width": 1.2, stroke: "#a0a0a0"});
    cb.chk = this.path([
        ["M", x + 0.2*dim, y + 0.3*dim],
        ["l", 0.15*dim*2, 0.2*dim*2],
        ["l", 0.3*dim*2, -0.45*dim*2]
    ]).attr({"stroke-width": 2});
    if (isChecked) {
        cb.box.attr({fill: "#97bd6c"});
        cb.chk.show();
    }
    else {
        cb.box.attr({fill: "#eeeeee"});
        cb.chk.hide();
    }
    cb.isChecked = isChecked;
    cb.cover = this.rect(x, y, dim, dim)
        .attr({fill: "#fff", opacity: 0, cursor: "pointer"})
        .click(function() {
            if (cb.active) {
                cb.isChecked = !cb.isChecked;
                this.isChecked = cb.isChecked;
                if (cb.isChecked) {
                    cb.box.attr({fill: "#97bd6c"});
                    cb.chk.show();
                }
                else {
                     cb.box.attr({fill: "#eeeeee"});
                     cb.chk.hide();
                }
            }
        });
    cb.cover.active = true;
    cb.activate = function() {
        cb.active = true;
        cb.cover.active = true;
        cb.cover.attr({fill: "#000", opacity: 0, cursor: "pointer"});
    }
    cb.deactivate = function() {
        cb.active = false;
        cb.cover.active = false;
        cb.cover.attr({fill: "#000", opacity: 0.2, cursor: "default"});
    }
    cb.uncheck = function() {
        cb.isChecked = false;
        cb.box.attr({fill: "#eeeeee"});
        cb.chk.hide();
        cb.cover.isChecked = false;
    }
    cb.check = function() {
        cb.isChecked = true;
        cb.box.attr({fill: "#97bd6c"});
        cb.chk.show();
        cb.cover.isChecked = true;
    }
    cb.refresh = function(x) {
        if (x) {
            cb.check();
        }
        else {
            cb.uncheck();
        }
    }
    cb.hideIt = function() {
        cb.cover.hide();
        cb.box.hide();
        cb.chk.hide();
        for (var i = 0; i < cb.label.length; i++) {
            cb.label[i].hide();
        }
    }
    cb.showIt = function() {
        cb.cover.show();
        cb.box.show();
        if (cb.isChecked) {
            cb.chk.show();
        }
        else {
            cb.chk.hide();
        }
        for (var i = 0; i < cb.label.length; i++) {
            cb.label[i].show();
        }
    }
    var cbset = this.set(cb.box, cb.chk, cb.cover);
    cb.move = function(x, y) {
        cbset.transform("t" + x + "," + y);
    }
    return(cb);
}
Raphael.fn.radio = function(x, y, whichChecked, labels, vertspace, horspace, lbspace, size, fontsize) {
    if (size === void 0) {
        size = 6.5;
    }
    if (vertspace === void 0) {
        vertspace = 25;
    }
    if (horspace === void 0) { 
        horspace = rep(0, labels.length);
    }
    if (lbspace === void 0) {
        lbspace = 14;
    }
    if (fontsize === void 0) {
        fontsize = 14;
    }
    var rd = new Array();
    rd.whichChecked = whichChecked;
    rd.label = new Array(labels.length);
    rd.cover = new Array(labels.length);
    rd.circle = new Array(labels.length);
    rd.fill = this.set();
    var newvert = 0;
    for (var i = 0; i < labels.length; i++) {
        rd.label[i] = this.text(x + horspace[i] + lbspace, y + newvert - 1, labels[i]).attr({"text-anchor": "start", "font-size": fontsize+"px"});
        rd.circle[i] = this.circle(x + horspace[i], y + newvert, size).attr({fill: "#eeeeee", "stroke": "#a0a0a0", "stroke-width": 1.2});
        rd.cover[i] = this.circle(x + horspace[i], y + newvert, size + 2).attr({fill: "#eeeeee", stroke: "none", "fill-opacity": 0, "cursor": "pointer"});
        rd.cover[i].i = i;
        rd.cover[i].click(function() {
            rd.fill.show();
            var BBox = this.getBBox();
            rd.fill.transform("t" + (BBox.x - x + size + 2) + "," + (BBox.y - y + size + 2));
            rd.whichChecked = this.i;
        });
        if (Array.isArray(vertspace)) {
            newvert = vertspace[i + 1];
        }
        else {
            newvert = (i + 1)*vertspace;
        }
    }
    rd.fill.push(this.circle(x, y, size - 0.5).attr({fill: "#97bd6c", stroke: "none"}));
    rd.fill.push(this.circle(x, y, size - 4.5).attr({fill: "#000000", stroke: "none"}));
    if (rd.whichChecked < 0) {
        rd.fill.hide();
    }
    else {
        rd.fill.show();
        rd.fill.transform("t" + (rd.circle[whichChecked].getBBox().x - x + size) + "," + (rd.circle[whichChecked].getBBox().y - y + size));
    }
    rd.moveTo = function(pos) {
        rd.fill.show();
        rd.whichChecked = pos;
        var BBox = rd.cover[pos].getBBox();
        rd.fill.transform("t" + (BBox.x - x + size + 2) + "," + (BBox.y - y + size + 2));
    }
    rd.hideIt = function() {
        for (var i = 0; i < rd.cover.length; i++) {
            rd.cover[i].hide();
            rd.circle[i].hide();
            rd.label[i].hide();
        }
        rd.fill.hide();
    }
    rd.showIt = function() {
        for (var i = 0; i < rd.cover.length; i++) {
            rd.cover[i].show();
            rd.circle[i].show();
            rd.label[i].show();
        }
        if (rd.whichChecked < 0) {
            rd.fill.hide();
        }
        else {
            rd.fill.show();
        }
    }
    return(rd);
}
function isNumeric0(n) {
    if (n.length == 0) {
        return false;
    }
    else {
        return !/^(NaN|-?Infinity)$/.test(+n);
    }
}
function isNumeric(obj) {   
    if (obj.length == 0) {
        return false;
    }
    else {
        if (obj instanceof Array) {
            var result = new Array(obj.length);
            for (var i = 0; i < obj.length; i++) {
                result[i] = (obj[i].length == 0) ? false : !/^(NaN|-?Infinity)$/.test(+obj[i]);
            }
            return(result);
        }
        else {
            return !/^(NaN|-?Infinity)$/.test(+obj);
        }
    }
}
function copy(obj, exclude) {
    if (null == obj || "object" != typeof obj) return obj;
    var clone;
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }
    if (obj instanceof Array) {
        clone = new Array();
        for (var i = 0, len = obj.length; i < len; i++) {
            if (exclude !== void 0) {
                if (exclude.indexOf(i) < 0) { 
                    clone.push(copy(obj[i]));
                }
            }
            else {
                clone.push(copy(obj[i]));
            }
        }
        return clone;
    }
    if (obj instanceof Object) {
        clone = new Object;
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                if (exclude !== void 0) {
                    if (exclude.indexOf(key) < 0) {
                        clone[key] = copy(obj[key]);
                    }
                }
                else {
                    clone[key] = copy(obj[key]);
                }
            }
        }
        return clone;
    }
}
function sortArray(x, empty = "last") {
	var sorted = x.slice();
	var sortlen = sorted.length;
	for (var i = 0, j; i < sortlen; i++) {
	    
	    tmp1 = sorted[i];
	    tmp2 = tmp1;
	    if (!isNumeric(tmp1)) {
	        tmp2 = tmp2.toLowerCase();
		}
		for (j = i - 1; j >= 0; j--) {
            tmp3 = sorted[j];
            if (!isNumeric(tmp3)) {
                tmp3 = tmp3.toLowerCase();
            }
            if (tmp3 > tmp2) {
                sorted[j + 1] = sorted[j];
            }
            else {
                break
            }
		}
		sorted[j + 1] = tmp1;
	}
	
	if (empty == "last") {
	    x = rep("", sortlen);
	    var position = 0;
	    for (var i = 0; i < sortlen; i++) {
	        if (sorted[i] != "") {
	            x[position] = sorted[i];
	            position += 1;
	        }
	    }
	    return x;
	}
	
	return sorted;
}
function objectsEqual(x, y) {
    'use strict';
    if (x === null || x === undefined || y === null || y === undefined) { return x === y; }
    if (x.constructor !== y.constructor) { return false; }
    if (x instanceof Function) { return x === y; }
    if (x instanceof RegExp) { return x === y; }
    if (x === y || x.valueOf() === y.valueOf()) { return true; }
    if (Array.isArray(x) && x.length !== y.length) { return false; }
    if (x instanceof Date) { return false; }
    if (!(x instanceof Object)) { return false; }
    if (!(y instanceof Object)) { return false; }
    var p = Object.keys(x);
    return Object.keys(y).every(function (i) { return p.indexOf(i) !== -1; }) &&
        p.every(function (i) { return objectsEqual(x[i], y[i]); });
}
function arraysEqual(a, b) {
    if (a === b) return true;
    if (a == null || b == null) return false;
    if (a.length != b.length) return false;
    for (var i = 0; i < a.length; ++i) {
        if (a[i] !== b[i]) return false;
    }
    return true;
}
function getKeys(obj) {
    if (obj === null) return(Array());
    return(Object.keys(obj));
}
function getTrueKeys(obj) { 
    if (obj === null) {
        return(Array());
    }
    var trueKeys = new Array();
    for (var key in obj) {
        if (obj[key]) {
            trueKeys.push(key);
        }
    }
    if (trueKeys.length == 0) {
        return(Array());
    }
    else {
        return(trueKeys);
    }
}
function changeCol(obj, oldname, newname) {
    var temp = new Array();
    for (var key1 in obj) {
        temp[key1] = new Array();
        for (var key2 in obj[key1]) {
            temp[key1][key2] = new Array();
            var oldkeys = getKeys(obj[key1][key2]);
            for (var i = 0; i < oldkeys.length; i++) {
                temp[key1][key2][(oldkeys[i] == oldname)?(newname):(oldkeys[i])] = obj[key1][key2][oldkeys[i]];
            }
        }
    }
    return(temp);
}
function sat(obj, options) {
    if (options == void 0) {
        options = {};
    }
    if (options.size == void 0) {
        options.size = 14;
    }
    if (options.anchor == void 0) {
        options.anchor = "start";
    }
    var optkeys = getKeys(options);
    var options2 = new Array();
    for (var i = 0; i < optkeys.length; i++) {
        options2[optkeys[i]] = options[optkeys[i]];
    }
    if (Array.isArray(obj)) {
        var keys = getKeys(obj);
        for (var i = 0; i < keys.length; i++) {
            if (optkeys.indexOf("clip") >= 0) {
                if (typeof(options.clip) != "string") {
                    options2.clip = options.clip[keys[i]];
                }
            }
            sat(obj[keys[i]], options2);
        }
    }
    else {
        if (obj.type == "text") {
            var BBox, clipattr;
            if (options.clip == void 0) {
                obj.attr({"text-anchor": options.anchor, "font-size": (options.size + "px")});
                if (options["font-weight"] !== void 0) {
                    obj.attr({"font-weight": options["font-weight"]});
                }
                if (options["color"] !== void 0) {
                    obj.attr({"fill": options["color"]});
                }
            }
            else {
                if (typeof(options.clip) == "string") {
                    clipattr = options.clip;
                }
                else {
                    BBox = options.clip.getBBox();
                    clipattr = BBox.x + "," + BBox.y + "," + (BBox.width - 3) + "," + BBox.height;
                }
                obj.attr({"text-anchor": options.anchor, "font-size": (options.size + "px"), "clip-rect": clipattr});
            }
        }
        else { 
            obj.attr({stroke: ((optkeys.indexOf("stroke") < 0)?"#a0a0a0":options["stroke"]), 'stroke-width': ((optkeys.indexOf("sw") < 0)?1:options.sw), fill: "#ffffff", "fill-opacity": 0});
        }
    }
    return(obj);
}
function setAttrCover(obj) {
    if (Array.isArray(obj)) {
        var keys = getKeys(obj);
        for (var i = 0; i < keys.length; i++) {
            setAttrCover(obj[keys[i]]);
        }
    }
    else {
        obj.attr({stroke: '#d0d0d0', 'stroke-width': 1, fill: "#ffffff", "fill-opacity": 0})
    }
    return(obj);
}
function round(x, y) {
    y = Math.pow(10, y);
    return(Math.round(x*y)/y);
}
function all(obj, rule, value) {
    if (value === void 0) {
        value = "";
    }
    var check = true;
    if (Array.isArray(obj)) {
        var keys = getKeys(obj);
        for (var i = 0; i < obj.length; i++) {
            if (Array.isArray(value)) {
                var check2 = false;
                for (var j = 0; j < value.length; j++) {
                    check2 = check2 || eval("obj[keys[i]]" + rule + value[j]);
                }
                check = check && check2;
            }
            else {
                check = check && eval("obj[keys[i]]" + rule + value);
            }
        }
    }
    else {
        if (Array.isArray(value)) {
            var check = false;
            for (var j = 0; j < value.length; j++) {
                check = check || eval("obj" + rule + value[j]);
            }
        }
        else {
            check = eval("obj" + rule, value);
        }
    }
    return(check);
}
function any(obj, rule, value) {
    if (value === void 0) {
        value = "";
    }
    var check = false;
    if (Array.isArray(obj)) {
        var keys = getKeys(obj);
        for (var i = 0; i < obj.length; i++) {
            if (Array.isArray(value)) {
                for (var j = 0; j < value.length; j++) {
                    check = check || eval("obj[keys[i]]" + rule + value[j]);
                }
            }
            else {
                check = check || eval("obj[keys[i]]" + rule + value);
            }
        }
    }
    else {
        if (Array.isArray(value)) {
            for (var j = 0; j < value.length; j++) {
                check = check || eval("obj" + rule + value[j]);
            }
        }
        else {
            check = eval("obj" + rule + value);
        }
    }
    return(check);
}
function rep(rule, times) {
    var result = new Array(times);
    for (var i = 0; i < times; i++) {
        result[i] = rule;
    }
    return(result);
}
function unique(obj) {
    var uniques = new Array();
    var present;
    for (var i = 0; i < obj.length; i++) {
        if (uniques.indexOf(obj[i]) < 0) {
            uniques.push(obj[i]);
        }
    }
    return(uniques);
}
function min(obj) { 
    var minval = null;
    for (var i = 0; i < obj.length; i++) {
        if (obj[i] !== null && isNumeric(obj[i])) {
            if (minval === null) {
                minval = obj[i];
            }
            else {
                if (minval > obj[i]) {
                    minval = obj[i];
                }
            }
        }
    }
    return(minval);
}
function max(obj) { 
    var maxval = null;
    for (var i = 0; i < obj.length; i++) {
        if (obj[i] !== null && isNumeric(obj[i])) {
            if (maxval === null) {
                maxval = obj[i];
            }
            else {
                if (maxval < obj[i]) {
                    maxval = obj[i];
                }
            }
        }
    }
    return(maxval);
}
function paste(obj, from, to, sep) {
    var result = obj[from];
    if (from < to) {
        for (var i = from + 1; i < to + 1; i++) {
            result += obj[i];
        }
    }
    return(result);
}
function makeRules(oldv, newv) {
    var rule = new Array();
    if (oldv.length > 0) {
        rule = new Array(oldv.length);
        for (var i = 0; i < oldv.length; i++) {
            rule[i] = oldv[i] + "=" + newv[i];
        }
    }
    return(rule)
}
function eraseRecodeValues(paper) {
    paper.oldv.texts.VALUE.attr({"text": ""});
    paper.oldv.texts.range.FROM.attr({"text": ""});
    paper.oldv.texts.range.TO.attr({"text": ""});
    paper.oldv.texts.LOWESTTO.attr({"text": ""});
    paper.oldv.texts.TOHIGHEST.attr({"text": ""});
    paper.newv.texts.VALUE.attr({"text": ""});
}
function checkRecodeSelections(colclicks, paper) {
    var cols = getTrueKeys(colclicks.recode.rules);
    eraseRecodeValues(paper);
    if (cols.length == 1) {
        var lr = cols[0].split("=");
        var lhs = lr[0].split(":");
        var rhs = lr[1];
        var idx = 0; 
        if (lhs.length > 1) {
            if (lhs[0] == "lo") {
                idx = 1;
                paper.oldv.texts.LOWESTTO.attr({"text": lhs[1]});
                paper.rules.oldv = lhs[1];
            }
            else if (lhs[1] == "hi") {
                idx = 3;
                paper.oldv.texts.TOHIGHEST.attr({"text": lhs[0]});
                paper.rules.oldv = lhs[0];
            }
            else {
                idx = 2;
                paper.oldv.texts.range.FROM.attr({"text": lhs[0]});
                paper.oldv.texts.range.TO.attr({"text": lhs[1]});
                paper.rules.oldv = [lhs[0], lhs[1]];
            }
        }
        else {
            if (lhs[0] == "missing") {
                idx = 4;
            }
            else if (lhs[0] == "else") {
                idx = 5;
            }
        }
        if (idx == 0) { 
            paper.oldv.texts.VALUE.attr({"text": lhs[0]});
            paper.rules.oldv = lhs[0];
        }
        if (rhs != "missing" && rhs != "copy") {
            paper.newv.texts.VALUE.attr({"text": rhs});
            paper.newradio.moveTo(0);
            paper.rules.newv = rhs;
        }
        else {
            if (rhs == "missing") {
                paper.newradio.moveTo(1);
            }
            else {
                paper.newradio.moveTo(2);
            }
        }
        paper.oldradio.moveTo(idx);
        paper.add.attr({"text": "Change"});
    }
    else {
        paper.add.attr({"text": "Add"});
    }
}
function deleteRule(colclicks, rule) {
    var keys = getKeys(colclicks.recode.rules);
    var temp = new Array();
    for (i = 0; i < keys.length; i++) {
        if (keys[i] != rule) {
            temp[keys[i]] = colclicks.recode.rules[keys[i]];
        }
    }
    colclicks.recode.rules = temp;
}
function unselect(colclicks, dialog, identifier) {
    if (colclicks[dialog][identifier] != void 0) {
        var keys = getKeys(colclicks[dialog][identifier]);
        for (i = 0; i < keys.length; i++) {
            colclicks[dialog][identifier][keys[i]] = false;
        }
    }
}
function scaleplot(paper) {
    var xyplotdata = paper.xyplotdata;
    var scale = paper.scale;
    var sx = paper.sx;
    var sy = paper.sy;
    var dim = paper.dim;
    var offset = paper.offset;
    var rdim = paper.rdim;
    var xcoord, ycoord;
    var rj = paper.randomjitter;
    if (paper.total !== void 0) {
        paper.total.remove();
        paper.mdlines.remove();
        paper.afv.remove();
        paper.ticks.remove();
        paper.pointsset.remove();
    }
    paper.total = paper.set();
    paper.afv = paper.set(); 
    paper.ticks = paper.set();
    paper.mdlines = paper.set(); 
    paper.pointsset = paper.set();
    paper.points = new Array();
    paper.total.push(paper.rect(sx, sy, scale*dim, scale*dim));
    paper.total.push(paper.path("M" + sx + "," + (sy + scale*dim) + " L" + (sx + scale*dim) + "," + sy).attr({"stroke": "#a0a0a0"}));
    paper.mdlines.push(paper.path("M" + sx + "," + (sy + scale*dim/2) +  " L" + (sx + scale*dim) + "," + (sy + scale*dim/2)).attr({"stroke-dasharray": "--"}));
    paper.mdlines.push(paper.path("M" + (sx + scale*dim/2) + "," + sy +  " L" + (sx + scale*dim/2) + "," + (sy + scale*dim)).attr({"stroke-dasharray": "--"}));
    paper.mdlines.attr({"stroke": "#a0a0a0"});
    for (var i = 0; i < 11; i++) {
        var vertick = 
        paper.ticks.push(paper.path("M" + (sx + scale*(offset + i*rdim/10)) + "," + (sy + scale*dim) + " L" + (sx + scale*(offset + i*rdim/10)) + "," + (sy + scale*dim + 7)));
        paper.ticks.push(paper.path("M" + (sx - 7) + "," + (sy + scale*(offset + i*rdim/10)) + " L" + sx + "," + (sy + scale*(offset + i*rdim/10)) ) );
        paper.afv.push(sat(paper.text(sx + scale*(offset + i*rdim/10), sy + scale*dim + 15, i/10), {"size": 12, "anchor": "middle"}));
        paper.afv.push(sat(paper.text(sx - 10, sy + scale*(offset + i*rdim/10), (10 - i)/10), {"size": 12, "anchor": "end"}));
        if (i == 5) {
            paper.afv.push(sat(paper.text(sx + scale*(offset + i*rdim/10), sy + scale*dim + 34, ((paper.negx.isChecked?"~":"") + paper.x)), {"size": 14, "anchor": "middle", "font-weight": "bold"}));
            var temp = sat(paper.text(sx - 38, sy + scale*(offset + i*rdim/10), ((paper.negy.isChecked?"~":"") + paper.y)), {"size": 14, "anchor": "end", "font-weight": "bold"});
            var BBox = temp.getBBox();
            temp.transform("t" + (BBox.width/2) + ",0r-90");
            paper.afv.push(temp);
        }
    }
    var point, txt, txtfundal, totalength;
    var hoverIn = function() {
        if (this.label.attr("text") != "" & !paper.labels.isChecked) {
            this.label[1].attr({"fill-opacity": 0.4});
            this.label.show();
        }
    }
    var hoverOut = function() {
        if (this.label.attr("text") != "" & !paper.labels.isChecked) {
            this.label[1].attr({"fill-opacity": 0});
            this.label.hide();
        }
    }
    if (xyplotdata.length > 0) { 
        var krj = Object.keys(rj);
        var px = xyplotdata[1];
        var py = xyplotdata[2];
        for (var i = 0; i < xyplotdata[0].length; i++) {
            xcoord = sx + scale*(offset + rdim*((paper.negx.isChecked)?(1 - px[i]):(px[i]))) + ((krj.length > 0)?(rj.x[i]):0);
            ycoord = sy + scale*(offset + rdim*((paper.negy.isChecked)?(py[i]):(1 - py[i]))) + ((krj.length > 0)?(rj.y[i]):0);
            paper.points[i] = paper.circle(xcoord, ycoord, 3);
            paper.points[i].hover(hoverIn, hoverOut, point, point);
            paper.pointsset.push(paper.points[i]);
        }
        if (paper.pof.isChecked) {
            if (paper.sufnec.whichChecked == 0) {
                paper.incl.attr({"text": "Inclusion: " + xyplotdata[3][paper.index][0]});
                paper.cov.attr({"text": "Coverage: " + xyplotdata[3][paper.index][1]});
                paper.PRI.attr({"text": "PRI: " + xyplotdata[3][paper.index][2]});
                paper.ron.hide();
            }
            else {
                paper.incl.attr({"text": "Inclusion: " + xyplotdata[4][paper.index][0]});              
                paper.cov.attr({"text": "Coverage: " + xyplotdata[4][paper.index][1]});
                paper.ron.attr({"text": "Relevance: " + xyplotdata[4][paper.index][2]});
                paper.PRI.hide()
            }
        }
        paper.pointsset.attr({fill: "#707070", "fill-opacity": paper.fill.isChecked?1:0});
    }
}
function createLabels(paper) {
    var xyplotdata = paper.xyplotdata;
    paper.labelsset.remove();
    paper.labelsset = paper.set();
    paper.labelsArray = new Array();
    var scale = paper.scale;
    var sx = paper.sx;
    var sy = paper.sy;
    var dim = paper.dim;
    var offset = paper.offset;
    var rdim = paper.rdim;
    var txt, txtfundal, outer, BBox, coords, x, y, r, twidth;
    if (xyplotdata.length > 0) { 
        for (var i = 0; i < xyplotdata[0].length; i++) {
            twidth = getTextWidth(xyplotdata[0][i])
            x = paper.points[i].attr("cx");
            y = paper.points[i].attr("cy");
            r = paper.points[i].attr("r");
            outer = paper.circle(x, y, r + twidth + 2*5).attr({"fill": "none", "stroke": "none"});
            coords = paper.points[i].getPointAtLength(paper.points[i].getTotalLength()*(90 - paper.labelRotation)/360);
            txtfundal = paper.rect(coords.x, coords.y - 8, twidth + 10, 16);
            txtfundal.attr({fill: "#c9c9c9", "fill-opacity": 0, stroke: "none"});
            txt = sat(paper.text(coords.x + 2.5, coords.y, xyplotdata[0][i]));
            txt.attr({"font-weight": "bold", "fill-opacity": 0.7});
            tempset = paper.set(paper.points[i], txt, txtfundal);
            BBox = tempset.getBBox();
            if (BBox.x + BBox.width > sx + scale*rdim) {
                txt.remove();
                txtfundal.remove();
                coords = outer.getPointAtLength(outer.getTotalLength()*(270 - paper.labelRotation)/360);
                txtfundal = paper.rect(coords.x, coords.y - 8, twidth + 10, 16);
                txtfundal.attr({fill: "#c9c9c9", "fill-opacity": 0, stroke: "none"});
                txt = sat(paper.text(coords.x + 2.5, coords.y, xyplotdata[0][i]));
                txt.attr({"font-weight": "bold", "fill-opacity": 0.7});
            }
            tempset = paper.set(paper.points[i], txt, txtfundal);
            paper.points[i].label = paper.set();
            paper.points[i].label.push(txt, txtfundal);
            if (paper.labelRotation > 0) {
                paper.points[i].label.transform("r-" + paper.labelRotation + "," + coords.x + "," + coords.y);
            }
            paper.labelsset.push(paper.points[i].label);
        }
    }
    if (paper.labels.isChecked) {
        paper.labelsset.show();
    }
    else {
        paper.labelsset.hide();
    }
}
function setPath(paper, wd) {
    paper.clear();
    var wds = wd.split("/");
    var x = 1;
    var dirwidth = 0;
    var direct;
    var allobjs = paper.set();
    for (i = 0; i < wds.length; i++) {
        if (wds[0] == "") {
            wds[0] = "root";
        }
        if (wds[i] != "") {
            dirwidth = getTextWidth(wds[i]);
            allobjs.push(sat(paper.text(x, 7, wds[i])));
            direct = paper.rect(x - 3, 0, dirwidth + 6, 18)
                .attr({stroke: "none", fill: "#fff", "fill-opacity": 0});
            direct.dir = wds[i];
            direct.dblclick(function() {
                paper.goToDir(this.dir)
            });
            allobjs.push(direct);
            x += dirwidth + 2;
            if (i < wds.length - 1*((wds[wds.length - 1] == "")?2:1)) {
                allobjs.push(sat(paper.text(x + 3 , 7, ">")));
                x += 15;
            }
        }
    }
    var lastX, groupX, groupWidth;
    function dragStartLarge(group) {
        return function() {
            lastX = 0;
            var BBox = group.getBBox();
            groupX = BBox.x;
            groupWidth = BBox.width;
        }
    };
    function dragMoveLarge(group) {
        return function(dx, dy) {
            if (groupWidth > 400) {
                var newX = dx - lastX;
                if (groupX + dx > 0) {
                    newX = 0 - groupX - lastX;
                }
                if (groupX + dx < (400 - groupWidth)) {
                    newX = (400 - groupWidth) - (groupX + lastX);
                    lastX = (400 - groupWidth) - groupX;
                }
                else {
                    lastX += newX;
                }
                group.translate(newX, 0);
            }
        }
    };
    function dragStopLarge(group) {
        return function() {
        }
    };
    allobjs.drag(dragMoveLarge(allobjs), dragStartLarge(allobjs), dragStopLarge(allobjs));
    paper.setSize(x + 20, 20);
}
function randomBetween(min, max) {
    return (min + Math.random()*(max - min + 1));
}
function reorder(obj, from, to) {
    var keys = getKeys(obj);
    var values = new Array(keys.length);
    for (var i = 0; i < keys.length; i++) {
        values[i] = obj[keys[i]];
    }
    keys.splice(to, 0, keys.splice(from, 1)[0]);
    values.splice(to, 0, values.splice(from, 1)[0]);
    var result = {};
    for (var i = 0; i < keys.length; i++) {
        result[keys[i]] = values[i];
    }
    return(result);
}
function duplicates(arr) {
    var len = arr.length,
        out = [],
        counts = {};
    for (var i = 0; i < len; i++) {
        var item = arr[i];
        counts[item] = counts[item] >= 1 ? counts[item] + 1 : 1;
    }
    for (var item in counts) {
        if (counts[item] > 1) {
            out[out.length] = item*1;
        }
    }
    return out;
}
function getScrollBarWidth() {
    var inner = document.createElement('p');
    inner.style.width = "100%";
    inner.style.height = "200px";
    var outer = document.createElement('div');
    outer.style.position = "absolute";
    outer.style.top = "0px";
    outer.style.left = "0px";
    outer.style.visibility = "hidden";
    outer.style.width = "200px";
    outer.style.height = "150px";
    outer.style.overflow = "hidden";
    outer.appendChild (inner);
    document.body.appendChild (outer);
    var w1 = inner.offsetWidth;
    outer.style.overflow = 'scroll';
    var w2 = inner.offsetWidth;
    if (w1 == w2) w2 = outer.clientWidth;
    document.body.removeChild (outer);
    return (w1 - w2);
};
function scaleShape(path, scale) {
    var parsed = Raphael.parsePathString(path);
    for (var j = 0; j < parsed.length; j++) {
        for (k = 1; k < parsed[j].length; k++) {
            parsed[j][k] = parsed[j][k]*scale;
        }
    }
    return(parsed.toString());
}
function getShape(x, venn, scale) {
    bigpath = "";
    for (var b = 0; b < x.length; b++) {
        var path = "M";
        var stb, endb, end;
        var checkb = rep(false, x[b].length);
        var counter = 0;
        while(counter < 1000) { 
            for (var i = 0; i < checkb.length; i++) {
                if (!checkb[i]) {
                    var y = venn[x[b][i]];
                    if (i == 0) {
                        for (var j = 0; j < y.length/2; j++) {
                            path += ((j == 1)?" C":"") + " " + round(y[2*j]*scale, 3) + "," + round(y[2*j + 1]*scale, 3);
                        }
                        checkb[i] = true;
                        end = y[y.length - 2] + " " + y[y.length - 1];
                    }
                    else {
                        stb = y[0] + " " + y[1];
                        endb = y[y.length - 2] + " " + y[y.length - 1]
                        if (end == stb) {
                            for (var j = 1; j < y.length/2; j++) {
                                path += " " + round(y[2*j]*scale, 3) + "," + round(y[2*j + 1]*scale, 3);
                            }
                            checkb[i] = true;
                            end = endb;
                        }
                        else if (end == endb) {
                            for (var j = y.length/2 - 2; j >= 0; j--) {
                                path += " " + round(y[2*j]*scale, 3) + "," + round(y[2*j + 1]*scale, 3);
                            }
                            checkb[i] = true;
                            end = stb;
                        }
                    }
                }
            }
            if (all(checkb, "== true")) {
                counter = 1001;
            }
            counter += 1
        }
        bigpath += " " + path + " z";
    }
    return(bigpath);
}
function customShape(rule, venn, scale, id) {
    var rowns = new Array();
    rule = rule.split("");
    var idis, i, j, k;
    var check = rep(true, rule.length);
    var keys = getKeys(id);
    for (i = 0; i < keys.length; i++) { 
        keys[i] = keys[i]*1;
        idis = id[keys[i]].split("");
        for (j = 0; j < rule.length; j++) {
            if (rule[j] != "-") {
                check[j] = any(idis, "==", j + 1);
                if (rule[j] == "0") {
                    check[j] = !check[j];
                }
            }
        }
        if (all(check, "== true")) {
            rowns[rowns.length] = i;
        }
    }
    var ids = new Array();
    for (i = 0; i < rowns.length; i++) {
        ids[i] = id[rowns[i]];
    }
    var inverted = any(rowns, "==", 0);
    if (rowns.length == 1 && rowns[0] == 0) {
        rowns = $(keys).not(rowns).get();
    }
    checkZone = function(from, rowns, checkz, venn) {
        var fromz = venn[1][from];
        var toz = new Array();
        for (var i = 0; i < rowns.length; i++) {
            if (!checkz[i]) {
                if (any(fromz, "==", venn[1][rowns[i]])) {
                    checkz[i] = true;
                    toz[toz.length] = rowns[i];
                }
            }
        }
        if (toz.length > 0) {
            for (var j = 0; j < toz.length; j++) {
                var checkz2 = checkZone(toz[j], rowns, checkz, venn);
                for (var i = 0; i < checkz.length; i++) {
                    checkz[i] = checkz[i] || checkz2[i];
                }
            }
        }
        return(checkz)
    }
    var result = new Array();
    if (rowns.length > 1) {
        var checkz = rep(false, rowns.length);
        checkz[0] = true;
        while(any(checkz, "== false")) {
            checkz = checkZone(rowns[0], rowns, checkz, venn);
            var temp1 = new Array();
            var temp2 = new Array();
            var checkz2 = new Array();
            for (i = 0; i < rowns.length; i++) {
                if (checkz[i]) {
                    temp1[temp1.length] = rowns[i];
                }
                else {
                    temp2[temp2.length] = rowns[i];
                    checkz2[checkz2.length] = false;
                }
            }
            result[result.length] = temp1;
            if (checkz2.length > 0) {
                rowns = copy(temp2);
                checkz = copy(checkz2);
                checkz[0] = true;
            }
        }
    }
    else {
        result[0] = [rowns];
    }
    for (var i = 0; i < result.length; i++) {
        var temp = venn[1][result[i][0]];
        if (result[i].length > 1) {
            for (var j = 1; j < result[i].length; j++) {
                temp = temp.concat(venn[1][result[i][j]]);
            }
        }
        result[i] = $(temp).not(duplicates(temp)).get();
    }
    return([getShape(result, venn[0], scale), inverted]);
}
function parseText(text, conditions) {
    text = text.replace("(", "");
    text = text.replace(")", "");
    text = text.replace(/\s/g, "");
    splitchar = "*";
    var parsedPlus = text.split("+");
    var largecheck = rep(false, parsedPlus.length);
    for (var i = 0; i < parsedPlus.length; i++) {
        var parsedStar = parsedPlus[i].split(splitchar);
        var upper = new Array(parsedStar.length);
        var lower = new Array(parsedStar.length);
        for (var j = 0; j < parsedStar.length; j++) {
            if (parsedStar[j][0] == "~") {
                parsedStar[j] = parsedStar[j].substring(1, parsedStar[j].length);
            }
            upper[j] = parsedStar[j].toUpperCase();
            lower[j] = parsedStar[j].toLowerCase();
            if (parsedStar[j] != upper[j] && parsedStar[j] != lower[j]) {
                return("error");
            }
        }
        var check = rep(false, parsedStar.length);
        for (var j = 0; j < parsedStar.length; j++) {
            check[j] = conditions.indexOf(upper[j]) < 0;
        }
        largecheck[i] = any(check, " == true")
    }
    if (any(largecheck, " == true")) {
        splitchar = "";
        var largecheck = rep(false, parsedPlus.length);
        for (var i = 0; i < parsedPlus.length; i++) {
            var parsedStar = parsedPlus[i].split(splitchar);
            var upper = new Array(parsedStar.length);
            for (var j = 0; j < parsedStar.length; j++) {
                if (parsedStar[j][0] == "~") {
                    parsedStar[j] = parsedStar[j].substring(1, parsedStar[j].length);
                }
                upper[j] = parsedStar[j].toUpperCase();
            }
            var check = rep(false, parsedStar.length);
            for (var j = 0; j < parsedStar.length; j++) {
                check[j] = conditions.indexOf(upper[j]) < 0;
            }
            largecheck[i] = any(check, " == true")
        }
        if (any(largecheck, " == true")) {
            return("error");
        }
    }
    finalResult = {};
    for (var i = 0; i < parsedPlus.length; i++) {
        var parsedStar = parsedPlus[i].split(splitchar);
        var upper = new Array(parsedStar.length);
        for (var j = 0; j < parsedStar.length; j++) {
            if (parsedStar[j][0] == "~") {
                parsedStar[j] = parsedStar[j].substring(1, parsedStar[j].length).toLowerCase();
            }
            upper[j] = parsedStar[j].toUpperCase();
        }
        var rule = "";
        for (var j = 0; j < conditions.length; j++) {
            var index = upper.indexOf(conditions[j]);
            rule += (index >= 0)?((parsedStar[index] == upper[index])?1:0):"-";
        }
        finalResult[parsedPlus[i]] = rule;
    }
    return(finalResult);
}
function caretPosition(jqueryItem) {
    var input = jqueryItem.get(0);
    if (!input) return; 
    if ('selectionStart' in input) {
        return input.selectionStart;
    } else if (document.selection) {
        input.focus();
        var sel = document.selection.createRange();
        var selLen = document.selection.createRange().text.length;
        sel.moveStart('character', -input.value.length);
        $("#sel").html(sel);
        $("#selLen").html(selLen);
        return sel.text.length - selLen;
    }
}
function getStyle(fresh = false) {
    var oStyle = {
        background: 'none',
        width: (fresh?651:(($("#txtcommand").length)?($("#txtcommand").width()):651)) + 'px',
        height: (fresh?19:(($("#txtcommand").length)?($("#txtcommand").height()):19)) + 'px',
        zIndex: '9000',
        padding: '0 0 0 0',
        border: 'none', 
        resize: 'none',
        outline: 'none',
        'font-size': '14px',
        'font-family': "Monaco,Menlo,Consolas,'Courier New',monospace",
        color: 'blue',
        'overflow-y': 'hidden'
    }
    var sStyle = '';
    for (var z in oStyle){
        sStyle += z + ':' + oStyle[z] + ';';
    }
    return(sStyle);
}
