/*
* tools.js
* http://tasuke.dna.affrc.go.jp/
*
* Author: Jungsok Kim, Ryutaro Itoh
*
* Copyright (c) 2013 National Institute of Agrobiological Sciences   
*
*/

///////////////////////////// Data for drawing chromosome
function _chromosome() {
  var o=new Object(); 
  o.width=parseInt((cvs2.width/2-20)*(_getChr(Objects.Chr).size/_getChr("max").size));
  if(o.width < 72){
    o.width=72;
  }
  o.centromere=parseInt(o.width*(_getChr(Objects.Chr).centromere/_getChr(Objects.Chr).size).toFixed(2));
  o.pos=parseInt(o.width*((Objects.Pos*_cvtZoom(Objects.Zoom))/_getChr(Objects.Chr).size));
  o.area=parseInt(o.width*(Objects.Block*_cvtZoom(Objects.Zoom))/_getChr(Objects.Chr).size);
  if(o.pos<1)o.pos=1;
  if(o.area<1)o.area=1;

  // Start position of details meter
  var h=cvs2.width/2;
  // Draw meter
  var max=_last(Objects.Zoom)/Objects.Block;
  o.s1=parseInt((Objects.Pos-1)/Objects.Block*h/5);
  o.s2=parseInt((max-(Objects.Pos-1)/Objects.Block)*h/5-h/5);
  if(o.s1>h/5*2)o.s1=h/5*2;
  if(o.s2>h/5*2)o.s2=h/5*2;
  return o;
}

///////////////////////////// Get maximum SNP gradation
function _getMaxGraDP() {
  var en=new Object(JSON.parse(storage.getItem("_Env")));
  return en.depth_max;
}

////////////////////////////// sort by key of object
function _sortByKey(a,b) {
  var keys = Array();
  var ret = new Object;
  
  for (var prop in a){keys.push(prop);}
  keys.sort();
  for (var key in keys){if(keys[key] != "remove"){ret[key]=keys[key];}}
  return ret;
}

///////////////////////////// Get maximum SNP gradation
function _getMaxGraSNP() {
  var en=new Object(JSON.parse(storage.getItem("_Env")));
  switch (Objects.Zoom) {
    case "100k":
      return en.snp_100k;
      break;
    case "90k":
      return en.snp_90k;
      break;
    case "80k":
      return en.snp_80k;
      break;
    case "70k":
      return en.snp_70k;
      break;
    case "60k":
      return en.snp_60k;
      break;
    case "50k":
      return en.snp_50k;
      break;
    case "40k":
      return en.snp_40k;
      break;
    case "30k":
      return en.snp_30k;
      break;
    case "20k":
      return en.snp_20k;
      break;
    case "10k":
      return en.snp_10k;
      break;
    case "9k":
      return en.snp_9k;
      break;
    case "8k":
      return en.snp_8k;
      break;
    case "7k":
      return en.snp_7k;
      break;
    case "6k":
      return en.snp_6k;
      break;
    case "5k":
      return en.snp_5k;
      break;
    case "4k":
      return en.snp_4k;
      break;
    case "3k":
      return en.snp_3k;
      break;
    case "2k":
      return en.snp_2k;
      break;
    case "1k":
      return en.snp_1k;
      break;
    case "900b":
      return en.snp_900b;
      break;
    case "800b":
      return en.snp_800b;
      break;
    case "700b":
      return en.snp_700b;
      break;
    case "600b":
      return en.snp_600b;
      break;
    case "500b":
      return en.snp_500b;
      break;
    case "400b":
      return en.snp_400b;
      break;
    case "300b":
      return en.snp_300b;
      break;
    case "200b":
      return en.snp_200b;
      break;
    case "100b":
      return en.snp_100b;
      break;
    case "90b":
      return en.snp_90b;
      break;
    case "80b":
      return en.snp_80b;
      break;
    case "70b":
      return en.snp_70b;
      break;
    case "60b":
      return en.snp_60b;
      break;
    case "50b":
      return en.snp_50b;
      break;
    case "40b":
      return en.snp_40b;
      break;
    case "30b":
      return en.snp_30b;
      break;
    case "20b":
      return en.snp_20b;
      break;
    case "10b":
      return en.snp_10b;
      break;
    case "9b":
      return en.snp_9b;
      break;
    case "8b":
      return en.snp_8b;
      break;
    case "7b":
      return en.snp_7b;
      break;
    case "6b":
      return en.snp_6b;
      break;
    case "5b":
      return en.snp_5b;
      break;
    case "4b":
      return en.snp_4b;
      break;
    case "3b":
      return en.snp_3b;
      break;
    case "2b":
      return en.snp_2b;
      break;
    case "1b":
      return en.snp_1b;
      break;
  }
}

///////////////////////////// Get maximum INDEL gradation
function _getMaxGraINDEL() {
  var en=new Object(JSON.parse(storage.getItem("_Env")));
  switch (Objects.Zoom) {
    case "100k":
      return en.indel_100k;
      break;
    case "90k":
      return en.indel_90k;
      break;
    case "80k":
      return en.indel_80k;
      break;
    case "70k":
      return en.indel_70k;
      break;
    case "60k":
      return en.indel_60k;
      break;
    case "50k":
      return en.indel_50k;
      break;
    case "40k":
      return en.indel_40k;
      break;
    case "30k":
      return en.indel_30k;
      break;
    case "20k":
      return en.indel_20k;
      break;
    case "10k":
      return en.indel_10k;
      break;
    case "9k":
      return en.indel_9k;
      break;
    case "8k":
      return en.indel_8k;
      break;
    case "7k":
      return en.indel_7k;
      break;
    case "6k":
      return en.indel_6k;
      break;
    case "5k":
      return en.indel_5k;
      break;
    case "4k":
      return en.indel_4k;
      break;
    case "3k":
      return en.indel_3k;
      break;
    case "2k":
      return en.indel_2k;
      break;
    case "1k":
      return en.indel_1k;
      break;
    case "900b":
      return en.indel_900b;
      break;
    case "800b":
      return en.indel_800b;
      break;
    case "700b":
      return en.indel_700b;
      break;
    case "600b":
      return en.indel_600b;
      break;
    case "500b":
      return en.indel_500b;
      break;
    case "400b":
      return en.indel_400b;
      break;
    case "300b":
      return en.indel_300b;
      break;
    case "200b":
      return en.indel_200b;
      break;
    case "100b":
      return en.indel_100b;
      break;
    case "90b":
      return en.indel_90b;
      break;
    case "80b":
      return en.indel_80b;
      break;
    case "70b":
      return en.indel_70b;
      break;
    case "60b":
      return en.indel_60b;
      break;
    case "50b":
      return en.indel_50b;
      break;
    case "40b":
      return en.indel_40b;
      break;
    case "30b":
      return en.indel_30b;
      break;
    case "20b":
      return en.indel_20b;
      break;
    case "10b":
      return en.indel_10b;
      break;
    case "9b":
      return en.indel_9b;
      break;
    case "8b":
      return en.indel_8b;
      break;
    case "7b":
      return en.indel_7b;
      break;
    case "6b":
      return en.indel_6b;
      break;
    case "5b":
      return en.indel_5b;
      break;
    case "4b":
      return en.indel_4b;
      break;
    case "3b":
      return en.indel_3b;
      break;
    case "2b":
      return en.indel_2b;
      break;
    case "1b":
      return en.indel_1b;
      break;
  }
}

///////////////////////////// Convert zoom down
function _cvtZoomUp(zoom) {
  switch (zoom) {
    case "90k":
      return "100k";
      break;
    case "80k":
      return "100k";
      break;
    case "70k":
      return "100k";
      break;
    case "60k":
      return "100k";
      break;
    case "50k":
      return "100k";
      break;
    case "40k":
      return "50k";
      break;
    case "30k":
      return "50k";
      break;
    case "20k":
      return "50k";
      break;
    case "10k":
      return "20k";
      break;
    case "9k":
      return "10k";
      break;
    case "8k":
      return "10k";
      break;
    case "7k":
      return "10k";
      break;
    case "6k":
      return "10k";
      break;
    case "5k":
      return "10k";
      break;
    case "4k":
      return "5k";
      break;
    case "3k":
      return "5k";
      break;
    case "2k":
      return "5k";
      break;
    case "1k":
      return "2k";
      break;
    case "900b":
      return "1k";
      break;
    case "800b":
      return "1k";
      break;
    case "700b":
      return "1k";
      break;
    case "600b":
      return "1k";
      break;
    case "500b":
      return "1k";
      break;
    case "400b":
      return "500b";
      break;
    case "300b":
      return "500b";
      break;
    case "200b":
      return "500b";
      break;
    case "100b":
      return "200b";
      break;
    case "90b":
      return "100b";
      break;
    case "80b":
      return "100b";
      break;
    case "70b":
      return "100b";
      break;
    case "60b":
      return "100b";
      break;
    case "50b":
      return "100b";
      break;
    case "40b":
      return "50b";
      break;
    case "30b":
      return "50b";
      break;
    case "20b":
      return "50b";
      break;
    case "10b":
      return "20b";
      break;
    case "9b":
      return "10b";
      break;
    case "8b":
      return "10b";
      break;
    case "7b":
      return "10b";
      break;
    case "6b":
      return "10b";
      break;
    case "5b":
      return "10b";
      break;
    case "4b":
      return "5b";
      break;
    case "3b":
      return "5b";
      break;
    case "2b":
      return "5b";
      break;
    case "1b":
      return "2b";
      break;
  }
}

///////////////////////////// Convert zoom down
function _cvtZoomDown(zoom) {
  switch (zoom) {
    case "100k":
      return "50k";
      break;
    case "90k":
      return "50k";
      break;
    case "80k":
      return "50k";
      break;
    case "70k":
      return "50k";
      break;
    case "60k":
      return "50k";
      break;
    case "50k":
      return "20k";
      break;
    case "40k":
      return "20k";
      break;
    case "30k":
      return "20k";
      break;
    case "20k":
      return "10k";
      break;
    case "10k":
      return "5k";
      break;
    case "9k":
      return "5k";
      break;
    case "8k":
      return "5k";
      break;
    case "7k":
      return "5k";
      break;
    case "6k":
      return "5k";
      break;
    case "5k":
      return "2k";
      break;
    case "4k":
      return "2k";
      break;
    case "3k":
      return "2k";
      break;
    case "2k":
      return "1k";
      break;
    case "1k":
      return "500b";
      break;
    case "900b":
      return "500b";
      break;
    case "800b":
      return "500b";
      break;
    case "700b":
      return "500b";
      break;
    case "600b":
      return "500b";
      break;
    case "500b":
      return "200b";
      break;
    case "400b":
      return "200b";
      break;
    case "300b":
      return "200b";
      break;
    case "200b":
      return "100b";
      break;
    case "100b":
      return "50b";
      break;
    case "90b":
      return "50b";
      break;
    case "80b":
      return "50b";
      break;
    case "70b":
      return "50b";
      break;
    case "60b":
      return "50b";
      break;
    case "50b":
      return "20b";
      break;
    case "40b":
      return "20b";
      break;
    case "30b":
      return "20b";
      break;
    case "20b":
      return "10b";
      break;
    case "10b":
      return "5b";
      break;
    case "9b":
      return "5b";
      break;
    case "8b":
      return "5b";
      break;
    case "7b":
      return "5b";
      break;
    case "6b":
      return "5b";
      break;
    case "5b":
      return "2b";
      break;
    case "4b":
      return "2b";
      break;
    case "3b":
      return "2b";
      break;
    case "2b":
      return "1b";
      break;
  }
}

///////////////////////////// Convert zoom
function _cvtZoom(zoom) {
  if(!zoom) return null;
  switch (zoom) {
    case "100k":
      return 100000;
      break;
    case "90k":
      return 90000;
      break;
    case "80k":
      return 80000;
      break;
    case "70k":
      return 70000;
      break;
    case "60k":
      return 60000;
      break;
    case "50k":
      return 50000;
      break;
    case "40k":
      return 40000;
      break;
    case "30k":
      return 30000;
      break;
    case "20k":
      return 20000;
      break;
    case "10k":
      return 10000;
      break;
    case "9k":
      return 9000;
      break;
    case "8k":
      return 8000;
      break;
    case "7k":
      return 7000;
      break;
    case "6k":
      return 6000;
      break;
    case "5k":
      return 5000;
      break;
    case "4k":
      return 4000;
      break;
    case "3k":
      return 3000;
      break;
    case "2k":
      return 2000;
      break;
    case "1k":
      return 1000;
      break;
    case "900b":
      return 900;
      break;
    case "800b":
      return 800;
      break;
    case "700b":
      return 700;
      break;
    case "600b":
      return 600;
      break;
    case "500b":
      return 500;
      break;
    case "400b":
      return 400;
      break;
    case "300b":
      return 300;
      break;
    case "200b":
      return 200;
      break;
    case "100b":
      return 100;
      break;
    case "90b":
      return 90;
      break;
    case "80b":
      return 80;
      break;
    case "70b":
      return 70;
      break;
    case "60b":
      return 60;
      break;
    case "50b":
      return 50;
      break;
    case "40b":
      return 40;
      break;
    case "30b":
      return 30;
      break;
    case "20b":
      return 20;
      break;
    case "10b":
      return 10;
      break;
    case "9b":
      return 9;
      break;
    case "8b":
      return 8;
      break;
    case "7b":
      return 7;
      break;
    case "6b":
      return 6;
      break;
    case "5b":
      return 5;
      break;
    case "4b":
      return 4;
      break;
    case "3b":
      return 3;
      break;
    case "2b":
      return 2;
      break;
    case "1b":
      return 1;
      break;
    default:
      var array = zoom.match(/^(\d+)([b,k]?)$/);
      if(!array) break;
      if(array[0]) {
        if(!array[1]) array[1] = 'b';
        var $v = parseInt(array[0]);
        if(array[1] === 'b') {
          return $v;
        } else {
          return 1000 * $v;
        }
      } else {
        return 1000;
      }
      break;
  }
}

///////////////////////////// Convert length of chromosome as zoom
function _checkPos(p,z){
  if(p<1) p=1;
  if(p+Objects.Block>_last(z)) p=_last(z)-Objects.Block+1;
  return p;
}

///////////////////////////// Convert length of chromosome as zoom
function _last(zoom){
  var l = parseInt(_getChr(Objects.Chr).size/_cvtZoom(zoom));
  if (_getChr(Objects.Chr).size%_cvtZoom(zoom)) l=l+1;
  return l;
}

///////////////////////////// Convert length of chromosome as zoom with chromosome
function _checkPos_Chr(t,z,c){
  var p=parseInt(t/_cvtZoom(z));
  if (t % _cvtZoom(z)) p++;
  if(p<1) p=1;
  if(p+Objects.Block>_last_Chr(z,c)) p=_last_Chr(z,c)-Objects.Block+1;
  return p;
}

///////////////////////////// Convert length of chromosome as zoom with different chromosome
function _last_Chr(zoom,chr){
  var l = parseInt(_getChr(chr).size/_cvtZoom(zoom));
  if (_getChr(chr).size%_cvtZoom(zoom)) return l=l+1;
  return l;
}

////////////////////////////////////////////////////////
// Sub functions for gradient
////////////////////////////////////////////////////////
var grad_DP = gra_hexcolor('#f2f2f2','#000000');
var grad_S = gra_hexcolor('#f0f8ff','#00ffff');
var grad_Snohit = gra_hexcolor('#f4f4ff','#1199ff');
var grad_INDEL = gra_hexcolor('#FFCB8E','#A52A2A');
var grad_CSTM_array= new Array();
grad_CSTM_array[0] = gra_hexcolor("#f5e9f7","#a229b8");
grad_CSTM_array[1] = gra_hexcolor("#e6edf1", "#094c78");
grad_CSTM_array[2] = gra_hexcolor("#f7f3e5", "#b88b04");
grad_CSTM_array[3] = gra_hexcolor("#eaf0ea", "#326b2d");
function gra_rgb(b,a){ return gra(b,a,parse_rgb,build_rgb) }
function gra_hexcolor(b,a){
  var ret;
  try{
    ret = gra(b,a,parse_hexcolor,build_hexcolor);
  }catch(e){
    _dlg_reset_confirm();
  }
  return ret;
}
function gra(b,a,parser,builder){
  var d = mop.apply(this,[b,a].map(parser));
  return function(i){ return builder([d[0](i),d[1](i),d[2](i)]); }
}
function mop(b,a){ return b.map(function(x,v){ var s=a[v]-x; return function(i){return Math.ceil(x+s*i) }}) }
function parse_rgb(c){ var r=/rgb\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)/(c);
  return r&&r.shift()&&r.map(function(i){return parseInt(i)}) }
function parse_hexcolor(c){ return [0,1,2].map(function(j){ return parseInt(c[j*2+1]+c[j*2+2],16) }) }
function build_rgb(c){ return "rgb("+c+")" }
function build_hexcolor(c){ return "#"+c.map(hex2).join("") }
function hex2(i){return ((i<16)?"0":"")+i.toString(16)}
Array.prototype.map=function(f){
  var ret=[];
  for(var i=0;i<this.length;i++) ret.push(f(this[i],i,this));
  return ret;
}
function _cvtColorAlpha(str) {
  R = hexToR(str);G = hexToG(str);B = hexToB(str);
  return "rgba("+R+","+G+","+B+","+0.6+")";
}
function hexToR(h) {return parseInt((cutHex(h)).substring(0,2),16)}
function hexToG(h) {return parseInt((cutHex(h)).substring(2,4),16)}
function hexToB(h) {return parseInt((cutHex(h)).substring(4,6),16)}
function cutHex(h) {return (h.charAt(0)=="#") ? h.substring(1,7):h}

////////////////////////////////////////////////////////
// Set menu bar
////////////////////////////////////////////////////////
/*
function _setMenu(){
  // Set chromosome on main page
  var o=new Object(JSON.parse(storage.getItem("_Chrs")));
  var en=new Object(JSON.parse(storage.getItem("_Env")));
  var ld="";
  var mode_name = "";
  for (var i=0;i<o.length;i++){
    ld=ld+"<option value='"+o[i].id+"'>"+en.chromosome_name+o[i].id+"</option>";
  }
  $('#prm_chr').find('option').remove().end().append(ld);
  $('#prm_chr').val(storage.getItem('_Chr'));
  $('#menu .zoom').text("Block size "+storage.getItem('_Zoom'));
  if(!_isUndefined(Objects.Conditions)){
    if(Objects.Conditions.length > 1){
      var condArray = storage.getItem("_CrtCondition").split("|");
      var cond = parseInt(condArray[0]);
      mode_name = en.cstm_name.substr(0, 10)+" : "+Objects.Conditions[cond].id.substr(0, 10);
      //mode_name = $.format("%30s",mode_name);
    }else{
      mode_name = en.cstm_name;
    }
  }else{
    mode_name = en.cstm_name;
  }

  ///Available within 4k///
  $('#menu .one').css("color", "black");

  var onchk = "<i class=\"fa fa-circle\" aria-hidden=\"true\"></i> ";
  var offchk = "<i class=\"fa fa-circle-o\" aria-hidden=\"true\"></i> ";

  if (storage.getItem('_sFlg') == "1"){
    $('#menu .menuLisSnp').empty()
    $('#menu .menuLisSnp').append(onchk+"<i id=\"effbox\" style=\"background-color:#00ffff;width:10px;height:12px;display:inline-block;\"></i>SNP : ON (s)")
    $('#menu .menuLisSnp').css("color", "black");
  } else {
    $('#menu .menuLisSnp').empty()
    $('#menu .menuLisSnp').append(offchk+" <i id=\"effbox\" style=\"background-color:#00ffff;width:10px;height:12px;display:inline-block;\"></i>SNP : OFF (s)")
    $('#menu .menuLisSnp').css("color", "black");
  }
  if (storage.getItem('_iFlg') == "1"){
    $('#menu .menuLisIndel').empty()
    $('#menu .menuLisIndel').append(onchk+" <i id=\"effbox\" style=\"background-color:#A52A2A;width:10px;height:12px;display:inline-block;\"></i>INDEL : ON (i)")
    $('#menu .menuLisIndel').css("color", "black");
  } else {
    $('#menu .menuLisIndel').empty()
    $('#menu .menuLisIndel').append(offchk+" <i id=\"effbox\" style=\"background-color:#A52A2A;width:10px;height:12px;display:inline-block;\"></i>INDEL : OFF (i)")
    $('#menu .menuLisIndel').css("color", "black");
  }
  if (storage.getItem('_dpFlg') == "1"){
    $('#menu .menuLisDP').empty()
    $('#menu .menuLisDP').append(onchk+" <i id=\"effbox\" style=\"background-color:#b2b2b2;width:10px;height:12px;display:inline-block;\"></i>DEPTH : ON (p)")
    $('#menu .menuLisDP').css("color", "black");
  } else {
    $('#menu .menuLisDP').empty()
    $('#menu .menuLisDP').append(offchk+" <i id=\"effbox\" style=\"background-color:#b2b2b2;width:10px;height:12px;display:inline-block;\"></i>DEPTH : OFF (p)")
    $('#menu .menuLisDP').css("color", "black");
  }

  var offEff = "<table CELLSPACING=0 CELLPADDING=0 style=\"border-collapse: collapse;border-spacing: 0;\"><tr><td><i class=\"fa fa-circle-o\" aria-hidden=\"true\"></i>&nbsp;</td><td><table CELLSPACING=0 CELLPADDING=0 style=\"border-collapse: collapse;border-spacing: 0;\"><tr><td><i style=\"background-color:#00BFFF;width:5px;height:6px;display:block;\"></i></td><td><i style=\"background-color:#FFFF00;width:5px;height:6px;display:block;\"></i></td></tr><tr><td><i style=\"background-color:#EE9A00;width:5px;height:6px;display:block;\"></i></td><td><i style=\"background-color:red;width:5px;height:6px;display:block;\"></i></td></tr></table></td><td>SnpEff : OFF (e)</td></tr></table>";
  var onEff = "<table CELLSPACING=0 CELLPADDING=0 style=\"border-collapse: collapse;border-spacing: 0;\"><tr><td><i class=\"fa fa-circle\" aria-hidden=\"true\"></i>&nbsp;</td><td><table CELLSPACING=0 CELLPADDING=0 style=\"border-collapse: collapse;border-spacing: 0;\"><tr><td><i style=\"background-color:#00BFFF;width:5px;height:6px;display:block;\"></i></td><td><i style=\"background-color:#FFFF00;width:5px;height:6px;display:block;\"></i></td></tr><tr><td><i style=\"background-color:#EE9A00;width:5px;height:6px;display:block;\"></i></td><td><i style=\"background-color:red;width:5px;height:6px;display:block;\"></i></td></tr></table></td><td>SnpEff : ON (e)</td></tr></table>";

  if (storage.getItem('_Eff') == "off"){
    if(storage.getItem("_Ref") == "Reference"){
      //$('#menu .eff').text("snpEff : OFF (e)")
      $('#menu .eff').empty();
      //$('#menu .eff').append(offchk+fourblock+"snpEff : OFF (e)")
      $('#menu .eff').append(offEff);
      $('#menu .eff').css("color", "black");
    }else{
      //$('#menu .eff').text("snpEff OFF (e)")
      //$('#menu .eff').css("color", "gray");
      $('#menu .eff').empty();
      //$('#menu .eff').append(offchk+fourblock+"snpEff : OFF (e)")
      $('#menu .eff').append(offEff)
      $('#menu .eff').css("color", "black");
    }
  } else {
    //$('#menu .eff').text("snpEff ON (e)")
    //$('#menu .eff').css("color", "yellow");
    $('#menu .eff').empty();
    //$('#menu .eff').append(onchk+fourblock+"snpEff : ON (e)")
    $('#menu .eff').append(onEff)
    $('#menu .eff').css("color", "black");
  }
  if (storage.getItem('_Snpblock') == "off"){
    //$('#menu .snpblock').text("Snpblock OFF (b)")
    $('#menu .snpblock').empty()
    $('#menu .snpblock').append(offchk+"Snpblock : OFF (b)")
    //$('#menu .snpblock').css("color", "white");
    $('#menu .snpblock').css("color", "black");
  } else {
    //$('#menu .snpblock').text("Snpblock ON (b)")
    $('#menu .snpblock').empty()
    $('#menu .snpblock').append(onchk+"Snpblock : ON (b)")
    //$('#menu .snpblock').css("color", "yellow");
    $('#menu .snpblock').css("color", "black");
  }
  if (storage.getItem('_Null') == "true"){
    //$('#menu .null').text("Highlight DEPTH=NA ON (n)")
    $('#menu .null').empty()
    $('#menu .null').append(onchk+"Highlight DEPTH=NA : ON (n)")
    //$('#menu .null').css("color", "yellow");
    $('#menu .null').css("color", "black");
  } else {
    //$('#menu .null').text("Highlight DEPTH=NA OFF (n)")
    $('#menu .null').empty()
    $('#menu .null').append(offchk+"Highlight DEPTH=NA : OFF (n)")
    //$('#menu .null').css("color", "white");
    $('#menu .null').css("color", "black");
  }
  if (storage.getItem('_Amino') == "off"){
    //$('#menu .amino').text("Amino acids OFF")
    $('#menu .amino').empty()
    $('#menu .amino').append(offchk+"Amino acids : OFF")
    //$('#menu .amino').css("color", "white");
    $('#menu .amino').css("color", "black");
  } else {
    //$('#menu .amino').text("Amino acids ON")
    $('#menu .amino').empty()
    $('#menu .amino').append(onchk+"Amino acids : ON")
    //$('#menu .amino').css("color", "yellow");
    $('#menu .amino').css("color", "black");
  }
  if (storage.getItem('_Indicator') == "off"){
    //$('#menu .indicator').text("Indicator : OFF (d)")
    $('#menu .indicator').empty()
    $('#menu .indicator').append(offchk+"Indicator : OFF (d)")
    //$('#menu .indicator').css("color", "white");
    $('#menu .indicator').css("color", "black");
  } else {
    //$('#menu .indicator').text("Indicator : ON (d)")
    $('#menu .indicator').empty()
    $('#menu .indicator').append(onchk+"Indicator : ON (d)")
    //$('#menu .indicator').css("color", "yellow");
    $('#menu .indicator').css("color", "black");
  }
  if (storage.getItem('_GWAS') == "off"){
    //$('#menu .GWAS').text("Show GWAS : OFF (g)")
    $('#menu .GWAS').empty()
    $('#menu .GWAS').append(offchk+"Show GWAS : OFF (g)")
    //$('#menu .GWAS').css("color", "white");
    $('#menu .GWAS').css("color", "black");
    storage.getItem('_GWAS') == "on"
  } else {
    //$('#menu .GWAS').text("Show GWAS ON (g)")
    $('#menu .GWAS').empty()
    $('#menu .GWAS').append(onchk+"Show GWAS : ON (g)")
    //$('#menu .GWAS').css("color", "yellow");
    $('#menu .GWAS').css("color", "black");
    storage.getItem('_GWAS') == "off"
  }
  if (storage.getItem('_Onebase') == "off"){
    if(storage.getItem("_Ref") == "Reference"){
      //$('#menu .one').text("Frequency mode : ON (f)")
      $('#menu .one').empty()
      //$('#menu .one').append(onchk+"Frequency mode : ON (f)")
      //$('#menu .one').append(offchk+"Absolute Position : OFF (f)")
      $('#menu .one').append(onchk+"Block mode : ON (f)")
      //$('#menu .one').css("color", "yellow");
      //$('#menu .one').css("color", "black");
    }else{
      //$('#menu .one').text("Frequency mode : ON (f)")
      $('#menu .one').empty()
      //$('#menu .one').append(onchk+"Frequency mode : ON (f)")
      //$('#menu .one').append(offchk+"Absolute Position : OFF (f)")
      $('#menu .one').append(onchk+"Block mode : ON (f)")
      //$('#menu .one').css("color", "gray");
    }
  } else {
    if(storage.getItem("_Ref") == "Reference"){
      //$('#menu .one').text("Frequency mode : OFF (f)")
      $('#menu .one').empty()
      //$('#menu .one').append(offchk+"Frequency mode : OFF (f)")
      //$('#menu .one').append(onchk+"Absolute Position : ON (f)")
      $('#menu .one').append(offchk+"Block mode : OFF (f)")
      //$('#menu .one').css("color", "white");
      //$('#menu .one').css("color", "black");
    }else{
      //$('#menu .one').text("Frequency mode : OFF (f)")
      $('#menu .one').empty()
      //$('#menu .one').append(offchk+"Frequency mode : OFF (f)")
      //$('#menu .one').append(onchk+"Absolute Position : ON (f)")
      $('#menu .one').append(offchk+"Block mode : OFF (f)")
      //$('#menu .one').css("color", "gray");
    }
  }
  if (storage.getItem('_CSTM') == "on"){
    $('#menu .cstm').text("View "+en.cstm_name+" : ON (c)");
    $('#menu .cstm').css("color", "yellow");
    if(Objects.Conditions.length > 1){
      $('#menu .condSelect').css("color", "white");
      $("#menu .condSelect").attr({href: "javascript:_dialog_cond()"});
    }else{
      $('#menu .condSelect').css("color", "gray");
      $("#menu .condSelect").attr({href: "javascript:void(0)"});
    }
    $('#menu .color_cstm').text("Color manager");
    //$('#menu .color_cstm').css("color", "white");
    $('#menu .color_cstm').css("color", "black");
  }
  if(_trackConflict()){
    $('#menu .viewObjId').empty();
    $('#menu .viewObjId').append(offchk+"Feature IDs : ON (t)")
    $('#menu .viewObjId').css("color", "#aaaaaa");
  }else if (storage.getItem('_TrackID') == "on"){
    $('#menu .viewObjId').empty();
    $('#menu .viewObjId').append(onchk+"Feature IDs : ON (t)");
    $('#menu .viewObjId').css("color", "black");
  } else {
    $('#menu .viewObjId').empty();
    $('#menu .viewObjId').append(offchk+"Feature IDs : Off (t)");
    $('#menu .viewObjId').css("color", "black");
  }

  var regHighlightsStr = storage.getItem('_RegHighlights');
  if(regHighlightsStr && JSON.parse(regHighlightsStr).length > 0) {
    $('#menu .regHighlight').css("color", "black");
  } else {
    $('#menu .regHighlight').css("color", "#aaaaaa");
    // Link invalidation is done in _dialog_clearHighlight()
  }

  var highlightVariantGene = storage.getItem("_HighlightVariantGene");
  if(highlightVariantGene) {
    $('#menu .variantHighlight').css("color", "black");
  } else {
    $('#menu .variantHighlight').css("color", "#aaaaaa");
    // Link invalidation is done in _dialog_clearVariantHighlight()
  }

  if(isVariantFiltered()) {
    $('#menu .variantFilter').css("color", "black");
  } else {
    $('#menu .variantFilter').css("color", "#aaaaaa");
    // Link invalidation is done in _dialog_clearVariantFilter()
  }
}
function isVariantFiltered() {
	return (isFilteredVariantDepth() || isFilteredVariantQual() || isFilteredVariantSnpEff());
}
function isFilteredVariantDepth() {
        return false;
}
function isFilteredVariantQual() {
        return false;
}
function isFilteredVariantSnpEff() {
        var effgrpsStr = storage.getItem("_EffGrps");
        var effgrps = [];
        try {
                if(effgrpsStr) effgrps = JSON.parse(effgrpsStr);
                if(effgrps.length < _getEffNoFilterArray().length) return true;
        } catch (error) {
                console.error(error);
        }
        return false;
}
*/

////////////////////////////////////////////////////////
// Set color
////////////////////////////////////////////////////////
var colorset = null;
function _getCachedColorset(){
	if(colorset === null) _setColor();
	return colorset;
}
function _setColor(){
  var o=new Object();
  if(storage.getItem('_Color')=="0"){
    //o.bgcolor="#595454";
    o.bgcolor="#fff";
    //o.tracktitle="khaki";
    o.tracktitle="#000";
    //o.metercolor="#fff";
    o.metercolor="#000";
    o.meterstring="#fff";
    o.pointercolor="#000";
    //o.pointercolor="#fff";
    //o.pointerstringcolor="#ffffff";
    o.pointerstringcolor="#000";
    o.chrcolor="rgb(60,179,113)";
    o.chrborder="#A4A4A4";
    //o.chrborder="#ffffff";
    //o.chrstringcolor="rgb(255,252,250)";
    o.chrstringcolor="#000";
    o.chrareacolor="rgba(245,0,0,0.6)";
    o.chrareaselected="rgba(245,222,179,0.4)";
    o.detaillinecolor="rgba(0,150,0,0.5)";
    o.detailarea="rgba(245,0,0,0.6)";
    o.detailareastroke="#fff";
    //o.detailstring="#fff";
    o.detailstring="#000";
    o.detailupstring="#000";
    o.detailareaselected="rgba(245,222,179,0.5)";
    o.connectline="yellow";
    //o.title="#f8f8ff";
    o.title="#000";
    //o.reftitle="#ffff00";
    o.reftitle="#000";
    //o.snptitle="khaki";
    o.snptitle="#000";
    //o.fasta="rgb(245,245,220)";
    o.fasta="black";
    //o.amino="rgb(245,245,220)";
    o.amino="black";
    //o.fastabold="rgb(245,245,0)";
    o.fastabold="brown";
    //o.locusid="rgb(255,255,255)";
    o.locusid="black";
    o.highlight="rgba(0,255,0,0.3)";
    o.centeringtrack="rgba(255,255,255,0.3)";
    o.centeringcell="rgba(0,0,0,0.3)";
    o.zoombar="#C0C0C0";
  } else if(storage.getItem('_Color')=="1") {
    o.bgcolor="#778899";
    //o.tracktitle="khaki";
    o.tracktitle="#000";
    o.metercolor="#bebebe";
    o.meterstring="beige";
    o.pointercolor="#fff";
    o.pointerstringcolor="#ffffff";
    o.chrcolor="rgb(0,150,0)";
    o.chrborder="#ffffff";
    o.chrstringcolor="rgb(255,252,250)";
    o.chrareacolor="rgba(245,0,0,0.6)";
    o.chrareaselected="rgba(245,222,179,0.4)";
    o.detaillinecolor="rgba(0,150,0,0.5)";
    o.detailarea="rgba(245,0,0,0.6)";
    o.detailareastroke="#fff";
    o.detailstring="#fff";
    o.detailupstring="#000";
    o.detailareaselected="rgba(245,222,179,0.5)";
    o.connectline="#000";
    o.title="#ffffff";
    o.reftitle="#000";
    o.snptitle="khaki";
    o.fasta="rgb(245,245,220)";
    o.amino="rgb(245,245,220)";
    o.fastabold="rgb(245,245,0)";
    o.locusid="rgb(255,255,255)";
    o.highlight="rgba(0,255,0,0.3)";
    o.centeringtrack="rgba(255,255,255,0.3)";
    o.centeringcell="rgba(0,0,0,0.3)";
    o.zoombar="#ffffff";
  } else if(storage.getItem('_Color')=="2") {
    o.bgcolor="#f8f8ff";
    o.tracktitle="#000000";
    o.metercolor="#000000";
    o.meterstring="#000000";
    o.pointercolor="#000000";
    o.pointerst000lor="#000000";
    o.chrcolor="rgb(255,140,0)";
    o.chrborder="#000000";
    o.chrstringcolor="rgb(0,0,0)";
    o.chrareacolor="rgba(0,250,0,0.6)";
    o.chrareaselected="rgba(255,255,255,0.5)";
    o.detaillinecolor="rgba(255,140,0,0.9)";
    o.detailarea="rgba(60,179,113,0.6)";
    o.detailareastroke="#000";
    o.detailstring="#000";
    o.detailupstring="rgb(0,0,0)";
    o.detailareaselected="rgba(255,255,255,0.5)";
    o.connectline="#000";
    o.title="#000000";
    o.reftitle="brown";
    o.snptitle="#000000";
    o.fasta="rgb(0,0,0)";
    o.amino="rgb(0,0,0)";
    o.fastabold="brown";
    o.locusid="rgb(0,0,0)";
    o.highlight="rgba(0,255,0,0.4)";
    o.centeringtrack="rgba(255,255,255,0.3)";
    o.centeringcell="rgba(0,0,0,0.3)";
    o.zoombar="#f8f8ff";
  }
  o.cursorline = "rgba(255,80,0,0.9)";	// rgba(0,255,0,0.9) -> rgba(0,210,0,0.9)
  o.cursorfill = "rgba(0,255,0,0.08)";
  o.highlightacc = "rgba(255,0,255,0.08)";
  o.highlightaccreg = "rgba(255,0,0,0.07)";
  o.highlightreg = "rgba(255,0,0,0.07)";
  o.cursoracc = "rgba(0,255,0,0.12)";

  colorset = o;

  return o;
}

///////////////////////////// Convert string of amino
function _get_amino(str) {
  var aminoStr='';
  switch (str.toUpperCase()) {
    case "AAA":aminoStr="K";break;
    case "AAC":aminoStr="N";break;
    case "AAG":aminoStr="K";break;
    case "AAT":aminoStr="N";break;
    case "ACA":aminoStr="T";break;
    case "ACC":aminoStr="T";break;
    case "ACG":aminoStr="T";break;
    case "ACT":aminoStr="T";break;
    case "AGA":aminoStr="R";break;
    case "AGC":aminoStr="S";break;
    case "AGG":aminoStr="R";break;
    case "AGT":aminoStr="S";break;
    case "ATA":aminoStr="I";break;
    case "ATC":aminoStr="I";break;
    case "ATG":aminoStr="M";break;
    case "ATT":aminoStr="I";break;
    case "CAA":aminoStr="Q";break;
    case "CAC":aminoStr="H";break;
    case "CAG":aminoStr="Q";break;
    case "CAT":aminoStr="H";break;
    case "CCA":aminoStr="P";break;
    case "CCC":aminoStr="P";break;
    case "CCG":aminoStr="P";break;
    case "CCT":aminoStr="P";break;
    case "CGA":aminoStr="R";break;
    case "CGC":aminoStr="R";break;
    case "CGG":aminoStr="R";break;
    case "CGT":aminoStr="R";break;
    case "CTA":aminoStr="L";break;
    case "CTC":aminoStr="L";break;
    case "CTG":aminoStr="L";break;
    case "CTT":aminoStr="L";break;
    case "GAA":aminoStr="E";break;
    case "GAC":aminoStr="D";break;
    case "GAG":aminoStr="E";break;
    case "GAT":aminoStr="D";break;
    case "GCA":aminoStr="A";break;
    case "GCC":aminoStr="A";break;
    case "GCG":aminoStr="A";break;
    case "GCT":aminoStr="A";break;
    case "GGA":aminoStr="G";break;
    case "GGC":aminoStr="G";break;
    case "GGG":aminoStr="G";break;
    case "GGT":aminoStr="G";break;
    case "GTA":aminoStr="V";break;
    case "GTC":aminoStr="V";break;
    case "GTG":aminoStr="V";break;
    case "GTT":aminoStr="V";break;
    case "TAA":aminoStr="*";break;
    case "TAC":aminoStr="Y";break;
    case "TAG":aminoStr="*";break;
    case "TAT":aminoStr="Y";break;
    case "TCA":aminoStr="S";break;
    case "TCC":aminoStr="S";break;
    case "TCG":aminoStr="S";break;
    case "TCT":aminoStr="S";break;
    case "TGA":aminoStr="*";break;
    case "TGC":aminoStr="C";break;
    case "TGG":aminoStr="W";break;
    case "TGT":aminoStr="C";break;
    case "TTA":aminoStr="L";break;
    case "TTC":aminoStr="F";break;
    case "TTG":aminoStr="L";break;
    case "TTT":aminoStr="F";break;
    default: aminoStr="-";break;
  }
  return aminoStr;
}

function _reverseNucleotide(rawseq){
  var seqArray = rawseq.toUpperCase().split("");
  var ret = "";
  for(var i=0; i<seqArray.length; i++){
    switch(seqArray[i]){
      case "A":seqArray[i]="T";break;
      case "G":seqArray[i]="C";break;
      case "C":seqArray[i]="G";break;
      case "T":seqArray[i]="A";break;
      case "M":seqArray[i]="K";break;
      case "K":seqArray[i]="M";break;
      case "R":seqArray[i]="Y";break;
      case "Y":seqArray[i]="R";break;
      default:seqArray[i]=seqArray[i]; break;
    }
  }
  ret = seqArray.join("");
  return ret;
}

function _setTitleColor(){
  var en = new Object(JSON.parse(storage.getItem("_Env")));
  var reserved_color = {};
  if("color_acc_group_color" in en && en["color_acc_group_color"]) reserved_color = en["color_acc_group_color"];

//  var color_list = new Array("#f8f8ff","#ffc125","#df7fb5","#8ad1a9","#56b4e9","#eee8aa","#7fffd4","#FFFF00");
//  var color_list = new Array("#fa8f8f","#ffc125","#df7fb5","#8ad1a9","#56b4e9","#eee8aa","#7fffd4","#FFFF00");
//  var color_list = new Array("#aadddd","#99ddff","#aaddaa","#aabbee","#00aaee","#ffffbb","#ffbbbb","#009999","#cccccc", "#ffbbdd");
  var color_list = new Array("#ee0077","#ffff33","#550000","#00aaee","#112288","#ff9900","#990088","#009944","#aa3311","#0077aa");

  if("color_acc_group_preset" in en && en["color_acc_group_preset"]  instanceof Array && en["color_acc_group_preset"].length > 0) color_list = en["color_acc_group_preset"];

  var ret = "";
  var color_list_count = 0;
  var str_array = new Array();
  var key_reg = new Array();
  var ori_name_sub = storage.getItem("_NameSub");
  var dest_subtitle = storage.getItem("_NameSub");
  if(dest_subtitle == "subvariety"){dest_subtitle = "grp";}
  var grps = _getList(dest_subtitle).sort();
  for(var i=0; i<grps.length; i++){
    if(!(grps[i] in key_reg)){
      key_reg[grps[i]] = 1;
      if(ori_name_sub in reserved_color && grps[i] in reserved_color[ori_name_sub]) {
        str_array.push("\""+grps[i]+"\":\"" + reserved_color[ori_name_sub][grps[i]] + "\"");
      } else {
        var colidx = color_list_count % color_list.length;
        str_array.push("\"" + grps[i] + "\":\"" + color_list[colidx] + "\"");
        color_list_count++;
      }
/*
      else if(color_list_count in color_list){
        str_array.push("\""+grps[i]+"\":\""+color_list[color_list_count]+"\"");
        color_list_count++;
      }else{
        var num = "#";
        for(var a=0; a<6; a++){num += (parseInt(5)+Math.floor(Math.random() *10)).toString(16);}
        str_array.push("\""+grps[i]+"\":\""+num+"\"");
      }
*/
    }
  }
  ret = str_array.join(",");
  ret = "{" + ret + "}";
  storage.setItem("_GrpCol",ret);
}

function escape_html (string) {
  if(typeof string !== 'string') {
    return string;
  }
  return string.replace(/[&'`"<>]/g, function(match) {
    return {
      '&': '&amp;',
      "'": '&#x27;',
      '`': '&#x60;',
      '"': '&quot;',
      '<': '&lt;',
      '>': '&gt;',
    }[match]
  });
}
function unescape_html(string) {
        if(typeof string !== 'string') {
                return string;
        }
        return string.replace(/&quot;/g,'"')
        .replace(/&#039;/g,"'")
        .replace(/&lt;/g,"<")
        .replace(/&gt;/g,">")
        .replace(/&#x60;/g,"`")
        .replace(/&amp;/g,"&");
}
function escape_regexp(string) {
	return string.replace(/([\|\?\/\.\^\$\[\]\{\}\(\)\+\-])/g, '\\$1' );
}
function escape_filename(string) {
//	var chars = ['&', '(', ')', '[', ']', '{', '}', '^', '=', ';', '!', "'", '+', ',', '`', '~', ' '];
	var chars = ['\\', '/', ':', '*', '?', '"', '<', '>', '|', '/']; 
	for(var i=0,len=chars.length; i<len; i++) {
		var regexp = new RegExp('\\' + chars[i], "g");
		string = string.replace(regexp, '_');
	}
	return string;
}

function _getEffNoFilterArray() {
	return [3,2,1,0,-1];
}
function _getEffFilterMap() {
	var impact2checked = {"3":false, "2":false, "1":false, "0":false, "-1":false};
	var effGrps = storage.getItem('_EffGrps');
	if(effGrps) {
		var impacts = JSON.parse(effGrps);
		for(var i=0, impactsLen=impacts.length; i<impactsLen; i++) {
			if(impacts[i].toString() in impact2checked) impact2checked[impacts[i].toString()] = true;
		}
	} else {
		impact2checked = {"3":true, "2":true, "1":true, "0":true, "-1":true};
	}
	return impact2checked;
}
function syncCheckboxEnabled(parentId, childId) {
	if($("#" + parentId).prop("checked")) {
		$("#" + childId).prop("disabled", false);
	} else {
		$("#" + childId).prop("disabled", true);
	}
}
function matchStart(params, data) {
    params.term = params.term || '';
    if (data.text.toUpperCase().indexOf(params.term.toUpperCase()) == 0) {
        return data;
    }
    return false;
}

function isGtIsZero(gt) {
	if(gt === '0/0' || gt === '0|0') {
		return true;
	} else {
		return false;
	}
}
function isEffFromat(str) {
	if (/^[^\|]+\(/.test(str)) {
		return true;
	} else {
		return false;
	}
}

function getGtrowAlleles(gtrowStr, refStr, altsAllStr) {
	var gtrowArr = gtrowStr.split(/[\/\|]/);
	var altsAllArr = altsAllStr.split(',');
	var destAltArr = [];
	var altStr = "";
	for(j=0,jLen=gtrowArr.length; j<jLen; j++) {
		if(gtrowArr[j] === '0') {
			destAltArr.push(refStr);
		} else {
//console.log("Test:" + j + " -> " + gtrowArr[j] + " -> " + altAllArr[gtrowArr[j]-1]);
			destAltArr.push(altsAllArr[gtrowArr[j]-1]);
		}
		altStr = destAltArr.join(",");
//console.log("Test2:" + altStr);
	}
	return altStr;
}
function hitAltsInEff(altsStr, eff_allele) {
	if(!altsStr) return false;

	var alts = altsStr.split(",");
/*
	if(alts[0]===eff_allele) {
		return true;
	} else if(alts.length>1 && alts[1]===eff_allele) {
		return true;
	} else {
		return false;
	}
*/
	var hit = false;
	for(var i=0, altsLen=alts.length; i<altsLen; i++) {
		var alt = alts[i];
		if(alt === eff_allele) {
			hit = true;
			break;
		}
	}
	return hit;
}
var effver2col = [];
function parseSnpEffStr(str) {
        var funcGetEffcol = function(ver) {
                var col = "";
                if(ver in effver2col) {
                        col = effver2col[ver];
                } else {
                        col = _getEffCol(ver);
                        effver2col[ver] = col;
                }
                return col;
        }

        var effver = ""; var id = ""; var name = ""; var eff = ""; var effType = ""; var allele = "";
        if(isEffFromat(str)) {
		str = str.replace(/[^\)]*$/, '');
		var effEach = str.split("|");
                effver = "EFF";
                effcol = funcGetEffcol(effver);
                effarr = effEach[0].split("(");
                eff = effarr[0];
                effType = effarr[1];
                if(effEach.length > 10) {
                        // new EFF format
                        id = effEach[effcol];
                        name = effEach[5];
//			allele = effEach[10].replace(/\)$/, '');
                } else {
                        // old EFF format
                        id = effEach[4];
                        name = effEach[7];
                }
        } else {
		var effEach = str.split("|");
                effver = "ANN";
                effcol = funcGetEffcol(effver);
                eff = effEach[1];
                effType = effEach[2];
                id = effEach[effcol];
                name = effEach[3];
                allele = effEach[0];
        }
        return {'effver':effver, 'id':id, 'name':name, 'eff':eff, 'effType':effType, 'allele':allele};
}
/*
function expandCanvasVertical(canvass, y, expandHeight, frame, endFunc) {
	var canvasVerticalImages = [];
	for(var j=0,jLen=canvass.length; j<jLen; j++) {
		canvasVerticalImages[j] = [null, null];

		var ctx = canvass[j].getContext('2d');
		var lowerImageHeight = canvass[j].height - y + 1;
		if(y>0) canvasVerticalImages[j][0] = ctx.getImageData(0, 0, canvass[j].width, y);
		if(lowerImageHeight>0) canvasVerticalImages[j][1] = ctx.getImageData(0, y, canvass[j].width, lowerImageHeight);
		ctx.clearRect(0, 0, ctx.canvas.clientWidth, ctx.canvas.clientHeight);
	}	

	var moveUnit = expandHeight / frame;

	var i = 0;
	function animate(timestamp) {
		var moveAmount = moveUnit * (i+1);
		for(var j=0,jLen=canvass.length; j<jLen; j++) {
			var ctx = canvass[j].getContext('2d');
//			canvass[j].height = canvass[j].height + moveUnit;
			ctx.fillStyle = '#595454';
			if(canvasVerticalImages[j][0] !== null) ctx.putImageData(canvasVerticalImages[j][0], 0, 0);
			if(canvasVerticalImages[j][1] !== null) {
				if(moveUnit >= 0) {
					// Expand
					var newY = y + moveAmount;
					ctx.fillRect(0, y, canvass[j].width, moveAmount);
					ctx.putImageData(canvasVerticalImages[j][1], 0, newY);
//					if(newY > canvass[j].height) { i=frame; break; }	// The process ends when the scroll goes off canvas.
				} else {
					// Collapse
					ctx.clearRect(0, y, ctx.canvas.clientWidth, canvass[j].height - y);
					var lowerTrimHeight = canvass[j].height - y + moveAmount;
					ctx.putImageData(canvasVerticalImages[j][1], 0, y+moveAmount, 0, moveAmount*(-1), canvass[j].width, lowerTrimHeight);
				}
			}
		}
		i++;
		if(i<frame) {
			window.requestAnimationFrame(animate);
		} else if(endFunc != null) {
			endFunc();
		}
	}
	window.requestAnimationFrame(animate);
}
*/
function getFormattedNumberString(num, digit) {
	numStr =  String(num.toFixed(digit));
	return numStr.replace(/0+$/, "");
}
function getRadioGroupValue(grpName) {
	var elements = document.getElementsByName(grpName);
	var val = null;
	for(var i=0,len=elements.length; i<len; i++) {
		if(elements[i].checked) {
			val = elements[i].value;
			break;
		}
	}
	return val;
}
function setRadioGroupValue(grpName, val) {
	var elements = document.getElementsByName(grpName);
	var hit = false;
	for(var i=0,len=elements.length; i<len; i++) {
		if(elements[i].value === val) {
			elements[i].checked = true;
			hit = true;
			break;
		}
	}
	return hit;
}
/*
function getMouseXY(e) {
	// Since this function is executing preventDefault(), it prevents default operation.
	var rect = e.target.getBoundingClientRect();
	e.preventDefault();
	var xy = {};
	// Getting coordinate(pc or iOS)
	if (isSmartPhone()) {	//iPad & iPhone & Android
		if(!e.touches && !e.changedTouches) return null;
		var touch = e.touches[0] || e.changedTouches[0];	// left:start,move right:end
		xy.x = parseInt(touch.pageX-(document.body.scrollLeft || document.documentElement.scrollLeft))-parseInt(rect.left);
		xy.y=parseInt(touch.pageY-(document.body.scrollTop || document.documentElement.scrollTop))-parseInt(rect.top);
	} else {	//PC
		xy.x=parseInt(e.clientX)-parseInt(rect.left);
		xy.y=parseInt(e.clientY)-parseInt(rect.top);
	}
	return xy;
}
*/
var isSmaPho = null;
function isSmartPhone() {
	if(isSmaPho === null) {
		var general = /Android|webOS|iPhone|iPad|iPod|BlackBerry|Nexus/i.test(navigator.userAgent);
		var safari = isSafari() && 'ontouchend' in document;
		var firefox = isFirefox() && /Mobile|Tablet/i.test(navigator.userAgent);
		var winPhone = /Windows\s*Phone/i.test(navigator.userAgent);
/*
		var safari = /macintosh/i.test(navigator.userAgent) && 'ontouchend' in document;
		var firefox = /Firefox/i.test(navigator.userAgent) && /Mobile|Tablet/i.test(navigator.userAgent);
		var winPhone = /Windows\s*Phone/i.test(navigator.userAgent);
*/
		isSmaPho = (general || safari || firefox || winPhone);
	}
	return isSmaPho;
}
function isFirefox() {
	return /Firefox/i.test(navigator.userAgent);
}
function isSafari() {
	return /macintosh/i.test(navigator.userAgent);
}
function isEdge() {
	return /Edg/i.test(navigator.userAgent);
}
function getOs() {
	var ua = window.navigator.userAgent.toLowerCase();
	var os = null;
	if(ua.indexOf("windows nt") !== -1) {
		os = "windows";
	} else if(ua.indexOf("android") !== -1) {
		os = "android";
	} else if(ua.indexOf("iphone") !== -1 || ua.indexOf("ipad") !== -1) {
		os = "ios";
	} else if(ua.indexOf("mac os x") !== -1) {
		os = "macos";
	}
	return os;
}
function isMacos() {
	return getOs() === "macos";
}
function isWindows() {
	return getOs() === "windows";
}

/*
var cvsForTrimImageData = document.createElement("canvas");
function trimImageData(imageData, x, y, w, h) {
	// Very heavy!!!
	var vCtx = cvsForTrimImageData.getContext('2d');
	cvsForTrimImageData.width = imageData.width;
	cvsForTrimImageData.height = imageData.height;
	vCtx.putImageData(imageData, 0, 0);
	return vCtx.getImageData(x, y, w, h);
}
*/
function rootingMidpoint(newick) {
//console.log("Before:" + newick);
	var t = new Tree();
	t.Parse(newick);
	t.MidpointRoot();
	t.ComputeWeights(t.root);

	var newickConv = t.WriteNewick();	// This function removes spaces in the ID string.(?)
//console.log("After:" + newickConv);
	clearTreelibTree(t);

	return newickConv;
}
/**
  To prevent memory leaks. It's like a one-way reference, so it may not be necessary, but just in case. 
*/
function clearTreelibTree(t) {
//	console.log("TreeNodes:" + t.nodes.length);
	for(var i=0,len=t.nodes.length; i<len; i++) {
//		console.log("label:" + i + "=" + t.nodes[i].label);
		clearNode( t.nodes[i]);
	}

/*
	var n = new NodeIterator(t.root);
	var q = n.Begin();
	while (q != null) {
		q = n.Next();
	}
*/
	t.root = null;
	t.label_to_node_map = null;
	t.nodes = null;
	t.add_there = null;
	t.curnode = null;
	t.line = null;
	t.html = null;

	function clearNode(q) {
		q.ancestor = null;
		q.child = null;
		q.sibling = null;
		q.xy = null;
		q.backarc = null;
		q.data = null;
	}
}
/*
function showtree(element_id, svgid) {
	var t = new Tree();
	var element = document.getElementById(element_id);
	var newick = element.value;
	t.Parse(newick);
	if (t.error != 0) {
		console.error('Error parsing tree');
	} else {
		t.ComputeWeights(t.root);
		var td = null;

		var selectmenu = document.getElementById('style');
		var drawing_type = (selectmenu.options[selectmenu.selectedIndex].value);

		switch (drawing_type) {
			case 'rectanglecladogram':
				td = new RectangleTreeDrawer();
				break;
			case 'phylogram':
				if (t.has_edge_lengths) {
					td = new PhylogramTreeDrawer();
				} else {
					td = new RectangleTreeDrawer();
				}
				break;
			case 'circle':
				td = new CircleTreeDrawer();
				break;
			case 'circlephylogram':
				if (t.has_edge_lengths) {
					td = new CirclePhylogramDrawer();
				} else {
					td = new CircleTreeDrawer();
				}
				break;
			case 'cladogram':
			default:
				td = new TreeDrawer();
				break;
		}
		// clear existing diagram, if any
		var svg = document.getElementById(svgid);
		while (svg.hasChildNodes()) {
			svg.removeChild(svg.lastChild);
		}

		var g = document.createElementNS('http://www.w3.org/2000/svg','g');
		g.setAttribute('id','viewport');
		svg.appendChild(g);

		var tdH = 10 * t.num_leaves;	//set tree height to adjust font size = 8px;
		td.Init(t, {svg_id: 'viewport', width:1000, height:tdH, fontHeight:5, root_length:0.1} );
		td.CalcCoordinates();
		td.Draw();

		// font size
		var cssStyle = document.createElementNS('http://www.w3.org/2000/svg','style');
		cssStyle.setAttribute('type','text/css');

		var font_size = Math.floor(td.settings.height/t.num_leaves);
		font_size = Math.max(font_size, 1);

		var style=document.createTextNode("text{font-size:" + font_size + "px;}");
		cssStyle.appendChild(style);

		svg.appendChild(cssStyle);

		var n = new NodeIterator(t.root);
		var q = n.Begin();
		while (q != null) {
			if (q.IsLeaf()) {
				switch (drawing_type) {
					case 'circle':
					case 'circlephylogram':
						var align = 'left';
						var angle = q.angle * 180.0/Math.PI;
						if ((q.angle > Math.PI/2.0) && (q.angle < 1.5 * Math.PI)) {
							align = 'right';
							angle += 180.0;
						}
						drawRotatedText('viewport', q.xy, q.label, angle, align);
						break;
					case 'cladogram':
					case 'rectanglecladogram':
					case 'phylogram':
					default:
						drawText('viewport', q.xy, q.label);
						break;
				}
			}
			q = n.Next();
		}

		// Scale to fit window
		var bbox = svg.getBBox();
		var scale = Math.min(td.settings.width/bbox.width, td.settings.height/bbox.height);

		// move drawing to centre of viewport
		var viewport = document.getElementById('viewport');
		viewport.setAttribute('transform', 'scale(' + scale + ')');

		// centre
		bbox = svg.getBBox();
		if (bbox.x < 0) {
			viewport.setAttribute('transform', 'translate(' + -bbox.x + ' ' + -bbox.y + ')');
		}

		// pan
		$('svg').svgPan('viewport');
}
*/
function showtree(id,svgid) {
  var t = new Tree();
  var element = document.getElementById(id);
  var svg = document.getElementById(svgid);
  var newick = element.value;
  var selectmenu  = document.getElementById('style');
  var drawing_type = (selectmenu.options[selectmenu.selectedIndex].value);
  var td;
  newick = newick.trim(newick);
  t.Parse(newick);
  t.ComputeWeights(t.root);

  switch (drawing_type){
    case 'phylogram':
      if (t.has_edge_lengths){
        td = new PhylogramTreeDrawer();
      }else{
        td = new RectangleTreeDrawer();
      }
      break;
    case 'circlephylogram':
      if (t.has_edge_lengths){
        td = new CirclePhylogramDrawer();
      }else{
        td = new CircleTreeDrawer();
      }
      break;
    case 'cladogram':
      td = new TreeDrawer();
      break;
  }

  while (svg.hasChildNodes()){svg.removeChild(svg.lastChild);}
  var g = document.createElementNS('http://www.w3.org/2000/svg','g');
  g.setAttribute('id','viewport');
  svg.appendChild(g);
  //var tdH = 8 * t.num_leaves; //set tree height to adjust font size = 8px;
  var tdH = 10 * t.num_leaves; //set tree height to adjust font size = 8px;
  //td.Init(t, {svg_id: 'viewport', width:296, height:147, fontHeight:5, root_length:0.1} );
  td.Init(t, {svg_id: 'viewport', width:1000, height:tdH, fontHeight:5, root_length:0.1} );
  td.CalcCoordinates();
  td.Draw();

  var cssStyle = document.createElementNS('http://www.w3.org/2000/svg','style');
  cssStyle.setAttribute('type','text/css');
  var font_size = Math.floor(td.settings.height/t.num_leaves);
  //console.log(td.settings.height);
  font_size = Math.max(font_size, 1);
  var style=document.createTextNode("text{font-size:" + font_size + "px;}");
  cssStyle.appendChild(style);
  svg.appendChild(cssStyle);

  var n = new NodeIterator(t.root);
  var q = n.Begin();
  while (q != null){
    if (q.IsLeaf()){
      switch (drawing_type){
        case 'circlephylogram':
          var align = 'left';
          var angle = q.angle * 180.0/Math.PI;
          if ((q.angle > Math.PI/2.0) && (q.angle < 1.5 * Math.PI)){
            align = 'right';
            angle += 180.0;
          }
          drawRotatedText('viewport', q.xy, q.label, angle, align)
          break;
        case 'cladogram':
        case 'phylogram':
        default:
          drawText('viewport', q.xy, q.label);
          break;
      }
    }
    q = n.Next();
  }

  // Scale to fit window
  var bbox = svg.getBBox();
  //var scale = Math.min(td.settings.width/bbox.width, td.settings.height/bbox.height);
  var scale = Math.min(td.settings.width/bbox.width, td.settings.height/bbox.height);
  // move drawing to centre of viewport
  var viewport = document.getElementById('viewport');
  viewport.setAttribute('transform', 'scale(' + scale + ')');

  // centre
  bbox = svg.getBBox();
  if (bbox.x < 0){
    viewport.setAttribute('transform', 'translate(' + -bbox.x + ' ' + -bbox.y + ')');
  }
  var svgw = parseInt(bbox.width/5);
  var svgh = parseInt(bbox.height/5);
  var frmw = parseInt(svg.getBoundingClientRect().width/2);
  var frmh = parseInt(svg.getBoundingClientRect().height/2);
  if(drawing_type == "circlephylogram"){
    svgw = svgw*(frmw/svgw);
    svgh = svgh*(frmh/svgh);
  } else {
    svgw = 50;
    svgh = 50;
  }
  viewport.setAttribute('transform', 'translate('+svgw+','+svgh+')');
  //viewport.setAttribute('transform', 'translate('+svgw+',50)');
  //viewport.setAttribute('transform', 'translate(50,50)');
  // pan
  //$('svg').svgPan('viewport');
  elem = document.getElementsByTagName('svg')[0];
  var svgPan = SVGPan(elem);

  clearTreelibTree(t);

  // If the window is already open, fit the svg to its size.
  // The resize event does that, so it's diverted.
  $(window).trigger('resize');
}
function decodeGffAttribute(str) {
	try {
		str = decodeURIComponent(str);
	} catch(e) {
		// Do nothing, because it eturns an exception if the string contains "%".
	}
	return str;
}
function getAdjustedZoom(st, en, blocks) {
	var blockSizeVar = [1,2,3,4,5,6,7,8,9,10,20,30,40,50,60,70,80,90,100,200,300,400,500,600,700,800,900,1000,
		2000,3000,4000,5000,6000,7000,8000,9000,10000,20000,30000,40000,50000,60000,70000,80000,90000,100000];
	var zoom;
	var start;
	for(i=0,len=blockSizeVar.length; i<len; i++) {
		zoom = blockSizeVar[i];
		start = Math.floor((st - 1) / zoom) * zoom + 1;
		end = Math.ceil(en / zoom) * zoom;
		var regionLen = end - start + 1;
		if(zoom * blocks >= regionLen) break;
	}
	return {'start':start, 'zoom':zoom};
}

var overflowObject = new Object();
var compressedStorage = true;
//var compressedStorage = false;
function _setCompressedData(key,val){
        if(compressedStorage == true){
                try{
                        storage.setItem(key,LZString.compressToUTF16(val));
                }catch(e){
                        // alert("The LocalStorage has overflowed. The TASUKE will be reset after 10 second.");
                        console.log(e.message);
                        setTimeout(_dlg_reset_confirm(),5000);
                        //overflowObject[key] = val;
                }
        }else{
                storage.setItem(key,val);
        }
}

function _getCompressedData(key){
        var val = "";
        if(compressedStorage == true){
                if (storage.getItem(key) === null) {
                        if(key in overflowObject){
                                //alert("Invalid key was found. The TASUKE will be reset after 10 second.");
                                console.log(e.message);
                                //setTimeout(_dlg_reset_confirm(),5000);
                                return;
                        }else{
                                //val = overflowObject[key];
                        }
                }else{
                        val = LZString.decompressFromUTF16(storage.getItem(key));
                }
        }else{
                val = storage.getItem(key);
        }
        return val;
}
