/*
	This heading must remain intact at all times.
	Copyright (c) 2010 Mark Mason.

	File:	Q-Cogo-Misc.js
	Use:	To provide miscellaneous operations for Q-Cogo, <http://www.q-cogo.com/>.
	Ver:	1.2 (Beta)

	Created by Mark Mason. Latest version available from <http://www.q-cogo.com/>.



	This file is part of Q-Cogo.
	
	Q-Cogo is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	
	Q-Cogo is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License
	along with Q-Cogo.  If not, see <http://www.gnu.org/licenses/>.
*/

function JsonMat(ptMt,Dim){var JM='[';if(Dim==2){for(var i=0;i<ptMt.length;i++){JM+='[';for(var j=0;j<ptMt[i].length;j++)JM+='"'+ptMt[i][j]+'"'+((j<ptMt[i].length-1)?', ':'');JM+=']'+((i<ptMt.length-1)?', ':'');}}
else if(Dim==1)for(var j=0;j<ptMt.length;j++)JM+='\''+ptMt[j]+'\''+((j<ptMt.length-1)?', ':'');JM+=']';return JM;}
function GetPointList(PtList,Multiple,Radial){var check=0;var ptMt=ParsePoints();PtList=PtList.replace(/\s+/g,'_');if(PtList=='*.*'&&Multiple){PtList=new Array(0);for(i=0;i<ptMt.length-1;i++)PtList[i]=ptMt[i];return(PtList);}
PtList=PtList.split(',');if(Multiple){var Range=0,k=0,Index1=-1,Index2=-1,Asterisk='';for(var j=0;j<PtList.length;j++){if(PtList[j].search(/\-/)<0){PtList[j]=((PtList[j])?FormatString(PtList[j],1,Radial):'Error_one_or_more_points_contain_null_values');}
else{Range=PtList[j].split('-');Range[0]=((Range[0])?FormatString(Range[0],1,Radial):'Error_one_or_more_points_contain_null_values');Range[1]=((Range[1])?FormatString(Range[1],1,Radial):'Error_one_or_more_points_contain_null_values');Range.sort(sortRange);ptMt.sort(sortByPt);for(k=0;k<ptMt.length;k++){if(Range[0].replace(/\*/g,'')==ptMt[k][0])Index1=k;else if(Range[1].replace(/\*/g,'')==ptMt[k][0])Index2=k;}
PtList.splice(j,1,Range[0]);if(Index1>-1&&Index2>-1){for(k=Index1+1;k<Index2;k++){PtList.splice(j+1,0,ptMt[k][0]);j++;}}
PtList.splice(j+1,0,Range[1]);j++;}}}
for(var j=0;j<PtList.length;j++){var PreAsterisk='',PostAsterisk='';if(PtList[j]=='Error_one_or_more_points_contain_null_values'){PtList.splice(j,1);j--;}
else{for(var i=j;i<PtList.length;i++){if(i!=j&&PtList[i]==PtList[j]&&PtList[i].search(/\*/)<0){PtList.splice(i,1);i--;}}
check=0;for(i=0;i<ptMt.length;i++){if((ptMt[i][0]==PtList[j].replace(/\*/g,'')&&Radial)||(ptMt[i][0]==PtList[j])){if(PtList[j].search(/\*/)==PtList[j].length-1)PostAsterisk='*';if(PtList[j].search(/\*/)==0)PreAsterisk='*';check=1;PtList[j]=[ptMt[i][0],ptMt[i][1],ptMt[i][2],ptMt[i][3],ptMt[i][4]];PtList[j][0]=PreAsterisk+PtList[j][0]+PostAsterisk;break;}}
if(!check){if(Radial)alert('Point \"'+PtList[j].replace(/\*/g,'')+'\" not found!');else alert('Point \"'+PtList[j]+'\" not found!');return check;}}}
return((Multiple)?PtList:PtList[0]);}
function SameCoords(Pt1,Pt2,Error){var check=0;if(Pt1[1]==Pt2[1]&&Pt1[2]==Pt2[2]){alert(Error+' points have identical northings and eastings!');check=1;}
return check;}
function SamePoint(Pt1,Pt2,Error){var check=0;if(Pt1==Pt2){alert(Error+' points are identical!');check=1;}
return check;}
function ParseDMS(Value,AllowExpr,AllowConst){var check=1;var negative=0;var ErrorStr='DMS angles must be simple expressions or numbers of the form DDD.MMSS (many seconds digits allowed)';Value=Value.replace(/\s+/g,'');if(Value.search(/\.\./)>0&&Value.search(/\.\./)!=Value.length-2){Value=Value.split('..',2);if(!GetPointList(Value[0],0,0)||!GetPointList(Value[1],0,0)||SamePoint(Value[0],Value[1],'Reference')||SameCoords(GetPointList(Value[0],0,0),GetPointList(Value[1],0,0),'Reference')||!(Value=PtPtInverse(0,Value[0],Value[1])[2])){check='X';return check;}
return Value;}
if(AllowExpr&&Value.search(/\+|\-|\*|\//)>=0){var Ops=Value.split(/[^(,),\-,\+,\*,\/]/);var Vals=Value.split(/[(,),\-,\+,\*,\/]/);Ops=RemoveBlank(Ops);Vals=RemoveBlank(Vals);if(Value[0].split(/[^(,),\-,\+,\*,\/]/)[0]){Value='';for(var n=0;n<Ops.length;n++){Value+=((Ops[n])?Ops[n]:'');Value+=((Vals[n])?ParseDMS(Vals[n],0,0):'');if(Value.search(/X/)>=0){check='X';return check;}}
Value+=((Vals[n])?ParseDMS(Vals[n],0,0):'');}
else{Value='';for(var p=0;p<Vals.length;p++){Value+=((Vals[p])?ParseDMS(Vals[p],0,0):'');Value+=((Ops[p])?Ops[p]:'');if(Value.search(/X/)>=0){check='X';return check;}}
Value+=((Ops[p])?Ops[p]:'');}
try{Value=eval(Value);}
catch(e){check='X';alert(ErrorStr);return check;}}
else{if(isNaN(parseFloat(Value))){check='X';alert(ErrorStr);return check;}
Value=parseFloat(Value);if(Value<0){Value=-1*Value;negative=1;}
var D=Math.floor(Value);var M=Math.floor(Round((Value-D)*100,10));var S=(Value-D-M/100)*10000;if(M>=60||S>=60){check='X';alert('DMS minutes and seconds must be less than 60');return check;}
Value=(D+(M/60)+(S/3600))*Math.PI/180;}
if(Value==Infinity){check='X';alert(ErrorStr);return check;}
Value=((negative)?-1*Value:Value);return((AllowConst)?Value+ParseDMS(qG('GlobalAz').value,0,0):Value);}
function ParseDecimal(Value,AllowExpr,AllowSF){var check=1;var ErrorStr='Distances, coordinates and scale factors must be simple expressions or numbers';if(Value.search(/\.\./)>0&&Value.search(/\.\./)!=Value.length-2){Value=Value.split('..',2);if(!GetPointList(Value[0],0,0)||!GetPointList(Value[1],0,0)||SamePoint(Value[0],Value[1],'Reference')||SameCoords(GetPointList(Value[0],0,0),GetPointList(Value[1],0,0),'Reference')||!(Value=PtPtInverse(0,Value[0],Value[1])[0])){check='X';return check;}
return Value;}
if(AllowExpr&&Value.search(/\+|\-|\*|\//)>=0){try{Value=eval(Value);}
catch(e){check='X';alert(ErrorStr);return check;}}
if(isNaN(parseFloat(Value))){check='X';alert(ErrorStr);return check;}
Value=parseFloat(Value);if(Value==Infinity){check='X';alert(ErrorStr);return check;}
return((AllowSF)?Value*qG('GlobalSF').value:Value);}
function ParsePoints(){var pointsContents=qG('pointsContents').value;pointsContents=pointsContents.replace(/\n/g,'');pointsContents=pointsContents.replace(/\s+/g,' ');var ptV=pointsContents.split(' ');var lP=(ptV.length-1)/5;var ptMt=new Array(lP);for(var i=0;i<=lP;i++)ptMt[i]=[ptV[0+i*5],ptV[1+i*5],ptV[2+i*5],ptV[3+i*5],ptV[4+i*5]];return ptMt;}
function FormatDMS(Value,DecimalForm){Value=parseFloat(Value);Value=Value*180/Math.PI;while(Value<0)Value+=360;Value=Value%360;var valuePrecision=parseInt(qG('APrecision').value);var VP=-1*(valuePrecision)*5;D=Math.floor(Value);M=Math.floor((Value-D)*60);S=(Value-D-M/60)*3600;var SMult=1;if(VP>5){S=S/VP;SMult=VP;VP=1;}
S=SMult*((valuePrecision>=0)?Round(S,valuePrecision):Math.round(Math.round(S)/VP)*VP);if(S==60){M+=1;S=0;}
if(M==60){D+=1;M=0;}
D=D%360;D=D+'';for(var i=D.length;i<=3;i++)D=' '+D;M=M+'';M=((M.length<2)?'0'+M:M);S=S+'';S=((S.search(/\./)<0&&valuePrecision>0)?S+'.':S);S=(((S.search(/\./)<2&&valuePrecision>0)||S.length<2)?'0'+S:S);SZeros=((valuePrecision>0)?S.length-S.search(/\./):valuePrecision+1);for(var j=SZeros;j<=valuePrecision;j++)S=S+'0';return((DecimalForm)?D+'.'+M+S.replace(/\./,''):D+'\u00B0'+M+'\''+S+'\"');}
function FormatArea(Value,Units){var valuePrecision=parseInt(qG('AreaPrecision').value);var valueUnits=parseInt(qG('AreaUnits').value);Value=((valueUnits==1)?parseFloat(Value)/10000:parseFloat(Value)/43560);Unit=((valueUnits==1)?' Ha':' acre');Value=RoundSig(Value,valuePrecision)+'';var lV=Value.length,n=0,go=0;for(var i=0;i<lV;i++){if(Value[i]!='.'&&Value[i]!='0'&&!go)go=1;if(Value[i]!='.'&&go)n++;}
Value+=((n<valuePrecision&&Value.search(/\./)<0)?'.':'');for(i=n;i<valuePrecision;i++)Value+='0';var curWidth=((Value.search(/\./)>-1)?Value.search(/\./):Value.length),valueWidth=7;for(i=curWidth;i<=valueWidth;i++)Value=' '+Value;return Value+((Units&&qG('AreaUnitDisp').value==1)?Unit:'');}
function FormatDecimal(Value,Precise,Units){var valuePrecision=10;if(!Precise)valuePrecision=parseInt(qG('DPrecision').value);var valueWidth=8+valuePrecision;Value=parseFloat(Value);Value=Round(Value,valuePrecision);Value=Value+'';if(Value.search(/\./)<0)Value+='.';valZeros=Value.length-Value.search(/\./);for(var j=valZeros;j<=valuePrecision;j++)Value=Value+'0';valSize=Value.length;Unit=((qG('DistUnits').value==1)?'m':'\'');for(var i=valSize;i<=valueWidth;i++)Value=' '+Value;return Value+((Units&&qG('DistUnitDisp').value==1)?Unit:'');}
function FormatGrade(Rise,Run){var valuePrecision=parseInt(qG('GPrecision').value);var valueWidth=7+valuePrecision;Rise=parseFloat(Rise);Run=parseFloat(Run);var Grade=100*Rise/Run;Grade=Round(Grade,valuePrecision);if(Grade>10000||Grade<-10000||isNaN(Grade)){Grade='      N/A';return Grade;}
else{Grade=Grade+'';if(Grade.search(/\./)<0)Grade=Grade+'.';valZeros=Grade.length-Grade.search(/\./);for(var j=valZeros;j<=valuePrecision;j++)Grade=Grade+'0';valSize=Grade.length;for(var i=valSize;i<=valueWidth;i++)Grade=' '+Grade;return Grade+'%';}}
function FormatStn(Value){Value=FormatDecimal(Value,0,1);Value=Value.replace(/\s+/g,'');var Sep=' + ';if(Value.search(/\-/)>=0){Value=Value.substring(1);Sep=' - ';}
var DP=Value.search(/\./);if(DP>2){var Val1=Value.substring(0,DP-2);var Val2=Value.substring(DP-2);Value=Val1+Sep+Val2;}
else if(DP<=2){for(var i=DP;i<2;i++)Value='0'+Value;Value='0'+Sep+Value;}
return Value;}
function FormatString(Value,Short,Radial){var PreAsterisk='',PostAsterisk='';if(Radial&&Value.search(/\*/)==Value.length-1){Value=Value.replace(/\*/g,'');PostAsterisk='*';}
else if(Radial&&Value.search(/\*/)==0){Value=Value.replace(/\*/g,'');PreAsterisk='*';}
var valueWidth=8;Value=Value.replace(/\s+/g,'_');Value=Value.substring(0,valueWidth);Value=PreAsterisk+Value+PostAsterisk;if(!Short){for(var i=Value.length;i<=valueWidth;i++)Value=Value+' ';}
return Value;}
function FormatPtList(PtList,Sort){var ptMt=ParsePoints();var lP=ptMt.length-2;ptMt.splice(lP+1,1);ptMt.sort(sortByPt);var lL=PtList.length-1;if(Sort)PtList.sort(sortByPt);var PtIndex=new Array(lL);for(var i=0;i<=lL;i++){for(var j=0;j<=lP;j++){if(PtList[i][0].replace(/\*/g,'')==ptMt[j][0]){PtIndex[i]=j;break;}}}
var i1=0,i2=0,PtString='';while(i1<=lL){for(i=i1;i<=lL;i++){i2=i;if(i2-i1!=PtIndex[i2]-PtIndex[i1]||PtList[i2][0].search(/\*/)>=0)break;}
if(i2<lL){PtString+=((i2-i1>2)?PtList[i1][0]+'-'+PtList[i2-1][0]:(i2-i1==2)?PtList[i1][0]+','+PtList[i2-1][0]:PtList[i1][0]);PtString+=',';}
else{PtString+=((i2-i1>2&&(PtIndex[i2]-PtIndex[i2-1]!=1||PtList[i2][0].search(/\*/)>=0))?PtList[i1][0]+'-'+PtList[i2-1][0]+','+PtList[i2][0]:(i2-i1>1&&(PtIndex[i2]-PtIndex[i2-1]!=1||PtList[i2][0].search(/\*/)>=0))?PtList[i1][0]+','+PtList[i2-1][0]+','+PtList[i2][0]:(i2-i1>1&&PtIndex[i2]-PtIndex[i2-1]==1)?PtList[i1][0]+'-'+PtList[i2][0]:(i2-i1==1)?PtList[i1][0]+','+PtList[i2][0]:PtList[i2][0]);break;}
if(PtList[i2][0].search(/\*/)<0)i1=i;else{if(i2>0)PtString+=PtList[i2][0]+','
i1=i+1;}}
return PtString;}
function CheckBlank(Value,errorString){var check=1;if(!Value){check=0;alert('Enter a'+errorString);}return check;}
function CheckPoint(Value){var check=1;var replace=0;if(Value.search(/\+|\-|\*|\/|\,|\.|\(|\)|\[|\]|\{|\}/)>=0){check=0;alert('Point names must not contain simple mathematical characters, dots, or commas');}
if(check){Value=FormatString(Value,1,0);var ptMt=ParsePoints();var lP=ptMt.length;for(var i=0;i<lP;i++){if(ptMt[i][0]==Value){var N=ptMt[i][1];var E=ptMt[i][2];var Z=ptMt[i][3];N=FormatDecimal(N,0,1);N=N.replace(/\s+/g,'');E=FormatDecimal(E,0,1);E=E.replace(/\s+/g,'');Z=FormatDecimal(Z,0,1);Z=Z.replace(/\s+/g,'');var Message='Point \"'+ptMt[i][0]+'\" exists!\n\n';Message+='     N:  \t\t'+N+'\n';Message+='     E:  \t\t'+E+'\n';Message+='     Z:  \t\t'+Z+'\n';Message+='     Desc:  \t\t'+ptMt[i][4]+'\n\n';Message+='Edit this point?';check=confirm(Message);if(check==1){DeletePoint(Value);return check;}
else return check;}}}
return check;}
function SetPoint(){var Pt=qG('Pt').value;var N=qG('N').value;var E=qG('E').value;var Z=qG('Z').value;var Desc=qG('Desc').value;if(!CheckBlank(Pt,' point name')||!CheckBlank(N,' northing')||!CheckBlank(E,'n easting')||!CheckBlank(Z,'n elevation')||!CheckBlank(Desc,' point description'))return;N=ParseDecimal(N,1,0);E=ParseDecimal(E,1,0);Z=ParseDecimal(Z,1,0);if(N!='X'&&E!='X'&&Z!='X'&&CheckPoint(Pt))AddPoint(Pt,N,E,Z,Desc);}
function AddPoint(Pt,N,E,Z,Desc){var ptMt=ParsePoints();lP=ptMt.length-1;ptMt[lP]=[Pt,N,E,Z,Desc];ClearSort();OutputPoints(ptMt,lP);ZoomSketch(0,0,0);}
function ClearSort(){var Titles=['Pt','N','E','Z','Desc'];var Objects=[qG('SortPt'),qG('SortN'),qG('SortE'),qG('SortZ'),qG('SortDesc')];for(var l=0;l<=4;l++){Objects[l].style.paddingRight='0.2em';Objects[l].style.backgroundImage='url(\'transparentpixel.gif\')';}}
function SortPoints(j){var Titles=['Pt','N','E','Z','Desc'];var Objects=[qG('SortPt'),qG('SortN'),qG('SortE'),qG('SortZ'),qG('SortDesc')];var ObTst=Objects[j].style.backgroundImage;if(ObTst.search(/down/)<0&&ObTst.search(/up/)<0){for(var l=0;l<=4;l++){Objects[l].style.paddingRight='0.2em';Objects[l].style.backgroundImage='url(\'transparentpixel.gif\')';}
Objects[j].style.paddingRight='16px';Objects[j].style.backgroundImage='url(\'images/arrow-down.png\')';}
else if(ObTst.search(/down/)>=0){Objects[j].style.backgroundImage='url(\'images/arrow-up.png\')';}
else if(ObTst.search(/up/)>=0){Objects[j].style.backgroundImage='url(\'images/arrow-down.png\')';}
var ptMt=ParsePoints();var lP=ptMt.length-2;ptMt.splice(lP+1,1);switch(j){case 0:ptMt.sort(sortByPt);break;case 1:ptMt.sort(sortByN);break;case 2:ptMt.sort(sortByE);break;case 3:ptMt.sort(sortByZ);break;case 4:ptMt.sort(sortByDesc);break;default:return;}
if(ObTst.search(/down/)>=0)ptMt=ptMt.reverse();OutputPoints(ptMt,lP);}
function chunkify(t){var tz=[],x=0,y=-1,n=0,i,j;while(i=(j=t.charAt(x++)).charCodeAt(0)){var m=(i==46||(i>=48&&i<=57));if(m!==n){tz[++y]='';n=m;}tz[y]+=j;}return tz;}
function sortRange(a,b){var aa=chunkify(a.toLowerCase()),bb=chunkify(b.toLowerCase());for(x=0;aa[x]&&bb[x];x++){if(aa[x]!==bb[x]){var c=Number(aa[x]),d=Number(bb[x]);if(c==aa[x]&&d==bb[x])return c-d;else return((aa[x]>bb[x])?1:-1);}}return aa.length-bb.length;}
function sortByPt(a,b){var aa=chunkify(a[0].toLowerCase()),bb=chunkify(b[0].toLowerCase());for(x=0;aa[x]&&bb[x];x++){if(aa[x]!==bb[x]){var c=Number(aa[x]),d=Number(bb[x]);if(c==aa[x]&&d==bb[x])return c-d;else return((aa[x]>bb[x])?1:-1);}}return aa.length-bb.length;}
function sortByDesc(a,b){var aa=chunkify(a[4].toLowerCase()),bb=chunkify(b[4].toLowerCase());for(x=0;aa[x]&&bb[x];x++){if(aa[x]!==bb[x]){var c=Number(aa[x]),d=Number(bb[x]);if(c==aa[x]&&d==bb[x])return c-d;else return((aa[x]>bb[x])?1:-1);}}return aa.length-bb.length;}
function sortByN(a,b){var x=parseFloat(a[1]);var y=parseFloat(b[1]);return((x<y)?-1:((x>y)?1:0));}
function sortByE(a,b){var x=parseFloat(a[2]);var y=parseFloat(b[2]);return((x<y)?-1:((x>y)?1:0));}
function sortByZ(a,b){var x=parseFloat(a[3]);var y=parseFloat(b[3]);return((x<y)?-1:((x>y)?1:0));}
function OutputPoints(ptMt,lP){var ptStr='';for(var i=0;i<=lP;i++){ptStr+=FormatString(ptMt[i][0],0,0)+' ';ptStr+=FormatDecimal(ptMt[i][1],1,0)+' ';ptStr+=FormatDecimal(ptMt[i][2],1,0)+' ';ptStr+=FormatDecimal(ptMt[i][3],1,0)+'      ';ptStr+=FormatString(ptMt[i][4],0,0)+' \n';}
PointsField=qG('pointsContents');PointsField.value=ptStr;ptStr='';for(i=0;i<=lP;i++){ptStr+=FormatString(ptMt[i][0],0,0)+' ';ptStr+=FormatDecimal(ptMt[i][1],0,0)+' ';ptStr+=FormatDecimal(ptMt[i][2],0,0)+' ';ptStr+=FormatDecimal(ptMt[i][3],0,0)+'      ';ptStr+=FormatString(ptMt[i][4],0,0)+' \n';}
ptStr+=' ';PointsField=qG('pointsContentsCopy');PointsField.value=ptStr;PointsField.scrollTop=PointsField.scrollHeight-PointsField.clientHeight;SketchPoints();}
function DeletePoint(PtList){var DeleteInput=0;if(!PtList){var PtList=qG('Delete').value;DeleteInput=1;}
if(!CheckBlank(PtList,' list of points to delete'))return;if(!(PtList=GetPointList(PtList,1,0)))return;var ptMt=ParsePoints();for(var j=0;j<PtList.length;j++){for(var i=0;i<ptMt.length;i++){if(ptMt[i][0]==PtList[j][0])ptMt.splice(i,1);}}
var Plural=((PtList.length>1)?'s':'');check=((DeleteInput)?confirm(PtList.length+' point'+Plural+' will be deleted!\n\nDelete point'+Plural+'?'):1);if(check==1){OutputPoints(ptMt,ptMt.length-2);ZoomSketch(0,0,0,'#PointsCanvas');if(DeleteInput)qG('Delete').value='';}}
function RewritePoints(){var ptMt=ParsePoints();lP=ptMt.length-2;OutputPoints(ptMt,lP);}
function OutputCogo(Results,Log){var HR=' _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _';Results='\n\n'+HR+'\n\n\n'+Results;OutputField=qG(Log);OutputField.value+=Results;OutputField.scrollTop=OutputField.scrollHeight-OutputField.clientHeight;}
function RemoveBlank(Arr){for(var i=0;i<Arr.length;i++){if(Arr[i]==''){Arr.splice(i,1);i--;}}return Arr;}
function Round(num,sig){return Math.round(num*Math.pow(10,sig))/Math.pow(10,sig);}
function RoundSig(num,sig){var factor=Math.pow(10,sig-Math.ceil(Math.log(Math.abs(num))/Math.LN10));return Math.round(num*factor)/factor;}