From 9111809881c7e4ff27ae179f95197d180b3c6fb5 Mon Sep 17 00:00:00 2001 From: MrAlienBoiGithub <97860398+MrAlienBoiGithub@users.noreply.github.com> Date: Thu, 3 Nov 2022 19:43:40 -0400 Subject: [PATCH] Added some things and added some ui --- imgs/back.png | Bin 0 -> 9906 bytes imgs/btd4.jpg | Bin 0 -> 45617 bytes imgs/calculator.png | Bin 0 -> 14468 bytes imgs/download.png | Bin 4293 -> 0 bytes imgs/flash.png | Bin 0 -> 70805 bytes imgs/retrobowl.png | Bin 0 -> 1554 bytes index.html | 8 +- sitemap.html | 20 +- tools/i2/Notifier.js | 136 + tools/i2/_draw.js | 5154 ++++ tools/i2/_draw2.js | 8618 +++++++ tools/i2/_draw3.js | 26155 +++++++++++++++++++++ tools/i2/_grid2.js | 2575 ++ tools/i2/_holder2.js | 3108 +++ tools/i2/_keyboard.js | 709 + tools/i2/_mathsInput.js | 3723 +++ tools/i2/_miscFuncs.js | 12393 ++++++++++ tools/i2/_style.css | 186 + tools/i2/_text2.js | 925 + tools/i2/construct.html | 57 + tools/i2/fonts/hobo-webfont.eot | Bin 0 -> 29519 bytes tools/i2/fonts/hobo-webfont.svg | 706 + tools/i2/fonts/hobo-webfont.ttf | Bin 0 -> 57040 bytes tools/i2/fonts/hobo-webfont.woff | Bin 0 -> 32432 bytes tools/i2/fonts/hobo-webfontd41d.eot | Bin 0 -> 29519 bytes tools/i2/fonts/segeo-print-webfont-2.svg | 249 + tools/i2/fonts/segeo-print-webfont.eot | Bin 0 -> 59430 bytes tools/i2/fonts/segeo-print-webfont.svg | Bin 0 -> 36608 bytes tools/i2/fonts/segeo-print-webfont.ttf | Bin 0 -> 59232 bytes tools/i2/fonts/segeo-print-webfont.woff | Bin 0 -> 38900 bytes tools/i2/fonts/smileymonster.eot | Bin 0 -> 14882 bytes tools/i2/fonts/smileymonster.svg | 279 + tools/i2/fonts/smileymonster.ttf | Bin 0 -> 14624 bytes tools/i2/fonts/smileymonster.woff | Bin 0 -> 8448 bytes tools/i2/fonts/smileymonsterd41d.eot | Bin 0 -> 14882 bytes 35 files changed, 64989 insertions(+), 12 deletions(-) create mode 100644 imgs/back.png create mode 100644 imgs/btd4.jpg create mode 100644 imgs/calculator.png delete mode 100644 imgs/download.png create mode 100644 imgs/flash.png create mode 100644 imgs/retrobowl.png create mode 100644 tools/i2/Notifier.js create mode 100644 tools/i2/_draw.js create mode 100644 tools/i2/_draw2.js create mode 100644 tools/i2/_draw3.js create mode 100644 tools/i2/_grid2.js create mode 100644 tools/i2/_holder2.js create mode 100644 tools/i2/_keyboard.js create mode 100644 tools/i2/_mathsInput.js create mode 100644 tools/i2/_miscFuncs.js create mode 100644 tools/i2/_style.css create mode 100644 tools/i2/_text2.js create mode 100644 tools/i2/construct.html create mode 100644 tools/i2/fonts/hobo-webfont.eot create mode 100644 tools/i2/fonts/hobo-webfont.svg create mode 100644 tools/i2/fonts/hobo-webfont.ttf create mode 100644 tools/i2/fonts/hobo-webfont.woff create mode 100644 tools/i2/fonts/hobo-webfontd41d.eot create mode 100644 tools/i2/fonts/segeo-print-webfont-2.svg create mode 100644 tools/i2/fonts/segeo-print-webfont.eot create mode 100644 tools/i2/fonts/segeo-print-webfont.svg create mode 100644 tools/i2/fonts/segeo-print-webfont.ttf create mode 100644 tools/i2/fonts/segeo-print-webfont.woff create mode 100644 tools/i2/fonts/smileymonster.eot create mode 100644 tools/i2/fonts/smileymonster.svg create mode 100644 tools/i2/fonts/smileymonster.ttf create mode 100644 tools/i2/fonts/smileymonster.woff create mode 100644 tools/i2/fonts/smileymonsterd41d.eot diff --git a/imgs/back.png b/imgs/back.png new file mode 100644 index 0000000000000000000000000000000000000000..309608f501ec81e8c2903e7d8daa1b3e732bbc93 GIT binary patch literal 9906 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 JtaD0e0sxDL%&q_c literal 0 HcmV?d00001 diff --git a/imgs/btd4.jpg b/imgs/btd4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..739196c6b6e7c90d5a956960a85d4bf656de6f48 GIT binary patch literal 45617 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°W72n=-Nq_X{{QTij!=JJXpAs#F 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 eRF@Q@K1}fGEJ*L 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

Xed_-n--fqHXK? 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 QfHv)Uy85}Sb4q9e0M_{KZU6uP literal 0 HcmV?d00001 diff --git a/imgs/download.png b/imgs/download.png deleted file mode 100644 index f17d5e7787bf6697bdb1b74c727f2b28aaf061ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4293 zcmeAS@N?(olHy`uVBq!ia0y~yV6fKQ0)|NsA;TEDz< zHvPSa)nr9*eSLjxZEaOmRZ~;b-S-cr7r7`c@p$;@sX^^$^M)_KcQRam^7rV`qi^57 z-Mo2oXJ_Y^FJB%!c#t{!+w$ehckkZ4^x#jMrY|pz6|Y{s`qD~k&6+jy=g*%wapEgm zy(dqeoH=vmrJmI0)4x_<-}d?YzsGO>zKIRGapOkhq_1y3|NHm#?(c{94_*Cz_QjRT zrQgro`7>kZkDiS`q9%X+`|j&u6Z#{a+GN|t>7 zdG^%byVL%h4*GM{?BAP{e;+Ppm}|m2_eTN)1AnQfi(^Q|oVPKY;kS2)@qNgtOq=Ix z*8WE2$VqPwZ#jWOe%>#gG#B>IEPM9<|D^J*QQMvhFdcm9c;C{vH#hS1^6FCYlS_i6 z2!QqdU;Zt)EXi}}wrBa1>4(21>_}ynY5!BdezE2px#s-7*p)gzS6H08rSLLt>7D-p zF=}@wc1rZ*&a^tWAoiuoosIYZ{bYXpyA&5a-hNjkFJMmo$uGSL3t!$Yt6{y{Z+B1L z`sAs5O_#SH{d(ChKwRS1$xYiYeBs^s;pWDNu0KDRT8kWIx~wj?&&%rNx+6)AFCVYU z*4L}%_K9!joIkfD#+tL+`~Hl~yh)Bn7e_?=VvG4PiCc5ofkT(R_pYn)TO`@&zH!Bu z+~Uo@WgbfRmYpwmUwpDvZ~yx})!RS+-T3dzrI&GA^b5Z@9+sCsS3dvB?ddF+@20P* z+3i02wDi@o(&Wm|8f7xm=ighuW(`wV*uDC$r3LjCaYY5QlGZj$`Ut;yJm2e=n|rx* zXwR>^kxo2(E8pGfipbTH$=~)``m~7j*Kg0R-Pd-YIb6%{trLw|CDDHDo%KP-<+|!*5sKb8ssEM5n!L6IR`p z+qCay)~~)-Ujt5W-`x8!a>3`=nYxQ?`6jxz7s}{-`D^7Or}$z0g=PNglU{9WpZwx? zQ16yI`{ri~oOkSR+kLKJ+I0V06BQ>U&N1};u z9&0;G+~4rQ7fq&Zd8%yDUw*ouyXW@x-M=3{zC8+g5q#TR!9J{YM{NC$oj(h7C1Uo9 zwX!K?Cib^KJ$G>7W1l7OS7^<;XJc(=_v;X6j{TX*YcwJz-3v@vwJWeRNg?m1htJLp z+m0`H(ugU0-K_AMIXG;?p+DE=Kb-c)^clmKwxD0uPWL-Dhwrso{?tAE&$ZLRCY+O( z==bgATKIlmVCdBFUEF3}R=am@UKR1uj`M5kzteJ)O)K?H_NoN!5d5|7#+j4bzO9+p z@~i0b!l>VmUMOGum}hOpazOI&dat|FzsFo+Dl2R6zMp=JU9Obf>Y}ja9fR84YkJEL z9I3y_aq92thPOo_BGb&L*|A?f-m+eDMzA5BYPeB5;42 zlbEIW(d$R{2kraXwMPDp&MT~(I^FcbGT-;}a({euk2zy&zd>`I&t`VNt(|Ko ze=phhUS#R5q>t6=D>FFmEW9abwM#_TWvQ*tecjoXF-+<@ER|QE{tow-E{S|T_c;H8 zjjml5mRHJe>?$c)!#>l;RQE;iG>2Z;4Y0RSg_Bom_s|CK~ZZV&H z!l3;?vR~@G)?@*j?hiX|_k=G#ekaZ8jl#LycPsBc6Y-yNRsUSN?5A!g7464Q`{zGd z(Q}hmY|GWdf>zwOHazrQ`Y%n#wal#I!Z*#QyZt6E-d}C#DDA!Din4cH(6?k?mG*B} zZtqq4t5)`8#)PBGcsy**CvTp*V+y07w_9+=S@2l40n8;pPUZFUz9Yg&Hb*2Cuk%0KPa?K!xzIbmJx z#2$ukf7T1UDYV?t!T+Z}^@;K_oAjI0ehR<5WL$N&GwSC;uj%WIf?D5BnzsM#+8-sF z=dXsJU$U&z=i2nig-KPHzV#%pm*uIt-Egl?vi^~poQ&5h+eh!hOD9|YQFh(*uUIAW zzuv4h-;Eb%zv)Qa{^q;;#_NTrmaY5MdR*#xxR3O$_4{tSwHr*^|GFT@N9vDqeiiGD z=9k~LckPRlk)0o-`#aiIscF|AMVH*Kzdqluulu*~me#8u(XQ=>iy2|myk0NdZyYi+Yw;(uE$&*k^7elVTiXBW zN&c-xjwfA9me1!=+~E7K*tPS5+0T2z+fVi{yScDF^~=7aLK*oh`RrErNxGbyrO_|B zCa&F&lUMnVuicV`Hu9JJP82`95x;pZM`^`pt7G&3{V>&OxVqazru?U6k&DJFkLBN{ z&k?Ae+|Qxur(Wx6^@mmcO@hn@@f$TYvjxt`&OMd6{oBf-fA#yObH>EhuJ=e>x{KZK zMZ+wg`}X^j|0vIYZ@n+uJKglwY|d^u_g#C;)?Khq36N~&|9oQS<@U+DpJr{1TO0QN zTvGFe_vY8Hl&o17bN`5J|C_RFA6|N7ly%GWr+j(n^5ki~X4A#bKmXn}%Gjz)P z|K}GtbZ|^y%HR9Rx6#jMUE#C*qxU4P{L%fw{*Jq~M0Jv8v5anY@M7x{CKjv0>T^E5k}4{uF$udbFt1^cCb?Yy(5zF<*+*3b1-OD~-+dGYkY zw4$w*+XJ04!hRWXq#kt@jMTrpaC&q0!aI9zpODymIbA}i*6rWUSWS^l{o++jclLa2 zdvRLn%cfudkG(MNIb*zuHsPsjHzT>s$eqF|@@ zTe9LD>z;jcO?H^Seh=&U*X&`3!qy7dpS>BSc0nc2brHXP-Obcc1)urT&3zA>%+@W7 zZO(RR7CR;Rq{}Y!Lby@Dsx(JV=KT}CYi{jnTXAN=x}b|XPL+3VSgpLNJ#Vs8eUfp_>i1?I%YF2+STA)wHT<>dOJTAeOWTWR_i3FvDG?oIChXS> z5+%ga)+9!q$T}3fc|zC1^B)9^CS*SJR#@&Z|JsQIg{f-mbly)g?|RK~%SiWupwzKH zjGj9r=-Y>WD`tMlptmgMF>v%r0>9-DeBekBMLY`(hXOwcLUV^+1d4)|B5T2AKK z!jU7o@U2uw45Pl}w9qd${M}0(PdhIDuqPp^a`VHl*^|yR%6zLlJ+HCAH1Dv&+N}w% zY-(;d?PI9rE#sVWZnoX^4WGWON{H~SI{T}m!y;|d8`aC*i_a^Grs)1jJG@|@N#wPd z?r=7(d+J{T3t!Goxua(+t9#^~^WqsUKC<@>vP3YriUk{YJ95D)R3|{lRc@m=koP0)3-{c?Me8> z=gTS^H>jHb2;}88OVF$Nw&k-zL+u(fNp{=vpRVV74!=-`( z!-%Yd+ai`$zQ~(kr|lFUs29y(Ju`df&8Xt2UkMwvKBg-uE}e6r=gZX%O-&CcEYVDl znl`;OI-zRHm&Q{KvT135mI^N4qZfK_S&Y~;B{Koh+8r4)><-B&tAs?#)NbMuc*gW) zq9os$Roy#wo}T3p?^2n$Fsk_npMD=ht>yd)JlmxH1x-7o)-v1c*+d`b`1uvPpQx>Q zvS-tp|7>m(?2<|TkICm2Fb*Gpp+dEI0gtV8szf7);F~1-qv!9_hGV#fZ z$BMDd@4K${m#0Sk+Mr{)V#5x$^=n$CbX99zBC-xevs_<1k?Z2~M;Bbgk`uFH7wy@% zv^Bfpky(V?a`_d{mNLjxn6jB~<(nv!w`aok4cTqeV}&fYKCtT6RAi10pT%cd`=BBE zOybFnr+$mAwDF1Oy0(Goo5QTO#=i-V{L>GAId~;9?Lao;z7L>{`+n?aIj=qU1oeg<50BP z)Th!3te#H=e--$&KbX$_=b>N2?w(4{i|kT@UzYv)5Vh}yi-vo$=ZBC5qC4W$9iz?o z!xlF_KJZn1N$=topWsFO@xs>cf1dwXI*(%l_s4ae?KfPG25j2rn51O?mH(m6-#{0Q z$L4IMj$f{PzklFF@}(2jYO!kFOXkc}?+#^L*l_J+r(~1VN$UkZVuHP%Q+-}+>MRP- zPt)zP~v^U3ndiLns-ztuui=_OW=BXy!;&jdFan|wLVa$cOE_xa=hT}xNKiQ@IjWnf@n@O1TaS?83{ F1OO#>i{AhM diff --git a/imgs/flash.png b/imgs/flash.png new file mode 100644 index 0000000000000000000000000000000000000000..78033d9a5dc4ef02027e6ef6817e4433493f6c22 GIT binary patch literal 70805 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 literal 0 HcmV?d00001 diff --git a/imgs/retrobowl.png b/imgs/retrobowl.png new file mode 100644 index 0000000000000000000000000000000000000000..3f6b6c7dfb4238adac0bb928d449d3d77ff2068d GIT binary patch literal 1554 zcmeAS@N?(olHy`uVBq!ia0y~yVAuu19Lx+140;!qA7)@+3<&TEarN(4{{R2~{b5oG0)@4nQR=@?EHFh`_sCp=e_yoPF%dWcjuRjN3Lw1 z_aI64MoY!pPyZ%vZTxt6^X*#aQ~fcg=EW7TG8|m7EG}QNCs#CYLd^R8%d3_(yk0tG zahdqMG$H>&>DG0VYgS~)S2G-5mc4hw)E)Vv%VJ%3=0@IKG=FV~_L2Oc<3|@wvk`8Q zX1K7fXI_*3<>|%CCPz%_@;qE%H7D0_R)zD`6Wdm;?K=|clFGoqQtRpB7*a9k?cJZj zx1vN26td_jByekSy_&q#HTo8l-i`-hOV@7Q9s73Q`}Obt|9@-wbk6KEwu-G@MMBm4 z_Lm$pczN8~L}mIN6M+dTo}Yp=Xm!#vEDxE>!%&+&)BV|3+*!I zoU6RB_=$a$6{GFurPn{C-uo$f>E=(-6V!NNTkTU4 znpvp3By>$>e%67mxX3F{-AnQ`d|$|L&Rsm&ZT}9opM7z=nzml6ag(=beamroV#~RS z7c4*V+xq?DmDNhS@myxQ{>!!7$`ngovuae{ZdoACzadla_vzD1RtNl@vq-<~*7VYa zbAy*GkGZ}qd1K0QuCBO)+WvhSASFZY9k>sv_^YY!f-dzXg zZ=HT{X4&-r6<1>adU(mNo3fDe$CEj?RK7g97MbxoRJ5v=x2jn7^y78U?k-UO_9F8X z@2@`3XA6EEx6IIN`+V)o?-NA_T0g#@v~JaTJ}2)F>#erymMr9seQ|uzE7E(wtM>j{L8nq`)f0^gzv3zW#0UKuMWxVDPC0)eUWvi z>D)ODH!nV4W!|b+vXFZ(`>uKQFN{;z&g*}OD&XQrA9V|n^j(iPM*muHEl1Yhp#72F z8qbFPq6-tx(8u`MRdG`_5`1Y*%={ZJfD_>u@VKgmq z(&|Ufjz+2pK0%Xze!UUVmc7B#{mZ$?OFNHhEi6_k>GNZ=bzfD#hgbJuzm(h(_4Rpv zlinrh{yDjPR{FF?$uA!@e=XHI>wI2Nz{(w*RKEK8SESC&U;pLLJ;ft-fA(Htzao6# zM);QRKH(ef-5o%llz-v*|I%NpSB*~gUb>#mJDe@~&gk@$|EBPz4q{0Di^VG5R(V?%xu4Lx z6Cp3hp~I8i`rb_AZ=Qsi-Lfy$iJQ6u%U>k!RS)};Q5}7yOYZ7-0nuF-TJ^r1EBUKd zAZ)w*zM|Zo>n$;h+zq&YeJ|YmzUyCej`aU>X5$xG-~UeU*K;w9y?5t_<C>Ny{%ttNvEtNW-`x1aeiu$Ad~cS!_A(I^hxMv^ z7yps?GR4Z`5#zmy&5xPf+Up-b*mCb6+a>u4xy!;On|m#*+#LK_4y9+jntNf(jTMK) zH_ok-wFhZ-V&7#~dbRz|Lie82OYNSWTj4HqQgoM!=cUef^|lpvH%vG-_ZR~M1B0il KpUXO@geCx#I2efl literal 0 HcmV?d00001 diff --git a/index.html b/index.html index 3fea555..f19eded 100644 --- a/index.html +++ b/index.html @@ -1,10 +1,10 @@ -Home - - -Home + + +

Home

diff --git a/sitemap.html b/sitemap.html index 54fb58b..9d812d8 100644 --- a/sitemap.html +++ b/sitemap.html @@ -5,15 +5,17 @@ - back + back
- Calculator -
- Retro-bowl -
- BTD4 -
- Flash +   +   +   +   +   + calculator + retro-bowl + BTD4 + Flash Player
2048
@@ -36,6 +38,8 @@ Vex 5
Drift +
+ constructs '; + windowContent += '' + for (var i = 0; i < arr.length; i++) { + data[i] = arr[i].toDataURL(); + windowContent += ''; + } + windowContent += ''; + windowContent += ''; + var printWin = window.open(); + printWin.document.open(); + printWin.document.write(windowContent); + printWin.document.addEventListener('load', function() { + printWin.focus(); + printWin.print(); + //printWin.document.close(); + //printWin.close(); + //printWin.onfocus = function () {printWin.close();} + setTimeout(function() { + printWin.document.close(); + printWin.close(); + },500); + }, true); +} + +function getHighestZIndex() { + var elems = container.childNodes; + var highest = 0; + for (var i = 0; i < elems.length; i++) { + if (elems[i].nodeType !== 1) continue; + var zIndex = Number(elems[i].style.zIndex); + if ((zIndex > highest) && (zIndex != 'auto')) highest = zIndex; + } + return highest; +} +function getNodes() { + var elems = container.childNodes; + var obj = []; + for (var i = 0; i < elems.length; i++) { + if (elems[i].nodeType !== 1) continue; + var zIndex = Number(elems[i].style.zIndex); + var rect = []; + if (!un(elems[i].data)) { + rect[0] = elems[i].data[100]; + rect[1] = elems[i].data[101]; + rect[2] = elems[i].data[102]; + rect[3] = elems[i].data[103]; + } + var opacity = elems[i].style.opacity; + var pointerEvents = elems[i].style.pointerEvents == 'auto' ? true : false; + obj.push({zIndex:zIndex,node:elems[i],rect:rect,opacity:opacity,pointerEvents:pointerEvents}); + } + obj.sort(function(a,b) { + return a.zIndex - b.zIndex; + }); + return obj; +} + +function funcLog() { + augment(function(name, fn) { + console.log("fn " + name); + }); +} +function augment(withFn) { + var name, fn; + for (name in window) { + fn = window[name]; + var exceptions = ['mouseCoordsChange', 'xWindowToCanvas', 'yWindowToCanvas', 'xCanvasToWindow','yCanvasToWindow', 'setTimeout', 'clearTimeout', 'dragMove', 'pointerEventsListen', 'hitTestMouseOver', 'mathsInputCursorBlink', 'drawMathsInputText', 'drawMathsText', 'mapArray', 'removeTags', 'buildArray', 'clearCorrectingInterval', 'logMe', 'boolean', 'replaceAll', 'showObj', 'dist', 'distancePointToLineSegment', 'closestPointOnLineSegment', 'escapeRegExp', 'resizeCanvas', 'arraysEqual','def','un','resizeCanvas3','updateMouse','addListener','createCanvas','newctx','clone','addEventListener','addListenerStart','addListenerEnd','resize','showObj','hideObj','removeEventListener','addListenerMove','clearCanvas','active','inactive','stopDefaultBackspaceBehaviour']; + if (typeof fn === 'function' && exceptions.indexOf(name) == -1) { + window[name] = (function(name, fn) { + var args = arguments; + return function() { + withFn.apply(this, args); + return fn.apply(this, arguments); + } + })(name, fn); + } + } +} +//funcLog(); + +var mainCanvasLeft = 0; +var mainCanvasTop = 0; +var mainCanvasWidth = 1200; +var mainCanvasHeight = 700; +var mainCanvasMargins = [0,0,0,0]; //l,t,r,b - used in teach: edit mode +var canvasDisplayLeft = 0; +var canvasDisplayTop = 0; +if (typeof mainCanvasBorderWidth == 'undefined') var mainCanvasBorderWidth = 10; +if (typeof mainCanvasFillStyle == 'undefined') var mainCanvasFillStyle = mainCanvasFillStyle || '#FFC'; +var mainCanvasMode = 'full'; + +var pi = String.fromCharCode(0x03C0); +var times = String.fromCharCode(0x00D7); +var divide = String.fromCharCode(0x00F7); +var degrees = String.fromCharCode(0x00B0); +var infinity = String.fromCharCode(0x221E); +var lessThanEq = String.fromCharCode(0x2264); +var moreThanEq = String.fromCharCode(0x2265); +var notEqual = String.fromCharCode(0x2260); +var theta = String.fromCharCode(0x03B8); +var plusMinus = String.fromCharCode(0x00B1); +var minusPlus = String.fromCharCode(0x2213); +var tab = String.fromCharCode(0x21F4); +var br = String.fromCharCode(0x23CE); + +var key1 = []; +var key1Data = []; + +var mouse = {x:0, y:0}; +function updateMouse(e) { + if (un(e)) return; + if (e.touches) { + if (un(e.touches[0])) return; + var x = e.touches[0].pageX; + var y = e.touches[0].pageY; + } else { + var x = e.clientX || e.pageX; + var y = e.clientY || e.pageY; + } + + if (!un(draw) && !un(draw.div)) { + var bounds = draw.drawCanvas[0].getBoundingClientRect(); + mouse.x = (x - bounds.left) * (1200 / bounds.width); + mouse.y = (y - bounds.top) * (1700 / bounds.height); + draw.mouse = [mouse.x,mouse.y]; + } else { + mouse.x = xWindowToCanvas(x); + mouse.y = yWindowToCanvas(y); + if (!un(draw) && !un(draw.drawRelPos)) { + draw.mouse = canvasPosToDrawPos([mouse.x,mouse.y]); + } + } +} +function canvasPosToDrawPos(pos) { + if (un(draw)) return pos; + return [ + (pos[0]-draw.drawRelPos[0])/draw.scale, + (pos[1]-draw.drawRelPos[1])/draw.scale + ]; +} +function drawPosToCanvasPos(pos) { + if (un(draw)) return pos; + return [ + pos[0]*draw.scale+draw.drawRelPos[0], + pos[1]*draw.scale+draw.drawRelPos[1] + ]; +} + +var dragObject = []; +var dragObjectData = []; +var currentDragId; +var dragOffset = {x:0, y:0}; +var dragArea = {xMin:0, xMax:0, yMin:0, yMax:0}; + +var currMathsInput; +var currMathsInputId; +var mathsInputCursorBlinkInterval; +var mathsInputCursorState; +var mathsInputCursor = {x:0, top:0, bottom:0} +var charMap = [[42,215,215], [48,48,41], [49,49,33], [50,50,34], [51,51,163], [52,52,36], [53,53,37], [54,54,94], [55,55,38], [56,56,215], [57,57,40], [96,48,48], [97,49,49], [98,50,50], [99,51,51], [100,52,52], [101,53,53], [102,54,54], [103,55,55], [104,56,56], [105,57,57], [106,215,215], [107,43,43], [109,189,189], [110,190,190], [111,191,191], [186,59,58], [187,61,43], [188,44,60], [189,45,95], [190,46,62], [191,247,63], [192,39,64], [219,91,123], [220,92,124], [221,93,125], [222,35,126]]; +var endInputExceptions = []; +var currSlider; +var slider = []; +var draw; +var keyboard = []; +var keyboardData = []; +var keyboardVis = []; +var showKeys = []; +var hideKeys = []; +var zIndexFront = 1000; + +var canvases = [[]]; +var mathsInput = [[]]; + +var container = document.getElementById('canvascontainer'); +var canvas = document.getElementById('canvas'); +var ctx = canvas.getContext('2d'); + +var canvasDisplayRect; +var canvasDisplayWidth; +var canvasDisplayHeight; +var canvasMetrics = canvas.getBoundingClientRect(); + +var openhand = 'url("cursors/openhand.cur"), auto'; +var closedhand = 'url("cursors/closedhand.cur"), auto'; + +var logPointerEvents = false; +window.addEventListener("touchstart", function (e) { + updateMouse(e); + if (un(e.target.allowDefault) || e.target.allowDefault == false) e.preventDefault(); + if (logPointerEvents) + console.log('touchstart', e.target); +}, true); +window.addEventListener("touchmove", function (e) { + updateMouse(e); + if (un(e.target.allowDefault) || e.target.allowDefault == false) e.preventDefault(); + if (logPointerEvents) + console.log('touchmove', e.target); +}, true); +window.addEventListener("touchend", function (e) { + if (un(e.target.allowDefault) || e.target.allowDefault == false) e.preventDefault(); + if (logPointerEvents) + console.log('touchend', e.target); +}, true); +window.addEventListener("pointerstart", function (e) { + if (un(e.target.allowDefault) || e.target.allowDefault == false) e.preventDefault(); + if (logPointerEvents) console.log('pointerstart'); +}, true); +window.addEventListener("pointermove", function (e) { + if (un(e.target.allowDefault) || e.target.allowDefault == false) e.preventDefault(); + //if (logPointerEvents) console.log('pointermove'); +}, true); +window.addEventListener("pointerend", function (e) { + if (un(e.target.allowDefault) || e.target.allowDefault == false) e.preventDefault(); + if (logPointerEvents) console.log('pointerend'); +}, true); +window.addEventListener("mousedown", function (e) { + updateMouse(e); + if (e.target.allowDefault === true) return; + e.preventDefault(); + if (logPointerEvents) console.log('mousedown'); +}, true); +window.addEventListener("mousemove", function (e) { + updateMouse(e); + if (un(e.target.allowDefault) || e.target.allowDefault == false) e.preventDefault(); + if (logPointerEvents) + console.log('mousemove'); +}, true); +window.addEventListener("mouseup", function (e) { + if (e.target.allowDefault === true) return; + e.preventDefault(); + if (logPointerEvents) console.log('mouseup'); +}, true); + +window.addEventListener('resize', resize, false); +window.addEventListener('orientationchange', resize, false); +window.addEventListener('keydown', stopDefaultBackspaceBehaviour, false); +//window.addEventListener('mousemove', updateMouse, false); +//window.addEventListener('touchstart', updateMouse, false); +//window.addEventListener('touchmove', updateMouse, false); +var foc = true; +var blinking = false; +window.addEventListener('focus', function () { + foc = true; + if (blinking == true) { + setTimeout(function () { + mathsInputCursorBlinkInterval = setCorrectingInterval(function () { + mathsInputCursorBlink() + }, 600); + }, 100); + } +}); +window.addEventListener('blur', function () { + foc = false; + if (blinking == true) { + clearCorrectingInterval(mathsInputCursorBlinkInterval); + inputCursorState = false; + drawMathsInputText(currMathsInput); + } + if (typeof currMathsInput !== 'undefined') + endMathsInput(); +}); +var makePDFLoaded = false; +function loadMakePDF(callback) { + if (makePDFLoaded == false) { + makePDFLoaded = true; + window.callback = callback; + loadScript('pdfmake.min.js',loadMakePDF2); + } else { + callback(); + } +} +function loadMakePDF2() { + var callback = window.callback; + delete window.callback; + loadScript('vfs_fonts.js',callback); +} + +/*window.addEventListener('touchstart',touchPreventDefault,false); +window.addEventListener('touchmove',touchPreventDefault,false); +window.addEventListener('touchend',touchPreventDefault,false); +var touchPreventDefault = function(e) { + e.preventDefault; +} + +var isMobile = { + Android: function () { + return navigator.userAgent.match(/Android/i); + }, + BlackBerry: function () { + return navigator.userAgent.match(/BlackBerry/i); + }, + iOS: function () { + return navigator.userAgent.match(/iPhone|iPad|iPod/i); + }, + Opera: function () { + return navigator.userAgent.match(/Opera Mini/i); + }, + Windows: function () { + return navigator.userAgent.match(/IEMobile/i) || navigator.userAgent.match(/WPDesktop/i); + }, + any: function () { + return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows()); + } +} +if (isMobile.any()) { + document.getElementsByTagName("head")[0].innerHTML += '' + document.ontouchmove = function (event) { + event.preventDefault() + } +} +*/ + +if (isTask == true) { + var page = []; + if (boolean(window.sortPages,true) == true) { + pages.sort(function(a,b) { + return Number(a.order) - Number(b.order); + }); + } + //pages.reverse(); + prevPages.sort(function(a,b) { + var d1 = new Date(a.timestamp).getTime(); + var d2 = new Date(b.timestamp).getTime(); + return d2-d1; + }); + for (var i = 0; i < prevPages.length; i++) { + if (Number(prevPages[i].percentage < 100)) continue; + var pageId = Number(prevPages[i].pageid); + for (var j = 0; j < pages.length; j++) { + if (Number(pages[j].pageid) == pageId) { + if (un(pages[j].prev)) { + pages[j].prev = prevPages[i].timestamp; + } + break; + } + } + } +} + +if (typeof FontFace !== 'undefined') { + document.fonts.ready.then(function(res) { + //console.log(document.fonts.size, 'FontFaces loaded.'); + //console.log('result',res); + loadScripts1(); + }, function(err) { + //console.log('error',err); + loadScripts1(); + }); +} else { + loadScripts1(); +} + +function loadScripts1() { + for (var i = 0; i < scriptsToLoad.length; i++) { + loadScript(scriptsToLoad[i], scriptLoaded); + } +} +function loadScript(url, callback, errorCallback, index) { + if (un(errorCallback)) errorCallback = function() {}; + var head = document.getElementsByTagName('head')[0]; + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.charset = "UTF-8"; + var url2 = typeof cacheVersion === 'string' ? url + '?' + cacheVersion : url; + script.src = url2; + script.index = index; + script.onload = function() { + if (!un(callback)) callback(this.index); + }; + script.onerror = function() { + if (!un(errorCallback)) errorCallback(this.index); + }; + head.appendChild(script); +} +function scriptLoaded() { + loadedScriptCount++; + if (loadedScriptCount >= scriptsToLoad.length) { + clearCanvas(); + if (boolean(isTask,true) == true) { + window.holder = createHolder(); + showPage(0); + } else { + if (!un(scriptsToLoad2)) { + for (var i = 0; i < scriptsToLoad2.length; i++) { + loadScript(scriptsToLoad2[i]); + } + } + } + resize(); + if (isTask == true) { + TimeMe.initialize({ + currentPageName: "page", + idleTimeoutInSeconds: idleTimeoutInSeconds + }); + TimeMe.callWhenUserLeaves(function(){ + inactive(); + }); + TimeMe.callWhenUserReturns(function(){ + active(); + }); + if (typeof taskLogData == 'undefined') return; + if (Number(taskLogData.status) < 3) { + TimeMe.callAfterTimeElapsedInSeconds(reportIntervalInSeconds,reportHandler); + } + } + } +} +var inactiveBox; + +function openPHP(url,postVars) { + var form = document.createElement("form"); + form.target = "_blank"; + form.method = "POST"; + form.action = url; + form.style.display = "none"; + + for (var key in postVars) { + var input = document.createElement("input"); + input.type = "hidden"; + input.name = key; + input.value = encodeURIComponent(postVars[key]); + form.appendChild(input); + } + + document.body.appendChild(form); + form.submit(); + document.body.removeChild(form); +} + +if (isTask == true) { + var pointsMode = false; + + var pageLoadErrorCount = 0; + var faded = false; + function createInactiveBox() { + inactiveBox = document.createElement('canvas'); + inactiveBox.width = 400; + inactiveBox.height = 120; + inactiveBox.setAttribute('position', 'absolute'); + inactiveBox.setAttribute('cursor', 'auto'); + inactiveBox.setAttribute('draggable', 'false'); + inactiveBox.setAttribute('class', 'buttonClass'); + + var ctx = inactiveBox.getContext('2d'); + ctx.lineCap = "round"; + ctx.lineJoin = "round"; + ctx.clearRect(0, 0, 400, 120); + ctx.fillStyle = "#FFF"; + ctx.strokeStyle = "#000"; + ctx.lineWidth = 6; + ctx.beginPath(); + ctx.moveTo(3, 3); + ctx.lineTo(397, 3); + ctx.lineTo(397, 117); + ctx.lineTo(3, 117); + ctx.lineTo(3, 3); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + ctx.font = "80px Hobo"; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillStyle = "#000"; + ctx.fillText('Inactive', 200, 66); + inactiveBox.style.pointerEvents = 'default'; + inactiveBox.style.cursor = 'default'; + inactiveBox.style.zIndex = 900000000; + resize(); + return inactiveBox; + } + + //* + var idleTimeoutInSeconds = 2*60; + var reportIntervalInSeconds = 4*60; + function reportHandler() { + if (Number(taskLogData.status) == 3 || userType !== 0) return; + logTime(); + if (un(TimeMe)) return; + clearReportTimeouts(); + var time = Math.round(TimeMe.getTimeOnPageInSeconds('page')); + TimeMe.callAfterTimeElapsedInSeconds(time+reportIntervalInSeconds,reportHandler); + } + function clearReportTimeouts() { + for (var i = TimeMe.timeElapsedCallbacks.length - 1; i >= 0; i--) { + TimeMe.timeElapsedCallbacks[i].pending = false; + } + } + + function active(e) { + /*unfadePage(); + /if (!un(inactiveBox)) { + if (inactiveBox.parentNode == container) { + container.removeChild(inactiveBox); + } + }*/ + removeListenerStart(window,active); + //console.log('active'); + } + function inactive() { + /*fadePage(); + if (un(inactiveBox)) inactiveBox = createInactiveBox(); + container.appendChild(inactiveBox);*/ + addListenerStart(window,active); + //console.log('inactive'); + } + + function logTime() { + if (userType !== 0) return; + if (Number(taskLogData.percentage) == 100 || Number(taskLogData.status) == 3) { + clearReportTimeouts(); + return; + } + var timeSpent = Math.round(TimeMe.getTimeOnPageInSeconds('page')/60); + var browserInfo = getBrowserInfo(); + var params = "tasksLogId="+taskLogData.taskNumber+"&timeSpent="+timeSpent+"&browserInfo="+browserInfo; + //console.log('logTime params: ', params); + var sendReportX = new XMLHttpRequest(); + sendReportX.open("post", "_logTime.php", true); + sendReportX.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + sendReportX.errorCallback = function() {}; + sendReportX.onreadystatechange = function() { + if (this.readyState == 4) { + if (this.status !== 200) { + if (typeof this.errorCallback == 'function') this.errorCallback(); + return; + } + //console.log('logTime sent'); + //reportHandler(); + } + } + sendReportX.send(params); + } + + //*/ + + /* + var inactiveAfter = 300; + var inactiveStartTime; + var inactiveTime = 0; + var isActive = true; + var startTime = new Date(); + var activityMonitor = setTimeout(function(){inactive()}, inactiveAfter*1000); + + window.addEventListener('mouseup', active, false); + window.addEventListener('touchend', active, false); + window.addEventListener('mousedown', active, false); + window.addEventListener('touchstart', active, false); + window.addEventListener('keydown', active, false); + window.addEventListener('blur', inactive, false); + + function active(event) { + if (!un(event) && event.target.nodeName !== 'TEXTAREA') event.preventDefault(); + if (isTask == true) { + clearTimeout(activityMonitor); + activityMonitor = setTimeout(function(){inactive()}, inactiveAfter*1000); + if (isActive == false && pageIndex !== pages.length) { + isActive = true; + var currentTime = new Date(); + inactiveTime += (currentTime - inactiveStartTime); + if (inactiveBox.parentNode == container) container.removeChild(inactiveBox); + unfadePage(); + } + } + } + function inactive() { + clearTimeout(activityMonitor); + isActive = false; + inactiveStartTime = new Date(); + if (boolean(window.logPages,false) == false) { + fadePage(); + if (un(inactiveBox)) inactiveBox = createInactiveBox(); + container.appendChild(inactiveBox); + } + } + + function getTimeSpent() { + var currentTime = new Date(); + var ms = currentTime - startTime - inactiveTime; + return Math.floor(ms/1000); + } + function getTime() { + var currentTime = new Date(); + var timeSpent = currentTime - startTime - inactiveTime; + timeSpent /= 60000; + return Math.round(timeSpent+Number(taskLogData.prevTime)); + } + + function logTime() { + if (userType !== 0) return; + if (Number(taskLogData.percentage) == 100 || Number(taskLogData.status) == 3) { + clearTimeout(minuteCounter); + return; + } + if (isActive == true) { + var timeSpent = getTime(); + var browserInfo = getBrowserInfo(); + var params = "tasksLogId="+taskLogData.taskNumber+"&time="+timeSpent+"&inactiveTime="+inactiveTime+"&browserInfo="+browserInfo; + //console.log('logTime params: ', params); + var sendReportX = new XMLHttpRequest(); + sendReportX.open("post", "_logTime.php", true); + sendReportX.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + sendReportX.onload = function() { + //var response = (this.responseText).split(';'); + //console.log('logTime response: ', this.responseText); + taskLogData.totalTime = this.responseText; + connectionErrorMessage = 0; + } + sendReportX.onreadystatechange = function() { + if (sendReportX.readyState == 4 && sendReportX.status !== 200 && connectionErrorMessage == 0) { + alert('Error connecting to the MathsPad database. Your results may not be logged. Check your internet connection.'); + connectionErrorMessage = 1; + } + } + sendReportX.send(params); + } + clearTimeout(minuteCounter); + minuteCounter = setTimeout(function(){logTime()}, 4*60000); + } + if (userType == 0) { + taskLogData.prevTime = taskLogData.totalTime; + taskLogData.prevStatus = taskLogData.status; + taskLogData.prevPercentage = taskLogData.percentage; + var connectionErrorMessage; + var minuteCounter = setTimeout(function(){logTime()}, 4*60000); + } + //*/ + + function fadePage() { + if (faded == true) return; + var nodes = container.childNodes; + for (var n = 0; n < nodes.length; n++) { + if (nodes[n].nodeType !== 1 || nodes[n] == inactiveBox) continue; + nodes[n].savedOpacity = nodes[n].style.opacity; + nodes[n].style.opacity = 0.5; + } + faded = true; + } + function unfadePage() { + var nodes = container.childNodes; + for (var n = 0; n < nodes.length; n++) { + if (nodes[n].nodeType !== 1) continue; + var opacity = !un(nodes[n].savedOpacity) ? nodes[n].savedOpacity : 1; + nodes[n].style.opacity = opacity; + delete nodes[n].savedOpacity; + } + faded = false; + } + + function createHolder() { + var prev = playButton(20,20,50,prevPage,{dir:'left',fillColor:'#3FF',lineWidth:4,radius:8,zIndex:1000}); + var next = playButton(1200-20-50,20,50,nextPage,{dir:'right',fillColor:'#3FF',lineWidth:4,radius:8,zIndex:1000}); + var reload = newctx({rect:[1200-130,20,50,50],pe:true,zIndex:1000}); + addListener(reload.canvas,reloadPage); + drawRefreshButton(reload,4,4,42,'#C9F','left'); + var canvas = newctx({rect:[0,0,1200,700],z:1}).canvas; + showObj(canvas); + var check = newctx({rect:[1200-340,20,200,50],pe:true,z:10000000}).canvas; + text({ctx:check.ctx,textArray:['<><>Check Answer'],left:2,top:2,width:196,height:46,align:'center',vertAlign:'middle',box:{type:'loose',borderColor:'#000',borderWidth:4,radius:8,color:'#6F9'}}); + addListenerEnd(check,checkPage); + var home2 = newctx({rect:[712,555,386,80],pe:true,zIndex:1000}).canvas; + text({ctx:home2.ctx,rect:[2,2,382,76],text:['<><>Return to My Profile '],align:'right',vertAlign:'middle',box:{type:'loose',borderColor:'#000',borderWidth:4,color:'#FFF',radius:8},allowSpaces:true}); + var home = newctx({rect:[80,20,50,50],pe:true,zIndex:1000}).canvas; + roundedRect2(home.ctx,2,2,46,46,8,4,'#000','#FFF'); + var homeImage = new Image; + homeImage.onload = function() { + holder.home.ctx.drawImage(homeImage, 4.5, 4, 42, 42); + + var image = holder.home.ctx.getImageData(0, 0, 500, 200); + var imageData = image.data, + length = imageData.length; + for (var i=0; i < length; i+=4) { + if (imageData[i] == 255 && imageData[i+1] == 255 && imageData[i+2] == 255) imageData[i+3] = 50; + } + image.data = imageData; + + roundedRect2(home.ctx,2,2,46,46,8,4,'#000','#FFF'); + holder.home.ctx.putImageData(image, 0, 0); + roundedRect2(holder.home.ctx,2,2,46,46,8,4,'#000'); + + holder.home2.ctx.drawImage(homeImage, 12, 8, 42*1.5, 42*1.5); + } + homeImage.src = "/Images/logoSmall.PNG"; + var loading = newctx({pe:true,z:1000000}).canvas; + loading.style.cursor = 'default'; + textBox(loading.ctx,[425,295,350,110],['<><>Loading...'],'#3FF'); + var disablePeCanvas = newctx({pe:true,zIndex:1000000000000}).canvas; + disablePeCanvas.style.cursor = 'default'; + var summary = newctx().canvas; + var summary2 = newctx({z:3}).canvas; + return {prev:prev,next:next,reload:reload.canvas,canvas:canvas,check:check,home:home,loading:loading,feedback:[],feedbackButton:[],completed:[],disablePeCanvas:disablePeCanvas,summary:summary,summary2:summary2,home2:home2}; + } + function prevPage() { + if (pageIndex == 0) return; + hidePage(); + pageIndex--; + showPage(); + } + function nextPage() { + if (pageIndex == pages.length - 1) return; + hidePage(); + pageIndex++; + showPage(); + } + function goToPage(num) { + if (un(num)) num = pages.length - 1; + if (num < 0 || num > pages.length-1) return; + hidePage(); + pageIndex = roundToNearest(num,1); + showPage(); + } + function hidePage() { + endMathsInput(); + unfadePage(); + keyboardHardOpen = false; + hideKeyboard2(); + if (pageIndex < pages.length) { + for (var c = 0; c < canvases[pageIndex].length; c++) { + if (canvases[pageIndex][c].parentNode == container) { + container.removeChild(canvases[pageIndex][c]); + } + } + for (var m = 0; m < mathsInput[pageIndex].length; m++) { + hideMathsInput(mathsInput[pageIndex][m]); + } + for (var s = 0; s < pages[pageIndex].stars.length; s++) { + hideObj(pages[pageIndex].stars[s]); + //pages[pageIndex].stars[s].stopSpin(); + } + } + if (!un(draw) && !un(draw.drawCanvas)) { + pages[pageIndex].paths = draw.path; + pages[pageIndex].beforeDraw = draw.beforeDraw; + pages[pageIndex].afterDraw = draw.afterDraw; + pages[pageIndex].drawMode = draw.drawMode; + + delete draw.beforeDraw; + delete draw.afterDraw; + draw.path = []; + drawCanvasPaths(); + calcCursorPositions(); + } + holder.canvas.ctx.clearRect(0,80,1200,620); + hideObj(holder.feedbackButton[pageIndex]); + hideObj(holder.completed[pageIndex]); + hideObj(holder.summary); + hideObj(holder.summary2); + hideObj(holder.home2); + } + function showPage() { + /*if (pageIndex == pages.length) { + showSummaryPage(); + return; + }*/ + active(); + if (un(page[pageIndex])) { + pageLoadErrorCount++; + loadPage(function() { + if (boolean(p.taskPageAutoLoad,false) == true) { + taskPageAutoLoad(p); + }; + pages[pageIndex].paths = draw.path; + pages[pageIndex].beforeDraw = draw.beforeDraw; + pages[pageIndex].afterDraw = draw.afterDraw; + showPage(); + },function() { + page[pageIndex] = undefined; + if (pageLoadErrorCount < 3) prevPage(); + Notifier.error('Error connecting to the server. The page cannot be loaded.'); + }); + } else { + for (var c = 0; c < canvases[pageIndex].length; c++) { + if (canvases[pageIndex][c].vis == true) { + container.appendChild(canvases[pageIndex][c]); + } + } + for (var m = 0; m < mathsInput[pageIndex].length; m++) { + showMathsInput(mathsInput[pageIndex][m]); + } + + holder.canvas.ctx.clearRect(0,0,1200,700); + if (boolean(window.logPages,false) == false) { + textBox(holder.canvas.ctx,[140,20,100,50],['<><>'+String(pageIndex+1)+' / '+String(pages.length)],'#CCF'); + drawPoints(); + for (var s = 0; s < pages[pageIndex].stars.length; s++) { + if (pages[pageIndex].stars[s].show == true) { + showObj(pages[pageIndex].stars[s]); + } + } + if (pages[pageIndex].completed == false && un(pages[pageIndex].prev)) { + if (boolean(page[pageIndex].showHolderCheckButton,true) == true) { + showObj(holder.check); + } else { + hideObj(holder.check); + } + holder.reload.style.opacity = 1; + holder.reload.style.pointerEvents = 'auto'; + if (pointsMode == true) holder.canvas.ctx.rect2({rect:[650+2,20+2,200-4,50-4],lineWidth:4,color:'#000',radius:8}); + if (!un(holder.feedback[pageIndex]) && holder.feedback[pageIndex].fb == true) { + showObj(holder.feedbackButton[pageIndex]); + } else { + hideObj(holder.feedbackButton[pageIndex]); + } + } else if (pages[pageIndex].completed == true) { + hideObj(holder.check); + showObj(holder.completed[pageIndex]); + text({ctx:holder.canvas.ctx,left:650,width:410,top:20,height:50,align:'center',vertAlign:'middle',textArray:['<><><>Page Completed!']}); + holder.reload.style.opacity = 0.5; + holder.reload.style.pointerEvents = 'none'; + hideObj(holder.feedbackButton[pageIndex]); + } else if (!un(pages[pageIndex].prev)) { + if (boolean(page[pageIndex].showHolderCheckButton,true) == true) { + showObj(holder.check); + } else { + hideObj(holder.check); + } + showObj(holder.completed[pageIndex]); + holder.reload.style.opacity = 1; + holder.reload.style.pointerEvents = 'auto'; + if (pointsMode == true) holder.canvas.ctx.rect2({rect:[650+2,20+2,200-4,50-4],lineWidth:4,color:'#000',radius:8}); + if (!un(holder.feedback[pageIndex]) && holder.feedback[pageIndex].fb == true) { + showObj(holder.feedbackButton[pageIndex]); + } else { + hideObj(holder.feedbackButton[pageIndex]); + } + } + showObj(holder.reload); + showObj(holder.home); + } else { + var filename = String(pageData[pageIndex].pageid); + while (filename.length < 4) filename = "0"+filename; + textBox(holder.canvas.ctx,[80,20,100,50],['<><>'+String(filename)],'#CCF'); + if (typeof preview !== 'undefined' && !un(preview.button)) showObj(preview.button); + } + showObj(holder.prev); + showObj(holder.next); + hideObj(holder.loading); + if (boolean(page[pageIndex].allowReload,true) == false) { + hideObj(holder.reload); + } else { + showObj(holder.reload); + } + + if (!un(draw) && !un(draw.drawCanvas)) { + draw.path = pages[pageIndex].paths || []; + draw.beforeDraw = pages[pageIndex].beforeDraw; + draw.afterDraw = pages[pageIndex].afterDraw; + if (!un(pages[pageIndex].drawMode)) { + if (draw.drawMode !== pages[pageIndex].drawMode) { + changeDrawMode(pages[pageIndex].drawMode); + } + } + drawCanvasPaths(); + calcCursorPositions(); + for (var i = 0; i < draw.drawCanvas.length; i++) showObj(draw.drawCanvas); + showObj(draw.cursorCanvas); + } + + if (pageIndex == 0) { + holder.prev.style.opacity = 0.5; + holder.prev.style.pointerEvents = 'none'; + } else { + holder.prev.style.opacity = 1; + holder.prev.style.pointerEvents = 'auto'; + } + if (pageIndex == pages.length - 1) { + holder.next.style.opacity = 0.5; + holder.next.style.pointerEvents = 'none'; + } else { + holder.next.style.opacity = 1; + holder.next.style.pointerEvents = 'auto'; + } + + if (typeof keyboardButton1[pageIndex] !== 'undefined') { + showObj(keyboardButton1[pageIndex]); + } + resize(); + p = page[pageIndex]; + if (boolean(window.logPages,false) == true) { + if (typeof pageDataLog == 'function') pageDataLog(); + if (typeof savePagePNG == 'function') savePagePNG(); + } + pageLoadErrorCount = 0; + } + } + /*function showSummaryPage() { + holder.canvas.ctx.clearRect(0,0,1200,700); + hideObj(holder.check); + hideObj(holder.reload); + hideObj(holder.home); + holder.next.style.opacity = 0.5; + holder.next.style.pointerEvents = 'none'; + + var ctx = holder.summary.ctx; + var ctx2 = holder.summary2.ctx; + ctx.clear(); + ctx2.clear(); + text({ctx:ctx,rect:[120,5,1000,80],text:['<><>Task Summary'],vertAlign:'middle'}); + + var l = 712; + var t = 130; + text({ctx:ctx,left:l,width:386,top:t,height:46,vertAlign:'middle',textArray:['<>'+userName],box:{type:'loose',radius:8,color:'#CFF',borderColor:'#000',borderWidth:4,padding:10}}); + if (pointsMode == true) { + text({ctx:ctx,left:l,width:386,top:t,height:46,align:'right',vertAlign:'middle',textArray:['<><><>'+userPoints+' '],allowSpaces:true}); + ctx.beginPath(); + ctx.lineWidth = 4; + ctx.strokeStyle = '#000'; + drawStar({ctx:ctx,c:[l+361,t+23],r:12}); + ctx.closePath(); + ctx.stroke(); + ctx.beginPath(); + ctx.fillStyle = '#00F'; + drawStar({ctx:ctx,c:[l+361,t+23],r:12}); + ctx.fill(); + } + + var cells = []; + var comp = 0; + var totalPoints = 0; + for (var r = 0; r < pages.length; r++) { + if (pages[r].completed == true) { + comp++; + totalPoints += pages[r].points; + cells[r] = [ + {text:['Page '+(r+1)],color:'#CFC'}, + {minWidth:180,text:['<>Completed!'],color:'#CFC'} + ]; + if (pointsMode == true) cells[r].push({minWidth:200,color:'#CFC'}); + } else if (!un(pages[r].prev)) { + comp++; + cells[r] = [ + {text:['Page '+(r+1)],color:'#CFC'}, + {minWidth:180,text:['<>Completed!'],color:'#CFC'} + ]; + if (pointsMode == true) cells[r].push({minWidth:200,color:'#CFC'}); + } else { + cells[r] = [ + {text:['Page '+(r+1)]}, + {minWidth:180,text:['Keep Trying!']} + ]; + if (pointsMode == true) cells[r].push({minWidth:200}); + } + } + + var table = drawTable2({ + ctx:ctx, + left:102, + top:t, + minCellWidth:100, + minCellHeight:40, + horizAlign:'center', + text:{font:'Hobo',size:28,color:'#000'}, + outerBorder:{show:true,width:4,color:'#000'}, + innerBorder:{show:true,width:2,color:'#000'}, + cells:cells + }); + + if (pointsMode == true) { + for (var r = 0; r < pages.length; r++) { + var c = table.cell[r][2]; + var rect = [c.left,c.top,c.width,c.height]; + var l = rect[0]+rect[2]/2-(pages[r].points-1)*17.5; + var t = (table.yPos[r]+table.yPos[r+1])/2; + ctx2.lineWidth = 4; + if (pages[r].completed == true) { + ctx2.strokeStyle = '#000'; + ctx2.fillStyle = '#FC3'; + } else if (!un(pages[r].prev)) { + ctx2.strokeStyle = '#666'; + ctx2.fillStyle = '#CFC'; + } else { + ctx2.strokeStyle = '#666'; + ctx2.fillStyle = '#FFC'; + } + for (var s = 0; s < pages[r].points; s++) { + ctx2.beginPath(); + drawStar({ctx:ctx2,c:[l,t],r:12}); + ctx2.closePath(); + ctx2.stroke(); + ctx2.beginPath(); + drawStar({ctx:ctx2,c:[l,t],r:12}); + ctx2.fill(); + l += 35; + } + } + } + + if (pointsMode == true) { + textBox(ctx2,[712,190,386,350],['<>Task Progress: '+String(taskLogData.percentage)+'%'+br+br+'Points earned: '+totalPoints+br+br+'Time Spent: '+String(taskLogData.totalTime)+' mins'+br+br+'<><>Your progress has'+br+'been logged.'],'#CFF'); + } else { + textBox(ctx2,[712,190,386,350],['<>Task Progress: '+String(taskLogData.percentage)+'%'+br+br+'Time Spent: '+String(taskLogData.totalTime)+' mins'+br+br+'<><>Your progress has'+br+'been logged.'],'#CFF'); + } + + showObj(holder.summary); + showObj(holder.summary2); + showObj(holder.home2); + inactive(); + resize(); + }*/ + function loadPage(callback,errorCallback) { + showObj(holder.loading); + holder.prev.style.pointerEvents = 'none'; + holder.prev.style.opacity = 0.5; + holder.next.style.pointerEvents = 'none'; + holder.next.style.opacity = 0.5; + if (un(errorCallback)) errorCallback = function() {}; + var filename = pages[pageIndex].pageid; + while (filename.length < 4) { + filename = "0" + filename; + } + pages[pageIndex].filename = filename; + if (!un(pages[pageIndex].prev)) { + pages[pageIndex].maxPoints = 1; + pages[pageIndex].points = 1; + holder.completed[pageIndex] = textCanvas([600-450/2,285,450,170],[''],'#EEE').canvas; + resizeCanvas3(holder.completed[pageIndex]); + var date = new Date(pages[pageIndex].prev.slice(0,pages[pageIndex].prev.indexOf(" "))); + pages[pageIndex].prevDate = ['Sun','Mon','Tues','Weds','Thurs','Fri','Sat'][date.getDay()]+" "+date.getDate()+" "+['Jan','Feb','Mar','Apr','May','June','July','Aug','Sep','Oct','Nov','Dec'][date.getMonth()]+" "+date.getFullYear(); + if (pointsMode == true) { + text({ctx:holder.completed[pageIndex].ctx,left:0,top:16,width:450,height:100,textArray:['<><>Page Completed!<>'+br+'<><>Completed on '+pages[pageIndex].prevDate+br+'Click to practise again for an extra point'],align:'center'}); + } else { + text({ctx:holder.completed[pageIndex].ctx,left:0,top:28,width:450,height:100,textArray:['<><>Page Completed!<>'+br+'<><>Completed on '+pages[pageIndex].prevDate],align:'center'}); + text({ctx:holder.completed[pageIndex].ctx,left:450-100,top:0,width:100,height:30,textArray:['<><>'+times+'<>Dismiss'],align:'center',vertAlign:'middle'}); + } + holder.completed[pageIndex].style.pointerEvents = 'auto'; + holder.completed[pageIndex].style.zIndex = 10000000; + addListener(holder.completed[pageIndex],function() { + hideObj(holder.completed[pageIndex]); + }); + } else { + pages[pageIndex].maxPoints = Number(pages[pageIndex].points); + pages[pageIndex].points = Number(pages[pageIndex].points); + } + pages[pageIndex].completed = false; + pages[pageIndex].stars = []; + var l = 650+100-(pages[pageIndex].maxPoints-1)*17.5; + if (pointsMode == true) { + for (var s = 0; s < pages[pageIndex].maxPoints; s++) { + var star = newctx({rect:[l+s*35-17.5,45-17.5,35,35],z:100000000}).canvas; + star.color = ['#FC3','#000']; + star.draw = function() { + this.ctx.clear(); + this.ctx.beginPath(); + this.ctx.lineWidth = 4; + this.ctx.strokeStyle = this.color[1]; + drawStar({ctx:this.ctx,c:[17.5,17.5],r:12}); + this.ctx.closePath(); + this.ctx.stroke(); + this.ctx.beginPath(); + this.ctx.fillStyle = this.color[0]; + drawStar({ctx:this.ctx,c:[17.5,17.5],r:12}); + this.ctx.fill(); + } + star.draw(); + star.click = function() { + do { + var color = ['#FC3','#F00','#33F','#3FF','#0C0','#CCC'][ran(0,5)]; + } while(this.color[0] == color); + this.color[0] = color; + this.draw(); + } + star.show = true; + pages[pageIndex].stars[s] = star; + } + } + var feedbackButton = textCanvas([1130,90,50,50],['<><>!'],'#FC9').canvas; + feedbackButton.style.zIndex = 1000; + addListenerStart(feedbackButton,toggleFeedback); + holder.feedbackButton[pageIndex] = feedbackButton; + window.p = page[pageIndex] = {}; + mathsInput[pageIndex] = []; + canvases[pageIndex] = []; + loadScript('pages/'+filename+'.js',function() { + page[pageIndex] = p; + callback(); + },errorCallback); + } + function reloadPage() { + for (var c = 0; c < canvases[pageIndex].length; c++) { + canvases[pageIndex][c].data[100] = canvases[pageIndex][c].data[0]; + canvases[pageIndex][c].data[101] = canvases[pageIndex][c].data[1]; + resizeCanvas3(canvases[pageIndex][c]); + canvases[pageIndex][c].style.zIndex = canvases[pageIndex][c].z; + } + for (var m = 0; m < mathsInput[pageIndex].length; m++) { + setMathsInputText(mathsInput[pageIndex][m],[""]); + } + if (!un(page[pageIndex].dragSnapPos)) { + for (var d = 0; d < page[pageIndex].dragSnapPos.length; d++) { + page[pageIndex].dragSnapPos[d][4] = null; + } + } + p = page[pageIndex]; + if (!un(page[pageIndex].reload)) { + page[pageIndex].reload(); + } else if (!un(page[pageIndex].taskPageAutoLoad)) { + taskPageAutoReload(page[pageIndex]); + } + if (!un(page[pageIndex].clear)) { + page[pageIndex].clear(); + } + removeFeedback(); + } + function shuffleTableCells(obj) { + var cells = []; + for (var r = 0; r < obj.cells.length; r++) { + for (var c = 0; c < obj.cells[r].length; c++) { + cells.push(obj.cells[r][c]); + } + } + cells = shuffleArray(cells); + for (var r = 0; r < obj.cells.length; r++) { + for (var c = 0; c < obj.cells[r].length; c++) { + obj.cells[r][c] = cells.shift(); + } + } + return obj; + } + function taskPageAutoLoad(p) { + //console.log(p); + p.ctx1 = newctx(); + p.ctx2 = newctx({z:1000}); + + var paths = p.paths; + var shuffleInputs = []; + var shufflePos = []; + + var dragCheckMode = 'none'; + var dragCanvasCount = 0; + var dragAreaCount = 0; + var dragSnapAreaCount = 0; + for (var p2 = 0; p2 < paths.length; p2++) { + var path = paths[p2]; + if (un(path.isInput)) continue; + if (path.isInput.type == 'drag') { + dragCanvasCount++; + } else if (path.isInput.type == 'dragArea') { + if (path.isInput.snap == true) { + dragSnapAreaCount++; + } else { + dragAreaCount++; + } + } + } + if (dragCanvasCount > 0) { + if (dragSnapAreaCount > 0) { + dragCheckMode = 'dragArea'; + } else { + dragCheckMode = 'dragCanvas'; + } + } + + for (var p2 = 0; p2 < paths.length; p2++) { + var path = paths[p2]; + if (un(path.isInput)) continue; + for (var o = 0; o < path.obj.length; o++) { + var obj = path.obj[o]; + if (obj.type == 'text2' && path.isInput.type == 'text') { + obj.draw = false; + if (typeof p.inputs == 'undefined') p.inputs = []; + var input = createMathsInput2({left:obj.rect[0],top:obj.rect[1],width:obj.rect[2],height:obj.rect[3],fontSize:32,algText:boolean(path.isInput.algText,false)}); + input.pathObj = obj; + input.type = 'text'; + if (!un(path.isInput.tickStyle)) input.tickStyle = path.isInput.tickStyle; + p.inputs.push(input); + } else if (obj.type == 'table2' && path.isInput.type == 'select') { + if (typeof p.inputs == 'undefined') p.inputs = []; + obj.isInput = path.isInput; + var multiSelect = boolean(path.isInput.multiSelect,false); + var checkSelectCount = boolean(path.isInput.checkSelectCount,true); + var shuffle = boolean(path.isInput.shuffle,false); + + var input = {type:'select',buttons:[],multiSelect:multiSelect,checkSelectCount:checkSelectCount,shuffle:shuffle}; + p.inputs.push(input); + + if (shuffle == true) shuffleTableCells(obj); + + var yPos = obj.top; + for (var r = 0; r < obj.cells.length; r++) { + var xPos = obj.left; + for (var c = 0; c < obj.cells[r].length; c++) { + var button = newctx({rect:[xPos,yPos,obj.widths[c],obj.heights[r]],pE:true,z:100}).canvas; + button.input = input; + button.cell = obj.cells[r][c]; + button.row = r; + button.col = c; + button.obj = obj; + addListener(button,taskPageSelectButtonClick); + input.buttons.push(button); + + obj.cells[r][c].toggle = false; + xPos += obj.widths[c]; + } + yPos += obj.heights[r]; + } + } else if (path.isInput.type == 'drag') { + updateBorder(path); + var rect = path.tightBorder; + rect[0] -= 3; + rect[1] -= 3; + rect[2] += 6; + rect[3] += 6; + + var dragCanvas = newctx({rect:rect,drag:true,pe:true}).canvas; + dragCanvas.ctx.translate(-rect[0],-rect[1]); + for (var o = 0; o < path.obj.length; o++) { + drawObjToCtx(dragCanvas.ctx,path,path.obj[o],1,1,0,0); + } + + if (dragCheckMode == 'dragCanvas') { + if (typeof p.inputs == 'undefined') p.inputs = []; + p.inputs.push({type:'drag',canvas:dragCanvas,shuffle:path.isInput.shuffle,value:path.isInput.value}); + } + + if (typeof p.dragCanvases == 'undefined') p.dragCanvases = []; + p.dragCanvases.push({canvas:dragCanvas,shuffle:path.isInput.shuffle,value:path.isInput.value}); + + if (path.isInput.shuffle == true) { + shuffleInputs.push(dragCanvas); + shufflePos.push([rect[0],rect[1],shufflePos.length]); + } + + paths.splice(p2,1); + p2--; + } else if (path.isInput.type == 'dragArea') { + updateBorder(path); + var rect = path.tightBorder; + rect[0] -= 3; + rect[1] -= 3; + rect[2] += 6; + rect[3] += 6; + + if (typeof p.dragAreas == 'undefined') p.dragAreas = []; + p.dragAreas.push({rect:rect,value:path.isInput.value,snap:path.isInput.snap}); + + if (dragCheckMode == 'dragArea') { + if (typeof p.inputs == 'undefined') p.inputs = []; + p.inputs.push({type:'dragArea',rect:rect,value:path.isInput.value}); + } + + if (path.isInput.snap == true) { + if (typeof p.dragSnapPos == 'undefined') p.dragSnapPos = []; + p.dragSnapPos.push(clone(rect.slice(0,4))); + } + } + } + } + if (shuffleInputs.length > 0) { + shufflePos = shuffleArray(shufflePos); + for (var i = 0; i < shuffleInputs.length; i++) { + shuffleInputs[i].data[0] = shufflePos[i][0]; + shuffleInputs[i].data[1] = shufflePos[i][1]; + shuffleInputs[i].data[100] = shufflePos[i][0]; + shuffleInputs[i].data[101] = shufflePos[i][1]; + shuffleInputs[i].style.zIndex = shufflePos[i][2]+10; + shuffleInputs[i].z = shufflePos[i][2]+10; + resizeCanvas(shuffleInputs[i]); + } + } + taskPageAutoDrawPaths(p); + + if (!un(p.keyboard)) addKeyboard(p.keyboard); + } + function taskPageAutoDrawPaths(p) { + p.ctx1.clear(); + for (var p2 = 0; p2 < p.paths.length; p2++) { + for (var o = 0; o < p.paths[p2].obj.length; o++) { + var obj = p.paths[p2].obj[o]; + if (boolean(obj.draw,true) == true) { + drawObjToCtx(p.ctx1,p.paths[p2],obj); + } + } + } + } + function taskPageSelectButtonClick(e) { + var cells = e.target.obj.cells; + var cell = e.target.cell; + + if (cell.toggle == true) { + delete cell.toggle; + } else if (e.target.input.multiSelect == true) { + cell.toggle = true; + } else { + for (var r = 0; r < cells.length; r++) { + for (var c = 0; c < cells[r].length; c++) { + if (r == e.target.row && c == e.target.col) { + cells[r][c].toggle = true; + } else { + delete cells[r][c].toggle; + } + } + } + } + + taskPageAutoDrawPaths(page[pageIndex]); + } + function taskPageAutoCheck(p) { + var checkVars = {check:true,a:[],qs:p.inputs.length}; + + for (var q = 0; q < p.inputs.length; q++) { + var input = p.inputs[q]; + if (input.type == 'text') { + var answer = false; + if (input.stringJS !== "") { + var obj = input.pathObj; + var ans = removeTags(clone(input.richText)); + for (var a = 0; a < obj.ans.length; a++) { + if (arraysEqual(ans,removeTags(clone(obj.ans[a])))) { + answer = true; + break; + } + } + } + checkVars.a[q] = {type:'text',check:answer}; + } else if (input.type == 'select') { + var answer = []; + var toggleCount = 0; + var ansCount = 0; + for (var b = 0; b < input.buttons.length; b++) { + var cell = input.buttons[b].cell; + var ans = boolean(cell.ans,false); + if (ans == true) ansCount++; + var toggle = boolean(cell.toggle,false); + if (toggle == true) toggleCount++; + answer[b] = {toggle:toggle,answer:ans}; + } + if (input.checkSelectCount == true) { + if (toggleCount < ansCount || toggleCount == 0) { + checkVars.check = false; + checkVars.fb = "You need to select more answers."; + } else if (toggleCount > ansCount) { + checkVars.check = false; + checkVars.fb = "You have selected too many answers."; + } + } + checkVars.a[q] = {type:'select',check:answer}; + } else if (input.type == 'drag') { + var check = false; + var hit = false; + for (var a = 0; a < p.dragAreas.length; a++) { + var rect = p.dragAreas[a].rect; + if (hitTestRect2(input.canvas,rect[0],rect[1],rect[2],rect[3])) { + hit = true; + if (input.value == p.dragAreas[a].value) check = true; + break; + } + } + if (hit == false) { + checkVars.check = false; + checkVars.fb = "You need to drag all the boxes into position."; + } + checkVars.a[q] = {type:'dragArea',check:check}; + } else if (input.type == 'dragArea') { + var check = false; + var hit = false; + for (var d = 0; d < p.dragCanvases.length; d++) { + var rect = input.rect; + if (hitTestRect2(p.dragCanvases[d].canvas,rect[0],rect[1],rect[2],rect[3])) { + hit = true; + if (input.value == p.dragCanvases[d].value) check = true; + break; + } + } + if (hit == false) { + checkVars.check = false; + checkVars.fb = "You need to drag boxes into the positions."; + } + checkVars.a[q] = {type:'dragArea',check:check}; + } + } + + return checkVars; + } + function taskPageAutoMark(p,r) { + p.ctx2.clear(); + + for (var q = 0; q < p.inputs.length; q++) { + if (p.inputs[q].type !== 'text') continue; + var data = p.inputs[q].data; + if (p.inputs[q].tickStyle == 'small') { + var mult = 0.6; + var l2 = data[100]+data[102]-40*mult-3; + var t2 = data[101]+data[103]-50*mult-3; + if (r.m[q] == 1) { + drawTick(p.ctx2,40*mult,50*mult,'#060',l2,t2,7*mult); + } else { + drawCross(p.ctx2,40*mult,50*mult,'#F00',l2,t2,7*mult); + } + p.inputs[q].markPos = [l2,t2,40*mult,50*mult]; + p.inputs[q].markctx = p.ctx2; + } else { + var l2 = data[100]+data[102]+15; + var t2 = data[101]+0.5*data[103]-25; + if (r.m[q] == 1) { + drawTick(p.ctx2,40,50,'#060',l2,t2,7); + } else { + drawCross(p.ctx2,40,50,'#F00',l2,t2,7); + } + p.inputs[q].markPos = [l2,t2,40,50]; + p.inputs[q].markctx = p.ctx2; + } + } + } + function taskPageAutoReload(p) { + p.ctx2.clear(); + } + + function checkPage() { + if (!un(holder.checkPageIndex) && holder.checkPageIndex > -1) return; + holder.checkPageIndex = pageIndex; + deselectMathsInput(); + if (typeof page[holder.checkPageIndex].check == 'function') { + var c = page[holder.checkPageIndex].check(); + var checkFileName = String(pages[holder.checkPageIndex].filename); + } else if (!un(page[holder.checkPageIndex].taskPageAutoLoad)) { + var c = taskPageAutoCheck(page[holder.checkPageIndex]); + var checkFileName = 'autoCheck'; + } else { + delete holder.checkPageIndex; + return; + } + + if (c.check == false) { + if (!un(c.fb)) { + drawFeedback(c.fb); + } + delete holder.checkPageIndex; + } else { + hideObj(holder.disablePeCanvas); + hideObj(holder.check); + + var xmlHttp = new XMLHttpRequest(); + xmlHttp.open("post", "pages/"+checkFileName+".php", true); + xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + xmlHttp.errorCallback = function() { + Notifier.error('Error connecting to the server. Please try again shortly...'); + hideObj(holder.disablePeCanvas); + delete holder.checkPageIndex; + setTimeout(function() { + if (boolean(page[pageIndex].showHolderCheckButton,true) == true) { + showObj(holder.check); + } else { + hideObj(holder.check); + } + },2000); + }; + xmlHttp.onerror = xmlHttp.errorCallback; + xmlHttp.onreadystatechange = function() { + if (this.readyState !== 4) return; + if (this.status >= 400) { + if (typeof this.errorCallback == 'function') this.errorCallback(); + delete holder.checkPageIndex; + return; + } + + if (!un(document.origin) && document.origin.indexOf('localhost') > -1) console.log(this.responseText); + var response = JSON.parse(this.responseText); + if (!un(document.origin) && document.origin.indexOf('localhost') > -1) console.log('check php response:',response); + pages[holder.checkPageIndex].pageLogKey = response.pageLogKey; + if (un(pages[holder.checkPageIndex].checkCount)) pages[holder.checkPageIndex].checkCount = 0; + pages[holder.checkPageIndex].checkCount++; + p = page[holder.checkPageIndex]; + if (!un(p.mark)) { + p.mark(response); + } else if (!un(p.taskPageAutoLoad)) { + taskPageAutoMark(p,response); + } + if (typeof taskLogData !== 'undefined' && taskLogData !== null) { + taskLogData.percentage = response.percentage; + taskLogData.totalTime = response.timeSpent; + taskLogData.status = response.status; + } + if (response.correct == 1) { + pages[holder.checkPageIndex].completed = true; + hideObj(holder.feedbackButton[holder.checkPageIndex]); + hideObj(holder.feedback[holder.checkPageIndex]); + holder.canvas.ctx.clearRect(650,20,200,50); + removeFeedback(); + if (Number(response.percentage) == 100) { + showTaskCompleteMessage(); + } else { + reportHandler(); + } + if (boolean(page[holder.checkPageIndex].allowInteractionAfterCompletion,false) == false) { + for (var c = 0; c < canvases[holder.checkPageIndex].length; c++) { + canvases[holder.checkPageIndex][c].style.pointerEvents = 'none'; + } + for (var m = 0; m < mathsInput[holder.checkPageIndex].length; m++) { + mathsInput[holder.checkPageIndex][m].cursorCanvas.style.pointerEvents = 'none'; + } + if (!un(draw) && !un(draw.cursorCanvas)) { + pages[holder.checkPageIndex].drawMode = 'none'; + changeDrawMode('none'); + } + } + if (pointsMode == true) { + window.count = 0; + window.int = setCorrectingInterval(function() { + var star = pages[holder.checkPageIndex].stars[window.count]; + star.data[100] = Math.max(star.data[100]-9,613-17.5); + resizeCanvas3(star); + if (star.data[100] <= 613-17.5) { + userPoints++; + drawPoints(); + window.count++; + if (window.count == pages[holder.checkPageIndex].points) { + clearCorrectingInterval(window.int); + setTimeout(function() { + for (s = 0; s < pages[holder.checkPageIndex].maxPoints; s++) { + var star = pages[holder.checkPageIndex].stars[s]; + if (s < pages[holder.checkPageIndex].points) { + star.show = true; + star.data[100] = 600-(pages[holder.checkPageIndex].points-1)*17.5+s*35-17.5; + star.data[101] = 360; + resizeCanvas3(star); + star.style.pointerEvents = 'auto'; + addListener(star,star.click); + } else { + star.show = false; + hideObj(star); + } + } + if (!un(holder.completed[holder.checkPageIndex])) { + hideObj(holder.completed[holder.checkPageIndex]); + } + holder.completed[holder.checkPageIndex] = textCanvas([600-450/2,285,450,130],[''],['#F6F','#6FF','#FF6','#6F6','#66F','#F66'].ran()).canvas; + showObj(holder.completed[holder.checkPageIndex]); + text({ctx:holder.completed[holder.checkPageIndex].ctx,left:0,top:12,width:450,height:100,textArray:['<><>Page Completed!'],align:'center'}); + holder.completed[holder.checkPageIndex].style.pointerEvents = 'auto'; + holder.completed[holder.checkPageIndex].style.zIndex = 10000000; + addListener(holder.completed[holder.checkPageIndex],function() { + hideObj(holder.completed[holder.checkPageIndex]); + for (var s = 0; s < pages[holder.checkPageIndex].stars.length; s++) { + hideObj(pages[holder.checkPageIndex].stars[s]); + } + }); + hideObj(holder.disablePeCanvas); + showPage(); + },800); + } + } + },1000/26); + } else { + if (!un(holder.completed[holder.checkPageIndex])) { + hideObj(holder.completed[holder.checkPageIndex]); + } + holder.completed[holder.checkPageIndex] = textCanvas([600-450/2,285,450,130],[''],['#F6F','#6FF','#FF6','#6F6','#66F','#F66'].ran()).canvas; + var ctx = holder.completed[holder.checkPageIndex].ctx; + text({ctx:ctx,left:0,top:12,width:450,height:100,textArray:['<><>Page Completed!'],align:'center'}); + + ctx.beginPath(); + ctx.lineWidth = 4; + ctx.strokeStyle = '#000'; + drawStar({ctx:ctx,c:[450/2-35,90],r:12}); + ctx.closePath(); + ctx.stroke(); + ctx.beginPath(); + ctx.fillStyle = '#FC3'; + drawStar({ctx:ctx,c:[450/2-35,90],r:12}); + ctx.fill(); + + ctx.beginPath(); + ctx.lineWidth = 4; + ctx.strokeStyle = '#000'; + drawStar({ctx:ctx,c:[450/2,90],r:12}); + ctx.closePath(); + ctx.stroke(); + ctx.beginPath(); + ctx.fillStyle = '#FC3'; + drawStar({ctx:ctx,c:[450/2,90],r:12}); + ctx.fill(); + + ctx.beginPath(); + ctx.lineWidth = 4; + ctx.strokeStyle = '#000'; + drawStar({ctx:ctx,c:[450/2+35,90],r:12}); + ctx.closePath(); + ctx.stroke(); + ctx.beginPath(); + ctx.fillStyle = '#FC3'; + drawStar({ctx:ctx,c:[450/2+35,90],r:12}); + ctx.fill(); + + holder.completed[holder.checkPageIndex].style.pointerEvents = 'auto'; + holder.completed[holder.checkPageIndex].style.zIndex = 100000; + + hideObj(holder.disablePeCanvas); + if (holder.checkPageIndex === pageIndex) { + addListener(holder.completed[holder.checkPageIndex],function() { + hideObj(holder.completed[holder.checkPageIndex]); + }); + showPage(); + if (Number(response.percentage) < 100) { + showObj(holder.completed[holder.checkPageIndex]); + } else { + hideObj(holder.completed[holder.checkPageIndex]); + } + } + } + } else { + hideObj(holder.disablePeCanvas); + if (holder.checkPageIndex === pageIndex) { + setTimeout(function() { + if (boolean(page[pageIndex].showHolderCheckButton,true) == true) { + showObj(holder.check); + } else { + hideObj(holder.check); + } + },4000); + if (pointsMode == true && pages[holder.checkPageIndex].points > 1) { + pages[holder.checkPageIndex].points--; + var star = pages[holder.checkPageIndex].stars[pages[holder.checkPageIndex].points]; + star.color = ['#CFF','#999']; + star.draw(); + } + if (!un(response.fb)) { + drawFeedback(response.fb); + } + } + } + delete holder.checkPageIndex; + } + c.points = pages[holder.checkPageIndex].points; + if (!un(pages[holder.checkPageIndex].pageLogKey)) c.pageLogKey = pages[holder.checkPageIndex].pageLogKey; + c.userKey = userKey; + c.userType = userType; + c.pageId = pages[holder.checkPageIndex].pageid; + c.checkCount = pages[holder.checkPageIndex].checkCount || 0; + c.pageIds = []; + c.browserInfo = getBrowserInfo(); + c.timeSpent = Math.round(TimeMe.getTimeOnPageInSeconds('page')/60); + for (var p = 0; p < pages.length; p++) { + c.pageIds.push(pages[p].pageid); + } + if (typeof taskLogData !== 'undefined' && taskLogData !== null) { + c.tasksLogId = taskLogData.taskNumber; + } + if (!un(document.origin) && document.origin.indexOf('localhost') > -1) console.log(c); + console.log(c, "c="+encodeURIComponent(JSON.stringify(c))); + xmlHttp.send("c="+encodeURIComponent(JSON.stringify(c))); + } + } + function drawPoints() { + holder.canvas.ctx.clearRect(250,20,390,50); + text({ctx:holder.canvas.ctx,left:252,width:386,top:22,height:46,vertAlign:'middle',textArray:['<>'+userName],box:{type:'loose',radius:8,color:'#CFF',borderColor:'#000',borderWidth:4,padding:10}}); + if (pointsMode == true) { + text({ctx:holder.canvas.ctx,left:252,width:386,top:22,height:46,align:'right',vertAlign:'middle',textArray:['<><><>'+userPoints+' '],allowSpaces:true}); + holder.canvas.ctx.beginPath(); + holder.canvas.ctx.lineWidth = 4; + holder.canvas.ctx.strokeStyle = '#000'; + drawStar({ctx:holder.canvas.ctx,c:[613,45],r:12}); + holder.canvas.ctx.closePath(); + holder.canvas.ctx.stroke(); + holder.canvas.ctx.beginPath(); + holder.canvas.ctx.fillStyle = '#00F'; + drawStar({ctx:holder.canvas.ctx,c:[613,45],r:12}); + holder.canvas.ctx.fill(); + } + } + function drawFeedback(txt) { + if (typeof txt == 'string') txt = [txt]; + txt.unshift('<>'); + if (un(holder.feedback[pageIndex])) { + holder.feedback[pageIndex] = newctx({pe:true,z:100000}).canvas; + addListenerEnd(holder.feedback[pageIndex],toggleFeedback); + } + holder.feedback[pageIndex].fb = true; + var fb = holder.feedback[pageIndex]; + showObj(holder.feedbackButton[pageIndex]); + var size = text({ctx:fb.ctx,textArray:txt,align:'center',measureOnly:true,left:800,top:90+5,width:400,height:610,box:{type:'tight',color:'#FC9',borderColor:'#000',borderWidth:4,radius:8,padding:15}}); + fb.data[100] = 1120 - size.tightRect[2]; + fb.data[101] = 90; + fb.data[102] = size.tightRect[2]; + fb.data[103] = size.tightRect[3]; + fb.width = size.tightRect[2]; + fb.height = size.tightRect[3]; + resizeCanvas3(fb); + textBox(fb.ctx,[0,0,size.tightRect[2],size.tightRect[3]],txt,'#FC9'); + showObj(holder.feedback[pageIndex]); + }; + function toggleFeedback() { + if (un(holder.feedback[pageIndex])) return; + if (holder.feedback[pageIndex].parentNode == container) { + hideObj(holder.feedback[pageIndex]); + } else { + showObj(holder.feedback[pageIndex]); + } + }; + function removeFeedback() { + if (!un(holder.feedback[pageIndex])) { + holder.feedback[pageIndex].fb = false; + hideObj(holder.feedbackButton[pageIndex]); + hideObj(holder.feedback[pageIndex]); + } + } + + var taskCompleteMessage; + function loadTaskCompleteMessage() { + //create purple background + taskCompleteMessage = newctx({rect:[20,94,1150,576],vis:false,z:1000000,pE:true,page:false}).canvas; + taskCompleteMessage.style.backgroundColor = "#C6F"; + taskCompleteMessage.style.borderRadius = "5px"; + taskCompleteMessage.style.border = "4px solid black" + taskCompleteMessage.style.cursor = "auto"; + //addListener(taskCompleteMessage,dismissTaskCompleteMessage); + + taskCompleteMessage.starYellow = new Image; + taskCompleteMessage.starYellowPointy = new Image; + taskCompleteMessage.starWhite6points = new Image; + + taskCompleteMessage.starYellow.src = "../Images/starYellow.png"; + taskCompleteMessage.starYellowPointy.src = "../Images/starYellowPointy.png"; + taskCompleteMessage.starWhite6points.src = "../Images/starWhite6points.png"; + + taskCompleteMessage.stars = []; + + //create dismiss button + var dismiss = newctx({rect:[1050, 120, 110, 26],vis:false,z:10000001,pE:true,page:false}).canvas; + dismiss.ctx.font = "30px Arial"; + dismiss.ctx.fillStyle = "#FFF"; + dismiss.ctx.textAlign = "center"; + dismiss.ctx.textBaseline = "middle"; + dismiss.ctx.fillText("Dismiss", 55, 13); + //addListener(dismiss, dismissTaskCompleteMessage) + taskCompleteMessage.stars.push(dismiss); + + for (var i = 0; i < 9; i++) { + var l = [100,750,700,300,950,900,820,1000,500][i]-6; + var t = [150,120,400,120,300,120,250,500,100][i]; + var w = [126,126,252,189,189,126,126,126,252][i]; + var star = newctx({rect:[l,t,w,w],vis:false,z:100000002+i,page:false}).canvas; + star.style.cursor = "auto"; + taskCompleteMessage.stars.push(star); + } + resize(); + } + function showTaskCompleteMessage() { + if (un(taskCompleteMessage)) loadTaskCompleteMessage(); + hideObj(holder.completed[pageIndex]); + showObj(taskCompleteMessage); + for (i = 0; i < taskCompleteMessage.stars.length; i++) { + showObj(taskCompleteMessage.stars[i]); + taskCompleteMessage.stars[i].style.zIndex = 1000000; + } + + taskCompleteMessage.ctx.fillStyle = "#FFF"; + taskCompleteMessage.ctx.textAlign = "center"; + taskCompleteMessage.ctx.textBaseline = "middle"; + + if (userType == 'Pupil') { + taskCompleteMessage.ctx.font = "110px Hobo"; + taskCompleteMessage.ctx.fillText("Task Complete!", 350, 275); + taskCompleteMessage.ctx.font = "55px Hobo"; + taskCompleteMessage.ctx.fillText("Well done, " + userName, 350, 420); + taskCompleteMessage.ctx.font = "55px Hobo"; + taskCompleteMessage.ctx.fillText("Your result has been logged", 350, 490); + } else { + taskCompleteMessage.ctx.font = "110px Hobo"; + taskCompleteMessage.ctx.fillText("Task Complete!", 350, 375); + } + + taskCompleteMessage.interval = setInterval(function(){taskCompleteMessage.rotate()},25); + + taskCompleteMessage.rotate = function() { + for (i = 1; i < taskCompleteMessage.stars.length; i++) { + taskCompleteMessage.stars[i].ctx.clear(); + taskCompleteMessage.stars[i].ctx.translate(taskCompleteMessage.stars[i].data[2] / 2, taskCompleteMessage.stars[i].data[3] / 2); + taskCompleteMessage.stars[i].ctx.rotate(Math.PI / 180); + taskCompleteMessage.stars[i].ctx.translate(taskCompleteMessage.stars[i].data[2] / -2, taskCompleteMessage.stars[i].data[3] / -2); + } + taskCompleteMessage.stars[1].ctx.drawImage(taskCompleteMessage.starYellow,20,20,86,86); + taskCompleteMessage.stars[2].ctx.drawImage(taskCompleteMessage.starYellow,20,20,86,86); + taskCompleteMessage.stars[3].ctx.drawImage(taskCompleteMessage.starYellow,60,60,172,172); + taskCompleteMessage.stars[4].ctx.drawImage(taskCompleteMessage.starWhite6points,24,33,141,123); + taskCompleteMessage.stars[5].ctx.drawImage(taskCompleteMessage.starWhite6points,24,33,141,123); + taskCompleteMessage.stars[6].ctx.drawImage(taskCompleteMessage.starYellowPointy,20,21,86,84); + taskCompleteMessage.stars[7].ctx.drawImage(taskCompleteMessage.starYellowPointy,20,21,86,84); + taskCompleteMessage.stars[8].ctx.drawImage(taskCompleteMessage.starYellowPointy,20,21,86,84); + taskCompleteMessage.stars[9].ctx.drawImage(taskCompleteMessage.starYellowPointy,40,42,152,148); + } + addListener(window,dismissTaskCompleteMessage); + } + function dismissTaskCompleteMessage() { + hideObj(taskCompleteMessage); + for (i = 0; i < taskCompleteMessage.stars.length; i++) { + hideObj(taskCompleteMessage.stars[i]); + } + if (!un(taskCompleteMessage.interval)) { + clearInterval(taskCompleteMessage.interval); + } + removeListener(window,dismissTaskCompleteMessage); + } + + function createHorizPos(num,width,left,right) { + if (typeof left == 'undefined') left = width; + if (typeof right == 'undefined') right = width; + var space = (1200 - num * width - left - right) / (num-1); + var arr = []; + for (var i = 0; i < num; i++) { + arr[i] = left+i*(width+space); + } + return arr; + } + function createVertPos(num,height,top,bottom) { + if (typeof top == 'undefined') top = height; + if (typeof bottom == 'undefined') bottom = height; + var space = (620 - num * height - top - bottom) / (num-1); + var arr = []; + for (var i = 0; i < num; i++) { + arr[i] = 80+top+i*(height+space); + } + return arr; + } + + function swipedetect(el, callback){ + var touchsurface = el, + swipedir, + startX, + startY, + distX, + distY, + dist, + threshold = 150, //required min distance traveled to be considered swipe + restraint = 100, // maximum distance allowed at the same time in perpendicular direction + allowedTime = 400, // maximum time allowed to travel that distance + elapsedTime, + startTime, + handleswipe = callback || function(swipedir){} + + touchsurface.addEventListener('touchstart', function(e){ + var touchobj = e.changedTouches[0] + swipedir = 'none' + dist = 0 + startX = touchobj.pageX + startY = touchobj.pageY + startTime = new Date().getTime() // record time when finger first makes contact with surface + e.preventDefault() + }, false) + + touchsurface.addEventListener('touchmove', function(e){ + e.preventDefault() // prevent scrolling when inside DIV + }, false) + + touchsurface.addEventListener('touchend', function(e){ + var touchobj = e.changedTouches[0] + distX = touchobj.pageX - startX // get horizontal dist traveled by finger while in contact with surface + distY = touchobj.pageY - startY // get vertical dist traveled by finger while in contact with surface + elapsedTime = new Date().getTime() - startTime // get time elapsed + if (elapsedTime <= allowedTime){ // first condition for awipe met + if (Math.abs(distX) >= threshold && Math.abs(distY) <= restraint){ // 2nd condition for horizontal swipe met + swipedir = (distX < 0)? 'left' : 'right' // if dist traveled is negative, it indicates left swipe + } + else if (Math.abs(distY) >= threshold && Math.abs(distX) <= restraint){ // 2nd condition for vertical swipe met + swipedir = (distY < 0)? 'up' : 'down' // if dist traveled is negative, it indicates up swipe + } + } + if (e.target.drag !== true) handleswipe(swipedir) + e.preventDefault() + }, false) + } + swipedetect (canvas, function(swipedir){ + // swipedir contains either "none", "left", "right", "top", or "down" + + if (swipedir == 'left') { + nextPage(); + } else if (swipedir == 'right') { + prevPage(); + } + }) +} +function getBrowserInfo() { + /** + * JavaScript Client Detection + * (C) viazenetti GmbH (Christian Ludwig) + */ + + var unknown = '-'; + + // screen + var screenSize = ''; + if (screen.width) { + width = (screen.width) ? screen.width : ''; + height = (screen.height) ? screen.height : ''; + screenSize += '' + width + " x " + height; + } + + // browser + var nVer = navigator.appVersion; + var nAgt = navigator.userAgent; + var browser = navigator.appName; + var version = '' + parseFloat(navigator.appVersion); + var majorVersion = parseInt(navigator.appVersion, 10); + var nameOffset, verOffset, ix; + + // Opera + if ((verOffset = nAgt.indexOf('Opera')) != -1) { + browser = 'Opera'; + version = nAgt.substring(verOffset + 6); + if ((verOffset = nAgt.indexOf('Version')) != -1) { + version = nAgt.substring(verOffset + 8); + } + } + // Opera Next + if ((verOffset = nAgt.indexOf('OPR')) != -1) { + browser = 'Opera'; + version = nAgt.substring(verOffset + 4); + } + // Edge + else if ((verOffset = nAgt.indexOf('Edge')) != -1) { + browser = 'Microsoft Edge'; + version = nAgt.substring(verOffset + 5); + } + // MSIE + else if ((verOffset = nAgt.indexOf('MSIE')) != -1) { + browser = 'Microsoft Internet Explorer'; + version = nAgt.substring(verOffset + 5); + } + // Chrome + else if ((verOffset = nAgt.indexOf('Chrome')) != -1) { + browser = 'Chrome'; + version = nAgt.substring(verOffset + 7); + } + // Safari + else if ((verOffset = nAgt.indexOf('Safari')) != -1) { + browser = 'Safari'; + version = nAgt.substring(verOffset + 7); + if ((verOffset = nAgt.indexOf('Version')) != -1) { + version = nAgt.substring(verOffset + 8); + } + } + // Firefox + else if ((verOffset = nAgt.indexOf('Firefox')) != -1) { + browser = 'Firefox'; + version = nAgt.substring(verOffset + 8); + } + // MSIE 11+ + else if (nAgt.indexOf('Trident/') != -1) { + browser = 'Microsoft Internet Explorer'; + version = nAgt.substring(nAgt.indexOf('rv:') + 3); + } + // Other browsers + else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) { + browser = nAgt.substring(nameOffset, verOffset); + version = nAgt.substring(verOffset + 1); + if (browser.toLowerCase() == browser.toUpperCase()) { + browser = navigator.appName; + } + } + // trim the version string + if ((ix = version.indexOf(';')) != -1) version = version.substring(0, ix); + if ((ix = version.indexOf(' ')) != -1) version = version.substring(0, ix); + if ((ix = version.indexOf(')')) != -1) version = version.substring(0, ix); + + majorVersion = parseInt('' + version, 10); + if (isNaN(majorVersion)) { + version = '' + parseFloat(navigator.appVersion); + majorVersion = parseInt(navigator.appVersion, 10); + } + + // mobile version + var mobile = /Mobile|mini|Fennec|Android|iP(ad|od|hone)/.test(nVer); + + // cookie + var cookieEnabled = (navigator.cookieEnabled) ? true : false; + + if (typeof navigator.cookieEnabled == 'undefined' && !cookieEnabled) { + document.cookie = 'testcookie'; + cookieEnabled = (document.cookie.indexOf('testcookie') != -1) ? true : false; + } + + // system + var os = unknown; + var clientStrings = [ + {s:'Windows 10', r:/(Windows 10.0|Windows NT 10.0)/}, + {s:'Windows 8.1', r:/(Windows 8.1|Windows NT 6.3)/}, + {s:'Windows 8', r:/(Windows 8|Windows NT 6.2)/}, + {s:'Windows 7', r:/(Windows 7|Windows NT 6.1)/}, + {s:'Windows Vista', r:/Windows NT 6.0/}, + {s:'Windows Server 2003', r:/Windows NT 5.2/}, + {s:'Windows XP', r:/(Windows NT 5.1|Windows XP)/}, + {s:'Windows 2000', r:/(Windows NT 5.0|Windows 2000)/}, + {s:'Windows ME', r:/(Win 9x 4.90|Windows ME)/}, + {s:'Windows 98', r:/(Windows 98|Win98)/}, + {s:'Windows 95', r:/(Windows 95|Win95|Windows_95)/}, + {s:'Windows NT 4.0', r:/(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/}, + {s:'Windows CE', r:/Windows CE/}, + {s:'Windows 3.11', r:/Win16/}, + {s:'Android', r:/Android/}, + {s:'Open BSD', r:/OpenBSD/}, + {s:'Sun OS', r:/SunOS/}, + {s:'Linux', r:/(Linux|X11)/}, + {s:'iOS', r:/(iPhone|iPad|iPod)/}, + {s:'Mac OS X', r:/Mac OS X/}, + {s:'Mac OS', r:/(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/}, + {s:'QNX', r:/QNX/}, + {s:'UNIX', r:/UNIX/}, + {s:'BeOS', r:/BeOS/}, + {s:'OS/2', r:/OS\/2/}, + {s:'Search Bot', r:/(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/} + ]; + for (var id in clientStrings) { + var cs = clientStrings[id]; + if (cs.r.test(nAgt)) { + os = cs.s; + break; + } + } + + var osVersion = unknown; + + if (/Windows/.test(os)) { + var a = /Windows (.*)/.exec(os) + if (a instanceof Array) { + osVersion = a[1]; + } + os = 'Windows'; + } + + switch (os) { + case 'Mac OS X': + var a = /Mac OS X (10[\.\_\d]+)/.exec(nAgt); + if (a instanceof Array) { + osVersion = a[1]; + } + break; + + case 'Android': + var a = /Android ([\.\_\d]+)/.exec(nAgt); + if (a instanceof Array) { + osVersion = a[1]; + } + break; + + case 'iOS': + var a = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer); + if (a instanceof Array) { + osVersion = a[1] + '.' + a[2] + '.' + (a[3] | 0); + } + break; + } + + window.browserinfo = { + screen: screenSize, + browser: browser, + browserVersion: version, + browserMajorVersion: majorVersion, + mobile: mobile, + os: os, + osVersion: osVersion, + cookies: cookieEnabled, + }; + return os+" "+osVersion+" "+browser+" "+version+" "+screenSize+" mobile:"+mobile+" cookies:"+cookieEnabled; +} + +function stopDefaultBackspaceBehaviour(e) { + if (e.keyCode == 8 && e.target.nodeName !== 'TEXTAREA' && e.target.nodeName !== 'INPUT' && e.target.nodeName !== 'TD') { + e.preventDefault ? e.preventDefault() : e.returnValue = false; + } +} +function xWindowToCanvas(xCoord) { + return mainCanvasWidth * ((xCoord - canvasMetrics.left) / (canvasDisplayWidth)); +} +function xCanvasToWindow(xCoord) { + return canvasMetrics.left + (xCoord / mainCanvasWidth) * canvasDisplayWidth; +} +function yWindowToCanvas(yCoord) { + return mainCanvasHeight * ((yCoord - canvasMetrics.top) / canvasDisplayHeight); +} +function yCanvasToWindow(yCoord) { + return canvasMetrics.top + (yCoord / mainCanvasHeight) * canvasDisplayHeight; +} + +function addListener(toButton, yourFunction) { + toButton.addEventListener("touchend", yourFunction, false) + toButton.addEventListener("mouseup", yourFunction, false) +} +function removeListener(toButton, yourFunction) { + toButton.removeEventListener("touchend", yourFunction, false) + toButton.removeEventListener("mouseup", yourFunction, false) +} +function addListenerStart(toButton, yourFunction) { + toButton.addEventListener("touchstart", yourFunction, false) + toButton.addEventListener("mousedown", yourFunction, false); +} +function removeListenerStart(toButton, yourFunction) { + toButton.removeEventListener("touchstart", yourFunction, false) + toButton.removeEventListener("mousedown", yourFunction, false); +} +function addListenerMove(toButton, yourFunction) { + toButton.addEventListener("touchmove", yourFunction, false) + toButton.addEventListener("mousemove", yourFunction, false) +} +function removeListenerMove(toButton, yourFunction) { + toButton.removeEventListener("touchmove", yourFunction, false) + toButton.removeEventListener("mousemove", yourFunction, false) +} +function addListenerEnd(toButton, yourFunction) { + toButton.addEventListener("touchend", yourFunction, false) + toButton.addEventListener("mouseup", yourFunction, false); +} +function removeListenerEnd(toButton, yourFunction) { + toButton.removeEventListener("touchend", yourFunction, false) + toButton.removeEventListener("mouseup", yourFunction, false); +} + +function resize() { + var totalWidth = mainCanvasWidth + mainCanvasMargins[0] + mainCanvasMargins[2]; + var totalHeight = mainCanvasHeight + mainCanvasMargins[1] + mainCanvasMargins[3]; + var aspectRatio = totalWidth / totalHeight; + if (window.innerWidth / window.innerHeight > aspectRatio) { + var totalDisplayWidth = window.innerHeight * aspectRatio; + var totalDisplayHeight = window.innerHeight; + } else { + var totalDisplayWidth = window.innerWidth; + var totalDisplayHeight = window.innerWidth / aspectRatio; + } + canvasDisplayWidth = totalDisplayWidth * (mainCanvasWidth / totalWidth); + canvasDisplayHeight = totalDisplayHeight * (mainCanvasHeight / totalHeight); + canvasDisplayLeft = (window.innerWidth - totalDisplayWidth) / 2 + mainCanvasMargins[0] * (totalDisplayWidth/totalWidth); + canvasDisplayTop = (window.innerHeight - totalDisplayHeight) / 2 + mainCanvasMargins[1] * (totalDisplayHeight/totalHeight); + + //canvas.style.left = canvasDisplayLeft + 'px'; + //canvas.style.top = canvasDisplayTop + 'px'; + //canvas.style.top = '0px'; + canvas.style.width = canvasDisplayWidth + 'px'; + canvas.style.height = canvasDisplayHeight + 'px'; + canvasDisplayRect = canvas.getBoundingClientRect(); + + if (typeof inactiveBox !== 'undefined') resizeCanvas(inactiveBox, 400, 290, 400, 120); + if (boolean(isTask,true) == true) { + resizeCanvas3(holder.prev); + resizeCanvas3(holder.next); + resizeCanvas3(holder.reload); + resizeCanvas3(holder.canvas); + resizeCanvas3(holder.check); + resizeCanvas3(holder.home); + resizeCanvas3(holder.home2); + resizeCanvas3(holder.loading); + resizeCanvas3(holder.summary); + resizeCanvas3(holder.summary2); + resizeCanvas3(holder.disablePeCanvas); + for (var i = 0; i < holder.feedback.length; i++) { + if (!un(holder.feedback[i])) resizeCanvas3(holder.feedback[i]); + } + for (var i = 0; i < holder.feedbackButton.length; i++) { + if (!un(holder.feedbackButton[i])) resizeCanvas3(holder.feedbackButton[i]); + } + for (var i = 0; i < holder.completed.length; i++) { + if (!un(holder.completed[i])) resizeCanvas3(holder.completed[i]); + } + if (pageIndex < pages.length) { + if (!un(pages[pageIndex].stars)) { + for (var s = 0; s < pages[pageIndex].stars.length; s++) { + resizeCanvas3(pages[pageIndex].stars[s]); + } + } + } + } + if (!un(canvases[pageIndex])) { + for (var c = 0; c < canvases[pageIndex].length; c++) { + resizeCanvas3(canvases[pageIndex][c]); + } + } + if (!un(taskCompleteMessage)) { + resizeCanvas3(taskCompleteMessage); + for (var c = 0; c < taskCompleteMessage.stars.length; c++) { + resizeCanvas3(taskCompleteMessage.stars[c]); + } + } + if (!un(mathsInput[pageIndex])) { + for (var m = 0; m < mathsInput[pageIndex].length; m++) { + resizeCanvas3(mathsInput[pageIndex][m].canvas); + resizeCanvas3(mathsInput[pageIndex][m].cursorCanvas); + } + } + if (!un(slider[pageIndex])) { + for (var s = 0; s < slider[pageIndex].length; s++) { + resizeCanvas3(slider[pageIndex][s].backCanvas); + resizeCanvas3(slider[pageIndex][s].sliderCanvas); + resizeCanvas3(slider[pageIndex][s].labelCanvas); + } + } + for (var j = 0; j < keyboard.length; j++) { + if (typeof keyboard[j] !== 'undefined') { + resizeCanvas3(keyboardButton1); + resizeCanvas3(keyboardButton2); + resizeCanvas(keyboard[j], keyboardData[j][100], keyboardData[j][101], keyboardData[j][2], keyboardData[j][3]); + for (var i = 0; i -1) { + var pos = closestPos; + if (typeof pos[4] !== 'undefined' && pos[4] !== null) { + pos[4].data[100] = pos[4].data[0]; + pos[4].data[101] = pos[4].data[1]; + resizeCanvas3(pos[4]); + } + dragObj.data[100] = pos[0]; + dragObj.data[101] = pos[1]; + resizeCanvas3(dragObj); + pos[4] = dragObj; + } + } + if (typeof dragObj.ondragstop == 'function') dragObj.ondragstop(); + if (typeof dragObj.dragStop == 'function') dragObj.dragStop(); + dragObj = null; +} + +function showObj(obj, hideAfter) { + if (un(obj)) return; + if (!isElement(obj)) { + if (obj instanceof Array) { + for (var i = 0; i < obj.length; i++) { + showObj(obj[i]); + } + } else if (typeof obj == 'object') { + for (var i in obj) { + showObj(obj[i]); + } + } + return; + } + if (!un(draw) && !un(draw.div) && draw.drawCanvas.indexOf(obj) > -1) { + draw.div.children[0].appendChild(obj); + } else { + container.appendChild(obj); + resizeCanvas3(obj); + } + obj.vis = true; + if (typeof hideAfter == 'number') { + setTimeout(function () { + hideObj(obj) + }, hideAfter); + } +} +function hideObj(obj) { + if (un(obj)) return; + if (!isElement(obj)) { + if (obj instanceof Array) { + for (var i = 0; i < obj.length; i++) { + hideObj(obj[i]); + } + } else if (typeof obj == 'object') { + for (var i in obj) { + hideObj(obj[i]); + } + } + return; + } + obj.vis = false; + if (obj.parentNode == container) { + container.removeChild(obj) + } else if (!un(draw) && !un(draw.div) && draw.drawCanvas.indexOf(obj) > -1 && !un(obj.parentNode)) { + obj.parentNode.removeChild(obj); + } +} diff --git a/tools/i2/_keyboard.js b/tools/i2/_keyboard.js new file mode 100644 index 0000000..5de2e55 --- /dev/null +++ b/tools/i2/_keyboard.js @@ -0,0 +1,709 @@ + +function mathsInputFrac(e) {mathsInputElement("['frac', [''], ['']]");} +function mathsInputPow(e) {mathsInputElement("['power', false, ['']]");} +function mathsInputSubs(e) {mathsInputElement("['subs', [''], ['']]");} +function mathsInputRoot(e) {mathsInputElement("['root', [''], ['']]");} +function mathsInputSqrt(e) {mathsInputElement("['sqrt', ['']]");} +function mathsInputSin(e) {mathsInputElement("['sin', ['']]");} +function mathsInputCos(e) {mathsInputElement("['cos', ['']]");} +function mathsInputTan(e) {mathsInputElement("['tan', ['']]");} +function mathsInputInvSin(e) {mathsInputElement("['sin-1', ['']]");} +function mathsInputInvCos(e) {mathsInputElement("['cos-1', ['']]");} +function mathsInputInvTan(e) {mathsInputElement("['tan-1', ['']]");} +function mathsInputLn(e) {mathsInputElement("['ln', ['']]");} +function mathsInputLog(e) {mathsInputElement("['log', ['']]");} +function mathsInputLogBase(e) {mathsInputElement("['logBase', [''], ['']]");} +function mathsInputAbs(e) {mathsInputElement("['abs', ['']]");} +function mathsInputExp(e) {mathsInputElement("['exp', ['']]");} +function mathsInputSigma1(e) {mathsInputElement("['sigma1', ['']]");} +function mathsInputSigma2(e) {mathsInputElement("['sigma2', [''], [''], ['']]");} +function mathsInputInt1(e) {mathsInputElement("['int1', ['']]");} +function mathsInputInt2(e) {mathsInputElement("['int2', [''], [''], ['']]");} +function mathsInputVectorArrow(e) {mathsInputElement("['vectorArrow', ['']]");} +function mathsInputBar(e) {mathsInputElement("['bar', ['']]");} +function mathsInputHat(e) {mathsInputElement("['hat', ['']]");} +function mathsInputRecurring(e) {mathsInputElement("['recurring', ['']]");} +function mathsInputColVector2d(e) {mathsInputElement("['colVector2d', [''], ['']]");} +function mathsInputColVector3d(e) {mathsInputElement("['colVector3d', [''], [''], ['']]");} +function mathsInputMixedNum(e) {mathsInputElement("['mixedNum', [''], [''], ['']]");} +function mathsInputLim(e) {mathsInputElement("['lim', [''], ['']]");} + +var keyboardButton1 = []; +var keyboardButton2 = []; +var keyboardChars = [ + {display:'0x00D7',name:'times'}, + {display:'0x00F7',name:'divide'}, + {display:'0x00B0',name:'degrees'}, + {display:'0x221E',name:'infinity'}, + {display:'0x2261',name:'equivalence'}, + {display:'0x2248',name:'approximately'}, + {display:'0x2264',name:'lessEqual'}, + {display:'0x2265',name:'moreEqual'}, + {display:'0x03C0',name:'pi'}, + {display:'0x2260',name:'notEqual'}, + {display:'0x03B8',name:'theta'}, + {display:'0x00B1',name:'plusMinus'}, + {display:'0x2213',name:'minusPlus'}, + {display:'0x2B1A',name:'dottedSquare'}, + {display:'0x2115',name:'naturals'}, + {display:'0x2124',name:'integers'}, + {display:'0x211A',name:'rationals'}, + {display:'0x211D',name:'reals'}, + {display:'0x2208',name:'elementOf'}, + {display:'0x2209',name:'notElementOf'}, + {display:'0x221D',name:'proportionalTo'}, + {display:'0x2220',name:'angle'}, + {display:'0x2229',name:'intersection'}, + {display:'0x222A',name:'union'}, + {display:'0x2234',name:'therefore'}, + {display:'0x2190',name:'leftArrow'}, + {display:'0x2191',name:'upArrow'}, + {display:'0x2192',name:'rightArrow'}, + {display:'0x2193',name:'downArrow'} +]; +var keyboardElements = [ + {display:['<>x',['power',false,['']]],name:'power',func:mathsInputPow}, + {display:['<>x',['power',false,['']]],name:'pow',func:mathsInputPow}, + {display:['x',['subs',false,['']]],name:'subs',func:mathsInputSubs}, + {display:[['frac',[''],['']]],name:'frac',func:mathsInputFrac}, + {display:[['sqrt',['']]],name:'sqrt',func:mathsInputSqrt}, + {display:[['root',[''],['']]],name:'root',func:mathsInputRoot}, + {display:[['sin',['']]],name:'sin',func:mathsInputSin}, + {display:[['sin-1',['']]],name:'sin-1',func:mathsInputInvSin}, + {display:[['cos',['']]],name:'cos',func:mathsInputCos}, + {display:[['cos-1',['']]],name:'cos-1',func:mathsInputInvCos}, + {display:[['tan',['']]],name:'tan',func:mathsInputTan}, + {display:[['tan-1',['']]],name:'tan-1',func:mathsInputInvTan}, + {display:[['ln',['']]],name:'ln',func:mathsInputLn}, + {display:[['log',['']]],name:'log',func:mathsInputLog}, + {display:[['logBase',[''],['']]],name:'logBase',func:mathsInputLogBase}, + {display:[['abs',['']]],name:'abs',func:mathsInputAbs}, + {display:[['exp',['']]],name:'exp',func:mathsInputExp}, + {display:[['sigma1',['']]],name:'sigma1',func:mathsInputSigma1}, + {display:[['sigma2',[''],[''],['']]],name:'sigma2',func:mathsInputSigma2}, + {display:[['int1',['']]],name:'int1',func:mathsInputInt1}, + {display:[['int2',[''],[''],['']]],name:'int2',func:mathsInputInt2}, + {display:[['vectorArrow',['']]],name:'vectorArrow',func:mathsInputVectorArrow}, + {display:[['bar',['']]],name:'bar',func:mathsInputBar}, + {display:[['hat',['']]],name:'hat',func:mathsInputHat}, + {display:[['recurring',['']]],name:'recurring',func:mathsInputRecurring}, + {display:[['colVector2d',[''],['']]],name:'colVector2d',func:mathsInputColVector2d}, + {display:[['colVector3d',[''],[''],['']]],name:'colVector3d',func:mathsInputColVector3d}, + {display:[['mixedNum',[''],[''],['']]],name:'mixedNum',func:mathsInputMixedNum}, + {display:[['lim',[''],['']]],name:'lim',func:mathsInputLim} +]; + +function addKeyboard(object) { + if (object.type == 'num') { + object.keyArray = [ + ['1','2','3'], + ['4','5','6'], + ['7','8','9'], + ['-','0','delete'] + ]; + } + object.keySize = 50; + object.fontSize = 36; + createKeyboard(object); +} + +function createKeyboard(object) { + var keySize = object.keySize || 60; + var fontSize = object.fontSize || 40; + var keyPadding = object.keyPadding || 5; + var align = object.align || 'left'; + var keyArray = object.keyArray; + var backColor = object.backColor || '#F0F'; + var algText = object.algText || false; + var rows = object.keyArray.length; + var keyButtonLeft = object.keyButtonLeft || 1130; + var keyButtonTop = object.keyButtonTop || 630; + var keyButtonSize = object.keyButtonSize || 50; + var dragArea = object.dragArea || [15,100,15,15]; // this is xMin, yMin, xMax, yMax + //var textInputMode = boolean(object.textInputMode,false); + + var cols; + for (var i = 0; i < keyArray.length; i++) { + if (!cols) {cols = keyArray[i].length}; + cols = Math.max(cols, keyArray[i].length); + } + + // width and height of keyboard + var width = cols * (keySize + keyPadding) + keyPadding * 5; + var height = rows * (keySize + keyPadding) + 40 + keyPadding * 4; + + var left = object.left || 1180 - width; + var top = object.top || 620 - height; + + // create keyboard canvas + var canvasInstance = document.createElement('canvas'); + canvasInstance.width = width; + canvasInstance.height = height; + canvasInstance.setAttribute('position', 'absolute'); + canvasInstance.setAttribute('cursor', 'auto'); + canvasInstance.setAttribute('draggable', 'false'); + canvasInstance.setAttribute('class', 'buttonClass'); + canvasInstance.style.zIndex = 800000; + canvasInstance.style.cursor = openhand; + //canvasInstance.textInputMode = textInputMode; + + canvasInstance.dragArea = dragArea; + var contextInstance = canvasInstance.getContext('2d'); + roundedRect(contextInstance, 2, 2, width - 4, height - 4, 15, 4, '#000', backColor) + contextInstance.strokeStyle = '#333'; + contextInstance.lineWidth = 4; + contextInstance.beginPath(); + contextInstance.moveTo(15, 25); + contextInstance.lineTo(width - 55, 25); + contextInstance.closePath(); + contextInstance.stroke(); + contextInstance.font = '20px Arial'; + contextInstance.textBaseline = 'middle'; + contextInstance.textAlign = 'center'; + contextInstance.fillStyle = backColor; + contextInstance.fillRect((0.5 * width - 20) - 0.5 * contextInstance.measureText('Keyboard').width - 10, 20, contextInstance.measureText('Keyboard').width + 20, 10); + contextInstance.fillStyle = '#333'; + contextInstance.fillText('Keyboard', 0.5 * width - 20, 25); + + canvasInstance.data = [left, top, width, height]; + for (var i = 0; i < 4; i++) { + canvasInstance.data[100+i] = canvasInstance.data[i]; + } + canvasInstance.data[116] = false; + keyboard[pageIndex] = canvasInstance; + keyboardData[pageIndex] = canvasInstance.data; + + // make draggable + addListenerStart(keyboard[pageIndex],dragKeyboardStart) + keyboardVis[pageIndex] = false; + + // create keys + var rowNum; + var colNum; + var keyCount = 0; + key1[pageIndex] = []; + key1Data[pageIndex] = []; + + for (var rowNum = 0; rowNum < keyArray.length; rowNum++) { + var keyLeft; + if (align == 'left') { + keyLeft = left + keyPadding * 3; + } else if (align == 'right') { + keyLeft = left + width - keyArray[rowNum].length * (keySize + keyPadding) - keyPadding * 2; + } else { + keyLeft = left + 0.5 * (width - keyArray[rowNum].length * (keySize + keyPadding)); + } + var keyTop = top + keyPadding + 40 + keyPadding + rowNum * (keySize + keyPadding); + + for (var colNum = 0; colNum < keyArray[rowNum].length; colNum++) { + var canvasInstance = document.createElement('canvas'); + canvasInstance.width = keySize; + canvasInstance.height = keySize; + canvasInstance.setAttribute('position', 'absolute'); + canvasInstance.setAttribute('cursor', 'auto'); + canvasInstance.setAttribute('draggable', 'false'); + canvasInstance.setAttribute('class', 'buttonClass'); + canvasInstance.style.zIndex = 800000; + canvasInstance.style.opacity = 0.7; + canvasInstance.style.pointerEvents = 'none'; + + var contextInstance = canvasInstance.getContext('2d'); + + canvasInstance.name = keyArray[rowNum][colNum]; + canvasInstance.value = clone(keyArray[rowNum][colNum]); + canvasInstance.func = function(){}; + canvasInstance.relFontSize = 1; + canvasInstance.keySize = keySize; + canvasInstance.element = false; + canvasInstance.static = false; + + for (var c = 0; c < keyboardChars.length; c++) { + if (canvasInstance.value == keyboardChars[c].name) { + canvasInstance.value = String.fromCharCode(keyboardChars[c].display); + break; + } + } + for (var e = 0; e < keyboardElements.length; e++) { + if (canvasInstance.value == keyboardElements[e].name) { + canvasInstance.relFontSize = 0.7; + if (['frac','mixedNum'].indexOf(canvasInstance.value) > -1) { + canvasInstance.relFontSize = 0.6; + } + if (['sqrt','root'].indexOf(canvasInstance.value) > -1) { + canvasInstance.relFontSize = 0.6; + } + canvasInstance.value = keyboardElements[e].display; + canvasInstance.func = keyboardElements[e].func; + canvasInstance.element = true; + break; + } + } + + if (canvasInstance.element == true || ['leftArrow','rightArrow','del','delete'].indexOf(canvasInstance.name) > -1) { + canvasInstance.color = '#FF0'; // color for operators + canvasInstance.colorPressed = '#990'; + canvasInstance.keyType = 'operator'; + } else if (['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'].indexOf(canvasInstance.name) > -1) { + canvasInstance.color = '#AFF'; // color for letters + canvasInstance.colorPressed = '#6CC'; + canvasInstance.keyType = 'letter'; + } else if(['0','1','2','3','4','5','6','7','8','9',0,1,2,3,4,5,6,7,8,9].indexOf(canvasInstance.name) > -1) { + canvasInstance.color = '#AFA'; // color for numbers + canvasInstance.colorPressed = '#9C9'; + canvasInstance.keyType = 'number'; + } else { + canvasInstance.color = '#FB8'; // color for misc + canvasInstance.colorPressed = '#C95'; + canvasInstance.keyType = 'misc'; + } + + canvasInstance.ctx = contextInstance; + canvasInstance.ctx.canvas = canvasInstance; + canvasInstance.keySize = keySize; + canvasInstance.algText = algText; + canvasInstance.fontSize = fontSize; + + drawKey(canvasInstance.ctx,canvasInstance.name,canvasInstance.algText,false,canvasInstance.keySize,canvasInstance.fontSize); + addListenerStart(canvasInstance,keyStart); + + var relKeyLeft = keyLeft - left; + var relKeyTop = keyTop - top; + + key1[pageIndex][keyCount] = canvasInstance; + key1Data[pageIndex][keyCount] = [keyLeft, keyTop, keySize, keySize, relKeyLeft, relKeyTop, keyArray[rowNum][colNum]]; + key1Data[pageIndex][keyCount][100] = keyLeft; + key1Data[pageIndex][keyCount][101] = keyTop; + + keyLeft += keySize + keyPadding; + keyCount++; + } + } + + if (typeof object.staticKeys !== 'undefined') { + var keys2 = object.staticKeys; + var keyLeft = object.staticKeyPos[0]; + var keyTop = object.staticKeyPos[1]; + var staticKeySize = object.staticKeySize || keySize; + var staticKeyPadding = object.staticKeyPadding || keyPadding; + for (var k = 0; k < keys2.length; k++) { + var canvasInstance = document.createElement('canvas'); + canvasInstance.width = staticKeySize; + canvasInstance.height = staticKeySize; + canvasInstance.setAttribute('position', 'absolute'); + canvasInstance.setAttribute('cursor', 'auto'); + canvasInstance.setAttribute('draggable', 'false'); + canvasInstance.setAttribute('class', 'buttonClass'); + canvasInstance.style.zIndex = 800000; + canvasInstance.style.opacity = 0.7; + canvasInstance.style.pointerEvents = 'none'; + + var contextInstance = canvasInstance.getContext('2d'); + + canvasInstance.name = keys2[k]; + canvasInstance.value = keys2[k]; + canvasInstance.func = function(){}; + canvasInstance.keySize = staticKeySize; + canvasInstance.relFontSize = 1; + canvasInstance.element = false; + canvasInstance.static = true; + container.appendChild(canvasInstance); + + for (var c = 0; c < keyboardChars.length; c++) { + if (canvasInstance.value == keyboardChars[c].name) { + canvasInstance.value = String.fromCharCode(keyboardChars[c].display); + break; + } + } + for (var e = 0; e < keyboardElements.length; e++) { + if (canvasInstance.value == keyboardElements[e].name) { + canvasInstance.relFontSize = 0.9; + if (['frac','mixedNum'].indexOf(canvasInstance.value) > -1) { + canvasInstance.relFontSize = 0.8; + } + canvasInstance.value = keyboardElements[e].display; + canvasInstance.func = keyboardElements[e].func; + canvasInstance.element = true; + break; + } + } + + canvasInstance.ctx = contextInstance; + canvasInstance.ctx.canvas = canvasInstance; + canvasInstance.staticKeySize = staticKeySize; + canvasInstance.algText = algText; + canvasInstance.fontSize = fontSize; + + if (canvasInstance.element == true || ['leftArrow','rightArrow','del','delete'].indexOf(canvasInstance.name) > -1) { + canvasInstance.color = '#FF0'; // color for operators + canvasInstance.colorPressed = '#990'; + canvasInstance.keyType = 'operator'; + } else if (['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'].indexOf(canvasInstance.name) > -1) { + canvasInstance.color = '#AFF'; // color for letters + canvasInstance.colorPressed = '#6CC'; + canvasInstance.keyType = 'letter'; + } else if(['0','1','2','3','4','5','6','7','8','9',0,1,2,3,4,5,6,7,8,9].indexOf(canvasInstance.name) > -1) { + canvasInstance.color = '#CFC'; // color for numbers + canvasInstance.colorPressed = '#9C9'; + canvasInstance.keyType = 'number'; + } else { + canvasInstance.color = '#CCC'; // color for misc + canvasInstance.colorPressed = '#AAA'; + canvasInstance.keyType = 'misc'; + } + + drawKey(canvasInstance.ctx,canvasInstance.name,canvasInstance.algText,false,canvasInstance.staticKeySize,canvasInstance.fontSize); + addListenerStart(canvasInstance,keyStart); + + var relKeyLeft = keyLeft - left; + var relKeyTop = keyTop - top; + + key1[pageIndex][keyCount] = canvasInstance; + key1Data[pageIndex][keyCount] = [keyLeft, keyTop, staticKeySize, staticKeySize, relKeyLeft, relKeyTop, keys2[k]]; + key1Data[pageIndex][keyCount][100] = keyLeft; + key1Data[pageIndex][keyCount][101] = keyTop; + + keyLeft += staticKeySize + staticKeyPadding; + keyCount++; + } + } + + // create close button + var canvasInstance = document.createElement('canvas'); + canvasInstance.width = 40; + canvasInstance.height = 40; + canvasInstance.setAttribute('position', 'absolute'); + canvasInstance.setAttribute('cursor', 'auto'); + canvasInstance.setAttribute('draggable', 'false'); + canvasInstance.setAttribute('class', 'buttonClass'); + canvasInstance.style.zIndex = 800000; + addListener(canvasInstance, hideKeyboard) + + var contextInstance = canvasInstance.getContext('2d'); + roundedRect(contextInstance, 3, 3, 34, 34, 8, 6, '#000', '#000'); + contextInstance.strokeStyle = '#FFF'; + contextInstance.fillStyle = '#FFF'; + contextInstance.lineWidth = 5; + contextInstance.beginPath(); + + contextInstance.moveTo(10, 30); + contextInstance.lineTo(30, 30); + + contextInstance.closePath(); + contextInstance.stroke(); + key1[pageIndex][keyCount] = canvasInstance; + key1Data[pageIndex][keyCount] = [left + width - 40, top+1, 40, 40, width - 40, 0, 'closeKeyboard']; + key1Data[pageIndex][keyCount][100] = left + width - 40; + key1Data[pageIndex][keyCount][101] = top+1; + + // create showKeyboard button + var s = keyButtonSize; + var canvasInstance = document.createElement('canvas'); + canvasInstance.width = s; + canvasInstance.height = s; + canvasInstance.setAttribute('position', 'absolute'); + canvasInstance.setAttribute('cursor', 'auto'); + canvasInstance.setAttribute('draggable', 'false'); + canvasInstance.setAttribute('class', 'buttonClass'); + canvasInstance.style.zIndex = 8000000000000; + addListener(canvasInstance, showKeyboard) + var contextInstance = canvasInstance.getContext('2d'); + roundedRect(contextInstance, s*1.5/50, s*1.5/50, s*47/50, s*47/50, s*5/50, s*3/50, '#000', backColor); + roundedRect(contextInstance, s*9/50, s*9/50, s*8/50, s*8/50, s*3/50, s*2/50, '#000', '#AFF'); + roundedRect(contextInstance, s*21/50, s*9/50, s*8/50, s*8/50, s*3/50, s*2/50, '#000', '#AFF'); + roundedRect(contextInstance, s*33/50, s*9/50, s*8/50, s*8/50, s*3/50, s*2/50, '#000', '#AFF'); + roundedRect(contextInstance, s*9/50, s*21/50, s*8/50, s*8/50, s*3/50, s*2/50, '#000', '#AFF'); + roundedRect(contextInstance, s*21/50, s*21/50, s*8/50, s*8/50, s*3/50, s*2/50, '#000', '#AFF'); + roundedRect(contextInstance, s*33/50, s*21/50, s*8/50, s*8/50, s*3/50, s*2/50, '#000', '#AFF'); + roundedRect(contextInstance, s*9/50, s*33/50, s*8/50, s*8/50, s*3/50, s*2/50, '#000', '#AFF'); + roundedRect(contextInstance, s*21/50, s*33/50, s*8/50, s*8/50, s*3/50, s*2/50, '#000', '#AFF'); + roundedRect(contextInstance, s*33/50, s*33/50, s*8/50, s*8/50, s*3/50, s*2/50, '#000', '#AFF'); + canvases[pageIndex].push(canvasInstance); + showKeys[pageIndex] = canvasInstance; + var dataInstance = [keyButtonLeft, keyButtonTop, s, s, true, false, true]; + dataInstance[100] = keyButtonLeft; + dataInstance[101] = keyButtonTop; + dataInstance[102] = s; + dataInstance[103] = s; + canvasInstance.data = dataInstance; + canvasInstance.ctx = contextInstance; + + keyboardButton1[pageIndex] = canvasInstance; + + // create hideKeyboard button + var canvasInstance = document.createElement('canvas'); + canvasInstance.width = s; + canvasInstance.height = s; + canvasInstance.setAttribute('position', 'absolute'); + canvasInstance.setAttribute('cursor', 'auto'); + canvasInstance.setAttribute('draggable', 'false'); + canvasInstance.setAttribute('class', 'buttonClass'); + canvasInstance.style.zIndex = 8000000000000; + addListener(canvasInstance, hideKeyboard) + var contextInstance = canvasInstance.getContext('2d'); + roundedRect(contextInstance, s*1.5/50, s*1.5/50, s*47/50, s*47/50, s*5/50, s*3/50, '#000', backColor); + canvases[pageIndex].push(canvasInstance); + hideKeys[pageIndex] = canvasInstance; + var dataInstance = [keyButtonLeft, keyButtonTop, s, s, false, false, true]; + dataInstance[100] = keyButtonLeft; + dataInstance[101] = keyButtonTop; + dataInstance[102] = s; + dataInstance[103] = s; + canvasInstance.data = dataInstance; + canvasInstance.ctx = contextInstance; + + keyboardButton2[pageIndex] = canvasInstance; +} + +function keyStart(e) { + e.preventDefault(); + var name = e.target.name; + var ctx = e.target.ctx; + var algText = e.target.algText; + var keySize = e.target.keySize; + var fontSize = e.target.fontSize; + var element = e.target.element; + + addListenerEnd(e.target,keyStop); + drawKey(ctx,name,algText,true,keySize,fontSize); + + if (!un(draw) && !un(draw.keyboard) && draw.keyboard.active == true) { + textEdit.softKeyInput(e.target.name); + } else { + if (name == 'leftArrow') { + mathsInputLeftArrow(e); + } else if (name == 'rightArrow') { + mathsInputRightArrow(e) + } else { + if (boolean(element,false) == true) { + e.target.func.apply(); + } else { + softKeyMathsInput(e); + } + } + } +} + +function keyStop(e) { + var name = e.target.name; + var ctx = e.target.ctx; + var algText = e.target.algText; + var keySize = e.target.keySize; + var fontSize = e.target.fontSize; + drawKey(ctx,name,algText,false,keySize,fontSize); + removeListenerEnd(e.target,keyStop); +} + +var keyboardCurrFont = 'Arial'; +function updateKeyboardCurrFont() { + var prev = keyboardCurrFont; + + if (typeof currMathsInput == 'undefined') return; + if (currMathsInput.selected == true && currMathsInput.selectPos.length > 0) { + var cursorMapPos = currMathsInput.cursorMap[Math.max(currMathsInput.selectPos[0],currMathsInput.selectPos[1])]; + } else { + var cursorMapPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + } + + for (var pos = 0; pos < currMathsInput.allMap.length; pos++) { + if (arraysEqual(cursorMapPos,currMathsInput.allMap[pos]) == true) break; + var posText = currMathsInput.richText; + for (var pos2 = 0; pos2 < currMathsInput.allMap[pos].length - 1; pos2++) { + posText = posText[currMathsInput.allMap[pos][pos2]]; + } + posText = posText.slice(currMathsInput.allMap[pos][currMathsInput.allMap[pos].length - 1]); + var tagType = 'none'; + if (posText.indexOf('<>') == 0) { + keyboardCurrFont = posText.slice(7,pos3); + break; + } + } + } + } + if (keyboardCurrFont !== prev && typeof key1 !== 'undefined' && typeof key1[pageIndex] !== 'undefined') { + for (var k = 0; k < key1[pageIndex].length - 1; k++) { + if (key1[pageIndex][k].keyType == 'letter') { + drawKey(key1[pageIndex][k].ctx,key1[pageIndex][k].name,key1[pageIndex][k].algText,false,key1[pageIndex][k].keySize,key1[pageIndex][k].fontSize); + } + } + } +} + +function drawKey(ctx,name,algText,pressed,keySize,fontSize,font) { + var isAlgText = boolean(algText,false); + var isPressed = boolean(pressed,false); + if (!keySize) keySize = 60; + if (!fontSize) fontSize = 40; + if (!font) { + font = keyboardCurrFont; + } + if (isAlgText == true) font = 'algebra'; + fontSize = fontSize * ctx.canvas.relFontSize; + + ctx.clearRect(0,0,keySize,keySize); + if (isPressed == false) { + var color = ctx.canvas.color; + } else { + var color = ctx.canvas.colorPressed; + } + /*var color = '#AFF'; + if (boolean(ctx.canvas.element,false) == true || ['leftArrow','rightArrow','del','delete'].indexOf(name) > -1) { + font = 'algebra'; + if (isPressed == true) { + color = '#990'; + } else { + color = '#FF0'; + } + } else { + if (isPressed == true) { + color = '#6CC'; + } + }*/ + + switch (name) { + case 'leftArrow' : + case 'left' : + text({ctx:ctx,textArray:[],left:1.5,top:1.5,height:keySize-3,width:keySize-3,align:'center',vertAlign:'middle',box:{type:'loose',radius:6,color:color,borderWidth:3}}); + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(45*keySize/60, 30*keySize/60); + ctx.lineTo(15*keySize/60, 30*keySize/60); + ctx.lineTo(25*keySize/60, 20*keySize/60); + ctx.moveTo(15*keySize/60, 30*keySize/60); + ctx.lineTo(25*keySize/60, 40*keySize/60); + ctx.stroke(); + break; + case 'rightArrow' : + case 'right' : + text({ctx:ctx,textArray:[],left:1.5,top:1.5,height:keySize-3,width:keySize-3,align:'center',vertAlign:'middle',box:{type:'loose',radius:6,color:color,borderWidth:3}}); + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(15*keySize/60, 30*keySize/60); + ctx.lineTo(45*keySize/60, 30*keySize/60); + ctx.lineTo(35*keySize/60, 20*keySize/60); + ctx.moveTo(45*keySize/60, 30*keySize/60); + ctx.lineTo(35*keySize/60, 40*keySize/60); + ctx.stroke(); + break; + case 'delete' : + case 'del' : + text({ctx:ctx,textArray:[],left:1.5,top:1.5,height:keySize-3,width:keySize-3,align:'center',vertAlign:'middle',box:{type:'loose',radius:6,color:color,borderWidth:3}}); + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillStyle = "#000"; + ctx.font = (fontSize/2)+"px Arial"; + ctx.fillText("DEL", keySize * 0.5, keySize * 0.65); + break; + default : + var txt = []; + if (typeof ctx.canvas.value == 'number') { + txt = [String(ctx.canvas.value)]; + } else if (typeof ctx.canvas.value == 'string') { + txt = [ctx.canvas.value]; + } if (typeof ctx.canvas.value == 'object') { + txt = ctx.canvas.value; + } + text({ctx:ctx,text:txt,font:font,fontSize:fontSize,rect:[1.5,1.5,keySize-3,keySize-3],align:[0,0],box:{type:'loose',radius:6,color:color,borderWidth:3}}); + + break; + } +} + +var keyboardHardClosed = false; // has the user clicked the minimise button? +var keyboardHardOpen = false; // has the user clicked the minimise button? + +function showKeyboard(event) { + event.preventDefault(); + keyboardHardClosed = false; + keyboardHardOpen = true; + showKeyboard2(); +} + +function showKeyboard2(lightUp) { + if (un(keyboard[pageIndex])) return; + if (boolean(lightUp,false) == true) { + for (var i = 0; i < key1[pageIndex].length; i++) { + key1[pageIndex][i].style.opacity = 1; + key1[pageIndex][i].style.pointerEvents = 'auto'; + } + } + if (keyboardHardClosed) return; + if (keyboard[pageIndex].parentNode !== container) { + container.appendChild(keyboard[pageIndex]); + for (i = 0; i < key1[pageIndex].length; i++) { + if (boolean(key1[pageIndex][i].static,false) == false) { + container.appendChild(key1[pageIndex][i]); + } + } + if (showKeys[pageIndex].parentNode == container) {container.removeChild(showKeys[pageIndex])}; + container.appendChild(hideKeys[pageIndex]) + keyboardVis[pageIndex] = true; + } +} + + +function hideKeyboard(event) { + event.preventDefault(); + keyboardHardClosed = true; + keyboardHardOpen = false; + hideKeyboard2(); +} + +function hideKeyboard2(lightDown) { + if (un(keyboard[pageIndex])) return; + if (boolean(lightDown,false) == true) { + for (var i = 0; i < key1[pageIndex].length - 1; i++) { + key1[pageIndex][i].style.opacity = 0.7; + key1[pageIndex][i].style.pointerEvents = 'none'; + } + } + if (keyboard[pageIndex].parentNode == container && keyboardHardOpen == false) { + container.removeChild(keyboard[pageIndex]); + for (var i = 0; i < key1[pageIndex].length; i++) { + if (boolean(key1[pageIndex][i].static,false) == false) { + container.removeChild(key1[pageIndex][i]); + } + } + if (hideKeys[pageIndex].parentNode == container) {container.removeChild(hideKeys[pageIndex])}; + container.appendChild(showKeys[pageIndex]); + keyboardVis[pageIndex] = false; + } +} + +var drag; +function dragKeyboardStart(e) { + updateMouse(e); + e.target.style.cursor = closedhand; + e.target.dragOffset = [mouse.x-e.target.data[100],mouse.y-e.target.data[101]]; + drag = e.target; + addListenerMove(window, dragKeyboardMove ) + addListenerEnd(window, dragKeyboardStop ) +} +function dragKeyboardMove(e) { + e.target.style.cursor = closedhand; + updateMouse(e); + var l = mouse.x - drag.dragOffset[0]; + l = Math.max(l,drag.dragArea[0]); + l = Math.min(l,mainCanvasWidth-drag.dragArea[2]-drag.data[102]); + var t = mouse.y - drag.dragOffset[1]; + t = Math.max(t,drag.dragArea[1]); + t = Math.min(t,mainCanvasHeight-drag.dragArea[3]-drag.data[103]); + drag.data[100] = l; + drag.data[101] = t; + resizeCanvas2(drag,l,t); + + for (i = 0; i < key1[pageIndex].length; i++) { + if (boolean(key1[pageIndex][i].static,false) == false) { + key1Data[pageIndex][i][100] = l + key1Data[pageIndex][i][4]; + key1Data[pageIndex][i][101] = t + key1Data[pageIndex][i][5]; + resizeCanvas2(key1[pageIndex][i],key1Data[pageIndex][i][100],key1Data[pageIndex][i][101]); + } + } +} +function dragKeyboardStop(e) { + removeListenerMove(window, dragKeyboardMove) + removeListenerEnd(window, dragKeyboardStop) + e.target.style.cursor = openhand; + //drag.style.cursor = 'url("../images/cursors/openhand.cur"), auto'; +} \ No newline at end of file diff --git a/tools/i2/_mathsInput.js b/tools/i2/_mathsInput.js new file mode 100644 index 0000000..0fa3ff2 --- /dev/null +++ b/tools/i2/_mathsInput.js @@ -0,0 +1,3723 @@ +/*if (typeof console == "undefined") { + this.console = { log: function (msg) { alert(msg); } }; +}*/ + +/* +text selection - double click should select word +cursor up/down - remember horizPos of original line and find closest in other lines + +*/ + +/* example of basic use: +var j000inputs = inputs({ + inputs:[ + {left:900,top:300,width:100,height:50,algText:true}, + {left:900,top:500,width:100,height:50,algText:true} + ], + leftText:[ + ['Perimeter = '], + ['Area = '] + ], + rightText:[ + ['cm'], + ['cm',['pow',false,['2']]] + ], + checkFuncs:[ + function(mathsInput) { + if (mathsInput.stringJS == 'correctAns') { + return true; + } else { + return false + } + }, + function(mathsInput) { + if (mathsInput.stringJS == 'correctAns') { + return true; + } else { + return false + } + } + ], +}); +*/ +function inputs(object) { + var inputs = object.inputs; + var checkFuncs = object.checkFuncs; + var buttonPos = object.buttonPos || [990,620]; + var taskComplete = boolean(object.taskCompleted,true); + var buttonVis = boolean(object.buttonVisible,true); + + var inputArray = []; + + for (var i = 0; i < inputs.length; i++) { + var zIndex = inputs[i].zIndex || 2; + // mathsInput + var input = createMathsInput2(inputs[i]); + var vis = boolean(inputs[i].visible,true); + + // leftText && rightText + var button3 = document.createElement('canvas'); + button3.width = 1200; + button3.height = 700; + button3.setAttribute('position', 'absolute'); + button3.setAttribute('cursor', 'auto'); + button3.setAttribute('draggable', 'false'); + button3.setAttribute('class', 'buttonClass'); + var data3 = [0,0,1200,700,vis,false,false,zIndex]; + if (vis == true) container.appendChild(button3); + data3[130] = true; + for (var j = 0; j < 8; j++) {data3[100+j] = data3[j];} + button3.style.zIndex = zIndex; + button3.style.pointerEvents = 'none'; + canvases[pageIndex].push(button3); + button3.data = data3; + button3.ctx = button3.getContext('2d'); + + if (typeof object.leftText == 'object') { + if (typeof object.leftText[i] == 'object') { + var maxLines = inputs[i].maxLines || 1; + var fontSize = inputs[i].fontSize || 0.5 * (inputs[i].height / maxLines); + var textColor = inputs[i].textColor || '#000'; + drawMathsText(button3.ctx,object.leftText[i],fontSize,inputs[i].left-4,inputs[i].top+0.5*inputs[i].height,false,[],'right','middle',textColor); + input.leftText = object.leftText[i]; + } + } + if (typeof object.rightText == 'object') { + if (typeof object.rightText[i] == 'object') { + var maxLines = inputs[i].maxLines || 1; + var fontSize = inputs[i].fontSize || 0.5 * (inputs[i].height / maxLines); + var textColor = inputs[i].textColor || '#000'; + drawMathsText(button3.ctx,object.rightText[i],fontSize,inputs[i].left+inputs[i].width+15,inputs[i].top+0.5*inputs[i].height,false,[],'left','middle',textColor); + var textMeasure = drawMathsText(button3.ctx,object.rightText[i],fontSize,inputs[i].left+inputs[i].width+15,inputs[i].top+0.5*inputs[i].height,false,[],'left','middle',textColor,'measure'); + if (typeof inputs[i].offset === 'undefined') inputs[i].offset = [textMeasure[0]+5,0]; + input.rightText = object.rightText[i]; + } + } + if (typeof inputs[i].offset === 'undefined') inputs[i].offset = [0,0]; + + input.leftRightTextCanvas = button3; + input.canvas.leftRightTextCanvas = button3; + + // what if... maths input is entered through tab key, rather than clicking on?? + addListener(input.canvas, function() { + hideObj(this.tick,this.tick.data); + hideObj(this.cross,this.cross.data); + }); + + // tick canvas + var button = document.createElement('canvas'); + button.width = 40; + button.height = 50; + button.setAttribute('position', 'absolute'); + button.setAttribute('cursor', 'auto'); + button.setAttribute('draggable', 'false'); + button.setAttribute('class', 'buttonClass'); + var data = [inputs[i].left+inputs[i].width+13+inputs[i].offset[0],inputs[i].top+0.5*inputs[i].height-25+inputs[i].offset[1],40,50,false,false,false,zIndex]; + data[130] = true; + for (var j = 0; j < 8; j++) {data[100+j] = data[j];} + button.style.zIndex = zIndex; + button.style.pointerEvents = 'none'; + canvases[pageIndex].push(button3); + button.data = data; + button.ctx = button.getContext('2d'); + drawTick(button.ctx,40,50); + + // cross canvas + var button2 = document.createElement('canvas'); + button2.width = 32; + button2.height = 40; + button2.setAttribute('position', 'absolute'); + button2.setAttribute('cursor', 'auto'); + button2.setAttribute('draggable', 'false'); + button2.setAttribute('class', 'buttonClass'); + var data2 = [inputs[i].left+inputs[i].width+20+inputs[i].offset[0],inputs[i].top+0.5*inputs[i].height-20+inputs[i].offset[1],32,40,false,false,false,zIndex]; + data2[130] = true; + for (var j = 0; j < 8; j++) {data2[100+j] = data2[j];} + button2.style.zIndex = zIndex; + button.style.pointerEvents = 'none'; + canvases[pageIndex].push(button3); + button2.data = data2; + button2.ctx = button2.getContext('2d'); + drawCross(button2.ctx,32,40); + + input.tick = button; + input.cross = button2; + input.canvas.tick = button; + input.canvas.cross = button2; + + inputArray.push(input); + } + if (boolean(object.checkAnsButton,true)) { + // check answer button + var button = document.createElement('canvas'); + button.width = 180; + button.height = 50; + button.setAttribute('position', 'absolute'); + button.setAttribute('cursor', 'auto'); + button.setAttribute('draggable', 'false'); + button.setAttribute('class', 'buttonClass'); + var data = [buttonPos[0],buttonPos[1],180,50,buttonVis,false,true,2]; + if (buttonVis == true) container.appendChild(button); + data[130] = true; + for (var i = 0; i < 8; i++) {data[100+i] = data[i];} + button.style.zIndex = 2; + canvases[pageIndex].push(button3); + button.data = data; + button.ctx = button.getContext('2d'); + if (inputs.length > 1) { + drawTextBox(button,button.ctx,button.data,'#6F9','#000',4,'28px Hobo','#000','center','Check Answers'); + } else { + drawTextBox(button,button.ctx,button.data,'#6F9','#000',4,'28px Hobo','#000','center','Check Answer'); + } + + addListener(button,function() { + var inputs = this.inputs; + var checkFuncs = this.checkFuncs; + var complete = true; + for (var i = 0; i < inputs.length; i++) { + if (checkFuncs[i](inputs[i]) == true) { + hideObj(inputs[i].cross,inputs[i].cross.data); + showObj(inputs[i].tick,inputs[i].tick.data); + } else { + hideObj(inputs[i].tick,inputs[i].tick.data); + showObj(inputs[i].cross,inputs[i].cross.data,3000); + complete = false; + } + } + if (complete == true) { + taskCompleted(); + } + }); + + button.inputs = inputArray; + button.checkFuncs = checkFuncs; + button.taskComplete = true; + button.textCanvas = button3; + return button; + } +} + +function hideAllInputs() { + for (var i = 0; i < mathsInput[pageIndex].length; i++) { + hideMathsInput(mathsInput[pageIndex][i]); + } +} +function showAllInputs() { + for (var i = 0; i < mathsInput[pageIndex].length; i++) { + showMathsInput(mathsInput[pageIndex][i]); + } +} +function hideMathsInput(mathsInput) { + hideObj(mathsInput.canvas); + if (typeof mathsInput.cursorCanvas !== 'undefined') { + hideObj(mathsInput.cursorCanvas); + } + if (typeof mathsInput.tick !== 'undefined') { + hideObj(mathsInput.tick); + } + if (typeof mathsInput.cross !== 'undefined') { + hideObj(mathsInput.cross); + } + if (typeof mathsInput.leftRightTextCanvas !== 'undefined') { + hideObj(mathsInput.leftRightTextCanvas); + } +} +function showMathsInput(mathsInput) { + showObj(mathsInput.canvas); + if (typeof mathsInput.cursorCanvas !== 'undefined') { + showObj(mathsInput.cursorCanvas); + } + if (typeof mathsInput.leftRightTextCanvas !== 'undefined') { + showObj(mathsInput.leftRightTextCanvas); + } +} +function moveMathsInput(input,left,top) { + if (typeof input.data == 'undefined') return; + var dx = left - input.data[100]; + var dy = top - input.data[101]; + + input.data[100] += dx; + input.data[101] += dy; + resizeCanvas2(input.canvas,input.data[100],input.data[101]); + + if (typeof input.cross !== 'undefined') { + input.cross.data[100] += dx; + input.cross.data[101] += dy; + resizeCanvas2(input.cross,input.cross.data[100],input.cross.data[101]); + } + + if (typeof input.tick !== 'undefined') { + input.tick.data[100] += dx; + input.tick.data[101] += dy; + resizeCanvas2(input.tick,input.tick.data[100],input.tick.data[101]); + } + + input.cursorData[100] += dx; + input.cursorData[101] += dy; + resizeCanvas2(input.cursorCanvas,input.cursorData[100],input.cursorData[101]); + + if (typeof input.leftRightTextCanvas !== 'undefined') { + input.leftRightTextCanvas.data[100] += dx; + input.leftRightTextCanvas.data[101] += dy; + resizeCanvas2(input.leftRightTextCanvas,input.leftRightTextCanvas.data[100],input.leftRightTextCanvas.data[101]); + } +} +function enlargeMathsInput(input,sf) { // be careful! + if (typeof sf !== 'number') return; + //var l = input.data[100]; + //var t = input.data[101]; + + //input.data[102] = input.data[102] * sf; + //input.data[103] = input.data[103] * sf; + + resizeCanvas(input.canvas,input.data[100],input.data[101],input.data[102]*sf,input.data[103]*sf); + + /*input.cursorData[100] += (l -input.cursorData[100])*sf; + input.cursorData[101] += (t -input.cursorData[101])*sf; + input.cursorData[102] = input.cursorData[102] * sf; + input.cursorData[103] = input.cursorData[103] * sf; + resizeCanvas(input.cursorCanvas,input.cursorData[100],input.cursorData[101],input.cursorData[102],input.cursorData[103]); + + if (typeof input.cross !== 'undefined') { + input.cross.data[100] += (l -input.cross.data[100])*sf; + input.cross.data[101] += (t -input.cross.data[101])*sf; + input.cross.data[102] = input.cross.data[102] * sf; + input.cross.data[103] = input.cross.data[103] * sf; + resizeCanvas(input.cross,input.cross.data[100],input.cross.data[101],input.cross.data[102],input.cross.data[103]); + } + + if (typeof input.tick !== 'undefined') { + input.tick.data[100] += (l -input.tick.data[100])*sf; + input.tick.data[101] += (t -input.tick.data[101])*sf; + input.tick.data[102] = input.tick.data[102] * sf; + input.tick.data[103] = input.tick.data[103] * sf; + resizeCanvas(input.tick,input.tick.data[100],input.tick.data[101],input.tick.data[102],input.tick.data[103]); + } + + if (typeof input.leftRightTextCanvas !== 'undefined') { + input.leftRightTextCanvas.data[100] += (l -input.leftRightTextCanvas.data[100])*sf; + input.leftRightTextCanvas.data[101] += (t -input.leftRightTextCanvas.data[101])*sf; + input.leftRightTextCanvas.data[102] = input.leftRightTextCanvas.data[102] * sf; + input.leftRightTextCanvas.data[103] = input.leftRightTextCanvas.data[103] * sf; + resizeCanvas(input.leftRightTextCanvas,input.leftRightTextCanvas.data[100],input.leftRightTextCanvas.data[101],input.leftRightTextCanvas.data[102],input.leftRightTextCanvas.data[103]); + }*/ +} +function setMathsInputZIndex(input,zIndex) { + if (typeof input.leftRightTextCanvas == 'object') input.leftRightTextCanvas.style.zIndex = zIndex; + if (typeof input.tick == 'object') input.tick.style.zIndex = zIndex; + if (typeof input.cross == 'object') input.cross.style.zIndex = zIndex; + if (typeof input.canvas == 'object') input.canvas.style.zIndex = zIndex; + if (typeof input.cursorCanvas == 'object') input.cursorCanvas.style.zIndex = zIndex; +} +function setMathsInputFont(input,font) { + removeTagsOfType(input.richText,'font'); + input.richText.unshift('<>'); + input.richText = combineSpacesTextArray(input.richText); + removeTagsOfType(input.startRichText,'font'); + input.startRichText.unshift('<>'); + input.startRichText = combineSpacesTextArray(input.startRichText); + input.startTags = removeTagsOfType(input.startTags,'font'); + input.startTags = '<>'+input.startTags; + currMathsInput = input; + mathsInputMapCursorPos(); + mathsInputCursorCoords(); + deselectMathsInput(); +} +function setMathsInputColor(input,color) { + removeTagsOfType(input.richText,'color'); + input.richText.unshift('<>'); + input.richText = combineSpacesTextArray(input.richText); + removeTagsOfType(input.startRichText,'color'); + input.startRichText.unshift('<>'); + input.startRichText = combineSpacesTextArray(input.startRichText); + input.startTags = removeTagsOfType(input.startTags,'color'); + input.startTags = '<>'+input.startTags; + var saveCurrMathsInput = currMathsInput; + currMathsInput = input; + drawMathsInputText(input); + deselectMathsInput(); +} +function setMathsInputText(mathsInputObj, opt_newText, opt_newCursorPos) { // eg. setMathsInputText(j37mathsInput[3]); + var newText; + if (un(opt_newText)) { + newText = [""]; + } else if (typeof opt_newText == 'string') { + newText = [opt_newText]; + } else if (typeof opt_newText == 'number') { + newText = String(opt_newText); + newText = [newText]; + } else { + newText = clone(opt_newText); + } + mathsInputObj.text = newText; + if (!un(mathsInputObj.startTags)) newText.unshift(mathsInputObj.startTags); + mathsInputObj.richText = newText; + currMathsInput = mathsInputObj; + mathsInputMapCursorPos(); + if (typeof opt_newCursorPos !== 'undefined') { + mathsInputObj.cursorPos = opt_newCursorPos; + } else { + mathsInputObj.cursorPos = mathsInputObj.cursorMap.length - 1; + } + mathsInputCursorCoords(); + deselectMathsInput(); +} +function setMathsInputTextToInitialTags(m) { + var newText = ""; + if (!un(m.richText) && typeof m.richText[0] == 'string' && m.richText[0].indexOf('<<') == 0) { + for (c = 2; c < m.richText[0].length; c++) { + if (m.richText[0].slice(c).indexOf('>>') == 0 && m.richText[0].slice(c).indexOf('>><<') !== 0) { + newText = m.richText[0].slice(0,c+2); + break; + } + } + } + setMathsInputText(m,[newText],0); +} + +function drawTick(ctx,width,height,color,left,top,lineWidth) { + if (!left) left = 0; + if (!top) top = 0; + if (!width) width = 75; + if (!height) height = 75; + if (!color) color = '#F0F'; + if(!lineWidth) lineWidth = 8; + ctx.save(); + ctx.lineWidth = lineWidth; + ctx.strokeStyle = color; + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + ctx.beginPath(); + ctx.moveTo(left+4,top+0.5*height); + ctx.lineTo(left+width/3,top+height-4); + ctx.lineTo(left+width-4,top+4); + ctx.stroke(); + ctx.restore(); +} +function drawCross(ctx,width,height,color,left,top,lineWidth) { + if (!left) left = 0; + if (!top) top = 0; + if (!width) width = 75; + if (!height) height = 75; + if (!color) color = '#F00'; + if(!lineWidth) lineWidth = 8; + ctx.save(); + ctx.lineWidth = lineWidth; + ctx.strokeStyle = color; + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + ctx.beginPath(); + ctx.moveTo(left+4,top+4); + ctx.lineTo(left+width-4,top+height-4); + ctx.moveTo(left+width-4,top+4); + ctx.lineTo(left+4,top+height-4); + ctx.stroke(); + ctx.restore(); +} + +function createMathsInput2(object) { + // non-optional: + var left = object.left; + var top = object.top; + var width = object.width; + var height = object.height; + + // optional & defaults: + var varSize = object.varSize; // varSize:{minWidth:50,maxWidth:400,minHeight:50,maxHeight:300,padding:5} + var visible; + if (typeof object.visible == 'boolean') {visible = object.visible} else {visible = true}; + var zIndex = object.zIndex || 2; + var algText; + if (typeof object.algText == 'boolean') {algText = object.algText} else {algText = false}; + var textArray = object.textArray || [""]; + var leftPoint = object.leftPoint || 10; + var textColor = object.textColor || '#000'; + var textAlign = object.textAlign || 'center'; + var vertAlign = object.vertAlign || 'middle'; + var transparent; + if (typeof object.transparent == 'boolean') {transparent = object.transparent} else {transparent = false}; + var maxChars = object.maxChars || 1000000000; + var backColor = object.backColor || '#FFF'; + var selectColor = object.selectColor || '#FCF'; + var border; + if (typeof object.border == 'boolean') {border = object.border} else {border = true}; + var borderWidth = object.borderWidth || 3; + var borderDash = object.borderDash || []; + var borderColor = object.borderColor || '#000'; + var maxLines = object.maxLines || 1; + var fontSize = object.fontSize || 0.5 * (height/maxLines); + var selectable = boolean(object.selectable,false); + var pointerCanvas = object.pointerCanvas || false; + + var inputObject = createMathsInput(0, left, top, width, height, visible, zIndex, algText, textArray, leftPoint, fontSize, textColor, textAlign, transparent, maxChars, backColor, selectColor, border, borderColor, maxLines, vertAlign, varSize, borderWidth, borderDash, selectable, pointerCanvas); + return inputObject; +} + +// creates mathsInput canvas +function createMathsInput(id, left, top, width, height, visible, zIndex, algText, textArray, leftPoint, fontSize, textColor, textAlign, transparent, maxChars, backColor, selectColor, border, borderColor, maxLines, vertAlign, varSize, borderWidth, borderDash,selectable, pointerCanvas) { + if (!maxLines) {maxLines = 1}; + if (!zIndex) {zIndex = 2}; + if (!algText) {algText = false}; + if (!fontSize) (fontSize = (height/maxLines) * 0.75); + if (!textColor) (textColor = '#000'); + if (!textAlign) {textAlign = 'center'}; + if (textAlign == 'center') {leftPoint = 0.5 * width}; + if (!vertAlign) {vertAlign = 'middle'}; + if (!textArray) {textArray = [""]}; + var font = 'Arial'; + if (algText == true) font = 'algebra'; + var startText = textArray.slice(0); + var startTags = "<><><><><><><>"; + textArray.unshift(startTags); + var startRichText = textArray.slice(0); + if (!leftPoint) {leftPoint = 10}; + if (typeof transparent !== 'boolean') {transparent = false}; + if (!maxChars) {maxChars = 100000000} + if (!backColor) backColor = "#fff"; + var currBackColor = backColor; + if (!selectColor) selectColor = '#FCF'; + if (typeof border !== 'boolean') border = true; + if (!borderWidth) borderWidth = 5; + if (!borderDash) borderDash = []; + if (!borderColor) borderColor = '#000'; + if (!startText) startText = ['']; + if (typeof selectable !== 'boolean') selectable = false; + if (!pointerCanvas) pointerCanvas = false; + + function makeInputCanvas(l,t,w,h,v,d,p,z) { + var canvas = document.createElement('canvas'); + canvas.setAttribute('class', 'inputClass'); + canvas.setAttribute('width', w); + canvas.setAttribute('height', h); + canvas.setAttribute('left', l); + canvas.setAttribute('top', t); + canvas.setAttribute('position', 'absolute'); + canvas.style.border = 'none'; + canvas.style.zIndex = z; + if (p == false) { + canvas.style.pointerEvents = 'none'; + } else { + canvas.style.pointerEvents = 'auto'; + } + canvas.data = [l,t,w,h,v,d,p,z]; + canvas.ctx = canvas.getContext('2d'); + for (var i = 0; i < 8; i++) {canvas.data[i+100] = canvas.data[i]}; + resizeCanvas3(canvas); + return canvas; + } + + var textCanvas = makeInputCanvas(left, top, width, height, visible, false, false, zIndex); + var cursorCanvas = makeInputCanvas(left, top, width, height, visible, false, true, zIndex); + cursorCanvas.addEventListener('mousedown', startMathsInput, false); + cursorCanvas.addEventListener('touchstart', startMathsInput, false); + + var input = { + id:id, + canvas:textCanvas, + ctx:textCanvas.ctx, + data:textCanvas.data, + cursorCanvas:cursorCanvas, + cursorctx:cursorCanvas.ctx, + cursorData:cursorCanvas.data, + active:true, + stringJS:"", + text:startText, + richText:startRichText, + textLoc:[], + cursorPos:0, + cursorMap:[], + algText:algText, + leftPoint:leftPoint, + fontSize:fontSize, + textColor:textColor, + textAlign:textAlign, + transparent:transparent, + maxChars:maxChars, + backColor:backColor, + selectColor:selectColor, + border:border, // boolean + borderWidth:borderWidth, + borderDash:borderDash, + borderColor:borderColor, + currBackColor:currBackColor, + startText:startText, + startRichText:startRichText, + startTags:startTags, + maxLines:maxLines, + preText:'', + postText:'', + vertAlign:vertAlign, + varSize:varSize, + selectable:selectable, + selectPos:[], + selected:false, + setBackColor:function(color) { + this.backColor = color; + drawMathsInputText(this); + }, + pointerCanvas:pointerCanvas + }; + //input.creationTime = (new Date()).getTime(); + mathsInput[pageIndex].push(input); + currMathsInput = input; + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + mathsInputCursorCoords(); + deselectMathsInput(); + if (isTask == false && visible == true) showMathsInput(input); + return input; +} + +function mathsInputFrac(e) {mathsInputElement("['frac', [''], ['']]");} +function mathsInputPow(e) {mathsInputElement("['power', false, ['']]");} +function mathsInputSubs(e) {mathsInputElement("['subs', [''], ['']]");} +function mathsInputRoot(e) {mathsInputElement("['root', [''], ['']]");} +function mathsInputSqrt(e) {mathsInputElement("['sqrt', ['']]");} +function mathsInputSin(e) {mathsInputElement("['sin', ['']]");} +function mathsInputCos(e) {mathsInputElement("['cos', ['']]");} +function mathsInputTan(e) {mathsInputElement("['tan', ['']]");} +function mathsInputInvSin(e) {mathsInputElement("['sin-1', ['']]");} +function mathsInputInvCos(e) {mathsInputElement("['cos-1', ['']]");} +function mathsInputInvTan(e) {mathsInputElement("['tan-1', ['']]");} +function mathsInputLn(e) {mathsInputElement("['ln', ['']]");} +function mathsInputLog(e) {mathsInputElement("['log', ['']]");} +function mathsInputLogBase(e) {mathsInputElement("['logBase', [''], ['']]");} +function mathsInputAbs(e) {mathsInputElement("['abs', ['']]");} +function mathsInputExp(e) {mathsInputElement("['exp', ['']]");} +function mathsInputSigma1(e) {mathsInputElement("['sigma1', ['']]");} +function mathsInputSigma2(e) {mathsInputElement("['sigma2', [''], [''], ['']]");} +function mathsInputInt1(e) {mathsInputElement("['int1', ['']]");} +function mathsInputInt2(e) {mathsInputElement("['int2', [''], [''], ['']]");} +function mathsInputVectorArrow(e) {mathsInputElement("['vectorArrow', ['']]");} +function mathsInputBar(e) {mathsInputElement("['bar', ['']]");} +function mathsInputHat(e) {mathsInputElement("['hat', ['']]");} +function mathsInputRecurring(e) {mathsInputElement("['recurring', ['']]");} +function mathsInputColVector2d(e) {mathsInputElement("['colVector2d', [''], ['']]");} +function mathsInputColVector3d(e) {mathsInputElement("['colVector3d', [''], [''], ['']]");} +function mathsInputMixedNum(e) {mathsInputElement("['mixedNum', [''], [''], ['']]");} +function mathsInputLim(e) {mathsInputElement("['lim', [''], ['']]");} + +function mathsInputCut() { + if (currMathsInput.selected == false) return; + mathsInputCopy(); + currMathsInput.cursorPos = Math.min(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selPos = []; + currMathsInput.selected = false; + deleteSelected(); + removeSelectTags(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + mathsInputCursorCoords(); +} +function mathsInputCopy() { + if (currMathsInput.selected == false) return; + var sel = false; + clipboard = arrayHandler(clone(currMathsInput.richText)); + + //console.log(clipboard); + + function arrayHandler(array) { + for (var l = 0; l < array.length; l++) { + if (typeof array[l] == 'string') { + if (l > 0 || array.length == 1 || ['frac','power','pow','subs','subscript','sin','cos','tan','ln','log','logBase','sin-1','cos-1','tan-1','abs','exp','root','sqrt','sigma1','sigma2','int1','int2','recurring','bar','hat','vectorArrow','colVector2d','colVector3d','mixedNum','lim'].indexOf(array[l]) == -1) { + array[l] = stringHandler(array[l]); + } + } else { + array[l] = arrayHandler(array[l]); + if (sel == false) { + array.splice(l,1); + l--; + } + } + } + return array; + } + function stringHandler(string) { + var delPos = []; + if (sel == true) delPos[0] = 0; + var savedTags = ''; + for (var j = 0; j < string.length; j++) { + var slice = string.slice(j); + if (slice.indexOf('<>') == 0) { + delPos[0] = j+17; + sel = true; + } + if (slice.indexOf('<>') == 0) { + delPos[1] = j; + sel = false; + } + /*if (sel == true && (slice.indexOf('<>')+2); + }*/ + } + if (delPos.length > 0) { + if (delPos.length == 1) { + return string.slice(delPos[0])+savedTags; + } else { + return string.slice(delPos[0],delPos[1]); + } + } else { + return string; + } + } +} +function mathsInputPaste() { + if (typeof clipboard !== 'object' || clipboard == [] || arraysEqual(clipboard,[''])) return; + var elementString = JSON.stringify(clipboard); + + if (currMathsInput.selected == true) { + currMathsInput.cursorPos = Math.min(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selPos = []; + currMathsInput.selected = false; + deleteSelected(); + removeSelectTags(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + mathsInputCursorCoords(); + } + + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + var parent = currMathsInput.richText; + for (var aa = 0; aa < cursorPos.length - 1; aa++) {parent = parent[cursorPos[aa]]}; + var pos = cursorPos[cursorPos.length - 1]; + pos = adjustForBreakPoints(pos); + var parentPos = cursorPos[cursorPos.length - 2]; + var evalString = 'currMathsInput.richText' + for (var aa = 0; aa < cursorPos.length - 2; aa++) { + evalString += '[' + cursorPos[aa] + ']'; + } + var before = parent.slice(0,pos); + var after = parent.slice(pos); + + //console.log('before:',before); + //console.log('after:',after); + + var newParent = clone(clipboard); + newParent.unshift(before); + newParent.push(after); + + //console.log('newParent:',newParent); + + eval(evalString+" = newParent;"); + + //console.log('currMathsInput.richText:',currMathsInput.richText); + + var cursorPosCount = 0; + arrayHandler(clipboard); + + function arrayHandler(array) { + for (var l = 0; l < array.length; l++) { + if (typeof array[l] == 'string') { + if (array.length == 1 || ['frac','power','pow','subs','subscript','sin','cos','tan','ln','log','logBase','sin-1','cos-1','tan-1','abs','exp','root','sqrt','sigma1','sigma2','int1','int2','recurring','bar','hat','vectorArrow','colVector2d','colVector3d','mixedNum','lim'].indexOf(array[l]) == -1) { + cursorPosCount += array[l].length; + } + } else { + arrayHandler(array[l]); + } + } + } + + mathsInputMapCursorPos(); + currMathsInput.cursorPos += cursorPosCount; + mathsInputCursorCoords(); + currMathsInput.preText = ''; + currMathsInput.postText = ''; +} + +function mathsInputElement(elementString) { + if (currMathsInput.selected == true) { + currMathsInput.cursorPos = Math.min(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selPos = []; + currMathsInput.selected = false; + deleteSelected(); + removeSelectTags(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + mathsInputCursorCoords(); + } + + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + var gparent = currMathsInput.richText; + for (var aa = 0; aa < cursorPos.length - 2; aa++) { + gparent = gparent[cursorPos[aa]] + }; + var parent = gparent[cursorPos[cursorPos.length-2]]; + var parentPos = cursorPos[cursorPos.length-2]; + var pos = cursorPos[cursorPos.length-1]; + pos = adjustForBreakPoints(pos); + var before = parent.slice(0,pos); + var after = parent.slice(pos); + if (!un(currMathsInput.preText) && currMathsInput.preText !== null && currMathsInput.preText !== '') { + before += currMathsInput.preText; + } + if (!un(currMathsInput.postText) && currMathsInput.postText !== null && currMathsInput.postText !== '') { + after = currMathsInput.postText + after; + } + gparent.splice(parentPos,1,before,eval(elementString),after); + + mathsInputMapCursorPos(); + currMathsInput.cursorPos++; + mathsInputCursorCoords(); + currMathsInput.preText = ''; + currMathsInput.postText = ''; +} + +function mathsInputNewLine() { + // get cursorPos + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + + if (typeof currMathsInput.richText[cursorPos[0]] == 'string') { + var slicePos = cursorPos[1]; + slicePos = adjustForBreakPoints(slicePos); + // check if there is an align tag + if (currMathsInput.richText[cursorPos[0]].slice(slicePos).indexOf('<>')+2; + } + slicePos = mathsInputAvoidTagSplit(currMathsInput.richText[cursorPos[0]],slicePos); + + currMathsInput.richText[cursorPos[0]] = currMathsInput.richText[cursorPos[0]].slice(0,slicePos) + '<
>' + currMathsInput.richText[cursorPos[0]].slice(slicePos); + mathsInputMapCursorPos(); + currMathsInput.cursorPos += 1; + mathsInputCursorCoords(); + } else { + // jump forward from element to next string (if it exists) and insert <
> + var cursorPosShiftCount = 0; + for (var i = currMathsInput.cursorPos; i < currMathsInput.cursorMap.length; i++) { + cursorPosShiftCount++; + // if the next element has been reached + if (cursorPos[0] < currMathsInput.cursorMap[i][0]) { + if (typeof currMathsInput.richText[currMathsInput.cursorMap[i][0]] == 'string') { + cursorPos = currMathsInput.cursorMap[i]; + currMathsInput.richText[currMathsInput.cursorMap[i][0]] = '<
>'+currMathsInput.richText[currMathsInput.cursorMap[i][0]]; + break; + } else { + // this shouldn't happen?? All elements will be separated by a text string + } + } else if (i == currMathsInput.cursorMap.length - 1) { + currMathsInput.richText.push('<
>'); + cursorPosShiftCount += 6; + + } + } + + mathsInputMapCursorPos(); + currMathsInput.cursorPos += cursorPosShiftCount; + mathsInputCursorCoords(); + } +} +function mathsInputTab(howMany) { + if (un(howMany)) howMany = 1; + var ins = ""; + for (var i = 0; i < howMany; i++) { + ins += String.fromCharCode(0x21F4); + } + // get cursorPos + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + + if (typeof currMathsInput.richText[cursorPos[0]] == 'string') { + var slicePos = cursorPos[1]; + slicePos = adjustForBreakPoints(slicePos); + // check if there is an align tag + if (currMathsInput.richText[cursorPos[0]].slice(slicePos).indexOf('<>')+2; + } + currMathsInput.richText[cursorPos[0]] = currMathsInput.richText[cursorPos[0]].slice(0,slicePos) + ins + currMathsInput.richText[cursorPos[0]].slice(slicePos); + mathsInputMapCursorPos(); + currMathsInput.cursorPos += howMany; + mathsInputCursorCoords(); + } else { + // jump forward from element to next string (if it exists) and insert <
> + var cursorPosShiftCount = 0; + for (var i = currMathsInput.cursorPos; i < currMathsInput.cursorMap.length; i++) { + cursorPosShiftCount++; + // if the next element has been reached + if (cursorPos[0] < currMathsInput.cursorMap[i][0]) { + if (typeof currMathsInput.richText[currMathsInput.cursorMap[i][0]] == 'string') { + cursorPos = currMathsInput.cursorMap[i]; + currMathsInput.richText[currMathsInput.cursorMap[i][0]] = ins+currMathsInput.richText[currMathsInput.cursorMap[i][0]]; + break; + } else { + // this shouldn't happen?? All elements will be separated by a text string + } + } else if (i == currMathsInput.cursorMap.length - 1) { + currMathsInput.richTexts.push(String.fromCharCode(0x21F4)); + cursorPosShiftCount += howMany; + + } + } + + mathsInputMapCursorPos(); + currMathsInput.cursorPos += cursorPosShiftCount; + mathsInputCursorCoords(); + } +} +function mathsInputLeftArrow(e) { + if (currMathsInput.cursorPos > 0) { + currMathsInput.cursorPos--; + mathsInputCursorCoords(); + } else { + mathsInputTabPrev(); + } +} +function mathsInputRightArrow(e) { + if (currMathsInput.cursorPos < currMathsInput.cursorMap.length - 1) { + currMathsInput.cursorPos++; + mathsInputCursorCoords(); + } else { + mathsInputTabNext(); + } +} +function mathsInputTabPrev() { + var dx,dy,x,y,currBest1,currBest2; + for (var i = 0; i < mathsInput[pageIndex].length; i++) { + if (i == currMathsInputId || mathsInput[pageIndex][i].canvas.parentNode !== container || mathsInput[pageIndex][i].active == false) continue; + if (mathsInput[pageIndex][i].data[100] < mathsInput[pageIndex][currMathsInputId].data[100] && mathsInput[pageIndex][i].data[101] == mathsInput[pageIndex][currMathsInputId].data[101]) { // if directly to the left + if (typeof dy == 'undefined') { + dy = 0; + dx = mathsInput[pageIndex][currMathsInputId].data[100] - mathsInput[pageIndex][i].data[100]; + currBest1 = i; + } else if (dy > 0 || (dy == 0 && mathsInput[pageIndex][currMathsInputId].data[100] - mathsInput[pageIndex][i].data[100] < dx)) { + dy = 0; + dx = mathsInput[pageIndex][currMathsInputId].data[100] - mathsInput[pageIndex][i].data[100]; + currBest1 = i; + } + } else if (mathsInput[pageIndex][i].data[101] < mathsInput[pageIndex][currMathsInputId].data[101]) { // if above + if (typeof dy == 'undefined') { + dy = mathsInput[pageIndex][currMathsInputId].data[101] - mathsInput[pageIndex][i].data[101]; + dx = mathsInput[pageIndex][currMathsInputId].data[100] - mathsInput[pageIndex][i].data[100]; + currBest1 = i; + } else if (dy > mathsInput[pageIndex][currMathsInputId].data[101] - mathsInput[pageIndex][i].data[101] || (dy == mathsInput[pageIndex][currMathsInputId].data[101] - mathsInput[pageIndex][i].data[101] && mathsInput[pageIndex][currMathsInputId].data[100] - mathsInput[pageIndex][i].data[100] < dx)) { + dy = mathsInput[pageIndex][currMathsInputId].data[101] - mathsInput[pageIndex][i].data[101]; + dx = mathsInput[pageIndex][currMathsInputId].data[100] - mathsInput[pageIndex][i].data[100]; + currBest1 = i; + } + + } else if ((mathsInput[pageIndex][i].data[100] > mathsInput[pageIndex][currMathsInputId].data[100] && mathsInput[pageIndex][i].data[101] == mathsInput[pageIndex][currMathsInputId].data[101]) || mathsInput[pageIndex][i].data[101] > mathsInput[pageIndex][currMathsInputId].data[101]) { // if directly to the right or below + if (typeof y == 'undefined') { + y = mathsInput[pageIndex][i].data[101]; + x = mathsInput[pageIndex][i].data[100]; + currBest2 = i; + } else if (mathsInput[pageIndex][i].data[101] > y || (mathsInput[pageIndex][i].data[101] == y && mathsInput[pageIndex][i].data[100] > x)) { + y = mathsInput[pageIndex][i].data[101]; + x = mathsInput[pageIndex][i].data[100]; + currBest2 = i; + } + } + } + if (typeof currBest1 !== 'undefined') { + currMathsInput.preText = ''; + currMathsInput.postText = ''; + deselectMathsInput(mathsInput[pageIndex][currBest1],true); + startMathsInput(mathsInput[pageIndex][currBest1]); + currMathsInput.cursorPos = currMathsInput.cursorMap.length - 1; + mathsInputCursorCoords(); + } else if (typeof currBest2 !== 'undefined') { + currMathsInput.preText = ''; + currMathsInput.postText = ''; + deselectMathsInput(mathsInput[pageIndex][currBest2],true); + startMathsInput(mathsInput[pageIndex][currBest2]); + currMathsInput.cursorPos = 0; + mathsInputCursorCoords(); + } +} +function mathsInputTabNext() { + var index = mathsInput[pageIndex].indexOf(currMathsInput); + if (index > -1) { + var newIndex = (index+1) % mathsInput[pageIndex].length; + currMathsInput.preText = ''; + currMathsInput.postText = ''; + deselectMathsInput(mathsInput[pageIndex][newIndex],true); + startMathsInput(mathsInput[pageIndex][newIndex]); + currMathsInput.cursorPos = currMathsInput.cursorMap.length - 1; + mathsInputCursorCoords(); + } + return; + + /*var dx,dy,x,y,currBest1,currBest2; + for (var i = 0; i < mathsInput[pageIndex].length; i++) { + if (i == currMathsInputId || mathsInput[pageIndex][i].canvas.parentNode !== container || mathsInput[pageIndex][i].active == false) continue; + if (mathsInput[pageIndex][i].data[100] > mathsInput[pageIndex][currMathsInputId].data[100] && mathsInput[pageIndex][i].data[101] == mathsInput[pageIndex][currMathsInputId].data[101]) { // if directly to the right + if (typeof dy == 'undefined') { + dy = 0; + dx = mathsInput[pageIndex][i].data[100] - mathsInput[pageIndex][currMathsInputId].data[100]; + currBest1 = i; + } else if (dy > 0 || (dy == 0 && mathsInput[pageIndex][i].data[100] - mathsInput[pageIndex][currMathsInputId].data[100] < dx)) { + dy = 0; + dx = mathsInput[pageIndex][i].data[100] - mathsInput[pageIndex][currMathsInputId].data[100]; + currBest1 = i; + } + } else if (mathsInput[pageIndex][i].data[101] > mathsInput[pageIndex][currMathsInputId].data[101]) { // if below + if (typeof dy == 'undefined') { + dy = mathsInput[pageIndex][i].data[101] - mathsInput[pageIndex][currMathsInputId].data[101]; + dx = mathsInput[pageIndex][i].data[100] - mathsInput[pageIndex][currMathsInputId].data[100]; + currBest1 = i; + } else if (dy > mathsInput[pageIndex][i].data[101] - mathsInput[pageIndex][currMathsInputId].data[101] || (dy == mathsInput[pageIndex][i].data[101] - mathsInput[pageIndex][currMathsInputId].data[101] && mathsInput[pageIndex][i].data[100] - mathsInput[pageIndex][currMathsInputId].data[100] < dx)) { + dy = mathsInput[pageIndex][i].data[101] - mathsInput[pageIndex][currMathsInputId].data[101]; + dx = mathsInput[pageIndex][i].data[100] - mathsInput[pageIndex][currMathsInputId].data[100]; + currBest1 = i; + } + + } else if ((mathsInput[pageIndex][i].data[100] < mathsInput[pageIndex][currMathsInputId].data[100] && mathsInput[pageIndex][i].data[101] == mathsInput[pageIndex][currMathsInputId].data[101]) || mathsInput[pageIndex][i].data[101] < mathsInput[pageIndex][currMathsInputId].data[101]) { // if directly to the left or above + if (typeof y == 'undefined') { + y = mathsInput[pageIndex][i].data[101]; + x = mathsInput[pageIndex][i].data[100]; + currBest2 = i; + } else if (mathsInput[pageIndex][i].data[101] < y || (mathsInput[pageIndex][i].data[101] == y && mathsInput[pageIndex][i].data[100] < x)) { + y = mathsInput[pageIndex][i].data[101]; + x = mathsInput[pageIndex][i].data[100]; + currBest2 = i; + } + } + } + if (typeof currBest1 !== 'undefined') { + currMathsInput.preText = ''; + currMathsInput.postText = ''; + deselectMathsInput(mathsInput[pageIndex][currBest1],true); + startMathsInput(mathsInput[pageIndex][currBest1]); + currMathsInput.cursorPos = currMathsInput.cursorMap.length - 1; + mathsInputCursorCoords(); + } else if (typeof currBest2 !== 'undefined') { + currMathsInput.preText = ''; + currMathsInput.postText = ''; + deselectMathsInput(mathsInput[pageIndex][currBest2],true); + startMathsInput(mathsInput[pageIndex][currBest2]); + currMathsInput.cursorPos = 0; + mathsInputCursorCoords(); + }*/ +} + +function mathsInputAvoidTagSplit(txt,slicePos) { + // check that a tag is not being split - if so adjust slicePos + var leftText = txt.slice(0,slicePos); + var rightText = txt.slice(slicePos); + //console.clear(); + //console.log(txt,slicePos); + //console.log(leftText); + //console.log(rightText); + var tagLeft = false; + var tagLeftCount = 0; + for (var i = 0; i < leftText.length; i++) { + tagLeftCount++; + //console.log('left '+i+':',leftText.slice(leftText.length - i)); + if (leftText.slice(leftText.length - i).indexOf('>>') == 0) break; + if (leftText.slice(leftText.length - i).indexOf('<<') == 0) { + tagLeft = true; + break; + } + } + var tagRight = false; + var tagRightCount = 0; + for (var j = 0; j < rightText.length; j++) { + tagRightCount++; + //console.log('right '+j+':',rightText.slice(j)); + if (rightText.slice(j).indexOf('<<') == 0) break; + if (rightText.slice(j).indexOf('>>') == 0) { + tagRight = true; + break; + } + } + //console.log(tagLeft,tagRight,tagLeftCount,tagRightCount); + if (tagLeft == true && tagRight == true) { + if (tagLeftCount <= tagRightCount) { + slicePos -= tagLeftCount; + } else { + slicePos += tagRightCount; + } + } + // test if '<',slicePos,'<' or '>',slicePos,'>' + if (leftText.slice(-1) == '<' && rightText.slice(0,1) == '<' && rightText.slice(0,2) !== '<<') slicePos--; + if (leftText.slice(-1) == '>' && leftText.slice(-2) !== '>>' && rightText.slice(0,1) == '>') slicePos++; + + return slicePos; +} + +function startMathsInput(e,startCursorPos) { + deselectMathsInput(e,true); + window.addEventListener('keydown', hardKeyMathsInput, false); + canvas.addEventListener('mousedown', endMathsInput, false); // clicking anywhere on the canvas will end the input + canvas.addEventListener('touchstart', endMathsInput, false); // touching anywhere on the canvas will end the input + /*// clicking a holder button will end the input + if (typeof holderButton !== 'undefined') { + for (i = 0; i < 3; i++) { + holderButton[i].addEventListener('mousedown', endMathsInput, false); + holderButton[i].addEventListener('touchstart', endMathsInput, false); + } + } + for (i = 0; i < taskObject[pageIndex].length; i++) { // clicking any other object will also end the input + if (endInputExceptions.indexOf(taskObject[pageIndex][i]) == -1) { + taskObject[pageIndex][i].addEventListener('mousedown', endMathsInput, false); + taskObject[pageIndex][i].addEventListener('touchstart', endMathsInput, false); + } + }*/ + if (e.target) { + var inputCanvas = e.target; + } else { + var inputCanvas = e; + }; + for (i = 0; i < mathsInput[pageIndex].length; i++) { + if (mathsInput[pageIndex][i].cursorCanvas == inputCanvas || mathsInput[pageIndex][i].canvas == inputCanvas || mathsInput[pageIndex][i] == inputCanvas) { + currMathsInput = mathsInput[pageIndex][i]; + currMathsInputId = i; + } + } + if (currMathsInput.transparent == false && currMathsInput.selectColor !== 'none') { + currMathsInput.canvas.style.backgroundColor = "#FCF"; + } else if (currMathsInput.transparent == false || currMathsInput.selectColor == 'none') { + currMathsInput.canvas.style.backgroundColor = "#transparent"; + } + inputState = true; // allows the onscreen keys to function + var closestPos = getClosestTextPos(); + //console.log(currMathsInput,closestPos,currMathsInput.selectable,startCursorPos); + if (currMathsInput.selectable == true && typeof startCursorPos == 'undefined') { + currMathsInput.selectPos = [closestPos,closestPos]; + setSelectPositions(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + if (typeof currMathsInput.pointerCanvas !== 'object') { + addListenerMove(currMathsInput.cursorCanvas,selectTextMove); + addListenerEnd(currMathsInput.cursorCanvas,selectTextStop); + } + } else { + mathsInputMapCursorPos(); + currMathsInput.cursorPos = startCursorPos || Number(closestPos) || 0; + //console.log(currMathsInput,currMathsInput.cursorPos,currMathsInput.cursorMap[currMathsInput.cursorPos],currMathsInput.textLoc); + mathsInputCursorCoords(); + updateKeyboardCurrFont(); + showKeyboard2(true); + } + if (!un(currMathsInput.markPos)) { + currMathsInput.markctx.clearRect(currMathsInput.markPos[0]-5,currMathsInput.markPos[1]-5,currMathsInput.markPos[2]+10,currMathsInput.markPos[3]+10); + } + //shiftOn = false; + //ctrlOn = false; + //altOn = false; + if (!un(window.textMenu) && typeof textMenu.show == 'function' && textMenu.showOnStartInput == true) { + textMenu.update(); + textMenu.show(); + } +} +function selectTextMove(e) { + updateMouse(e); + var closestPos = getClosestTextPos(); + //console.log(closestPos); + if (currMathsInput.selectPos[1] !== closestPos) { + currMathsInput.selectPos[1] = closestPos; + //console.log(currMathsInput.selectPos); + currMathsInput.selected = true; + setSelectPositions(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + } + console.log(currMathsInput.selectPos); + //console.log('selectTextMove',currMathsInput.selected); +} +function selectTextStop(e) { + //console.log(currMathsInput.selectPos); + removeListenerMove(currMathsInput.cursorCanvas,selectTextMove); + removeListenerEnd(currMathsInput.cursorCanvas,selectTextStop); + if (currMathsInput.selectPos[0] == currMathsInput.selectPos[1]) { + currMathsInput.cursorPos = currMathsInput.selectPos[0]; + currMathsInput.selectPos = []; + currMathsInput.selected = false; + setSelectPositions(); + mathsInputMapCursorPos(); + mathsInputCursorCoords(); + } + updateKeyboardCurrFont(); + showKeyboard2(true); +} +function getClosestTextPos(mathsInput) { + if (!mathsInput) mathsInput = currMathsInput; + // search through text character locations for a mouse hit test + var mousePos = [mouse.x-mathsInput.data[100],mouse.y-mathsInput.data[101]]; + //console.log(mathsInput); + if (typeof mathsInput.cursorMap == 'undefined') { + mathsInputMapCursorPos(); + } + var map = mathsInput.cursorMap; + var closestPos = 0; + var closestDist; + var vertDist; + var closestVertDist; + //console.log('getClosestTextPos()'); + //console.clear(); + for (var pos = 0; pos < map.length; pos++) { + var loc = mathsInput.textLoc; + for (var aa = 0; aa < map[pos].length; aa++) { + loc = loc[map[pos][aa]]; + }; + + /* + var ctx = mathsInput.ctx; + ctx.strokeStyle = '#F0F'; + ctx.beginPath(); + ctx.moveTo(loc.left,loc.top); + ctx.lineTo(loc.left,loc.top+loc.height); + ctx.stroke(); + */ + + if (!loc) continue; + + if (pos == 0) { + closestDist = distancePointToLineSegment(mousePos,[loc.left,loc.top],[loc.left,loc.top+loc.height]); + closestPos = pos; + vertDist = Math.min(Math.abs(mousePos[1]-loc.top),Math.abs(mousePos[1]-(loc.top+loc.height))); + if (mousePos[1] >= loc.top && mousePos[1] <= loc.top + loc.height) vertDist = 0; + closestVertDist = vertDist; + } else { + var newDist = distancePointToLineSegment(mousePos,[loc.left,loc.top],[loc.left,loc.top+loc.height]); + var newVertDist = Math.min(Math.abs(mousePos[1]-loc.top),Math.abs(mousePos[1]-(loc.top+loc.height))); + if (mousePos[1] >= loc.top && mousePos[1] <= loc.top + loc.height) newVertDist = 0; + if (newVertDist < closestVertDist || (newVertDist == closestVertDist && newDist < closestDist)) { + closestVertDist = newVertDist; + closestDist = newDist; + closestPos = pos; + } + } + + //console.log(pos,loc.left,newDist,closestDist,closestPos) + } + + /* + ctx.beginPath(); + ctx.moveTo(mousePos[0]-3,mousePos[1]-3); + ctx.lineTo(mousePos[0]+3,mousePos[1]+3); + ctx.moveTo(mousePos[0]-3,mousePos[1]+3); + ctx.lineTo(mousePos[0]+3,mousePos[1]-3); + ctx.stroke(); + */ + //console.log(closestPos); + if (isNaN(closestPos) || typeof closestPos == 'undefined' || closestPos == null) { + closestPos = 0; + } + return closestPos; +} +function endMathsInput(e) { + if (!un(e) && !un(keyboardButton1) && !un(keyboardButton1[pageIndex]) && e.target == keyboardButton1[pageIndex]) return; + if (!un(e) && !un(keyboardButton2) && !un(keyboardButton2[pageIndex]) && e.target == keyboardButton2[pageIndex]) return; + if (un(currMathsInput)) return; + if (currMathsInput.selected == true) { + removeSelectTags(); + mathsInputMapCursorPos(); + currMathsInput.cursorPos = Math.min(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selectPos = []; + mathsInputCursorCoords(); + currMathsInput.selected = false; + removeSelectTags(); + } + deselectMathsInput(e); + + if (typeof currMathsInput.onInputEnd == 'function') { + currMathsInput.onInputEnd(e); + } +} +function deselectMathsInput(e,diffInput) { + if (un(currMathsInput)) return; + currMathsInput.preText = ''; + currMathsInput.postText = ''; + if (currMathsInput.selected == true) { + removeSelectTags(); + mathsInputMapCursorPos(); + currMathsInput.cursorPos = Math.min(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selectPos = []; + mathsInputCursorCoords(); + currMathsInput.selected = false; + removeSelectTags(); + } + // test if the deselection is caused by a new input + var diffInputTest = -1; + if (typeof e == 'object' && e && typeof e.target == 'object' && e.target !== currMathsInput.canvas && !un(mathsInput[pageIndex])) { + for (var i = 0; i < mathsInput[pageIndex].length; i++) { + if (mathsInput[pageIndex][i].cursorCanvas == e.target) { + if (currMathsInput.canvas == e.target) { + return; // indicates the currently active mathsInput has been reclicked - do nothing + } else { + diffInputTest = i; + } + break; + } + } + } + + if ((typeof diffInput !== 'undefined' && diffInput == true) || diffInputTest > -1) { + + } else { + hideKeyboard2(true); + inputState = false; + // remove event listeners for mathsInput + window.removeEventListener('keydown', hardKeyMathsInput, false); + canvas.removeEventListener('mousedown', endMathsInput, false); + canvas.removeEventListener('touchstart', endMathsInput, false); + } + + //currMathsInput.currBackColor = currMathsInput.backColor; + /* + currMathsInput.ctx.clearRect(0, 0, currMathsInput.data[2], currMathsInput.data[3]); + if (currMathsInput.transparent == false && currMathsInput.backColor !== 'none') { + currMathsInput.ctx.fillStyle = currMathsInput.backColor || '#FFF'; + } + */ + + currMathsInput.canvas.style.backgroundColor = currMathsInput.backColor || 'transparent'; + if (currMathsInput.backColor == 'none') { + currMathsInput.canvas.style.backgroundColor = 'transparent' + } + + clearCorrectingInterval(mathsInputCursorBlinkInterval); + inputCursorState = false + blinking = false; + + currMathsInput.cursorctx.clearRect(0,0,1200,700); + + if ((typeof diffInput !== 'undefined' && diffInput == true) || diffInputTest > -1) { + if (typeof currMathsInput.onInputEnd == 'function') { + currMathsInput.onInputEnd(); + } + } else { + if (!un(window.textMenu)) { + if (typeof textMenu.show == 'function' && textMenu.showOnStartInput == true) { + textMenu.hide(); + } + } + } + if (typeof holderButton !== 'undefined') { + for (var i = 0; i < 3; i++) { + holderButton[i].removeEventListener('mousedown', endMathsInput, false); + holderButton[i].removeEventListener('mousedown', endMathsInput, false); + } + } + /*var startAt = 0; + if (taskKey[pageIndex]) {startAt = taskKey[pageIndex].length} + function listenerForI(i) { + taskObject[pageIndex][i].removeEventListener('mousedown', endMathsInput, false); + taskObject[pageIndex][i].removeEventListener('touchstart', endMathsInput, false); + } + if (taskObject[pageIndex]) { + for (var i = startAt; i < taskObject[pageIndex].length; i++) { // clicking any other object will also end the input + listenerForI(i); //'scopes' the variable i + } + }*/ + +} + +function setSelectPositions() { + // check for a need to adjust each selectPos + + var selPos1 = clone(currMathsInput.cursorMap[currMathsInput.selectPos[0]]); + if (typeof selPos1 !== 'undefined') { + var txt = clone(currMathsInput.richText); + for (var i = 0; i < selPos1.length - 1; i++) {txt = txt[selPos1[i]]}; + var txtSlice = txt.slice(0,selPos1[selPos1.length-1]); + if (txtSlice.indexOf('<>') > -1) { + selPos1[selPos1.length-1] -= 17; + } + if (txtSlice.indexOf('<>') > -1) { + selPos1[selPos1.length-1] -= 18; + } + } + + var selPos2 = clone(currMathsInput.cursorMap[currMathsInput.selectPos[1]]); + if (typeof selPos2 !== 'undefined') { + var txt = clone(currMathsInput.richText); + for (var i = 0; i < selPos2.length - 1; i++) {txt = txt[selPos2[i]]}; + var txtSlice = txt.slice(0,selPos2[selPos2.length-1]); + if (txtSlice.indexOf('<>') > -1) { + selPos2[selPos2.length-1] -= 17; + } + if (txtSlice.indexOf('<>') > -1) { + selPos2[selPos2.length-1] -= 18; + } + } + + removeSelectTags(); + + if (arraysEqual(currMathsInput.selectPos,[]) == false) { + if (currMathsInput.selectPos[0] == currMathsInput.selectPos[1]) { + insertTag('<><>',selPos1); + } else if (currMathsInput.selectPos[0] > currMathsInput.selectPos[1]) { + insertTag('<>',selPos1); + insertTag('<>',selPos2); + } else if (currMathsInput.selectPos[0] < currMathsInput.selectPos[1]) { + insertTag('<>',selPos2); + insertTag('<>',selPos1); + } + } + + function insertTag(insertion,cursorPos) { + // get the relevant string from currMathsInput.richText + var text = currMathsInput.richText; + for (var aa = 0; aa < cursorPos.length - 1; aa++) { + text = text[cursorPos[aa]]; + } + // pos is position of cursor + var pos = cursorPos[cursorPos.length - 1]; + + // adjust pos to account for breakPoints + if (typeof currMathsInput.breakPoints == 'object') { + for (var k = 0; k < currMathsInput.breakPoints.length - 1; k++) { + var breakPoint = currMathsInput.allMap[currMathsInput.breakPoints[k]]; + if (breakPoint[0] == cursorPos[0] && breakPoint[1] < cursorPos[1]) { + pos--; + } + } + } + // check that a tag is not being split - if so adjust pos + var leftText = text.slice(0,pos); + var rightText = text.slice(pos); + var tagLeft = false; + var tagLeftCount = 0; + for (var i = 0; i < leftText.length; i++) { + tagLeftCount++; + if (leftText.slice(leftText.length - i).indexOf('>>') == 0) break; + if (leftText.slice(leftText.length - i).indexOf('<<') == 0) { + tagLeft = true; + break; + } + } + var tagRight = false; + var tagRightCount = 0; + for (var j = 0; j < rightText.length; j++) { + tagRightCount++; + if (rightText.slice(j).indexOf('<<') == 0) break; + if (rightText.slice(j).indexOf('>>') == 0) { + tagRight = true; + break; + } + } + if (tagLeft == true && tagRight == true) { + if (tagLeftCount <= tagRightCount) { + pos -= tagLeftCount; + } else { + pos += tagRightCount; + } + } + var leftText = text.slice(0,pos); + var rightText = text.slice(pos); + if (leftText.slice(-1) == '<' && rightText.slice(0,1) == '<' && rightText.slice(0,2) !== '<<') pos--; + if (leftText.slice(-1) == '>' && leftText.slice(-2) !== '>>' && rightText.slice(0,1) == '>') pos++; + var textBefore = text.slice(0,pos); + var textAfter = text.slice(pos); + + text = textBefore + insertion + textAfter; + // replace the string + var evalString = 'currMathsInput.richText' + for (aa = 0; aa < cursorPos.length - 1; aa++) { + evalString += '[' + cursorPos[aa] + ']'; + } + eval(evalString + ' = text;'); + } +} +function removeSelectTags() { + var map1; + var map2; + var pos = []; + function arrayHandler(array) { + pos.push(0); + for (var l = array.length - 1; l >= 0; l--) { + pos[pos.length-1] = l; + if (typeof array[l] == 'string') { + array[l] = stringHandler(array[l]); + } else { + array[l] = arrayHandler(array[l]); + } + } + pos.pop(); + return array; + } + function stringHandler(string) { + //console.log(string,JSON.stringify(pos)); + for (var j = string.length - 1; j >= 0; j--) { + var slice = string.slice(j); + if (slice.indexOf('<>') == 0) { + string = string.slice(0,j)+string.slice(j+slice.indexOf('>>')+2); + if (typeof map1 !== 'undefined' && arraysEqual(pos,map1) == true && map2 > j) { + map2 = Math.max(0,map2-18); + } + } + if (slice.indexOf('<>') == 0) { + string = string.slice(0,j)+string.slice(j+slice.indexOf('>>')+2); + if (typeof map1 !== 'undefined' && arraysEqual(pos,map1) == true && map2 > j) { + map2 = Math.max(0,map2-17); + } + } + } + return string; + } + + //console.log(JSON.stringify(currMathsInput.cursorMap[currMathsInput.cursorPos])); + if (typeof currMathsInput.cursorPos == 'number') { + map1 = currMathsInput.cursorMap[currMathsInput.cursorPos]; + if (typeof map1 !== 'undefined') { + map2 = map1[map1.length-1]; + map1 = map1.slice(0,-1); + //console.log(JSON.stringify(map1),map2); + } + } + + currMathsInput.richText = arrayHandler(currMathsInput.richText); + + //console.log('---',map2); + if (typeof map2 !== 'undefined') { + currMathsInput.cursorMap[currMathsInput.cursorPos][currMathsInput.cursorMap[currMathsInput.cursorPos].length-1] = map2; + } + // adjust cursorPos if necessary + +} +function removeSelectTagsFromArray(textArray) { + var map1; + var map2; + var pos = []; + function arrayHandler(array) { + pos.push(0); + for (var l = array.length - 1; l >= 0; l--) { + pos[pos.length-1] = l; + if (typeof array[l] == 'string') { + array[l] = stringHandler(array[l]); + } else { + array[l] = arrayHandler(array[l]); + } + } + pos.pop(); + return array; + } + function stringHandler(string) { + //console.log(string,JSON.stringify(pos)); + for (var j = string.length - 1; j >= 0; j--) { + var slice = string.slice(j); + if (slice.indexOf('<>') == 0) { + string = string.slice(0,j)+string.slice(j+slice.indexOf('>>')+2); + if (typeof map1 !== 'undefined' && arraysEqual(pos,map1) == true && map2 > j) { + map2 = Math.max(0,map2-18); + } + } + if (slice.indexOf('<>') == 0) { + string = string.slice(0,j)+string.slice(j+slice.indexOf('>>')+2); + if (typeof map1 !== 'undefined' && arraysEqual(pos,map1) == true && map2 > j) { + map2 = Math.max(0,map2-17); + } + } + } + return string; + } + + return arrayHandler(textArray); +} +function deleteSelected() { + var sel = false; + currMathsInput.richText = arrayHandler(currMathsInput.richText); + + function arrayHandler(array) { + for (var l = 0; l < array.length; l++) { + if (typeof array[l] == 'string') { + if (l > 0 || array.length == 1 || ['frac','power','pow','subs','subscript','sin','cos','tan','ln','log','logBase','sin-1','cos-1','tan-1','abs','exp','root','sqrt','sigma1','sigma2','int1','int2','recurring','bar','hat','vectorArrow','colVector2d','colVector3d','mixedNum','lim'].indexOf(array[l]) == -1) { + array[l] = stringHandler(array[l]); + } + } else { + var preSel = false; + if (sel == true) {preSel = true}; + array[l] = arrayHandler(array[l]); + if (sel == true && preSel == true) { + array.splice(l,1); + l--; + } + } + } + return array; + } + function stringHandler(string) { + var delPos = []; + if (sel == true) delPos[0] = 0; + var savedTags = ''; + for (var j = 0; j < string.length; j++) { + var slice = string.slice(j); + if (slice.indexOf('<>') == 0) { + delPos[0] = j; + sel = true; + } + if (slice.indexOf('<>') == 0) { + delPos[1] = j + 18; + sel = false; + } + if (sel == true && (slice.indexOf('<>')+2); + } + } + if (delPos.length > 0) { + if (delPos.length == 1) { + return string.slice(0,delPos[0])+savedTags; + } else { + return string.slice(0,delPos[0])+savedTags+string.slice(delPos[1]); + } + } else { + return string; + } + } +} + +function mathsInputMapCursorPos() { // (re-)builds cursor map + currMathsInput.richText = reduceTags(currMathsInput.richText); + currMathsInput.richText = combineSpacesCursor(currMathsInput.richText); + + // create new cursor map + currMathsInput.textLoc = []; + drawMathsInputText(currMathsInput); + currMathsInput.cursorMap = mapArray(currMathsInput.textLoc,false); + + // create new allMap - this includes all markup tag characters + currMathsInput.allMap = mapArray(currMathsInput.textLoc,true); + + // move cursor positions in cursorMap from end to beginning of markup tags (except for beginning) + var cursorMap = currMathsInput.cursorMap; + + //console.log(currMathsInput.cursorPos,JSON.stringify(cursorMap)); + + for (var i = 1; i < cursorMap.length; i++) { + + // get text element + var richText = currMathsInput.richText; + for (var j = 0; j < cursorMap[i].length - 1; j++) richText = richText[cursorMap[i][j]]; + + // char is position of cursor + var char = cursorMap[i][cursorMap[i].length-1]; + + // adjust char to account for breakPoints + if (typeof currMathsInput.breakPoints == 'object') { + for (var k = 0; k < currMathsInput.breakPoints.length - 1; k++) { + var breakPoint = currMathsInput.allMap[currMathsInput.breakPoints[k]]; + if (breakPoint[0] == cursorMap[i][0] && breakPoint[1] < cursorMap[i][1]) { + char--; + } + } + } + + // if proceeded by a tag + if (richText.slice(char-2).indexOf('>>') == 0 && richText.slice(char-6).indexOf('<
>') !== 0) { + + // get text to the left of char + var leftText = richText.slice(0,char); + + // get tagCharCount to the left of char + var tagCharCount = 0; + for (var j = 0; j < leftText.length; j++) { + if (richText.slice(char-j,char).indexOf('<<') == 0 && (leftText.slice(char-j-2,char).indexOf('>>') !== 0 || leftText.slice(char-j-6,char).indexOf('<
>') == 0)) { + tagCharCount = j; + break; + } + } + + // check that it's not the very beginning + if (cursorMap[i][0] == 0 && cursorMap[i][1] == tagCharCount) continue; + + // alter the cursorMap by tagCharCount + currMathsInput.cursorMap[i][currMathsInput.cursorMap[i].length-1] -= tagCharCount; + } + + + } + + // update currMathsInput.text to be the same as currMathsInput.richText without any markuptags + currMathsInput.text = clone(currMathsInput.richText); + for (var p = 0; p < currMathsInput.text.length; p++) { + currMathsInput.text[p] = removeTags(currMathsInput.text[p]); + } +} +function combineSpacesCursor(array) { + //console.log(array.length); + if (array.length > 1) { + for (var gg = array.length - 1; gg >= 0; gg--) { + //console.log(gg, array[gg], typeof array[gg]); + if (typeof array[gg] == 'object') { + arrayString += '[' + gg + ']'; + combineSpacesCursor(array[gg]); + } else { + if (gg < array.length - 1 && typeof array[gg] == 'string' && typeof array[gg+1] == 'string') { + eval('currMathsInput.richText' + arrayString + '[' + gg + '] += currMathsInput.richText' + arrayString + '[' + (gg+1) + ']'); + eval('currMathsInput.richText' + arrayString + '.splice(gg+1, 1);'); + } + } + } + } + arrayString = arrayString.slice(0, arrayString.lastIndexOf('[') - arrayString.length); + return array; +} +function combineSpacesTextArray(array) { + if (array.length > 1) { + for (var i = array.length - 1; i >= 0; i--) { + if (typeof array[i] == 'object') { + if (i < array.length - 1 && typeof array[i] == 'string' && typeof array[i+1] == 'string') { + array[i] = array[i] + array[i+1]; + array.splice(i+1,1); + } + } else { + combineSpacesTextArray(array[i]); + } + } + } + return array; +} + +function mathsInputCursorCoords() { // updates cursor coordinates + drawMathsInputText(currMathsInput); + var char; + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + + if (typeof cursorPos == 'undefined') return; + + char = currMathsInput.textLoc; + for (var aa = 0 ; aa < cursorPos.length; aa++) { + char = char[cursorPos[aa]]; + } + + mathsInputCursor.x = char.left; + mathsInputCursor.top = char.top; + mathsInputCursor.bottom = char.top + char.height; + + //logText(true); + + //console.log('mathsInputCursor:',mathsInputCursor); + + inputCursorState = false; + clearCorrectingInterval(mathsInputCursorBlinkInterval); + drawMathsInputText(currMathsInput); + mathsInputCursorBlink(); + mathsInputCursorBlinkInterval = setCorrectingInterval(function(){mathsInputCursorBlink()}, 600); + blinking = true; + currMathsInput.stringJS = createJsString(); + if (typeof textMenu !== 'undefined' && typeof textMenu !== 'undefined' && typeof textMenu.update == 'function') textMenu.update(); +} +var showCursorPos = false; +function adjustForBreakPointsAllMap(pos) { + //console.log('pos:',pos); + if (typeof currMathsInput.breakPoints == 'object') { + //console.log('pos:',pos); + var map = currMathsInput.allMap[pos]; + for (var i = 0; i < currMathsInput.breakPoints.length - 1; i++) { + var iBreak = currMathsInput.allMap[currMathsInput.breakPoints[i]]; + if (iBreak[0] == map[0] && iBreak[1] < map[1]) { + pos--; + } + } + //console.log('pos:',pos); + } + return pos; +} +function adjustForBreakPoints(pos,map,breakPoints) { + if (typeof pos == 'undefined') pos = currMathsInput.cursorPos; + if (typeof map == 'undefined') map = currMathsInput.cursorMap[currMathsInput.cursorPos]; + if (typeof breakPoints == 'undefined') breakPoints = currMathsInput.breakPoints; + + if (typeof breakPoints == 'object') { + for (var i = 0; i < breakPoints.length - 1; i++) { + var iBreak = currMathsInput.allMap[breakPoints[i]]; + //if (i > 0 && iBreak[1] - currMathsInput.allMap[breakPoints[i-1]][1] <= 7) continue; + //console.log(iBreak,iBreak[0] == map[0] && iBreak[1] < map[1]); + if (iBreak[0] == map[0] && iBreak[1] < map[1]) { + pos--; + } + } + } + + return pos; +} +function mathsInputCursorBlink() { + if (inputCursorState == true) {inputCursorState = false} else {inputCursorState = true}; + + currMathsInput.cursorctx.clearRect(0,0,1200,700); + currMathsInput.cursorCanvas.style.zIndex = currMathsInput.canvas.style.zIndex + 1; + + if (showCursorPos == true) { + for (var i = 0; i < currMathsInput.cursorMap.length; i++) { + var cPos = currMathsInput.textLoc; + + for (var j = 0; j < currMathsInput.cursorMap[i].length; j++) { + cPos = cPos[currMathsInput.cursorMap[i][j]]; + } + + // adjust cPos to account for difference between canvas and cursor canvas positions + cPos.left += (currMathsInput.data[100] - currMathsInput.cursorData[100]); + cPos.top += (currMathsInput.data[101] - currMathsInput.cursorData[101]); + + //console.log(cPos); + + currMathsInput.cursorctx.save(); + currMathsInput.cursorctx.strokeStyle = '#F00'; + currMathsInput.cursorctx.lineWidth = 2; + currMathsInput.cursorctx.beginPath(); + currMathsInput.cursorctx.moveTo(cPos.left, cPos.top); + currMathsInput.cursorctx.lineTo(cPos.left, cPos.top + cPos.height); + currMathsInput.cursorctx.closePath(); + currMathsInput.cursorctx.stroke(); + currMathsInput.cursorctx.restore(); + } + } else if (inputCursorState == true && currMathsInput.selected == false) { + var hAdjust = currMathsInput.data[100] - currMathsInput.cursorData[100]; + var vAdjust = currMathsInput.data[101] - currMathsInput.cursorData[101]; + //console.log('blink',inputCursorState,currMathsInput.selected,mathsInputCursor,currMathsInput.cursorPos,hAdjust,vAdjust); + currMathsInput.cursorctx.save(); + currMathsInput.cursorctx.strokeStyle = currMathsInput.textColor; + currMathsInput.cursorctx.lineWidth = 2; + currMathsInput.cursorctx.beginPath(); + currMathsInput.cursorctx.moveTo(hAdjust + mathsInputCursor.x, vAdjust + mathsInputCursor.top); + currMathsInput.cursorctx.lineTo(hAdjust + mathsInputCursor.x, vAdjust + mathsInputCursor.bottom); + currMathsInput.cursorctx.closePath(); + currMathsInput.cursorctx.stroke(); + currMathsInput.cursorctx.restore(); + } else { + //console.log('blink',inputCursorState,currMathsInput.selected,mathsInputCursor,currMathsInput.cursorPos); + } +} + +//var shiftOn = false; +//window.addEventListener('keyup', shiftKeyUp, false); +/*function shiftKeyUp(e) { + e.preventDefault(); + if (e.keyCode == 16) { + shiftOn = false; + } +}*/ +function hardKeyMathsInput(e) { // if a key is pressed via the hardware keyboard + e.preventDefault(); + if (inputState == true) { + var charCode = e.keyCode; // determine which key has been pressed + var keysToIgnore = [16,17,18,27,33,34,35,36,46,112,113,114,115,116,117,118,119,120,121,122,123,144,223]; + if (e.getModifierState('Control')) { + if (charCode == 88) { //CTRL-x + mathsInputCut(); + } else if (charCode == 67) { //CTRL-c + mathsInputCopy(); + } else if (charCode == 86) { //CTRL-v + mathsInputPaste(); + } + return; + } + if (e.getModifierState('Alt')) return; + switch (charCode) { + case 37 : // left arrow + currMathsInput.preText = ''; + currMathsInput.postText = ''; + if (e.getModifierState('Shift') == true && currMathsInput.cursorPos > 0) { + if (currMathsInput.selected == true) { + currMathsInput.selectPos[1] = [currMathsInput.cursorPos-1]; + setSelectPositions(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + currMathsInput.cursorPos--; + } else { + currMathsInput.selected = true; + currMathsInput.selectPos = [currMathsInput.cursorPos,currMathsInput.cursorPos-1]; + setSelectPositions(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + currMathsInput.cursorPos--; + } + } else if (currMathsInput.selected == true) { + removeSelectTags(); + mathsInputMapCursorPos(); + currMathsInput.cursorPos = Math.min(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selectPos = []; + mathsInputCursorCoords(); + currMathsInput.selected = false; + removeSelectTags(); + } else if (currMathsInput.cursorPos > 0) { + currMathsInput.cursorPos--; + mathsInputCursorCoords(); + } else { + mathsInputTabPrev(); + } + break; + case 38 : // up arrow + currMathsInput.preText = ''; + currMathsInput.postText = ''; + if (e.getModifierState('Shift') == true) { + if (currMathsInput.selected == false) { + currMathsInput.selectPos[0] = currMathsInput.cursorPos; + } + } else if (currMathsInput.selected == true) { + removeSelectTags(); + mathsInputMapCursorPos(); + currMathsInput.cursorPos = Math.min(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selectPos = []; + mathsInputCursorCoords(); + currMathsInput.selected = false; + removeSelectTags(); + } + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + // get the parent of the current string + var parent = currMathsInput.richText; + for (var i = 0; i < cursorPos.length - 3; i++) {parent = parent[cursorPos[i]]}; + if (parent[0] == 'frac' && cursorPos[cursorPos.length - 3] == 2) { + currMathsInput.cursorPos -= cursorPos[cursorPos.length - 1]; + currMathsInput.cursorPos--; + mathsInputCursorCoords(); + } else { + // check if there is a row above + var lowerBreakPoints = []; + for (var i = 0; i < currMathsInput.breakPoints.length - 1; i++) { + if (currMathsInput.allMap[currMathsInput.breakPoints[i]][0] < currMathsInput.cursorMap[currMathsInput.cursorPos][0] || (currMathsInput.allMap[currMathsInput.breakPoints[i]][0] == currMathsInput.cursorMap[currMathsInput.cursorPos][0] && currMathsInput.allMap[currMathsInput.breakPoints[i]][1] < currMathsInput.cursorMap[currMathsInput.cursorPos][1])) { + lowerBreakPoints.unshift(currMathsInput.allMap[currMathsInput.breakPoints[i]]); + } + } + if (lowerBreakPoints.length == 0) { + // cursor is on top line + if (shiftOn == true) { + if (currMathsInput.selected == true) { + currMathsInput.selectPos[1] = 0; + } else { + currMathsInput.selectPos = [currMathsInput.cursorPos,0]; + currMathsInput.selected = true; + } + setSelectPositions(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + currMathsInput.cursorPos = 0; + } + } else { + // get top point of current cursor position + var textLoc = currMathsInput.textLoc; + for (var i = 0; i < currMathsInput.cursorMap[currMathsInput.cursorPos].length; i++) { + textLoc = textLoc[currMathsInput.cursorMap[currMathsInput.cursorPos][i]]; + } + var pos = [textLoc.left,textLoc.top]; + // search through textLocs + var closestPos; + var closestDist; + for (var i = 0; i < currMathsInput.cursorMap.length; i++) { + // position must be less than lowerBreakPoints[0] + if (currMathsInput.cursorMap[i][0] < lowerBreakPoints[0][0] || (currMathsInput.cursorMap[i][0] == lowerBreakPoints[0][0] && currMathsInput.cursorMap[i][1] < lowerBreakPoints[0][1])) { + // if it is above the current line + + // position must not be less than lowerBreakPoints[1] + if (lowerBreakPoints.length > 1) { + if (currMathsInput.cursorMap[i][0] < lowerBreakPoints[1][0] || (currMathsInput.cursorMap[i][0] == lowerBreakPoints[1][0] && currMathsInput.cursorMap[i][1] < lowerBreakPoints[1][1])) continue; + } + + var loc = currMathsInput.textLoc; + for (var j = 0; j < currMathsInput.cursorMap[i].length; j++) { + loc = loc[currMathsInput.cursorMap[i][j]]; + } + if (typeof closestPos == 'undefined') { + closestPos = i; + closestDist = distancePointToLineSegment(pos,[loc.left,loc.top],[loc.left,loc.top+loc.height]); + } else { + var newDist = distancePointToLineSegment(pos,[loc.left,loc.top],[loc.left,loc.top+loc.height]); + if (newDist < closestDist) { + closestPos = i; + closestDist = newDist; + } + } + } + } + if (shiftOn == true) { + if (currMathsInput.selected == true) { + currMathsInput.selectPos[1] = closestPos; + } else { + currMathsInput.selectPos = [currMathsInput.cursorPos,closestPos]; + currMathsInput.selected = true; + } + setSelectPositions(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + currMathsInput.cursorPos = closestPos; + } else { + currMathsInput.cursorPos = closestPos; + mathsInputCursorCoords(); + } + } + } + break; + case 39 : // right arrow + currMathsInput.preText = ''; + currMathsInput.postText = ''; + if (e.getModifierState('Shift') == true && currMathsInput.cursorPos > 0) { + if (currMathsInput.selected == true) { + currMathsInput.selectPos[1] = [currMathsInput.cursorPos+1]; + setSelectPositions(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + currMathsInput.cursorPos++; + } else { + currMathsInput.selected = true; + currMathsInput.selectPos = [currMathsInput.cursorPos,currMathsInput.cursorPos+1]; + setSelectPositions(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + currMathsInput.cursorPos++; + } + } else if (currMathsInput.selected == true) { + removeSelectTags(); + mathsInputMapCursorPos(); + currMathsInput.cursorPos = Math.max(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selectPos = []; + mathsInputCursorCoords(); + currMathsInput.selected = false; + removeSelectTags(); + } else if (currMathsInput.cursorPos < currMathsInput.cursorMap.length - 1) { + currMathsInput.cursorPos++; + mathsInputCursorCoords(); + } else { + mathsInputTabNext(); + } + break; + case 40 : // down arrow + currMathsInput.preText = ''; + currMathsInput.postText = ''; + if (e.getModifierState('Shift') == true) { + if (currMathsInput.selected == false) { + currMathsInput.selectPos[0] = currMathsInput.cursorPos; + } + } else if (currMathsInput.selected == true) { + removeSelectTags(); + mathsInputMapCursorPos(); + currMathsInput.cursorPos = Math.max(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selectPos = []; + mathsInputCursorCoords(); + currMathsInput.selected = false; + removeSelectTags(); + } + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + // get the parent of the current string + var parent = currMathsInput.richText; + for (var aa = 0; aa < cursorPos.length - 3; aa++) {parent = parent[cursorPos[aa]]}; + if (parent[0] == 'frac' && cursorPos[cursorPos.length - 3] == 1) { + // move to the beginning of the denominator text + do { + currMathsInput.cursorPos++; + var cursorPos2 = currMathsInput.cursorMap[currMathsInput.cursorPos]; + // get the parent of the cursorPos + var parent2 = currMathsInput.richText; + for (var aa = 0; aa < cursorPos2.length - 3; aa++) {parent2 = parent2[cursorPos2[aa]]}; + } while ((parent2 !== parent) || (parent2 == parent && cursorPos2[cursorPos.length - 3] !== 2)); + // move to the end of the denominator text + do { + currMathsInput.cursorPos++; + var cursorPos2 = currMathsInput.cursorMap[currMathsInput.cursorPos]; + } while (cursorPos2.length >= cursorPos.length); + currMathsInput.cursorPos--; + mathsInputCursorCoords(); + } else { + var higherBreakPoints = []; + for (var i = 0; i < currMathsInput.breakPoints.length - 1; i++) { + if (currMathsInput.allMap[currMathsInput.breakPoints[i]][0] > currMathsInput.cursorMap[currMathsInput.cursorPos][0] || (currMathsInput.allMap[currMathsInput.breakPoints[i]][0] == currMathsInput.cursorMap[currMathsInput.cursorPos][0] && currMathsInput.allMap[currMathsInput.breakPoints[i]][1] > currMathsInput.cursorMap[currMathsInput.cursorPos][1])) { + higherBreakPoints.push(currMathsInput.allMap[currMathsInput.breakPoints[i]]); + } + } + if (higherBreakPoints.length == 0) { + // cursor is on bottom line + if (shiftOn == true) { + if (currMathsInput.selected == true) { + currMathsInput.selectPos[1] = currMathsInput.cursorMap.length - 1; + } else { + currMathsInput.selectPos = [currMathsInput.cursorPos,currMathsInput.cursorMap.length - 1]; + currMathsInput.selected = true; + } + setSelectPositions(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + currMathsInput.cursorPos = currMathsInput.cursorMap.length - 1; + } + } else { + // get bottom point of current cursor position + var textLoc = currMathsInput.textLoc; + for (var i = 0; i < currMathsInput.cursorMap[currMathsInput.cursorPos].length; i++) { + textLoc = textLoc[currMathsInput.cursorMap[currMathsInput.cursorPos][i]]; + } + var pos = [textLoc.left,textLoc.top+textLoc.height]; + // search through textLocs + var closestPos; + var closestDist; + for (var i = 0; i < currMathsInput.cursorMap.length; i++) { + // position must be more than higherBreakPoints[0] + if (currMathsInput.cursorMap[i][0] > higherBreakPoints[0][0] || (currMathsInput.cursorMap[i][0] == higherBreakPoints[0][0] && currMathsInput.cursorMap[i][1] > higherBreakPoints[0][1])) { + // if it is above the current line + + // position must not be less than higherBreakPoints[1] + if (higherBreakPoints.length > 1) { + if (currMathsInput.cursorMap[i][0] > higherBreakPoints[1][0] || (currMathsInput.cursorMap[i][0] == higherBreakPoints[1][0] && currMathsInput.cursorMap[i][1] > higherBreakPoints[1][1])) continue; + } + var loc = currMathsInput.textLoc; + for (var j = 0; j < currMathsInput.cursorMap[i].length; j++) { + loc = loc[currMathsInput.cursorMap[i][j]]; + } + if (typeof closestPos == 'undefined') { + closestPos = i; + closestDist = distancePointToLineSegment(pos,[loc.left,loc.top],[loc.left,loc.top+loc.height]); + } else { + var newDist = distancePointToLineSegment(pos,[loc.left,loc.top],[loc.left,loc.top+loc.height]); + if (newDist < closestDist) { + closestPos = i; + closestDist = newDist; + } + } + } + } + if (shiftOn == true) { + if (currMathsInput.selected == true) { + currMathsInput.selectPos[1] = closestPos; + } else { + currMathsInput.selectPos = [currMathsInput.cursorPos,closestPos]; + currMathsInput.selected = true; + } + setSelectPositions(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + currMathsInput.cursorPos = closestPos; + } else { + currMathsInput.cursorPos = closestPos; + mathsInputCursorCoords(); + } + } + + } + break; + case 8 : // backspace key pressed + currMathsInput.preText = ''; + currMathsInput.postText = ''; + if (currMathsInput.selected == true) { + currMathsInput.cursorPos = Math.min(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selPos = []; + currMathsInput.selected = false; + deleteSelected(); + removeSelectTags(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + mathsInputCursorCoords(); + } else if (currMathsInput.cursorPos > 0) { + removeSelectTags(); + // get the relevant string from currMathsInput.richText + // ie. currMathsInput.richText[cursorPos[0]][cursorPos[1]]...[cursorPos[n]] + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + var pos = cursorPos[cursorPos.length - 1]; + pos = adjustForBreakPoints(pos); + + var text = currMathsInput.richText; + for (var i = 0; i < cursorPos.length - 1; i++) {text = text[cursorPos[i]]}; + + var parent; + if (cursorPos.length > 1) { + parent = currMathsInput.richText; + for (var i = 0; i < cursorPos.length - 2; i++) {parent = parent[cursorPos[i]]}; + } + var grandParent; + if (cursorPos.length > 2) { + grandParent = currMathsInput.richText; + for (var i = 0; i < cursorPos.length - 3; i++) {grandParent = grandParent[cursorPos[i]]}; + } + + if (text !== '') { + if (pos !== 0) { + //console.clear(); + //console.log(1,text,pos,text.slice(pos-1)); + if (text.slice(pos-6).indexOf('<
>') == 0) { + text = text.slice(0, pos-6) + text.slice(pos); + } else { + text = text.slice(0, pos-1) + text.slice(pos); + } + //console.log(2,text); + // replace the string + var evalString = 'currMathsInput.richText' + for (var aa = 0; aa < cursorPos.length - 1; aa++) { + // ugly string creation apprach in order to use eval() + evalString += '[' + cursorPos[aa] + ']'; + } + eval(evalString + ' = text;'); + } + } else { + if (parent.length == 1) { // ie. empty string is only sub-element + var elemsOneParam = ['sqrt', 'pow', 'power', 'subs', 'subscript', 'sin', 'cos', 'tan', 'sin-1', 'cos-1', 'tan-1', 'log', 'ln', 'abs', 'exp', 'sigma1', 'int1', 'vectorArrow', 'bar', 'hat', 'recurring']; + var elemsTwoParams = ['root', 'frac', 'logBase', 'colVector2d', 'lim']; + var elemsThreeParams = ['sigma2', 'int2', 'colVector3d', 'mixedNum']; + if (elemsOneParam.indexOf(grandParent[0]) > -1 || (elemsTwoParams.indexOf(grandParent[0]) > -1 && cursorPos[cursorPos.length - 3] == 1) || (elemsThreeParams.indexOf(grandParent[0]) > -1 && cursorPos[cursorPos.length - 3] == 1)) { // conditions to delete + // replace grandParent with ""; + var evalString = 'currMathsInput.richText' + for (var i = 0; i < cursorPos.length - 3; i++) { + // ugly string creation apprach in order to use eval() + evalString += '[' + cursorPos[i] + ']'; + } + eval(evalString + ' = "";'); + } + } + } + //console.log(JSON.stringify(currMathsInput.richText)); + mathsInputMapCursorPos(); + currMathsInput.cursorPos--; + mathsInputCursorCoords(); + } + break; + case 46 : // delete key + currMathsInput.preText = ''; + currMathsInput.postText = ''; + if (currMathsInput.selected == true) { + currMathsInput.cursorPos = Math.min(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selPos = []; + currMathsInput.selected = false; + deleteSelected(); + removeSelectTags(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + mathsInputCursorCoords(); + } else if (currMathsInput.cursorPos < currMathsInput.cursorMap.length - 1) { + // get the relevant string from currMathsInput.richText + // ie. currMathsInput.richText[cursorPos[0]][cursorPos[1]]...[cursorPos[n]] + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + var pos = cursorPos[cursorPos.length - 1]; + pos = adjustForBreakPoints(pos); + + var text = currMathsInput.richText; + for (var i = 0; i < cursorPos.length - 1; i++) {text = text[cursorPos[i]]}; + + if (cursorPos[cursorPos.length - 1] !== text.length) { + if (text.slice(pos).indexOf('<
>') == 0) { + text = text.slice(0,pos) + text.slice(pos+6); + } else if (text.slice(pos).indexOf('<<') == 0) { + //skip forward to end of tags + var text2 = text.slice(pos); + var endFound = false; + var charCount = 0; + do { + var c = text2.indexOf('>>') + 2; + charCount += c; + var text2 = text2.slice(c); + if (text2.indexOf('<<') !== 0) { + endFound = true; + } + } while (endFound == false); + pos += charCount; + text = text.slice(0,pos) + text.slice(pos+1); + } else { + text = text.slice(0,pos) + text.slice(pos+1); + } + // replace the string + var evalString = 'currMathsInput.richText' + for (var i = 0; i < cursorPos.length - 1; i++) { + // ugly string creation apprach in order to use eval() + evalString += '[' + cursorPos[i] + ']'; + } + eval(evalString + ' = text;'); + mathsInputMapCursorPos(); + mathsInputCursorCoords(); + } + } + break; + case 9 : // tab key pressed + if (typeof draw !== 'undefined' && draw.drawMode == 'textEdit') { + if (e.getModifierState('Control')) { + mathsInputTab(10); + } else if (e.getModifierState('Shift')) { + mathsInputTab(5); + } else { + mathsInputTab(); + } + } else { + mathsInputTabNext(); + } + break; + case 13 : // enter key pressed + if (currMathsInput.maxLines > 1 || (typeof draw !== 'undefined' && draw.drawMode == 'textEdit')) { + if (currMathsInput.selected == true) { + currMathsInput.cursorPos = Math.min(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selPos = []; + currMathsInput.selected = false; + deleteSelected(); + removeSelectTags(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + mathsInputCursorCoords(); + } + mathsInputNewLine(); + } else { + endMathsInput(e); + } + break; + case 27 : // escape key pressed + endMathsInput(e); + break; + /*case 16 : //shift key pressed + shiftOn = true; + window.addEventListener('keyup', shiftKeyUp, false); + break;*/ + default : + + // need to protect against << or >> being entered + + if (currMathsInput.text[0].length >= currMathsInput.maxChars) {break}; + if (e.getModifierState('Shift') == true && charCode == 54/* && currMathsInput.cursorMap[currMathsInput.cursorPos].length == 2*/) { // if hat symbol is used and current text element is a text string + mathsInputPow(); + break; + } + if (keysToIgnore.indexOf(charCode) == -1) { + + if (currMathsInput.selected == true) { + currMathsInput.cursorPos = Math.min(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selPos = []; + currMathsInput.selected = false; + deleteSelected(); + removeSelectTags(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + mathsInputCursorCoords(); + } + + var caps = false; + if (e.getModifierState('Shift') || e.getModifierState('CapsLock')) caps = true + + for (var ii = 0; ii < charMap.length; ii++) { + if (charCode == charMap[ii][0]) { + if (caps) { + charCode = charMap[ii][2]; + } else { + charCode = charMap[ii][1]; + } + } + } + // if it is a letter key and shift is not pressed, use lower case instead of upper case + if (!caps && charCode >= 65 && charCode <= 90) charCode += 32; + var keyValue = String.fromCharCode(charCode); + + // get the relevant string from currMathsInput.richText + // ie. currMathsInput.richText[cursorPos[0]][cursorPos[1]]...[cursorPos[n]] + //console.log(currMathsInput.cursorMap,currMathsInput.cursorPos,currMathsInput.cursorMap[currMathsInput.cursorPos]); + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + + /* + console.log('currMathsInput.cursorMap:',JSON.stringify(currMathsInput.cursorMap,null,4),currMathsInput.cursorMap.length-1); + console.log('currMathsInput.cursorPos:',JSON.stringify(currMathsInput.cursorPos,null,4)); + console.log('currMathsInput.cursorMap[currMathsInput.cursorPos]:',JSON.stringify(currMathsInput.cursorMap[currMathsInput.cursorPos],null,4)); + console.log('currMathsInput.richText[0]:',JSON.stringify(currMathsInput.richText[0],null,4),currMathsInput.richText[0].length-1); + console.log('cursorPos:',JSON.stringify(cursorPos,null,4)); + */ + + var text = currMathsInput.richText; + for (var i = 0; i < cursorPos.length - 1; i++) { + text = text[cursorPos[i]]; + } + + var slicePos = cursorPos[cursorPos.length - 1]; + slicePos = adjustForBreakPoints(slicePos); + + /*var prev = 0; + var breakPoints = currMathsInput.breakPoints; + for (var b = 0; b < breakPoints.length; b++) { + prev = breakPoints[b]; + }*/ + + //console.log(text,slicePos); + + slicePos = mathsInputAvoidTagSplit(text,slicePos); + /* this section replaced by the function above + // check that a tag is not being split - if so adjust slicePos + var leftText = text.slice(0,slicePos); + var rightText = text.slice(slicePos); + var tagLeft = false; + var tagLeftCount = 0; + for (var i = 0; i < leftText.length; i++) { + tagLeftCount++; + //console.log('left '+i+':',leftText.slice(leftText.length - i)); + if (leftText.slice(leftText.length - i).indexOf('>>') == 0) break; + if (leftText.slice(leftText.length - i).indexOf('<<') == 0) { + tagLeft = true; + break; + } + } + var tagRight = false; + var tagRightCount = 0; + for (var j = 0; j < rightText.length; j++) { + tagRightCount++; + //console.log('right '+j+':',rightText.slice(j)); + if (rightText.slice(j).indexOf('<<') == 0) break; + if (rightText.slice(j).indexOf('>>') == 0) { + tagRight = true; + break; + } + } + //console.log(tagLeft,tagRight,tagLeftCount,tagRightCount); + if (tagLeft == true && tagRight == true) { + if (tagLeftCount <= tagRightCount) { + slicePos -= tagLeftCount; + } else { + slicePos += tagRightCount; + } + } + // test if '<',slicePos,'<' or '>',slicePos,'>' + if (leftText.slice(-1) == '<' && rightText.slice(0,1) == '<' && rightText.slice(0,2) !== '<<') slicePos--; + if (leftText.slice(-1) == '>' && leftText.slice(-2) !== '>>' && rightText.slice(0,1) == '>') slicePos++;*/ + + if (un(currMathsInput.preText) || currMathsInput.preText == null) { + var pre = ""; + } else { + var pre = currMathsInput.preText; + } + if (un(currMathsInput.postText) || currMathsInput.postText == null) { + var post = ""; + } else { + var post = currMathsInput.postText; + } + + text = text.slice(0, slicePos) + pre + keyValue + post + text.slice(slicePos); + + // replace the string + var evalString = 'currMathsInput.richText' + for (var i = 0; i < cursorPos.length - 1; i++) { + // ugly string creation apprach in order to use eval() + evalString += '[' + cursorPos[i] + ']'; + } + eval(evalString + ' = text;'); + + //logText(); + mathsInputMapCursorPos(); + + // set the last number of cursorMap[cursorPos+1] to be one more than cursorMap[cursorPos] + //currMathsInput.cursorMap[currMathsInput.cursorPos + 1][currMathsInput.cursorMap[currMathsInput.cursorPos + 1].length - 1] = currMathsInput.cursorMap[currMathsInput.cursorPos][currMathsInput.cursorMap[currMathsInput.cursorPos].length - 1] + 1; + + currMathsInput.cursorPos += 1; + mathsInputCursorCoords(); + currMathsInput.preText = ''; + currMathsInput.postText = ''; + //logText(); + } + } + } +} +function softKeyMathsInput(e) { + //console.log(inputState); + if (inputState == true) { + /* + if (mathsInputDoubleInput == true) { + return; + } else { + mathsInputDoubleInput = true; + setTimeout(function() { + mathsInputDoubleInput = false; + }, 250); + } + */ + var keyNum; + var keyValue; + if (keyboard[pageIndex]) { + keyNum = key1[pageIndex].indexOf(e.target); + keyValue = key1Data[pageIndex][keyNum][6]; + } + //console.log(keyValue); + if (keyValue == 'delete') { // if it's the delete button + currMathsInput.preText = ''; + currMathsInput.postText = ''; + if (currMathsInput.selected == true) { + currMathsInput.cursorPos = Math.min(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selPos = []; + currMathsInput.selected = false; + deleteSelected(); + removeSelectTags(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + mathsInputCursorCoords(); + } else if (currMathsInput.cursorPos > 0) { + // get the relevant string from currMathsInput.richText + // ie. currMathsInput.richText[cursorPos[0]][cursorPos[1]]...[cursorPos[n]] + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + //console.log(cursorPos); + var pos = cursorPos[cursorPos.length - 1]; + //console.log(pos); + pos = adjustForBreakPoints(pos); + //console.log(pos); + + var text = currMathsInput.richText; + for (var i = 0; i < cursorPos.length - 1; i++) {text = text[cursorPos[i]]}; + + var parent; + if (cursorPos.length > 1) { + parent = currMathsInput.richText; + for (var i = 0; i < cursorPos.length - 2; i++) {parent = parent[cursorPos[i]]}; + } + var grandParent; + if (cursorPos.length > 2) { + grandParent = currMathsInput.richText; + for (var i = 0; i < cursorPos.length - 3; i++) {grandParent = grandParent[cursorPos[i]]}; + } + + if (text !== '') { + if (pos !== 0) { + //console.log(1,text,text.length,pos,text.slice(0, pos-1) + text.slice(pos)); + if (text.slice(pos-6).indexOf('<
>') == 0) { + text = text.slice(0, pos-6) + text.slice(pos); + } else { + text = text.slice(0, pos-1) + text.slice(pos); + } + //console.log(2,text); + // replace the string + var evalString = 'currMathsInput.richText' + for (var aa = 0; aa < cursorPos.length - 1; aa++) { + // ugly string creation apprach in order to use eval() + evalString += '[' + cursorPos[aa] + ']'; + } + eval(evalString + ' = text;'); + } + } else { + if (parent.length == 1) { // ie. empty string is only sub-element + var elemsOneParam = ['sqrt', 'pow', 'power', 'subs', 'subscript', 'sin', 'cos', 'tan', 'sin-1', 'cos-1', 'tan-1', 'log', 'ln', 'abs', 'exp', 'sigma1', 'int1', 'vectorArrow', 'bar', 'hat', 'recurring']; + var elemsTwoParams = ['root', 'frac', 'logBase', 'colVector2d', 'lim']; + var elemsThreeParams = ['sigma2', 'int2', 'colVector3d', 'mixedNum']; + if (elemsOneParam.indexOf(grandParent[0]) > -1 || (elemsTwoParams.indexOf(grandParent[0]) > -1 && cursorPos[cursorPos.length - 3] == 1) || (elemsThreeParams.indexOf(grandParent[0]) > -1 && cursorPos[cursorPos.length - 3] == 1)) { // conditions to delete elements + // replace grandParent with ""; + var evalString = 'currMathsInput.richText' + for (var i = 0; i < cursorPos.length - 3; i++) { + // ugly string creation apprach in order to use eval() + evalString += '[' + cursorPos[i] + ']'; + } + eval(evalString + ' = "";'); + } + } + } + mathsInputMapCursorPos(); + currMathsInput.cursorPos--; + mathsInputCursorCoords(); + } + } else { + // type text char + if (currMathsInput.text[0].length >= currMathsInput.maxChars) {return}; + + if (currMathsInput.selected == true) { + currMathsInput.cursorPos = Math.min(currMathsInput.selectPos[0],currMathsInput.selectPos[1]); + currMathsInput.selPos = []; + currMathsInput.selected = false; + deleteSelected(); + removeSelectTags(); + drawMathsInputText(currMathsInput); + mathsInputMapCursorPos(); + mathsInputCursorCoords(); + } + + // get the relevant string from currMathsInput.richText + // ie. currMathsInput.richText[cursorPos[0]][cursorPos[1]]...[cursorPos[n]] + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + + var text = currMathsInput.richText; + for (var aa = 0; aa < cursorPos.length - 1; aa++) { + text = text[cursorPos[aa]]; + } + + /* + // test if sin, cos, tan, ln or log have been written: + if (keyValue == 'n' && text.length > 1 && text.slice(cursorPos[cursorPos.length - 1] - 2, cursorPos[cursorPos.length - 1]) == 'si') { + // get cursorPos + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + // get parent + var parent = currMathsInput.richText; + for (var aa = 0; aa < cursorPos.length - 1; aa++) {parent = parent[cursorPos[aa]]}; + // get position of cursor in parent string + var pos = cursorPos[cursorPos.length - 1]; + var parentPos = cursorPos[cursorPos.length - 2]; + + var evalString = 'currMathsInput.richText' + for (var aa = 0; aa < cursorPos.length - 2; aa++) { + // ugly string creation apprach in order to use eval() + evalString += '[' + cursorPos[aa] + ']'; + } + eval(evalString + ".splice(parentPos, 1, parent.slice(0, pos - 2), ['sin', ['']], parent.slice(pos));"); + mathsInputMapCursorPos(); + currMathsInput.cursorPos--; + mathsInputCursorCoords(); + return; + } + if (keyValue == 's' && text.length > 1 && text.slice(cursorPos[cursorPos.length - 1] - 2, cursorPos[cursorPos.length - 1]) == 'co') { + // get cursorPos + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + // get parent + var parent = currMathsInput.richText; + for (var aa = 0; aa < cursorPos.length - 1; aa++) {parent = parent[cursorPos[aa]]}; + // get position of cursor in parent string + var pos = cursorPos[cursorPos.length - 1]; + var parentPos = cursorPos[cursorPos.length - 2]; + + var evalString = 'currMathsInput.richText' + for (var aa = 0; aa < cursorPos.length - 2; aa++) { + // ugly string creation apprach in order to use eval() + evalString += '[' + cursorPos[aa] + ']'; + } + eval(evalString + ".splice(parentPos, 1, parent.slice(0, pos - 2), ['cos', ['']], parent.slice(pos));"); + mathsInputMapCursorPos(); + currMathsInput.cursorPos--; + mathsInputCursorCoords(); + return; + } + if (keyValue == 'n' && text.length > 1 && text.slice(cursorPos[cursorPos.length - 1] - 2, cursorPos[cursorPos.length - 1]) == 'ta') { + console.log('tan'); + // get cursorPos + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + // get parent + var parent = currMathsInput.richText; + for (var aa = 0; aa < cursorPos.length - 1; aa++) {parent = parent[cursorPos[aa]]}; + // get position of cursor in parent string + var pos = cursorPos[cursorPos.length - 1]; + var parentPos = cursorPos[cursorPos.length - 2]; + + var evalString = 'currMathsInput.richText' + for (var aa = 0; aa < cursorPos.length - 2; aa++) { + // ugly string creation apprach in order to use eval() + evalString += '[' + cursorPos[aa] + ']'; + } + eval(evalString + ".splice(parentPos, 1, parent.slice(0, pos - 2), ['tan', ['']], parent.slice(pos));"); + mathsInputMapCursorPos(); + currMathsInput.cursorPos--; + mathsInputCursorCoords(); + return; + } + if (keyValue == 'n' && text.length > 0 && text.slice(cursorPos[cursorPos.length - 1] - 1, cursorPos[cursorPos.length - 1]) == 'l') { + console.log('ln'); + // get cursorPos + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + // get parent + var parent = currMathsInput.richText; + for (var aa = 0; aa < cursorPos.length - 1; aa++) {parent = parent[cursorPos[aa]]}; + // get position of cursor in parent string + var pos = cursorPos[cursorPos.length - 1]; + var parentPos = cursorPos[cursorPos.length - 2]; + + var evalString = 'currMathsInput.richText' + for (var aa = 0; aa < cursorPos.length - 2; aa++) { + // ugly string creation apprach in order to use eval() + evalString += '[' + cursorPos[aa] + ']'; + } + eval(evalString + ".splice(parentPos, 1, parent.slice(0, pos - 1), ['ln', ['']], parent.slice(pos));"); + mathsInputMapCursorPos(); + mathsInputCursorCoords(); + return; + } + if (keyValue == 'g' && text.length > 1 && text.slice(cursorPos[cursorPos.length - 1] - 2, cursorPos[cursorPos.length - 1]) == 'lo') { + console.log('log'); + // get cursorPos + var cursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos]; + // get parent + var parent = currMathsInput.richText; + for (var aa = 0; aa < cursorPos.length - 1; aa++) {parent = parent[cursorPos[aa]]}; + // get position of cursor in parent string + var pos = cursorPos[cursorPos.length - 1]; + var parentPos = cursorPos[cursorPos.length - 2]; + + var evalString = 'currMathsInput.richText' + for (var aa = 0; aa < cursorPos.length - 2; aa++) { + // ugly string creation apprach in order to use eval() + evalString += '[' + cursorPos[aa] + ']'; + } + eval(evalString + ".splice(parentPos, 1, parent.slice(0, pos - 2), ['log', ['']], parent.slice(pos));"); + mathsInputMapCursorPos(); + currMathsInput.cursorPos--; + mathsInputCursorCoords(); + return; + } + */ + + var slicePos = cursorPos[cursorPos.length - 1]; + slicePos = adjustForBreakPoints(slicePos); + //console.log(slicePos); + + slicePos = mathsInputAvoidTagSplit(text,slicePos); + /* the section below is replaced by the function above + // check that a tag is not being split - if so adjust slicePos + var leftText = text.slice(0,slicePos); + var rightText = text.slice(slicePos); + var tagLeft = false; + var tagLeftCount = 0; + for (var i = 0; i < leftText.length; i++) { + tagLeftCount++; + //console.log('left '+i+':',leftText.slice(leftText.length - i)); + if (leftText.slice(leftText.length - i).indexOf('>>') == 0) break; + if (leftText.slice(leftText.length - i).indexOf('<<') == 0) { + tagLeft = true; + break; + } + } + var tagRight = false; + var tagRightCount = 0; + for (var j = 0; j < rightText.length; j++) { + tagRightCount++; + //console.log('right '+j+':',rightText.slice(j)); + if (rightText.slice(j).indexOf('<<') == 0) break; + if (rightText.slice(j).indexOf('>>') == 0) { + tagRight = true; + break; + } + } + //console.log(tagLeft,tagRight,tagLeftCount,tagRightCount); + if (tagLeft == true && tagRight == true) { + if (tagLeftCount <= tagRightCount) { + slicePos -= tagLeftCount; + } else { + slicePos += tagRightCount; + } + } + // test if '<',slicePos,'<' or '>',slicePos,'>' + if (leftText.slice(-1) == '<' && rightText.slice(0,1) == '<' && rightText.slice(0,2) !== '<<') slicePos--; + if (leftText.slice(-1) == '>' && leftText.slice(-2) !== '>>' && rightText.slice(0,1) == '>') slicePos++;*/ + + + if (un(currMathsInput.preText) || currMathsInput.preText == null) { + var pre = ""; + } else { + var pre = currMathsInput.preText; + } + if (un(currMathsInput.postText) || currMathsInput.postText == null) { + var post = ""; + } else { + var post = currMathsInput.postText; + } + + text = text.slice(0, slicePos) + pre + keyValue + post + text.slice(slicePos); + + // replace the string + var evalString = 'currMathsInput.richText' + for (var i = 0; i < cursorPos.length - 1; i++) { + // ugly string creation approach in order to use eval() + evalString += '[' + cursorPos[i] + ']'; + } + eval(evalString + ' = text;'); + + // if followed by a power, test if a baseSpacer is now required + if (cursorPos[cursorPos.length - 1] + 1 == text.length && typeof currMathsInput.cursorMap[currMathsInput.cursorPos + 1] !== 'undefined') { + // get the next element after the parent + var nextCursorPos = currMathsInput.cursorMap[currMathsInput.cursorPos + 1]; + var nextElem = currMathsInput.richText; + for (var aa = 0; aa < nextCursorPos.length - 3; aa++) {nextElem = nextElem[nextCursorPos[aa]]}; + if (nextElem[0] == 'power' || nextElem[0] == 'subs') { + var baseSpacer = true; + if (/[a-zA-Z0-9)]/g.test(keyValue) == true) baseSpacer = false + var evalString = 'currMathsInput.richText' + for (var aa = 0; aa < nextCursorPos.length - 3; aa++) { + // ugly string creation apprach in order to use eval() + evalString += '[' + nextCursorPos[aa] + ']'; + } + eval(evalString + "[1] = " + baseSpacer + ";"); + } + } + + mathsInputMapCursorPos(); + currMathsInput.cursorPos += 1; + mathsInputCursorCoords(); + currMathsInput.preText = ''; + currMathsInput.postText = ''; + } + } +} + +//var date = new Date(); +//var prevMathsInputTime = date.getTime(); +var mathsInputDoubleInput = false; + +/*function setMathsInputBackColor(input,color) { + input.backColor = color; + drawMathsInputText(input); +}*/ +function measureMathsInputText(input) { + var leftPoint = 10; + if (input.textAlign == 'center') {leftPoint = 0.5 * input.data[2]}; + if (typeof input.richText[input.richText.length - 1] !== 'string') {input.richText.push('')}; + return drawMathsText(input.ctx, input.richText, input.fontSize, leftPoint, 0.5 * input.data[3], input.algText, input.textLoc, input.textAlign, 'middle', input.textColor, 'measure'); +} +function drawMathsInputText(input,ctxLocal,sf,useRelPos) { + if (typeof sf == 'undefined') sf = 1; + if (typeof ctxLocal == 'undefined') { + input.ctx.clearRect(0,0,input.data[102],input.data[103]); + var ctx = input.ctx; + var ownCanvas = true; + } else { + var ctx = ctxLocal; // will draw to a different canvas + var ownCanvas = false; + } + if (typeof input.richText[input.richText.length-1] !== 'string') {input.richText.push('')}; + var leftPoint = 10*sf; + var topPoint = 0; + + if (typeof input.varSize == 'object') { + if (input.textAlign == 'left') { + leftPoint = input.varSize.padding*sf; + if (typeof input.varSize.padding !== 'number') leftPoint = 10*sf; + } else if (input.textAlign == 'center') { + leftPoint = 0; + } else if (input.textAlign == 'right') { + leftPoint = 0 - input.varSize.padding*sf; + if (typeof input.varSize.padding !== 'number') leftPoint = -10*sf; + } + var minTightWidth = input.varSize.minWidth*sf || 50*sf; + var minTightHeight = input.varSize.minHeight*sf || 50*sf; + var padding = input.varSize.padding*sf; + var maxWidth = input.varSize.maxWidth*sf || input.data[102]*sf; + var maxHeight = input.varSize.maxHeight*sf || input.data[103]*sf; + } else { + if (input.textAlign == 'left') { + leftPoint = 10*sf; + } else if (input.textAlign == 'center') { + leftPoint = 0; + } else if (input.textAlign == 'right') { + + } + var minTightWidth = 50*sf; + var minTightHeight = 50*sf; + var padding = 0.01*sf; + var maxWidth = input.data[102]*sf; + var maxHeight = input.data[103]*sf; + } + + if (input.border == true) { + if (typeof input.varSize == 'object') { + var border = { + type:'tight', + color:input.backColor, + borderColor:input.borderColor, + borderWidth:input.borderWidth*sf, + dash:input.borderDash, + radius:input.borderRadius*sf || input.radius*sf || 0 + } + } else { + var radius = input.borderRadius*sf || input.radius*sf || 0; + var borderLeft = input.borderWidth*sf/2; + var borderTop = input.borderWidth*sf/2; + if (ownCanvas == false) { + borderLeft += input.data[100]*sf; + borderTop += input.data[101]*sf; + } + roundedRect(ctx,borderLeft,borderTop,input.data[102]*sf-input.borderWidth*sf,input.data[103]*sf-input.borderWidth*sf,radius,input.borderWidth*sf,input.borderColor,input.backColor,input.borderDash); + var border = {type:'none'}; + } + } else { + var border = {type:'none'}; + } + + if (ownCanvas == false) { + if (!isNaN(input.data[100])) { + leftPoint += input.data[100]*sf; + topPoint += input.data[101]*sf; + } else { + leftPoint += input.data[0]*sf; + topPoint += input.data[1]*sf; + } + if (boolean(useRelPos,true) == true && typeof draw !== 'undefined' && typeof draw !== 'undefined' && typeof draw.drawRelPos !== 'undefined') { + leftPoint -= draw.drawRelPos[0]*sf; + topPoint -= draw.drawRelPos[1]*sf; + } + } + + var lineSpacingFactor = input.lineSpacingFactor || 1.2; + var lineSpacingStyle = input.lineSpacingStyle || 'variable'; + + var drawText = text({ + context:ctx, + textArray:input.richText, + left:leftPoint, + top:topPoint, + width:maxWidth, + height:maxHeight, + allowSpaces:true, + textAlign:input.textAlign, + vertAlign:input.vertAlign, + minTightWidth:minTightWidth, + minTightHeight:minTightHeight, + padding:padding, + box:border, + sf:sf, + lineSpacingFactor:lineSpacingFactor, + spacingStyle:lineSpacingStyle + }); + + if (ownCanvas == true) { + input.textLoc = drawText.textLoc; + input.breakPoints = drawText.breakPoints; + input.tightRect = drawText.tightRect; + input.totalTextWidth = drawText.totalTextWidth; + input.maxWordWidth = drawText.maxWordWidth; + } + + /*if (typeof input.drawPath == 'object') { + if (input.tightRect[3] > input.drawPath.obj[0].height) { + input.drawPath.obj[0].height = input.tightRect[3]; + input.varSize.maxHeight = input.tightRect[3]; + updateBorder(input.drawPath); + drawCanvasPaths(); + } + }*/ + + if (typeof input.varSize == 'object' && ownCanvas == true) { + // resize cursor canvas + if (typeof input.tightRect == 'object') { + input.cursorData[100] = input.data[100] + input.tightRect[0]; + input.cursorData[101] = input.data[101] + input.tightRect[1]; + input.cursorData[102] = input.tightRect[2]; + input.cursorData[103] = input.tightRect[3]; + } else { + input.cursorData[100] = input.data[100]; + input.cursorData[101] = input.data[101]; + input.cursorData[102] = input.data[102]; + input.cursorData[103] = input.data[103]; + } + input.cursorCanvas.width = input.cursorData[102]; + input.cursorCanvas.height = input.cursorData[103]; + resizeCanvas(input.cursorCanvas,input.cursorData[100],input.cursorData[101],input.cursorData[102],input.cursorData[103]); + } + + if (!un(draw) && draw.drawMode == 'textEdit') { + var obj = sel(); + if (obj.type == 'text' && obj.mathsInput.canvas == input.canvas) { + if (input.cursorData[103] > obj.height || input.cursorData[102] > obj.width) { + for (var i = 0; i < draw.path.length; i++) { + if (draw.path[i].selected == true) { + if (input.cursorData[103] > obj.height) obj.height = input.cursorData[103]; + if (input.cursorData[102] > obj.width) obj.width = input.cursorData[102]; + updateBorder(draw.path[i]); + } + } + } + } + calcCursorPositions(); + drawSelectCanvas(); + } + //console.clear(); + //if (currMathsInput.id == 1) console.log(currMathsInput.id,JSON.stringify(currMathsInput.richText)); +} +function mathsInputAddChar(mInput,char) { + if (un(mInput)) mInput = currMathsInput; + // get last position + var cursorPos = mInput.cursorMap[mInput.cursorMap.length-1]; + var loc = mInput.textLoc; + for (var aa = 0 ; aa < cursorPos.length; aa++) { + loc = loc[cursorPos[aa]]; + } + //console.log(cursorPos); + + var font,fontSize,color,bold,italic; + arrayHandler(mInput.richText); + var newLoc = drawMathsText(mInput.ctx,char,fontSize,loc.left,loc.top+0.5*loc.height,false,[],'left','middle',color,'draw','none',bold,italic,font,false,1).textLoc; + + var evalString = ''; + for (var aa = 0 ; aa < cursorPos.length-1; aa++) { + evalString += '['+cursorPos[aa]+']'; + } + eval('mInput.richText'+evalString+'=mInput.richText'+evalString+'+char'); + eval('mInput.textLoc'+evalString+'.push(newLoc[0][1])'); + cursorPos[cursorPos.length-1]++; + mInput.cursorMap.push(cursorPos); + + //console.log(mInput); + + function markupTag(tag) { + if (tag.indexOf('< -1) bold = true; + if (tag.indexOf('false') > -1) bold = false; + } else if (tag.indexOf('< -1) italic = true; + if (tag.indexOf('false') > -1) italic = false; + } + } + function arrayHandler(arr) { + //console.log(JSON.stringify(arr)); + for (var i = 0; i < arr.length; i++) { + if (typeof arr[i] == 'object') { + arrayHandler(arr[i]); + } else if (typeof arr[i] == 'string') { + var splitText = splitMarkup(arr[i]); + for (var splitElem = 0; splitElem < splitText.length; splitElem++) { + if (splitText[splitElem].indexOf('<<') == 0 && splitText[splitElem].indexOf('<
>') !== 0) { + markupTag(splitText[splitElem],true); + } + } + } + } + } +} +function createJsString (angleMode) { + if (typeof angleMode == 'undefined') { + angleMode = currMathsInput.angleMode || 'deg'; + } + + var depth = 0; + var jsArray = ['']; + var js = ''; + var algArray = ['']; + var alg = ''; + var exceptions = ['Math.pow','Math.sqrt','Math.PI','Math.sin','Math.cos','Math.tan','Math.asin','Math.acos','Math.atan','Math.e','Math.log','Math.abs','sin','cos','tan']; + var position = [0]; + + for (var p = 0; p < currMathsInput.richText.length; p++) { + //console.log('Before ' + p + ' base element(s):', jsArray); + subJS(currMathsInput.richText[p],true); + position[depth]++; + //console.log('After ' + p + ' base elements:', jsArray); + } + + js = jsArray[0]; + alg = algArray[0]; + //console.log(js); + + function removeAllTagsFromString(str) { + for (var char = str.length-1; char > -1; char--) { + if (str.slice(char).indexOf('>>') == 0 && str.slice(char-1).indexOf('>>>') !== 0) { + for (var char2 = char-2; char2 > -1; char2--) { + if (str.slice(char2).indexOf('<<') == 0) { + str = str.slice(0,char2) + str.slice(char+2); + char = char2; + break; + } + } + } + } + return str; + } + + function subJS(elem, addMultIfNecc) { + if (typeof addMultIfNecc !== 'boolean') addMultIfNecc = true; + //console.log('subJS', elem); + if (typeof elem == 'string') { + //console.log('string'); + var subText = replaceAll(elem, ' ', ''); // remove white space + subText = removeAllTagsFromString(subText); + + subText = subText.replace(/\u00D7/g, '*'); // replace multiplications signs with * + subText = subText.replace(/\u00F7/g, '/'); // replace division signs with / + subText = subText.replace(/\u2264/g, '<='); // replace signs with <= + subText = subText.replace(/\u2265/g, '>='); // replace signs with >= + for (var c = 0; c < subText.length - 2; c++) { + if (subText.slice(c).indexOf('sin') == 0 || subText.slice(c).indexOf('cos') == 0 || subText.slice(c).indexOf('tan') == 0) { + if (subText.slice(c).indexOf('(') == 3) { + if (angleMode == 'rad') { + subText = subText.slice(0,c)+'Math.'+subText.slice(c); + c += 5; + } else { + subText = subText.slice(0,c)+'Math.'+subText.slice(c,c+4)+'(Math.PI/180)*'+subText.slice(c+4); + c += 19; + } + } + } + } + subText = timesBeforeLetters(subText); + // if following frac or power, add * if necessary + if (addMultIfNecc == true && jsArray[depth] !== '' && elem !== '' && /[ \+\-\=\u00D7\u00F7\u2264\u2265\<\>\])]/.test(elem.charAt(0)) == false) subText = '*' + subText; + jsArray[depth] += subText; + algArray[depth] += subText; + return; + } else if (elem[0] == 'frac') { + //console.log('frac'); + var subText = ''; + var subText2 = ''; + // if not proceeded by an operator, put a times sign in + if (jsArray[depth] !== '' && /[\+\-\u00D7\u00F7\*\/\=\[(]/.test(jsArray[depth].slice(-1)) == false) subText += "*"; + depth++; + position.push(0); + jsArray[depth] = ''; + subJS(elem[1], false); + jsArray[depth] = replaceAll(jsArray[depth], ' ', ''); // remove white space + subText += '((' + jsArray[depth] + ')/'; + subText2 += 'frac(' + jsArray[depth] + ','; + jsArray[depth] = ''; + subJS(elem[2], false); + jsArray[depth] = replaceAll(jsArray[depth], ' ', ''); // remove white space + subText += '(' + jsArray[depth] + '))'; + subText2 += jsArray[depth] + ')'; + jsArray[depth] = ''; + depth--; + position.pop(); + jsArray[depth] += subText; + algArray[depth] += subText2; + return; + } else if (elem[0] == 'sqrt') { + //console.log('sqrt'); + var subText = ''; + var subText2 = ''; + // if not proceeded by an operator, put a times sign in + if (jsArray[depth] !== '' && /[\+\-\u00D7\u00F7\*\/\=\[(]/.test(jsArray[depth].slice(-1)) == false) subText += "*"; + depth++; + position.push(0); + jsArray[depth] = ''; + subJS(elem[1], false); + jsArray[depth] = replaceAll(jsArray[depth], ' ', ''); // remove white space + subText += 'Math.sqrt('+ jsArray[depth] +')'; + subText2 += 'sqrt('+jsArray[depth]+')'; + jsArray[depth] = ''; + depth--; + position.pop(); + jsArray[depth] += subText; + algArray[depth] += subText2; + return; + } else if (elem[0] == 'root') { + //console.log(elem[0]); + var subText = ''; + var subText2 = ''; + // if not proceeded by an operator, put a times sign in + if (jsArray[depth] !== '' && /[\+\-\u00D7\u00F7\*\/\=\[(]/.test(jsArray[depth].slice(-1)) == false) subText += "*"; + depth++; + position.push(0); + jsArray[depth] = ''; + subJS(elem[2], false); + jsArray[depth] = replaceAll(jsArray[depth], ' ', ''); // remove white space + subText += '(Math.pow('+jsArray[depth]+','; + subText2 += 'root('+jsArray[depth]+','; + jsArray[depth] = ''; + subJS(elem[1], false); + jsArray[depth] = replaceAll(jsArray[depth], ' ', ''); // remove white space + subText += '(1/('+jsArray[depth]+'))))'; + subText2 += jsArray[depth]+')'; + jsArray[depth] = ''; + depth--; + position.pop(); + jsArray[depth] += subText; + algArray[depth] += subText2; + return; + } else if (elem[0] == 'sin' || elem[0] == 'cos' || elem[0] == 'tan') { + //console.log(elem[0]); + var subText = ''; + // if not proceeded by an operator, put a times sign in + if (jsArray[depth] !== '' && /[\+\-\u00D7\u00F7\*\/\=\[(]/.test(jsArray[depth].slice(-1)) == false) subText += "*"; + depth++; + position.push(0); + jsArray[depth] = ''; + subJS(elem[1], false); + jsArray[depth] = replaceAll(jsArray[depth], ' ', ''); // remove white space + var convertText1 = ''; + var convertText2 = ''; + if (angleMode == 'deg' || angleMode == 'degrees') { + convertText1 = '('; + convertText2 = ')*Math.PI/180'; + } + subText += 'Math.'+ elem[0] +'('+convertText1+jsArray[depth]+convertText2+')'; + jsArray[depth] = ''; + depth--; + position.pop(); + jsArray[depth] += subText; + algArray[depth] += subText; + return; + } else if (elem[0] == 'sin-1' || elem[0] == 'cos-1' || elem[0] == 'tan-1') { + //console.log(elem[0]); + var subText = ''; + // if not proceeded by an operator, put a times sign in + if (jsArray[depth] !== '' && /[\+\-\u00D7\u00F7\*\/\=\[(]/.test(jsArray[depth].slice(-1)) == false) subText += "*"; + depth++; + position.push(0); + jsArray[depth] = ''; + subJS(elem[1], false); + jsArray[depth] = replaceAll(jsArray[depth], ' ', ''); // remove white space + var convertText1 = ''; + var convertText2 = ''; + if (angleMode == 'deg' || angleMode == 'degrees') { + convertText1 = '(('; + convertText2 = ')*180/Math.PI)'; + } + subText += convertText1+'Math.a'+elem[0].slice(0,3)+'('+jsArray[depth]+')'+convertText2;; + jsArray[depth] = ''; + depth--; + position.pop(); + jsArray[depth] += subText; + algArray[depth] += subText; + return; + } else if (elem[0] == 'ln') { + //console.log(elem[0]); + var subText = ''; + // if not proceeded by an operator, put a times sign in + if (jsArray[depth] !== '' && /[\+\-\u00D7\u00F7\*\/\=\[(]/.test(jsArray[depth].slice(-1)) == false) subText += "*"; + depth++; + position.push(0); + jsArray[depth] = ''; + subJS(elem[1], false); + jsArray[depth] = replaceAll(jsArray[depth], ' ', ''); // remove white space + subText += 'Math.log('+jsArray[depth]+')'; + jsArray[depth] = ''; + position.pop(); + depth--; + jsArray[depth] += subText; + algArray[depth] += subText; + return; + } else if (elem[0] == 'log') { + //console.log(elem[0]); + var subText = ''; + // if not proceeded by an operator, put a times sign in + if (jsArray[depth] !== '' && /[\+\-\u00D7\u00F7\*\/\=\[(]/.test(jsArray[depth].slice(-1)) == false) subText += "*"; + depth++; + position.push(0); + jsArray[depth] = ''; + subJS(elem[1], false); + jsArray[depth] = replaceAll(jsArray[depth], ' ', ''); // remove white space + subText += '((Math.log('+jsArray[depth]+'))/(Math.log(10)))'; + jsArray[depth] = ''; + depth--; + position.pop(); + jsArray[depth] += subText; + algArray[depth] += subText; + return; + } else if (elem[0] == 'logBase') { + //console.log(elem[0]); + var subText = ''; + // if not proceeded by an operator, put a times sign in + if (jsArray[depth] !== '' && /[\+\-\u00D7\u00F7\*\/\=\[(]/.test(jsArray[depth].slice(-1)) == false) subText += "*"; + depth++; + position.push(0); + jsArray[depth] = ''; + subJS(elem[2], false); + jsArray[depth] = replaceAll(jsArray[depth], ' ', ''); // remove white space + subText += '((Math.log('+jsArray[depth]+'))/'; + jsArray[depth] = ''; + subJS(elem[1], false); + jsArray[depth] = replaceAll(jsArray[depth], ' ', ''); // remove white space + subText += '(Math.log('+jsArray[depth]+')))'; + jsArray[depth] = ''; + depth--; + position.pop(); + jsArray[depth] += subText; + algArray[depth] += subText; + return; + } else if (elem[0] == 'abs') { + //console.log(elem[0]); + var subText = ''; + // if not proceeded by an operator, put a times sign in + if (jsArray[depth] !== '' && /[\+\-\u00D7\u00F7\*\/\=\[(]/.test(jsArray[depth].slice(-1)) == false) subText += "*"; + depth++; + position.push(0); + jsArray[depth] = ''; + subJS(elem[1], false); + jsArray[depth] = replaceAll(jsArray[depth], ' ', ''); // remove white space + subText += 'Math.abs('+jsArray[depth]+')'; + jsArray[depth] = ''; + depth--; + position.pop(); + jsArray[depth] += subText; + algArray[depth] += subText; + return; + } else if (elem[0] == 'power' || elem[0] == 'pow') { + //console.log('power'); + + var baseSplitPoint = 0; + var trigPower = false; + //if the power is after a close bracket + if (jsArray[depth] !== '') { + if (jsArray[depth].charAt(jsArray[depth].length - 1) == ')') { + var bracketCount = 1 + for (jsChar = jsArray[depth].length - 2; jsChar >= 0; jsChar--) { + if (jsArray[depth].charAt(jsChar) == ')') {bracketCount++} + if (jsArray[depth].charAt(jsChar) == '(') {bracketCount--} + if (bracketCount == 0 && !baseSplitPoint) { + baseSplitPoint = jsChar; + break; + } + } + //if the power is after sin, cos or tan + + } else if (jsArray[depth].slice(jsArray[depth].length-3) == 'sin' || jsArray[depth].slice(jsArray[depth].length-3) == 'coa' || jsArray[depth].slice(jsArray[depth].length-3) == 'tan') { + trigPower = true; + //if the power is after a letter + } else if (/[A-Za-z]/g.test(jsArray[depth].charAt(jsArray[depth].length - 1)) == true) { + baseSplitPoint = jsArray[depth].length - 1; + //if the power is after a numerical digit + } else if (/[0-9]/g.test(jsArray[depth].charAt(jsArray[depth].length - 1)) == true) { + var decPoint = false; + for (jsChar = jsArray[depth].length - 2; jsChar >= 0; jsChar--) { + if (decPoint == false && jsArray[depth].charAt(jsChar) == '.') { + decPoint = true; + } else if (decPoint == true && jsArray[depth].charAt(jsChar) == '.') { + baseSplitPoint = jsChar + 1; + break; + } else if (/[0-9]/g.test(jsArray[depth].charAt(jsChar)) == false) { + baseSplitPoint = jsChar + 1; + break; + } + } + } else { + return ''; // error + } + } + + /*if (trigPower == true) { + var power = elem[2]; + if (typeof power == 'string') { + power = removeAllTagsFromString(power); + console.log(power); + if (power == '-1') { + jsArray[depth] = jsArray[depth].slice(0,-3) + 'Math.a' + jsArray[depth].slice(-3); + } else if (power == '2') { + + } + } + + }*/ + + var base = jsArray[depth].slice(baseSplitPoint); + jsArray[depth] = jsArray[depth].slice(0, baseSplitPoint); + depth++; + position.push(0); + jsArray[depth] = ''; + subJS(elem[2], false) + jsArray[depth] = replaceAll(jsArray[depth], ' ', ''); + if (trigPower == true) { + console.log(jsArray,jsArray[depth-1],jsArray[depth]); + if (jsArray[depth] == '-1') { + jsArray[depth-1] = jsArray[depth-1].slice(0,-3) + 'Math.a' + jsArray[depth-1].slice(-3); + } + } else { + var subText = 'Math.pow(' + base + ',' + jsArray[depth] + ')'; + var subText2 = base + '^' + jsArray[depth]; + } + jsArray[depth] = ''; + depth--; + position.pop(); + jsArray[depth] += subText; + algArray[depth] += subText2; + return; + } else if (typeof elem == 'object') { + //console.log('array'); + depth++; + position.push(0); + jsArray[depth] = ''; + for (var sub = 0; sub < elem.length; sub++) { + //console.log('depth:', depth); + //console.log('Before ' + sub + ' sub element(s):', jsArray); + subJS(elem[sub], addMultIfNecc); + //console.log('After ' + sub + ' sub element(s):', jsArray); + } + jsArray[depth-1] += jsArray[depth]; + algArray[depth-1] += algArray[depth]; + jsArray[depth] = ''; + depth--; + position.pop(); + //console.log('endOfArray', jsArray); + return; + } + } + + function timesBeforeLetters(testText) { + // find instances of letters - if proceeded by a number, add * + for (q = 0; q < testText.length; q++) { + if (q > 0) { + if (/[a-zA-Z]/g.test(testText.charAt(q)) == true && /[a-zA-Z0-9)]/.test(testText.charAt(q - 1)) == true) { + testText = testText.slice(0, q) + '*' + testText.slice(q); + } + // if an open bracket is proceeded by a letter, number or ), add * + if (/[\[(]/g.test(testText.charAt(q)) == true && testText.length > q && /[A-Za-z0-9)]/g.test(testText.charAt(q - 1)) == true) { + testText = testText.slice(0, q) + '*' + testText.slice(q); + } + } + for (var i = 0; i < exceptions.length; i++) { + if (testText.slice(q).indexOf(exceptions[i]) == 0) { + q += exceptions[i].length; + } + } + } + return testText; + } + var jsValue; + try { + jsValue = eval(js); + } catch (err) {} + return js; +} +function splitText(text) { + // find split points in text string + var splitPointCount = 0; + var textSplitPoints = []; + var delimiter = '%&^'; + for (i = 0; i <= text.length; i++) { + var fracStartPos = text.substring(i, text.length).indexOf('frac('); + var rootStartPos = text.substring(i, text.length).indexOf('root('); + var powerStartPos = text.substring(i, text.length).indexOf('power('); + if (fracStartPos !== -1) {fracStartPos += i}; + if (rootStartPos !== -1) {rootStartPos += i}; + if (powerStartPos !== -1) {powerStartPos += i}; + if (fracStartPos > -1 || rootStartPos > -1 || powerStartPos > -1) { + textSplitPoints[splitPointCount] = []; + if (fracStartPos == -1) {fracStartPos = 10000}; + if (rootStartPos == -1) {rootStartPos = 10000}; + if (powerStartPos == -1) {powerStartPos = 10000}; + textSplitPoints[splitPointCount][0] = Math.min(fracStartPos, rootStartPos, powerStartPos); + var openBracketCount = 0; + var closeBracketCount = 0; + for (j = textSplitPoints[splitPointCount][0]; j <= text.length; j++) { + if (!textSplitPoints[splitPointCount][1]) { + if (text.charAt(j) == '(') {openBracketCount++}; + if (text.charAt(j) == ')') {closeBracketCount++}; + if (openBracketCount > 0 && (openBracketCount == closeBracketCount)) { + textSplitPoints[splitPointCount][1] = j; + i = j; + } + } + } + splitPointCount++; + } + } + + if (textSplitPoints.length == 0) {return text} + + for (i = 0; i < textSplitPoints.length; i++) { + text = text.substring(0, textSplitPoints[i][0] + i * 2 * delimiter.length) + delimiter + text.substring(textSplitPoints[i][0] + i * 2 * delimiter.length, textSplitPoints[i][1] + 1 + i * 2 * delimiter.length) + delimiter + text.substring(textSplitPoints[i][1] + 1 + i * 2 * delimiter.length, text.length); + } + var splitArray = text.split(delimiter); + var returnArray = []; + for (i = 0; i < splitArray.length; i++) { + var type = 0; + if (splitArray[i].indexOf('frac(') == 0) {type = 'frac'}; + if (splitArray[i].indexOf('root(') == 0) {type = 'root'}; + if (splitArray[i].indexOf('power(') == 0) {type = 'power'}; + if (type == 0) { + if (splitArray[i] !== '') {returnArray.push(splitArray[i])} + } else { + var subArray = []; + subArray[0] = type; + + var params = splitArray[i].substring(type.length + 1, splitArray[i].length - 1); + var openBracketCount = 0; + var closeBracketCount = 0; + var splitPoint = -1; + + // find split point of params + for (j = 0; j <= params.length; j++) { + if (params.charAt(j) == '(') {openBracketCount++}; + if (params.charAt(j) == ')') {closeBracketCount++}; + if (params.charAt(j) == ',' && (openBracketCount == closeBracketCount) && splitPoint == -1) { + splitPoint = j; + } + } + subArray[1] = params.substring(0, splitPoint); + subArray[2] = params.substring(splitPoint + 1, params.length).trim(); + returnArray.push(subArray) + } + } + return returnArray; +} + +/*! correcting-interval 2.0.0 | Copyright 2014 Andrew Duthie | MIT License */ +/* jshint evil: true */ +/* usage example: +var startTime = Date.now(); +setCorrectingInterval(function() { + console.log((Date.now() - startTime) + 'ms elapsed'); +}, 1000); +*/ +;(function(global, factory) { + // Use UMD pattern to expose exported functions + if (typeof exports === 'object') { + // Expose to Node.js + module.exports = factory(); + } else if (typeof define === 'function' && define.amd) { + // Expose to RequireJS + define([], factory); + } + + // Expose to global object (likely browser window) + var exports = factory(); + for (var prop in exports) { + global[prop] = exports[prop]; + } +}(this, function() { + // Track running intervals + var numIntervals = 0, + intervals = {}; + + // Polyfill Date.now + var now = Date.now || function() { + return new Date().valueOf(); + }; + + var setCorrectingInterval = function(func, delay) { + var id = numIntervals++, + planned = now() + delay; + + // Normalize func as function + switch (typeof func) { + case 'function': + break; + case 'string': + var sFunc = func; + func = function() { + eval(sFunc); + }; + break; + default: + func = function() { }; + } + + function tick() { + func(); + + // Only re-register if clearCorrectingInterval was not called during function + if (intervals[id]) { + planned += delay; + intervals[id] = setTimeout(tick, planned - now()); + } + } + + intervals[id] = setTimeout(tick, delay); + return id; + }; + + var clearCorrectingInterval = function(id) { + clearTimeout(intervals[id]); + delete intervals[id]; + }; + + return { + setCorrectingInterval: setCorrectingInterval, + clearCorrectingInterval: clearCorrectingInterval + }; +})); +function logText(clearConsole) { + var input = currMathsInput; + if (clearConsole == true) console.clear(); + console.log('richText:',input.richText); + console.log('cursorMap:'); + for (var i = 0; i < input.cursorMap.length; i++) { + var char = input.richText; + for (var j = 0; j < input.cursorMap[i].length - 1; j++) {char = char[input.cursorMap[i][j]];}; + // check for breakPoints + var slicePos = input.cursorMap[i][input.cursorMap[i].length-1]; + if (typeof currMathsInput.breakPoints == 'object') { + var map = currMathsInput.cursorMap[i]; + for (var k = 0; k < currMathsInput.breakPoints.length - 1; k++) { + var iBreak = currMathsInput.allMap[currMathsInput.breakPoints[k]]; + if (iBreak[0] == map[0] && iBreak[1] < map[1]) { + slicePos--; + } + } + } + var char1 = char.slice(slicePos-5,slicePos); + var char2 = char.slice(slicePos,slicePos+5); + if (i == input.cursorPos) { + console.log('>'+i+':',input.cursorMap[i],char1,char2,'<<< cursorPos'); + } else { + console.log('>'+i+':',input.cursorMap[i],char1,char2); + } + } +} +function drawTextLocs() { + for (var loc = 0; loc < currMathsInput.cursorMap.length; loc++) { + var cursorPos = currMathsInput.cursorMap[loc]; + var evalString = 'currMathsInput.textLoc' + for (aa = 0; aa < cursorPos.length; aa++) { + evalString += '[' + cursorPos[aa] + ']'; + } + var pos = eval(evalString); + console.log(loc, pos.left); + currMathsInput.ctx.strokeStyle = '#00F'; + currMathsInput.ctx.lineWidth = 2; + currMathsInput.ctx.beginPath(); + currMathsInput.ctx.moveTo(pos.left,pos.top); + currMathsInput.ctx.lineTo(pos.left,pos.top+pos.height); + currMathsInput.ctx.closePath(); + currMathsInput.ctx.stroke(); + } +} +function isMouseOverText(mathsInput) { + if (typeof mathsInput == 'undefined') mathsInput = currMathsInput; + var locs = []; + for (var i = 0; i < mathsInput.cursorMap.length; i++) { + var textLoc = mathsInput.textLoc; + for (var j = 0; j < mathsInput.cursorMap[i].length; j++) {textLoc = textLoc[mathsInput.cursorMap[i][j]];}; + locs.push(textLoc); + } + var left = mathsInput.data[100]; + var top = mathsInput.data[101]; + for (var i = 0; i < locs.length; i++) { + if (mouse.x >= left + locs[i].left && mouse.x <= left + locs[i].left + locs[i].width && mouse.y >= top + locs[i].top && mouse.y <= top + locs[i].top + locs[i].height) { + return true; + } + } + return false; +} + + +function splitMarkup(element) { + // seperates markup tags from other text + var splitAt = [0]; + for (var c = 0; c < element.length; c++) { + if (element.slice(c).indexOf('<<') == 0 && element.slice(c).indexOf('<<<') !== 0) { + for (var d = c; d < element.length; d++) { + if (element.slice(d).indexOf('>>') == 0) { + splitAt.push(c,d+2); + break; + } + } + } else if (element.slice(c).indexOf(br) == 0) { + splitAt.push(c,c+1); + } + } + splitAt.push(element.length); + var returnArray = []; + for (var c = 0; c < splitAt.length-1; c++) { + returnArray.push(element.slice(splitAt[c],splitAt[c+1])) + } + return returnArray; +} +function reduceTags(textArray) { + var bold = {value:null,char:true}; + var italic = {value:null,char:true}; + var fontSize = {value:null,char:true}; + var font = {value:null,char:true}; + var color = {value:null,char:true}; + var backColor = {value:null,char:true}; + var align = {set:false}; + var selected = {value:null}; + + function arrayHandler(arr) { + for (var l = arr.length - 1; l >= 0; l--) { + if (typeof arr[l] == 'string') { + arr[l] = stringHandler(arr[l]); + } else if (typeof arr[l] == 'object') { + arr[l] = arrayHandler(arr[l]); + } + } + return arr; + } + + function stringHandler(string) { + // first split string into tag and non-tag elements + var splitString = splitMarkup(string); + + // work backwards through the string looking for tags + for (var j = splitString.length - 1; j >= 0; j--) { + if (splitString[j].indexOf('<<') == 0) { + for (var k = splitString[j].length; k >= 0; k--) { + var slice = splitString[j].slice(k); + if (slice.indexOf('<>')+2); + } else { + var value = splitString[j].slice(k+7,k+splitString[j].slice(k).indexOf('>>')).toLowerCase(); + if (value == bold.value) { + // repeated tag - !! + } else { + // new tag + bold.value = value; + bold.char = false; + } + } + } else if (slice.indexOf('<>')+2); + } else { + var value = splitString[j].slice(k+9,k+splitString[j].slice(k).indexOf('>>')).toLowerCase(); + if (value == italic.value) { + // repeated tag + } else { + // new tag + italic.value = value; + italic.char = false; + } + } + } else if (slice.indexOf('<>')+2); + } else { + var value = splitString[j].slice(k+11,k+splitString[j].slice(k).indexOf('>>')).toLowerCase(); + if (value == fontSize.value) { + // repeated tag + } else { + // new tag + fontSize.value = value; + fontSize.char = false; + } + } + } else if (slice.indexOf('<>')+2); + } else { + var value = splitString[j].slice(k+7,k+splitString[j].slice(k).indexOf('>>')).toLowerCase(); + if (value == font.value) { + // repeated tag + } else { + // new tag + font.value = value; + font.char = false; + } + } + } else if (slice.indexOf('<>')+2); + } else { + var value = splitString[j].slice(k+8,k+splitString[j].slice(k).indexOf('>>')).toLowerCase(); + if (value == color.value) { + // repeated tag + } else { + // new tag + color.value = value; + color.char = false; + } + } + } else if (slice.indexOf('<>')+2); + } else { + var value = splitString[j].slice(k+12,k+splitString[j].slice(k).indexOf('>>')).toLowerCase(); + if (value == backColor.value) { + // repeated tag + } else { + // new tag + backColor.value = value; + backColor.char = false; + } + } + } else if (slice.indexOf('<
>') == 0 || slice.indexOf(br) == 0) { + align.set = false; + } else if (slice.indexOf('<>')+2); + } + } else if (slice.indexOf('<>')).toLowerCase(); + //console.log('selected tag',j,k,value,JSON.stringify(splitString[j])); + if (value == selected.value) { + // repeated tag + } else { + // new tag + selected.value = value; + } + } + + } + } else { + if (splitString[j].length > 0) { + bold.char = true; + italic.char = true; + fontSize.char = true; + font.char = true; + color.char = true; + backColor.char = true; + } + } + } + string = ''; + for (var j = 0; j < splitString.length; j++) string += splitString[j]; + return string; + } + + if (typeof textArray == 'object') textArray = arrayHandler(textArray); + //var arrayString = ''; + //console.log('reduceTags()1',JSON.stringify(textArray)); + textArray = combineSpaces2(textArray); + + // find any adjacent text blocks and combine them + function combineSpaces2(textArray) { + if (textArray.length > 1) { + for (var gg = textArray.length - 1; gg >= 0; gg--) { + if (typeof textArray[gg] == 'object') { + //arrayString += '[' + gg + ']'; + combineSpaces2(textArray[gg]); + } else { + if (gg < textArray.length - 1 && typeof textArray[gg] == 'string' && typeof textArray[gg+1] == 'string') { + eval('textArray[' + gg + '] += textArray[' + (gg+1) + ']'); + eval('textArray.splice(gg+1, 1);'); + } + } + } + } + //arrayString = arrayString.slice(0, arrayString.lastIndexOf('[') - arrayString.length); + return textArray; + } + + //console.log('reduceTags()2',JSON.stringify(textArray)); + + return textArray; +} \ No newline at end of file diff --git a/tools/i2/_miscFuncs.js b/tools/i2/_miscFuncs.js new file mode 100644 index 0000000..5b49571 --- /dev/null +++ b/tools/i2/_miscFuncs.js @@ -0,0 +1,12393 @@ +//Javascript document + +var alg = { + toObj: function(txt) { + txt = simplifyText(clone(txt)); + txt = removeTags(txt); + var exp = []; + + //console.log('---'); + //console.log('txt',txt); + var terms = splitTerms(txt); + + //console.log('terms',clone(terms)); + + for (var t = 0; t < terms.length; t++) processTerm(terms[t]); + + //console.log('toObj:',JSON.stringify(exp)); + + return exp; + + function splitTerms(txt) { + var terms = [[]]; + var bracketCount = 0; + for (var i = 0; i < txt.length; i++) { + var txt1 = txt[i]; + if (typeof txt1 === 'string') { + for (var c = 0; c < txt1.length; c++) { + if (txt1.charAt(c) === '(') { + bracketCount++; + } else if (txt1.charAt(c) === ')') { + bracketCount--; + } else if (bracketCount === 0 && (txt1.charAt(c) === '+' || txt1.charAt(c) === '-')) { + if (c > 0) { + terms.last().push(txt1.slice(0,c)); + txt1 = txt1.slice(c); + c = 0; + } + if (terms.last().length > 0) { + terms.push([]); + } + } + } + terms.last().push(txt1); + } else { + terms.last().push(txt1); + } + } + return terms; + } + function getAlgFrac(term) { + //console.log('term:',clone(term)); + + var sign = 1; + if (term[0] === '-') { + sign = -1; + term.shift(); + } else if (term[0] === '+') { + term.shift(); + } + var num = alg.toObj(term[0][1]); + var denom = alg.toObj(term[0][2]); + + var coeff = [1,1]; + if (num.length === 1) { + coeff[0] *= num[0].coeff[0]; + coeff[1] *= num[0].coeff[1]; + num[0].coeff = [1,1]; + } else { + num = [{sign:1,coeff:[1,1],subterms:[[num,1]]}]; + } + if (denom.length === 1) { + coeff[0] *= denom[0].coeff[1]; + coeff[1] *= denom[0].coeff[0]; + denom[0].coeff = [1,1]; + for (var s = 0; s < denom[0].subterms.length; s++) { + denom[0].subterms[s][1] *= -1; + } + } else { + denom = [{sign:1,coeff:[1,1],subterms:[[denom,-1]]}]; + } + + var numSubterms = num[0].subterms; + var denomSubterms = denom[0].subterms; + var subterms = numSubterms.concat(denomSubterms); + //subterms = alg.simplifySubterms(subterms); + subterms = alg.orderSubterms(subterms); + + var obj = { + sign:sign, + coeff:coeff, + subterms:subterms + }; + + term.shift(); + return obj; + } + function processTerm(term) { + if (term.length === 0) return; + //console.log('term',clone(term)); + + var obj = {}; + exp.push(obj); + while (term[0] === '') term.shift(); + + var coeff = 1; + if ((term[0] === '-' || term[0] === '+') && typeof term[1] === 'object' && term[1][0] === 'frac') { + if (term[1][1].length === 1 && !isNaN(Number(term[1][1][0])) && term[1][2].length === 1 && !isNaN(Number(term[1][2][0]))) { + obj.sign = term[0] === '-' ? -1: 1; + obj.coeff = [Number(term[1][1][0]),Number(term[1][2][0])]; + term.shift(); + term.shift(); + } else { + exp[exp.length-1] = getAlgFrac(term); + } + } else if (typeof term[0] === 'string') { + var sign = 1; + if (term[0][0] === '-') { + sign = -1; + term[0] = term[0].slice(1); + } else if (term[0][0] === '+') { + term[0] = term[0].slice(1); + } + var charCount = 0; + for (var c = 1; c < term[0].length+1; c++) { + if (isNaN(Number(term[0].slice(0,c)))) { + term[0] = term[0].slice(charCount); + break; + } else { + coeff = Number(term[0].slice(0,c)); + charCount++; + if (c === term[0].length) { + term[0] = term[0].slice(charCount); + break; + } + } + } + obj.sign = sign; + obj.coeff = [coeff,1]; + } else if (typeof term[0] === 'object' && term[0][0] === 'frac') { + if (term[0][1].length === 1 && !isNaN(Number(term[0][1][0])) && term[0][2].length === 1 && !isNaN(Number(term[0][2][0]))) { + obj.sign = 1; + obj.coeff = [Number(term[0][1][0]),Number(term[0][2][0])]; + term.shift(); + } else { + exp[exp.length-1] = getAlgFrac(term); + } + } + while (term[0] === '') term.shift(); + + if (term.length === 0) { + obj.subterms = []; + return; + } + var subterms = []; + var count = 0; + while (!un(term[0]) && typeof term[0] === 'string' && count < 100) { + count++; + var subterms2 = []; + if (typeof term[0] === 'string') { + if (term[0][0] === '(') { + term[0] = term[0].slice(1); + if (term[0] === '') term.shift(); + var term2 = []; + var bracketCount = 1; + var count = 0; + while (bracketCount > 0 && count < 100) { + count++; + if (typeof term[0] === 'string') { + var str = term[0]; + for (var c = 0; c < str.length; c++) { + var char = str[c]; + if (char === '(') { + bracketCount++; + } else if (char === ')') { + bracketCount--; + if (bracketCount === 0) { + if (c > 0) term2.push(str.slice(0,c)); + term[0] = str.slice(c+1); + break; + } + } + } + if (bracketCount !== 0) { + term2.push(term[0]); + term.shift(); + } + } else if (typeof term[0] === 'object') { + term2.push(term[0]); + term.shift(); + } + } + subterms2 = [alg.toObj(term2),1]; + if (term[0] === '') term.shift(); + if (typeof term[0] === 'object' && term[0][0] === 'pow') { + if (!isNaN(Number(term[0][2]))) subterms2[1] = Number(term[0][2]); + term.shift(); + } + } else { + subterms2 = [term[0][0],1]; + term[0] = term[0].slice(1); + if (term[0] === '') term.shift(); + if (typeof term[0] === 'object' && term[0][0] === 'pow') { + if (!isNaN(Number(term[0][2]))) subterms2[1] = Number(term[0][2]); + term.shift(); + } + } + } + if (term[0] === '') term.shift(); + subterms.push(subterms2); + } + obj.subterms = subterms; + } + + }, + toText: function(exp,type) { + if (un(type)) type = 'default'; // default is fracTerms + var txt = ['']; + if (exp instanceof Array === false && typeof exp === 'object') exp = [exp]; + if (exp.length === 0) return ["0"]; + for (var e = 0; e < exp.length; e++) { + var term = exp[e]; + //console.log('term',term); + var isFirstTerm = e === 0 ? true : false; + var isOnlyTerm = exp.length === 1 ? true : false; + if (type === 'inline') { + txt = txt.concat(termToTextInline(term,isFirstTerm,isOnlyTerm)); + } else { + txt = txt.concat(termToTextFracTerms(term,isFirstTerm,isOnlyTerm)) + } + } + txt = simplifyText(txt); + return txt; + + function termToTextInline(term,isFirstTerm,isOnlyTerm) { + var txt = []; + var sign = term.sign === -1 ? '-' : '+'; + var coeff = term.coeff; + if (coeff[1] === 1) coeff = coeff[0]; + var subterms = term.subterms; + if (coeff === 1 && subterms.length > 0) coeff = ''; + if (isFirstTerm === false || sign === '-') txt.push(sign); + if (coeff instanceof Array) { + txt.push(['frac',[String(Math.abs(coeff[0]))],[String(Math.abs(coeff[1]))]]); + } else { + txt.push(String(coeff)); + } + var subtermsText = []; + for (var s = 0; s < subterms.length; s++) { + var sub = subterms[s]; + if (typeof sub[0] === 'string') { + subtermsText.push(sub[0]); + } else if (typeof sub[0] === 'object') { + var brackets = sub.length > 1 ? true : false; + if (brackets = true) subtermsText.push('('); + subtermsText = subtermsText.concat(alg.toText(sub[0])); + if (brackets = true) subtermsText.push(')'); + } + if (!un(sub[1]) && sub[1] !== 1) subtermsText.push(['pow',false,String(sub[1])]); + } + txt = txt.concat(subtermsText); + + return txt; + } + function termToTextFracTerms(term,isFirstTerm,isOnlyTerm) { + var num = []; + var denom = []; + + var subterms = term.subterms; + for (var s = 0; s < subterms.length; s++) { + var sub = subterms[s]; + var type = alg.getSubtermType(sub); + if (type === 'single') { + if (sub[1] > 0) { + num.push(sub[0]); + if (sub[1] > 1) num.push(['pow',false,String(sub[1])]); + } else { + denom.push(sub[0]); + if (sub[1] < -1) denom.push(['pow',false,String(Math.abs(sub[1]))]); + } + } else if (type === 'compound') { + if (sub[1] > 0) { + var brackets = subtermRequiresBrackets(sub); + if (brackets === true) num.push('('); + num = num.concat(alg.toText(sub[0])); + if (brackets === true) num.push(')'); + if (sub[1] > 1) num.push(['pow',false,String(sub[1])]); + } else { + var brackets = subtermRequiresBrackets(sub); + if (brackets === true) denom.push('('); + denom = denom.concat(alg.toText(sub[0])); + if (brackets === true) denom.push(')'); + if (sub[1] < -1) denom.push(['pow',false,String(Math.abs(sub[1]))]); + } + } + } + + function subtermRequiresBrackets(subterm) { + if (subterm[1] > 1 || subterm[1] < -1) return true; + if (subterm[0].length > 1) return true; + var exp = subterm[0][0].subterms; + if (exp.length > 1 || un(exp[0])) return true; + if (Math.abs(exp[0][1]) > 1) return false; + if (arraysEqual(exp[0][0].coeff,[1,1]) === true) return false; + return false; + } + + var coeff = term.coeff; + if (coeff[0] !== 1 || num.length === 0) num.unshift(String(coeff[0])); + if (coeff[1] !== 1 || denom.length === 0) denom.unshift(String(coeff[1])); + + var sign = term.sign === -1 ? '-' : '+'; + if (isFirstTerm === true && sign === '+') sign = ''; + + num = simplifyText(num); + denom = simplifyText(denom); + + var isFraction = (arraysEqual(denom,['1']) || denom.length === 0) ? false : true; + + if (isFraction) { + if (typeof num[0] === 'string' && num[0][0] === '(' && typeof num[num.length-1] === 'string' && num[num.length-1].slice(-1) === ')') { + var bracketCount = 1; + var bracketClosed = false; + for (var i = 0; i < num.length; i++) { + if (typeof num[i] !== 'string') continue; + for (var j = 0; j < num[i].length; j++) { + if (i == 0 && j == 0) continue; + if (i == num.length-1 && j == num[i].length-1) continue; + if (num[i][j] === '(') bracketCount++; + if (num[i][j] === ')') bracketCount--; + if (bracketCount === 0) { + bracketClosed = true; + j = num[i].length; + i = num.length; + } + } + } + if (bracketClosed === false) { + num[0] = num[0].slice(1); + num[num.length-1] = num[num.length-1].slice(0,-1); + num = simplifyText(num); + } + } + + if (typeof denom[0] === 'string' && denom[0][0] === '(' && typeof denom[denom.length-1] === 'string' && denom[denom.length-1].slice(-1) === ')') { + var bracketCount = 1; + var bracketClosed = false; + for (var i = 0; i < denom.length; i++) { + if (typeof denom[i] !== 'string') continue; + for (var j = 0; j < denom[i].length; j++) { + if (i == 0 && j == 0) continue; + if (i == denom.length-1 && j == denom[i].length-1) continue; + if (denom[i][j] === '(') bracketCount++; + if (denom[i][j] === ')') bracketCount--; + if (bracketCount === 0) { + bracketClosed = true; + j = denom[i].length; + i = denom.length; + } + } + } + if (bracketClosed === false) { + denom[0] = denom[0].slice(1); + denom[denom.length-1] = denom[denom.length-1].slice(0,-1); + denom = simplifyText(denom); + } + } + var txt = [sign,['frac',num,denom,1]]; + } else { + var txt = [sign].concat(num); + } + + return txt; + } + }, + + add: function(exp1,exp2) { + var exp3 = clone(exp1); + exp3 = exp3.concat(clone(exp2)); + //exp3 = alg.collect(exp3); + return exp3; + }, + subtract: function(exp1,exp2) { + var exp2 = clone(exp2); + var exp2 = alg.multiply(exp2,[{sign:-1,coeff:[1,1],subterms:[]}]); + var exp3 = alg.add(exp1,exp2); + return exp3; + }, + multiply: function(exp1,exp2,simplify) { + exp1 = clone(exp1); + exp2 = clone(exp2); + if (exp1.length > 1) exp1 = [{sign:1,coeff:[1,1],subterms:[[exp1,1]]}]; + if (exp2.length > 1) exp2 = [{sign:1,coeff:[1,1],subterms:[[exp2,1]]}]; + var term1 = clone(exp1[0]); + var subterms1 = term1.subterms; + var coeff1 = term1.coeff; + var term2 = clone(exp2[0]); + var subterms2 = term2.subterms; + var coeff2 = term2.coeff; + var sign3 = term1.sign*term2.sign; + var coeff3 = [coeff1[0]*coeff2[0],coeff1[1]*coeff2[1]]; + var subterms3 = subterms1.concat(subterms2); + if (boolean(simplify,true) === true) { + coeff3 = simplifyFrac2(coeff3); + subterms3 = alg.orderSubterms(subterms3); + subterms3 = alg.simplifySubterms(subterms3); + } + return [{sign:sign3,coeff:coeff3,subterms:subterms3}]; + }, + divide: function(exp1,exp2,simplify) { + var exp2 = alg.getExpressionReciprocal(exp2); + var exp3 = alg.multiply(exp1,exp2,simplify); + return exp3; + }, + + collect: function(exp) { + exp = clone(exp); + for (var e2 = exp.length-1; e2 >= 0; e2--) { + var term2 = exp[e2]; + var subterms2 = term2.subterms; + for (var e1 = e2-1; e1 >= 0; e1--) { + var term1 = exp[e1]; + var subterms1 = term1.subterms; + if (alg.likeTerms(term1,term2)) { + term1.coeff[0] *= term1.sign; + term2.coeff[0] *= term2.sign; + var coeff = addFracs2(term1.coeff,term2.coeff); + term1.sign = coeff[0]/coeff[1] < 0 ? -1 : 1; + term1.coeff = simplifyFrac2([Math.abs(coeff[0]),Math.abs(coeff[1])]); + exp.splice(e2,1); + break; + } + } + } + for (var e = 0; e < exp.length; e++) { + if (exp[e].coeff[0] === 0) { + exp.splice(e,1); + e--; + } + } + return exp; + }, + collectable: function(exp) { + var exp2 = clone(exp); + var exp3 = alg.collect(exp2); + return !isEqual(exp2,exp3); + }, + expand: function(exp) { + //console.log('exp',JSON.stringify(exp)); + var exp3 = clone(exp); + var exp2 = clone(exp); + + var count = 0; + do { + count++; + var exp2 = clone(exp3); + var exp3 = expandExp(exp3); + } while (!isEqual(exp2,exp3) && count < 10); + //console.log('exp3',JSON.stringify(exp3)); + return exp3; + + function expandExp(exp) { + var exp2 = clone(exp); + var exp3 = []; + for (var e = 0; e < exp2.length; e++) { + var term = exp2[e]; + + //console.log('term',JSON.stringify(term)); + + var num = alg.getTermNumerator(term); + var denom = alg.getTermDenominator(term); + + //console.log('num:',clone(num)); + //console.log('denom:',clone(denom)); + + var num2 = expandTerm(num); + var denom2 = expandTerm(denom); + + //console.log('num2:',clone(num2)); + //console.log('denom2:',clone(denom2)); + + var term2 = alg.divide(num2,denom2); + //console.log('term2',JSON.stringify(term2)); + + exp3 = exp3.concat(term2); + } + var exp4 = []; + for (var e = 0; e < exp3.length; e++) { + var term = exp3[e]; + if (term.sign === 1 && arraysEqual(term.coeff,[1,1]) && !un(term.subterms) && term.subterms.length === 1 && !un(term.subterms[0]) && alg.getSubtermType(term.subterms[0]) === 'compound' && term.subterms[0][1] === 1) { + exp4 = exp4.concat(term.subterms[0][0]); + } else { + exp4.push(term); + } + } + //console.log('exp4',JSON.stringify(exp4)); + return exp4; + } + function expandTerm(term) { + var term = clone(term); + + // apply powers to compound subterms + for (var s = 0; s < term.subterms.length; s++) { + var subterm = term.subterms[s]; + if (alg.getSubtermType(subterm) !== 'compound') continue; + if (subterm[1] > 1) { + var index = subterm[1]; + var subterm2 = clone(subterm[0]); + for (var i = 1; i < index; i++) { + subterm2 = alg.multiplyExpressions(subterm2,subterm[0]); + } + term.subterms[s] = [subterm2,1]; + } else if (subterm[1] < 1) { + var index = -1*subterm[1]; + var subterm2 = clone(subterm[0]); + for (var i = 1; i < index; i++) { + subterm2 = alg.multiplyExpressions(subterm2,subterm[0]); + } + term.subterms[s] = [subterm2,-1]; + } + } + + // mutiply subterms together + var count = 0; + while (term.subterms.length > 1 && count < 100) { + count++; + var subterm1 = term.subterms[0]; + var type1 = alg.getSubtermType(subterm1); + if (type1 === 'single') { + for (var i = 1; i < term.subterms.length; i++) { + var subterm2 = term.subterms[i]; + var type2 = alg.getSubtermType(subterm2); + if (type2 === 'compound') { + for (var s = 0; s < subterm2[0].length; s++) { + var subterm3 = subterm2[0][s].subterms; + subterm3.push(clone(subterm1)); + subterm2[0][s].subterms = alg.simplifySubterms(subterm3); + } + term.subterms.shift(); + break; + } + } + } else if (type1 === 'compound') { + var subterm2 = term.subterms[1]; + var type2 = alg.getSubtermType(subterm2); + if (type2 === 'single') { + for (var s = 0; s < subterm1[0].length; s++) { + var subterm3 = subterm1[0][s].subterms; + subterm3.push(clone(subterm2)); + subterm1[0][s].subterms = alg.simplifySubterms(subterm3); + } + term.subterms.splice(1,1); + } else if (type2 === 'compound') { + term.subterms[1] = [alg.multiplyExpressions(subterm1[0],subterm2[0]),1]; + term.subterms.shift(); + } + } + } + + var exp = []; + //console.log(JSON.stringify(term)); + if (!un(term.subterms[0])) { + if (alg.getSubtermType(term.subterms[0]) === 'compound') { + var terms2 = term.subterms[0][0]; + for (var s = 0; s < terms2.length; s++) { + var term2 = terms2[s]; + exp.push({ + sign:term.sign*term2.sign, + coeff:simplifyFrac2([term.coeff[0]*term2.coeff[0],term.coeff[1]*term2.coeff[1]]), + subterms:term2.subterms + }); + } + } else { + exp.push({ + sign:term.sign, + coeff:simplifyFrac2([term.coeff[0],term.coeff[1]]), + subterms:term.subterms + }); + } + } + if (exp.length === 0) exp.push({sign:term.sign,coeff:clone(term.coeff),subterms:[]}); + return exp; + } + }, + expandable: function(exp) { + var exp2 = clone(exp); + var exp3 = alg.expand(exp2); + return !isEqual(exp2,exp3); + }, + factorise: function(exp) { + var exp2 = clone(exp); + if (alg.collectable(exp2) === true) return exp; + //console.log('exp',JSON.stringify(exp)); + var exp3 = []; + + // look in each term for factorisable subterms + for (var t = 0; t < exp2.length; t++) { + var term = exp2[t]; + + var num = alg.getTermNumerator(term); + //console.log('num:',JSON.stringify(num)); + num = factoriseTerm(num); + + var denom = alg.getTermDenominator(term); + //console.log('denom:',JSON.stringify(denom)); + denom = factoriseTerm(denom); + + var term2 = alg.divide([num],[denom],false); + //console.log('term2:',JSON.stringify(term2)); + + exp3 = exp3.concat(term2); + } + + exp3 = factoriseExp(exp3); + return exp3; + + function factoriseTerm(term) { + var term = clone(term); + var factors = []; + //console.log('factorise term',clone(term)); + for (var s = 0; s < term.subterms.length; s++) { + var subterm = term.subterms[s]; + //console.log('-----',s); + //console.log('term',clone(term)); + //console.log('subterm',JSON.stringify(subterm)); + if (un(subterm)) continue; + if (alg.getSubtermType(subterm) === 'simple') continue; + var index = subterm[1]; + var exp = subterm[0]; + //console.log('exp',clone(exp)); + //console.log('index',index); + + var factor = alg.getHCF(exp); + //console.log('factor',JSON.stringify(factor)); + if (isEqual(factor,[{sign:1,coeff:[1,1],subterms:[]}])) continue; + var dividend = []; + for (var t = 0; t < exp.length; t++) { + var termdiv = alg.divide([exp[t]],factor); + termdiv = alg.simplify(termdiv); + dividend.push(termdiv[0]); + } + if (isEqual(dividend,[{sign:1,coeff:[1,1],subterms:[]}])) continue; + //console.log('dividend',JSON.stringify(dividend)); + + if (index !== 1) { + factor[0].coeff[0] = Math.pow(factor[0].coeff[0],index); + factor[0].coeff[1] = Math.pow(factor[0].coeff[1],index); + for (var s2 = 0; s2 < factor[0].subterms.length; s2++) { + factor[0].subterms[s2][1] = index; + } + } + + term.sign *= factor[0].sign; + term.coeff = [term.coeff[0]*factor[0].coeff[0],term.coeff[1]*factor[0].coeff[1]]; + factors = factors.concat(factor[0].subterms); + term.subterms[s] = [dividend,index]; + + //console.log('subterm',JSON.stringify(term.subterms[s])); + //console.log('term',JSON.stringify(term)); + } + term.subterms = factors.concat(term.subterms); + //console.log('term = '+JSON.stringify(term,null,2)); + return term; + } + function factoriseExp(exp) { + //console.log('exp',clone(exp)); + var factor = alg.getHCF(exp); + + //console.log('-----'); + //console.log('exp',JSON.stringify(exp)); + //console.log('factor',JSON.stringify(factor)); + + if (isEqual(factor,[{sign:1,coeff:[1,1],subterms:[]}])) { + var exp2 = exp; + } else { + var dividend = []; + for (var t = 0; t < exp.length; t++) { + var termdiv = alg.divide([exp[t]],factor); + termdiv = alg.simplify(termdiv); + dividend.push(termdiv[0]); + } + //console.log('dividend',JSON.stringify(dividend)); + + if (isEqual(dividend,[{sign:1,coeff:[1,1],subterms:[]}])) { + var exp2 = exp; + } else { + var exp2 = factor; + exp2[0].subterms.push([dividend,1]); + //console.log('exp2',JSON.stringify(exp2)); + } + } + + for (var t = 0; t < exp2.length; t++) { + var term = exp2[t]; + for (var s = 0; s < term.subterms.length; s++) { + var subterm = term.subterms[s]; + if (alg.getSubtermType(subterm) !== 'compound') continue; + term.subterms[s][0] = alg.quadFactorise(term.subterms[s][0]); + } + } + exp2 = alg.quadFactorise(exp2); + + //console.log('exp2',JSON.stringify(exp2)); + + return exp2; + } + }, + factorisable: function(exp) { + var exp2 = clone(exp); + var exp3 = alg.factorise(exp2); + return !isEqual(exp2,exp3); + }, + quadFactorise: function(exp) { + if (exp.length > 3) return exp; + var exp2 = clone(exp); + exp2 = alg.collect(exp2); + + var terms = []; + for (var t = 0; t < exp2.length; t++) { + var term = exp2[t]; + var subterms = term.subterms; + if (subterms.length === 0) { + terms[2] = term; + } else if (subterms[0][1] === 2) { + terms[0] = term; + } else if (subterms[0][1] === 1) { + terms[1] = term; + } + } + if (un(terms[0])) return exp; + if (un(terms[2])) return exp; + var vari = terms[0].subterms[0][0]; + if (un(terms[1])) terms[1] = {sign:1,coeff:[0,1],subterms:[[vari,1]]}; + if (un(terms[2])) terms[2] = {sign:1,coeff:[0,1],subterms:[]}; + + var a = terms[0].sign*terms[0].coeff[0]; + var b = terms[1].sign*terms[1].coeff[0]; + var c = terms[2].sign*terms[2].coeff[0]; + + var det = b*b-4*a*c; + if (det < 0) return exp; + var detroot = Math.sqrt(det); + if (detroot !== Math.round(detroot)) return exp; + + var roots = [ + simplifyFrac2([-b+detroot,2*a]), + simplifyFrac2([-b-detroot,2*a]) + ]; + + var sign1 = roots[0][0]/roots[0][1] < 0 ? 1 : -1; + var sign2 = roots[1][0]/roots[1][1] < 0 ? 1 : -1; + roots[0][0] = Math.abs(roots[0][0]); + roots[0][1] = Math.abs(roots[0][1]); + roots[1][0] = Math.abs(roots[1][0]); + roots[1][1] = Math.abs(roots[1][1]); + + var exp3 = [{sign:1,coeff:[1,1],subterms:[]}]; + + if (sign1 === sign2 && arraysEqual(roots[0],roots[1])) { + var exp3 = [{sign:1,coeff:[1,1],subterms:[[[{sign:1,coeff:[roots[0][1],1],subterms:[[vari,1]]},{sign:sign1,coeff:[roots[0][0],1],subterms:[]}],2]]}]; + } else { + var exp3 = [ + {sign:1,coeff:[1,1],subterms:[ + [[{sign:1,coeff:[roots[0][1],1],subterms:[[vari,1]]},{sign:sign1,coeff:[roots[0][0],1],subterms:[]}],1], + [[{sign:1,coeff:[roots[1][1],1],subterms:[[vari,1]]},{sign:sign2,coeff:[roots[1][0],1],subterms:[]}],1] + ]} + ]; + } + + + return exp3; + }, + simplify: function(exp) { // simplifies fractions + var exp2 = clone(exp); + for (var e = 0; e < exp2.length; e++) { + var term = exp2[e]; + term.coeff = simplifyFrac2(term.coeff); + term.subterms = alg.simplifySubterms(term.subterms); + for (var s = 0; s < term.subterms.length; s++) { + var subterm = term.subterms[s]; + if (alg.getSubtermType(subterm) === 'compound') { + term.subterms[s] = [alg.simplify(subterm[0]),subterm[1]]; + } + } + } + var exp3 = []; + for (var e = 0; e < exp2.length; e++) { + var term = exp2[e]; + if (term.sign === 1 && arraysEqual(term.coeff,[1,1]) && !un(term.subterms) && term.subterms.length === 1 && !un(term.subterms[0]) && alg.getSubtermType(term.subterms[0]) === 'compound' && term.subterms[0][1] === 1) { + exp3 = exp3.concat(term.subterms[0][0]); + } else { + exp3.push(term); + } + } + return exp3; + + + /*var exp2 = clone(exp); + alg.collect(exp2); + for (var t = 0; t < exp2.length; t++) { + var term = exp2[t]; + term.coeff = simplifyFrac2(term.coeff); + term.subterms = alg.simplifySubterms(term.subterms); + for (var s = 0; s < term.subterms.length; s++) { + var subterm = term.subterms[s]; + if (alg.getSubtermType(subterm) === 'compound') { + term.subterms[s] = [alg.simplify(subterm[0]),subterm[1]]; + } + } + term.subterms = alg.orderSubterms(term.subterms); + } + return exp2;*/ + }, + simplifiable: function(exp) { + var exp2 = clone(exp); + var exp3 = alg.simplify(exp2); + return !isEqual(exp2,exp3); + }, + + likeTerms: function(term1,term2) { + var subterms1 = clone(term1.subterms); + var subterms2 = clone(term2.subterms); + if (subterms1.length !== subterms2.length) return false; + for (var s1 = 0; s1 < subterms1.length; s1++) { + var sub1 = subterms1[s1]; + for (var s2 = 0; s2 < subterms2.length; s2++) { + var sub2 = subterms2[s2]; + if (alg.likeSubterm(sub1,sub2) === false || sub1[1] !== sub2[1]) return false; + } + return true; + } + return true; + }, + getTermNumerator: function(term) { + if (typeof term === 'string') return {sign:1,coeff:[1,1],subterms:[[term,1]]}; + var num = {sign:term.sign,coeff:[term.coeff[0],1],subterms:[]}; + for (var s = 0; s < term.subterms.length; s++) { + var subterm = term.subterms[s]; + if (subterm[1] >= 0) { + num.subterms.push(subterm); + } + } + return num; + }, + getTermDenominator: function(term) { + if (typeof term === 'string') return {sign:1,coeff:[1,1],subterms:[]}; + var denom = {sign:1,coeff:[term.coeff[1],1],subterms:[]}; + for (var s = 0; s < term.subterms.length; s++) { + var subterm = term.subterms[s]; + if (subterm[1] < 0) { + denom.subterms.push([subterm[0],-1*subterm[1]]); + } + } + return denom; + }, + multiplyExpressions: function(exp1,exp2) { + var exp3 = []; + for (var e1 = 0; e1 < exp1.length; e1++) { + var term1 = clone(exp1[e1]); + var subterms1 = term1.subterms; + var coeff1 = term1.coeff; + for (var e2 = 0; e2 < exp2.length; e2++) { + var term2 = clone(exp2[e2]); + var subterms2 = term2.subterms; + var coeff2 = term2.coeff; + var sign3 = term1.sign*term2.sign; + var coeff3 = simplifyFrac2([coeff1[0]*coeff2[0],coeff1[1]*coeff2[1]]); + var subterms3 = subterms1.concat(subterms2); + subterms3 = alg.simplifySubterms(subterms3); + exp3.push({sign:sign3,coeff:coeff3,subterms:subterms3}); + } + } + return exp3; + }, + getTermReciprocal: function(term) { + var term2 = { + sign:term.sign, + coeff:[term.coeff[1],term.coeff[0]], + subterms:[] + }; + for (var s = 0; s < term.subterms.length; s++) { + var sub = clone(term.subterms[s]); + sub[1] *= -1; + term2.subterms.push(sub); + } + return term2; + }, + getExpressionReciprocal: function(exp) { + var exp = clone(exp); + if (exp.length === 1) { + var term = alg.getTermReciprocal(exp[0]); + return [term]; + } else { + return [{sign:1,coeff:[1,1],subterms:[[exp,-1]]}]; + } + }, + simplifySubterms: function(subterms) { + var subterms = clone(subterms); + for (var v2 = subterms.length-1; v2 >= 0; v2--) { // combine like subterms + for (var v1 = v2-1; v1 >= 0; v1--) { + if (alg.likeSubterm(subterms[v2],subterms[v1])) { + subterms[v1][1] += subterms[v2][1]; + subterms.splice(v2,1); + break; + } + } + } + for (var v2 = subterms.length-1; v2 >= 0; v2--) { // remove subterms with power 0 + if (subterms[v2][1] === 0) subterms.splice(v2,1); + } + return subterms; + }, + orderSubterms: function(subterms) { + //console.log(subterms); + subterms.sort(function(a,b) { + if (isEqual(a[0],b[0])) return b[1]-a[1]; + var typeA = alg.getSubtermType(a); + var typeB = alg.getSubtermType(b); + if (typeA === 'single' && typeB === 'single') { + if (a[0] < b[0]) return -1; + if (a[0] > b[0]) return 1; + } else if (typeA === 'single') { + return -1; + } else if (typeB === 'single') { + return 1; + } + return 0; + }); + for (var s = 0; s < subterms.length; s++) { + var subterm = subterms[s]; + var type = alg.getSubtermType(subterm); + if (type === 'compound') { + //console.log(subterm); + for (var s2 = 0; s2 < subterm[0].length; s2++) { + var sub2 = subterm[0][s2]; + alg.orderSubterms(sub2.subterms); + } + } + } + return subterms; + }, + getSubtermType: function(subterm) { + if (subterm.length === 2 && typeof subterm[0] === 'string' && typeof subterm[1] === 'number') { + return 'single'; + } else { + return 'compound'; + } + }, + likeSubterm: function(subterm1,subterm2) { + if (un(subterm1) && un(subterm2)) return true; + var sub1 = subterm1[0]; + var sub2 = subterm2[0]; + if (un(sub1) && un(sub2)) return true; + if (isEqual(sub1,sub2)) return true; + if (sub1 instanceof Array && sub2 instanceof Array) { + if (sub1.length !== sub2.length) return false; + sub1 = clone(sub1); + sub2 = clone(sub2); + for (var s1 = 0; s1 < sub1.length; s1++) { + var sub11 = sub1[s1]; + var found = false; + for (var s2 = 0; s2 < sub2.length; s2++) { + var sub22 = sub2[s2]; + if (sub11.sign === sub22.sign && arraysEqual(sub11.coeff,sub22.coeff) === true && arraysEqual(sub11.subterms,sub22.subterms) === true) { + found = true; + break; + } + } + if (found === false) return false; + } + return true; + } + return false; + }, + getHCF: function(exp) { + var exp = clone(exp); + if (exp.length === 1) return [{sign:1,coeff:[1,1],subterms:[]}]; + var term = exp[0]; + var sign = term.sign; + var num = term.coeff[0]; + var denom = term.coeff[1]; + var subterms = clone(term.subterms); + for (var t = 1; t < exp.length; t++) { + var term = exp[t]; + if (term.sign === 1) sign = 1; + num = hcf(num,term.coeff[0]); + denom = hcf(denom,term.coeff[1]); + for (var s1 = 0; s1 < subterms.length; s1++) { + var subterm1 = subterms[s1]; + var found = false; + for (var s2 = 0; s2 < term.subterms.length; s2++) { + var subterm2 = term.subterms[s2]; + if (alg.likeSubterm(subterm1,subterm2) === true) { + found = true; + subterm1[1] = Math.min(subterm1[1],subterm2[1]); + break; + } + } + if (found === false) { + subterms.splice(s1,1); + s1--; + } + } + } + return [{sign:sign,coeff:[num,denom],subterms:subterms}]; + }, + + hasNegativeIndices: function(exp) { + for (var e = 0; e < exp.length; e++) { + if (exp[e][1] < 0) return true; + } + return false; + }, + getCommonDenominator: function(exp) { + var exp = clone(exp); + var denomCoeff = 1; + var denomSubterms = []; + for (var e = 0; e < exp.length; e++) { + var term = exp[e]; + denomCoeff = (denomCoeff*term.coeff[1])/hcf(denomCoeff,term.coeff[1]); + var subterms = term.subterms; + for (var s = 0; s < subterms.length; s++) { + if (subterms[s][1] < 0) { + subterms[s][1] *= -1; + denomSubterms.push(subterms[s]); + } + } + } + for (var s1 = denomSubterms.length-1; s1 >= 0; s1--) { + var sub1 = denomSubterms[s1]; + for (var s2 = s1-1; s2 >= 0; s2--) { + var sub2 = denomSubterms[s2]; + if (alg.likeSubterm(sub1,sub2) === true) { + sub2[1] = Math.max(sub1[1],sub2[1]); + denomSubterms.splice(s1,1); + break; + } + } + } + + var exp2 = {sign:1,coeff:denomCoeff,subterms:denomSubterms}; + return exp2; + } +} + +function bound(value, min, max, roundTo) { + if (!un(roundTo)) + value = roundToNearest(value, roundTo); + return Math.max(min, Math.min(max, value)); +} + +function pngVis(sf, dl) { + if (un(sf)) sf = 0.4; + + var ctx = newctx({ + vis: false + }); + + var obj = container.childNodes; + for (var o = 0; o < obj.length; o++) { + if (obj[o].nodeType !== 1) + continue; + if (obj[o].nodeName.toLowerCase() !== 'canvas') + continue; + var dims = obj[o].getBoundingClientRect(); + var left = roundToNearest(xWindowToCanvas(dims.left), 1); + var top = roundToNearest(yWindowToCanvas(dims.top), 1); + flattenCanvases(ctx.canvas, obj[o], left, top); + } + + if (sf !== 1) { + var w = mainCanvasWidth * sf; + var h = mainCanvasHeight * sf; + var ctx2 = newctx({ + rect: [0, 0, w, h], + vis: false + }); + ctx2.drawImage(ctx.canvas, 0, 0, w, h); + var imgURL = canvasToPNG(ctx2.canvas); + } else { + var imgURL = canvasToPNG(ctx.canvas); + } + + //window.open(imgURL,'_blank'); + + return imgURL; + /* + if (boolean(dl, true) == true) { + var dlLink = document.createElement('a'); + dlLink.download = 'dl.png'; + dlLink.href = imgURL; + dlLink.dataset.downloadurl = ["image/png", dlLink.download, dlLink.href].join(':'); + document.body.appendChild(dlLink); + dlLink.click(); + document.body.removeChild(dlLink); + return; + } + return imgURL;*/ +} +function canvasToPNG(canvas, filename, l, t, w, h) { + var width = mainCanvasWidth; + var height = mainCanvasHeight; + if (isElement(canvas)) { + width = canvas.width; + height = canvas.height; + } + var canvas2 = document.createElement('canvas'); + canvas2.width = width; + canvas2.height = height; + var ctx2 = canvas2.getContext('2d'); + if (isElement(canvas)) { + ctx2.drawImage(canvas, 0, 0); + } else if (typeof canvas == 'object') { + for (var i = 0; i < canvas.length; i++) { + ctx2.drawImage(canvas[i], canvas[i].data[100], canvas[i].data[101]); + } + } + var left = l || 0; + var top = t || 0; + var w2 = w || width; + var h2 = h || height; + var canvas3 = document.createElement('canvas'); + canvas3.width = w2; + canvas3.height = h2; + var ctx3 = canvas3.getContext('2d'); + ctx3.drawImage(canvas2, -left, -top); + var imgURL = canvas3.toDataURL("image/png"); + return imgURL; +} + +function calcRects(obj) { + if (un(obj)) + obj = {}; + var left = def([obj.left, 0]); + var top = def([obj.top, 80]); + if (typeof obj.margin == 'object') { + obj.marginLeft = obj.margin[0]; + obj.marginTop = obj.margin[1]; + obj.marginRight = obj.margin[2]; + obj.marginBottom = obj.margin[3]; + } + var marginLeft = def([obj.marginLeft, obj.margin, 20]); + var marginBottom = def([obj.marginBottom, obj.margin, 20]); + var marginTop = def([obj.marginTop, obj.margin, 20]); + var marginRight = def([obj.marginRight, obj.margin, 20]); + var paddingVert = def([obj.paddingVert, obj.padding, 40]); + var paddingHoriz = def([obj.paddingHoriz, obj.padding, 40]); + var rows = def([obj.rows, 2]); + var cols = def([obj.cols, 2]); + var order = def([obj.order, 'v']); + + var width = (1200 - left - marginLeft - marginRight - (cols - 1) * paddingHoriz) / cols; + var height = (700 - top - marginTop - marginBottom - (rows - 1) * paddingVert) / rows; + + var arr = []; + + for (var r = 0; r < rows; r++) { + for (var c = 0; c < cols; c++) { + if (order == 'h') { + var index = r * cols + c; + } else { + var index = c * rows + r; + } + arr[index] = [ + left + marginLeft + c * (width + paddingHoriz), + top + marginTop + r * (height + paddingVert), + width, + height, + left + marginLeft + c * (width + paddingHoriz) + width, + top + marginTop + r * (height + paddingVert) + height, + left + marginLeft + c * (width + paddingHoriz) + 0.5 * width, + top + marginTop + r * (height + paddingVert) + 0.5 * height, + ]; + } + } + if (!un(obj.ctx)) { // if ctx is supplied, draw rects + for (var i = 0; i < arr.length; i++) { + obj.ctx.save(); + obj.ctx.strokeStyle = '#000'; + obj.ctx.lineWidth = 2; + obj.ctx.strokeRect(arr[i][0], arr[i][1], arr[i][2], arr[i][3]); + obj.ctx.lineWidth = 1; + obj.ctx.strokeStyle = '#666'; + obj.ctx.setLineDash([15, 15]); + obj.ctx.beginPath(); + obj.ctx.moveTo(arr[i][0], arr[i][7]); + obj.ctx.lineTo(arr[i][4], arr[i][7]); + obj.ctx.moveTo(arr[i][6], arr[i][1]); + obj.ctx.lineTo(arr[i][6], arr[i][5]); + obj.ctx.stroke(); + obj.ctx.restore(); + } + } + return arr; +} + +var dropMenus = []; +function dropMenu(obj) { + obj.open = false; + obj.selected = -1; + obj.buttonColor = def([obj.buttonColor, obj.selectedColor, '#3FF']); + obj.buttonBorderColor = def([obj.buttonBorderColor, '#000']); + obj.buttonBorder = boolean(obj.buttonBorder, true); + obj.showDownArrow = boolean(obj.showDownArrow, true); + obj.selectedColor = def([obj.selectedColor, '#3FF']); + obj.unselectedColor = def([obj.unselectedColor, '#CFF']); + obj.z = def([obj.z, 100000000]); + obj.listShowMax = def([obj.listShowMax, -1]); + obj.overflow = false; + obj.fullWidth = clone(obj.listRect[2]); + obj.font = def([obj.font, 'Arial']); + obj.fontSize = def([obj.fontSize, 16]); + obj.align = def([obj.align, 'left']); + + obj.canvas1 = createCanvas(obj.buttonRect[0], obj.buttonRect[1], obj.buttonRect[2], obj.buttonRect[3], true, false, true, obj.z); + obj.canvas1.parent = obj; + obj.canvas1.click = function () { + var obj = this.parent; + obj.open = !obj.open; + if (obj.open == true) { + showObj(obj.canvas2); + showObj(obj.canvas3); + if (obj.overflow) + showScroller(obj.scroller); + addListenerMove(window, obj.move); + addListener(obj.canvas2, obj.click); + addListenerStart(window, obj.windowClickClose); + if (obj.overflow == true) + window.addEventListener("mousewheel", obj.mouseWheelHandler, false); + } else { + obj.close(); + } + } + obj.canvas1.draw = function () { + var obj = this.parent; + var ctx = this.ctx; + var w = obj.buttonRect[2]; + var h = obj.buttonRect[3]; + ctx.clearRect(0, 0, w, h); + if (obj.buttonBorder == true) { + text({ + ctx: obj.canvas1.ctx, + text: [obj.title], + left: 1.5, + top: 1.5, + width: w - 3, + height: h - 3, + align: 'center', + vertAlign: 'middle', + box: { + type: 'loose', + color: obj.buttonColor, + borderWidth: 3, + borderColor: obj.buttonBorderColor + } + }); + } else { + text({ + ctx: obj.canvas1.ctx, + text: [obj.title], + left: 1.5, + top: 1.5, + width: w - 3, + height: h - 3, + align: 'center', + vertAlign: 'middle' + }); + } + if (obj.showDownArrow == true) { + ctx.fillStyle = '#000'; + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + var l = w - 15; + var t = h / 2; + ctx.beginPath(); + ctx.moveTo(l - 8, t - 4); + ctx.lineTo(l + 8, t - 4); + ctx.lineTo(l, t + 8); + ctx.lineTo(l - 8, t - 4); + ctx.fill(); + } + } + obj.canvas1.draw(); + addListenerEnd(obj.canvas1, obj.canvas1.click); + obj.windowClickClose = function (e) { + for (var i = 0; i < dropMenus.length; i++) { + if (dropMenus[i].open == true) { + var obj = dropMenus[i]; + if (e.target !== obj.canvas1 && e.target !== obj.canvas2 && (un(obj.scroller) || (e.target !== obj.scroller.canvas && e.target !== obj.scroller.sliderCanvas))) { + obj.close(); + } + } + } + } + obj.close = function () { + var obj = this; + hideObj(obj.canvas2); + hideObj(obj.canvas3); + hideScroller(obj.scroller); + removeListenerMove(window, obj.move); + removeListener(obj.canvas2, obj.click); + removeListener(window, obj.windowClickClose); + if (obj.overflow == true) + window.removeEventListener("mousewheel", obj.mouseWheelHandler, false); + obj.selected = -1; + obj.open = false; + obj.draw(); + } + + obj.canvas2 = createCanvas(obj.listRect[0], obj.listRect[1], obj.listRect[2], obj.listRect[3], false, false, true, obj.z); // text - drawn once + obj.canvas2.parent = obj; + obj.canvas3 = createCanvas(obj.listRect[0], obj.listRect[1], obj.listRect[2], obj.listRect[3], false, false, false, obj.z + 1); // colors + obj.canvas3.parent = obj; + obj.scroller = createScroller({ + rect: [obj.listRect[0] + obj.listRect[2] - 20, obj.listRect[1], 20, obj.listRect[3] * obj.listShowMax], + z: obj.z + 2, + min: 0, + max: obj.data.length - obj.listShowMax, + inc: 1, + sliderHeight: (obj.listRect[3] * obj.listShowMax - 2 * 20) * (obj.listShowMax / obj.data.length), + funcMove: function (value) { + var obj = this.parent; + value = roundToNearest(value, 1); + obj.draw(value); + removeListenerMove(window, obj.move); + removeListener(obj.canvas2, obj.click); + removeListener(window, obj.windowClickClose); + }, + funcStop: function (value) { + var obj = this.parent; + value = roundToNearest(value, 1); + obj.draw(value); + addListenerMove(window, obj.move); + addListener(obj.canvas2, obj.click); + addListener(window, obj.windowClickClose); + } + }); + obj.scroller.parent = obj; + obj.mouseWheelHandler = function (e) { + // cross-browser wheel delta + var e = window.event || e; // old IE support + var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail))); + for (var i = 0; i < dropMenus.length; i++) { + if (dropMenus[i].open == true) { + var obj = dropMenus[i]; + setScrollerValue(obj.scroller, obj.scrollPos - delta, true); + break; + } + } + } + hideScroller(obj.scroller); + obj.updateData = function () { + var obj = this; + var height = this.listRect[3] * this.data.length; + if (this.listShowMax !== -1 && this.listShowMax < this.data.length) { + this.overflow = true; + this.listRect[2] = this.fullWidth - 20; + height = this.listRect[3] * this.listShowMax; + var s = this.scroller; + s.max = this.data.length - this.listShowMax; + s.sliderHeight = (height - 2 * 20) * (this.listShowMax / this.data.length); + s.incDist = (s.rect[3] - 2 * 20 - s.sliderHeight) / ((s.max - s.min) / s.inc); + s.sliderRect[3] = s.sliderHeight; + s.sliderCanvas.data[100] = s.sliderRect[0]; + s.sliderCanvas.data[101] = s.sliderRect[1]; + s.sliderCanvas.data[102] = s.sliderRect[2]; + s.sliderCanvas.data[103] = s.sliderRect[3]; + resizeCanvas(s.sliderCanvas, s.sliderCanvas.data[100], s.sliderCanvas.data[101], s.sliderCanvas.data[102], s.sliderCanvas.data[103]); + setScrollerValue(s, 0, true); + } else { + this.overflow = false; + this.listRect[2] = this.fullWidth; + } + this.scrollPos = 0; + this.canvas2.data[102] = this.listRect[2]; + this.canvas2.width = this.listRect[2]; + this.canvas2.data[103] = height; + this.canvas2.height = height; + resizeCanvas(this.canvas2, this.listRect[0], this.listRect[1], this.listRect[2], height); + this.canvas3.data[102] = this.listRect[2]; + this.canvas3.width = this.listRect[2]; + this.canvas3.data[103] = height; + this.canvas3.height = height; + resizeCanvas(this.canvas3, this.listRect[0], this.listRect[1], this.listRect[2], height); + obj.drawListText(); + obj.draw(); + + } + obj.drawListText = function () { + var obj = this; + var top = 0; + if (obj.overflow) + top -= obj.scrollPos * obj.listRect[3]; + var ctx = obj.canvas3.ctx; + for (var d = 0; d < obj.data.length; d++) { + text({ + ctx: ctx, + text: ['<><>' + obj.data[d]], + left: 0, + top: top, + width: obj.listRect[2], + height: obj.listRect[3], + align: this.align, + vertAlign: 'middle', + box: { + color: 'none', + borderWidth: 0.01 + } + }); + top += obj.listRect[3]; + ctx.strokeStyle = '#000'; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(0, top); + ctx.lineTo(obj.listRect[2], top); + ctx.stroke(); + } + ctx.strokeStyle = '#000'; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.lineTo(obj.listRect[2], 0); + ctx.moveTo(0, 0); + ctx.lineTo(0, top); + ctx.moveTo(obj.listRect[2], 0); + ctx.lineTo(obj.listRect[2], top); + ctx.stroke(); + } + obj.draw = function (newScrollPos) { + var obj = this; + var ctx = this.canvas2.ctx; + if (this.overflow == true) { + if (!un(newScrollPos) && newScrollPos !== obj.scrollPos) { + obj.scrollPos = newScrollPos; + var ctx2 = this.canvas3.ctx; + ctx2.clearRect(0, 0, this.listRect[2], this.listRect[3] * this.listShowMax); + obj.drawListText(); + } + ctx.fillStyle = this.unselectedColor; + ctx.fillRect(0, 0 - this.scrollPos * this.listRect[3], this.listRect[2], this.listRect[3] * this.data.length); + ctx.fillStyle = this.selectedColor; + ctx.fillRect(0, this.listRect[3] * this.selected - this.scrollPos * this.listRect[3], this.listRect[2], this.listRect[3]); + } else { + ctx.fillStyle = this.unselectedColor; + ctx.fillRect(0, 0, this.listRect[2], this.listRect[3] * this.data.length); + ctx.fillStyle = this.selectedColor; + ctx.fillRect(0, this.listRect[3] * this.selected, this.listRect[2], this.listRect[3]); + } + } + obj.updateData(); + obj.move = function (e) { + updateMouse(e); + var found = false; + for (var i = 0; i < dropMenus.length; i++) { + if (dropMenus[i].open == true) { + obj = dropMenus[i]; + var found = true; + break; + } + } + if (found == false) + return; + if (mouse.x < obj.listRect[0] || mouse.x > obj.listRect[0] + obj.listRect[2] || mouse.y < obj.listRect[1] || mouse.y > obj.listRect[1] + obj.data.length * obj.listRect[3]) { + if (obj.selected !== -1) { + obj.selected = -1; + obj.draw(); + } + } else { + var sel = Math.floor((mouse.y - obj.listRect[1]) / obj.listRect[3]); + if (obj.overflow == true) + sel += obj.scrollPos; + if (obj.selected !== sel) { + obj.selected = sel; + obj.draw(); + } + } + } + obj.click = function () { + var obj = this.parent; + if (!un(obj.func)) + obj.func.apply(); + obj.close(); + } + resize(); + obj.index = dropMenus.length; + dropMenus.push(obj); // place in global array + return obj; +} + +function drawTable(context, lineWidth, lineColor, l, t, hLines, vLines) { + context.save(); + context.beginPath(); + context.lineWidth = lineWidth; + context.strokeStyle = lineColor; + context.lineCap = 'round'; + // draw horizontal lines + for (h = 0; h < hLines.length; h++) { + context.moveTo(l + vLines[0], t + hLines[h]); + context.lineTo(l + vLines[vLines.length - 1], t + hLines[h]); + } + // draw vertical lines + for (v = 0; v < vLines.length; v++) { + context.moveTo(l + vLines[v], t + hLines[0]); + context.lineTo(l + vLines[v], t + hLines[hLines.length - 1]); + } + context.stroke(); + context.restore(); +} +function calcTable2(object) { + var left = object.left; + var top = object.top; + var cells = object.cells; + + var minCellWidth = object.minCellWidth || 80; + var maxCellWidth = object.maxCellWidth || 150; + var minCellHeight = object.minCellHeight || 100; + var minCellPadding = object.minCellPadding || 10; + var horizAlign = object.horizAlign || object.align || 'center'; + if (typeof object.text == 'object') { + var font = object.text.font || 'Arial'; + var fontSize = object.text.size || 32; + var textColor = object.text.color || '#000'; + } else { + var font = 'Arial'; + var fontSize = 32; + var textColor = '#000'; + } + + var numRows = cells.length; + var numCols = 0; + for (var i = 0; i < cells.length; i++) { + numCols = Math.max(cells[i].length, numCols); + } + var cellHeights = []; + for (var i = 0; i < numRows; i++) { + cellHeights[i] = minCellHeight; + } + var cellWidths = []; + for (var j = 0; j < numCols; j++) { + cellWidths[j] = minCellWidth; + } + var totalWidth = 0; + var totalHeight = 0; + + for (var i = 0; i < cells.length; i++) { + var maxHeight = minCellHeight; + for (var j = 0; j < cells[i].length; j++) { + if (typeof cells[i][j] == 'object') { + if (typeof cells[i][j].text !== 'object') + cells[i][j].text = []; + if (typeof cells[i][j].minWidth !== 'number') + cells[i][j].minWidth = 0; + if (typeof cells[i][j].minHeight !== 'number') + cells[i][j].minHeight = 0; + cells[i][j].text.unshift('<><><>'); + cells[i][j].text = reduceTags(cells[i][j].text); + var dims = drawMathsText(ctx, cells[i][j].text, fontSize, 0, 0, false, [], horizAlign, 'middle', text.color, 'measure'); + maxHeight = Math.max(dims[1] + 2 * minCellPadding, cells[i][j].minHeight, maxHeight); + cellWidths[j] = Math.max(dims[0] + 2 * minCellPadding, cells[i][j].minWidth, cellWidths[j]); + } + } + cellHeights[i] = Math.max(maxHeight, cellHeights[i]); + totalHeight += cellHeights[i]; + } + for (var j = 0; j < cellWidths.length; j++) { + totalWidth += cellWidths[j]; + } + + var horizPos = [left]; + for (var i = 0; i < cellWidths.length; i++) { + horizPos.push(horizPos[horizPos.length - 1] + cellWidths[i]) + } + + var vertPos = [top]; + for (var i = 0; i < cellHeights.length; i++) { + vertPos.push(vertPos[vertPos.length - 1] + cellHeights[i]) + } + + var cellDims = []; + + var topPos = top; + for (var i = 0; i < cells.length; i++) { + var leftPos = left; + cellDims[i] = []; + for (var j = 0; j < cells[i].length; j++) { + cellDims[i][j] = { + left: leftPos + 2, + top: topPos + 1, + width: cellWidths[j] - 9, + height: cellHeights[i] - 8, + border: false, + offset: [-40, 0.5 * (cellHeights[i] - 8) - 20], + fontSize: fontSize, + leftPoint: minCellPadding, + textColor: textColor, + textAlign: horizAlign, + fontSize: fontSize + }; + leftPos += cellWidths[j]; + } + topPos += cellHeights[i]; + } + + return { + cell: cellDims, + xPos: horizPos, + yPos: vertPos + }; +} +function drawTable2(object) { + /* EXAMPLE USAGE: + var j0001table1 = drawTable2({ + ctx:j0001buttonctx[0], + left:100, + top:150, + minCellWidth:80, + minCellHeight:50, + horizAlign:'center', + text:{font:'Arial',size:32,color:'#000'}, + outerBorder:{show:true,width:4,color:'#000'}, + innerBorder:{show:true,width:2,color:'#666',dash:[5,5]}, + cells:[ + [ // row 0{text:['<><>x'],color:'#CCF',minWidth:100,minHeight:70},{text:['<><>y'],color:'#CCF',minWidth:100,minHeight:70},{text:['<><><>z'],color:'#CCF',minWidth:100,minHeight:70}, + ], [ // row 1{},{text:['2']},{text:['<>3']}, + ], [ // row 2{text:['4']},{},{text:['<>6']}, + ], + ] + }); + + // CAN EASILY USE IN CONJUNCTION WITH INPUTS()... + inputs({ + inputs:[ + // j0001table1.cell[row][col] - nb. start counting from zero + j0001table1.cell[1][0], + j0001table1.cell[2][1] + ], + checkFuncs:[ + function(input) { + if (input.stringJS == '1') { + return true; + } else { + return false; + } + }, + function(input) { + if (input.stringJS == '5') { + return true; + } else { + return false; + } + }, + ] + }); + */ + + if (typeof object.sf !== 'undefined') { + var sf = object.sf; + } else { + var sf = 1; + } + var ctx = object.ctx || object.context; + var left = object.left * sf; + var top = object.top * sf; + var cells = object.cells; + + var minCellWidth = object.minCellWidth * sf || 80 * sf; + var maxCellWidth = object.maxCellWidth * sf || Math.max(1200 * sf, object.minCellWidth * sf); + var minCellHeight = object.minCellHeight * sf || 100 * sf; + var minCellPadding = object.minCellPadding * sf || 7 * sf; + var paddingH = minCellPadding; + var paddingV = minCellPadding; + if (typeof object.paddingH == 'number') + paddingH = object.paddingH * sf; + if (typeof object.paddingV == 'number') + paddingV = object.paddingV * sf; + var horizAlign = object.horizAlign || object.align || 'center'; + if (typeof object.text == 'object') { + var font = object.text.font || 'Arial'; + var fontSize = object.text.size || 32; + var textColor = object.text.color || '#000'; + } else { + var font = 'Arial'; + var fontSize = 32; + var textColor = '#000'; + } + if (typeof object.alpha == 'number') { + var alpha = object.alpha; + } else { + var alpha = 1; + } + var lineJoin = object.lineJoin || object.lineCap || 'round'; + var lineCap = object.lineCap || object.lineJoin || 'round'; + var outerBorder = {}; + if (typeof object.outerBorder == 'object') { + outerBorder.show = boolean(object.outerBorder.show, true); + outerBorder.width = object.outerBorder.width * sf || 4 * sf; + if (typeof object.outerBorder.color == 'undefined') { + outerBorder.color = colorA('#000', alpha); + } else { + outerBorder.color = colorA(object.outerBorder.color, alpha); + } + outerBorder.dash = object.outerBorder.dash || []; + } else { + outerBorder.show = true; + outerBorder.width = 4 * sf; + outerBorder.color = colorA('#000', alpha); + outerBorder.dash = []; + } + outerBorder.dash = enlargeDash(outerBorder.dash, sf); + var innerBorder = {}; + if (typeof object.innerBorder == 'object') { + innerBorder.show = boolean(object.innerBorder.show, true); + innerBorder.width = object.innerBorder.width * sf || 4 * sf; + if (typeof object.innerBorder.color == 'undefined') { + innerBorder.color = colorA('#000', alpha); + } else { + innerBorder.color = colorA(object.innerBorder.color, alpha); + } + innerBorder.dash = object.innerBorder.dash || []; + } else { + innerBorder.show = true; + innerBorder.width = 4 * sf; + innerBorder.color = colorA('#000', alpha); + innerBorder.dash = [20, 15]; + } + innerBorder.dash = enlargeDash(innerBorder.dash, sf); + var tableAlignHoriz = object.tableAlignHoriz || 'left'; // is the whole table centred on [left,top]? + var tableAlignVert = object.tableAlignVert || 'top'; + + var numRows = cells.length; + var numCols = 0; + for (var i = 0; i < cells.length; i++) { + numCols = Math.max(cells[i].length, numCols); + } + var cellHeights = []; + for (var i = 0; i < numRows; i++) { + cellHeights[i] = minCellHeight; + } + var cellWidths = []; + for (var j = 0; j < numCols; j++) { + cellWidths[j] = minCellWidth; + } + var totalWidth = 0; + var totalHeight = 0; + + if (typeof hiddenCanvas == 'undefined') { + var hiddenCanvas = document.createElement('canvas'); + hiddenCanvas.width = mainCanvasWidth * sf; + hiddenCanvas.height = mainCanvasHeight * sf; + hiddenCanvas.ctx = hiddenCanvas.getContext('2d'); + } + + for (var r = 0; r < cells.length; r++) { + var maxHeight = minCellHeight; + for (var c = 0; c < cells[r].length; c++) { + if (typeof cells[r][c] == 'object') { + if (typeof cells[r][c].text !== 'object') { + cells[r][c].text = []; + } else { + cells[r][c].text = clone(cells[r][c].text); + } + if (typeof cells[r][c].color !== 'string') + cells[r][c].color = 'none'; + if (typeof cells[r][c].minWidth !== 'number') + cells[r][c].minWidth = 0; + if (typeof cells[r][c].minHeight !== 'number') + cells[r][c].minHeight = 0; + var font2 = font; + var fontSize2 = fontSize; + var textColor2 = textColor; + if (!un(cells[r][c].font)) + font2 = cells[r][c].font; + if (!un(cells[r][c].fontSize)) + fontSize2 = cells[r][c].fontSize; + if (!un(cells[r][c].textColor)) + textColor2 = cells[r][c].textColor; + if (un(cells[r][c].styled)) { + cells[r][c].text.unshift('<><><>'); + cells[r][c].styled = true; + } + //var dims = drawMathsText(ctx,cells[r][c].text,fontSize,0,0,false,[],horizAlign,'middle',text.color,'measure'); + //maxHeight = Math.max(dims[1]+2*minCellPadding,cells[r][c].minHeight,maxHeight); + //cellWidths[c] = Math.max(dims[0]+2*minCellPadding,cells[r][c].minWidth,cellWidths[c]); + + var cellText = text({ + ctx: hiddenCanvas.ctx, + left: 0, + top: 0, + width: maxCellWidth, + textArray: cells[r][c].text, + minTightWidth: 5, + minTightHeight: 5, + box: cells[r][c].box, + sf: sf + }); + maxHeight = Math.max(cellText.tightRect[3] + 2 * paddingV, cells[r][c].minHeight * sf, maxHeight); + cellWidths[c] = Math.max(cellText.tightRect[2] + 3 * paddingH, cells[r][c].minWidth * sf, cellWidths[c]); + } + } + cellHeights[r] = Math.max(maxHeight, cellHeights[r]); + totalHeight += cellHeights[r]; + } + for (var j = 0; j < cellWidths.length; j++) { + totalWidth += cellWidths[j]; + } + + if (tableAlignHoriz == 'center') { + left = left - totalWidth / 2; + } else if (tableAlignHoriz == 'right') { + left = left - totalWidth; + } + + if (tableAlignVert == 'middle') { + top = top - totalHeight / 2; + } else if (tableAlignVert == 'bottom') { + top = top - totalHeight; + } + + ctx.save(); + ctx.lineCap = lineCap; + ctx.lineJoin = lineJoin; + + var cellDims = []; + + var horizPos = [left]; + for (var i = 0; i < cellWidths.length; i++) { + horizPos.push(horizPos[horizPos.length - 1] + cellWidths[i]) + } + + var vertPos = [top]; + for (var i = 0; i < cellHeights.length; i++) { + vertPos.push(vertPos[vertPos.length - 1] + cellHeights[i]) + } + + // write text to each cell + var topPos = top; + for (var i = 0; i < cells.length; i++) { + var leftPos = left; + cellDims[i] = []; + for (var j = 0; j < cells[i].length; j++) { + cellDims[i][j] = { + left: leftPos + 2, + top: topPos + 1, + width: cellWidths[j] - 9, + height: cellHeights[i] - 8, + border: false, + offset: [-40, 0.5 * (cellHeights[i] - 8) - 20], + fontSize: fontSize, + leftPoint: paddingH, + textColor: textColor, + textAlign: horizAlign, + fontSize: fontSize + }; + if (cells[i][j].highlight == true) { + if (typeof cells[i][j].color == 'undefined' || cells[i][j].color !== 'none') { + ctx.fillStyle = colorA(invertColor(cells[i][j].color), alpha); + } else { + ctx.fillStyle = colorA(invertColor('#FFC'), alpha); + } + ctx.fillRect(leftPos, topPos, cellWidths[j], cellHeights[i]); + } else if (typeof cells[i][j].color == 'undefined' || cells[i][j].color !== 'none') { + ctx.fillStyle = colorA(cells[i][j].color, alpha); + ctx.fillRect(leftPos, topPos, cellWidths[j], cellHeights[i]); + } + /*if (horizAlign == 'left') { + var dims = drawMathsText(ctx,cells[i][j].text,fontSize,leftPos+minCellPadding,topPos+0.5*cellHeights[i],false,[],horizAlign,'middle',textColor); + } else if (horizAlign == 'center') { + var dims = drawMathsText(ctx,cells[i][j].text,fontSize,leftPos+0.5*cellWidths[j],topPos+0.5*cellHeights[i],false,[],horizAlign,'middle',textColor); + } else if (horizAlign == 'right') { + var dims = drawMathsText(ctx,cells[i][j].text,fontSize,leftPos+cellWidths[j]-minCellPadding,topPos+0.5*cellHeights[i],false,[],horizAlign,'middle',textColor); + }*/ + var align = horizAlign; + if (!un(cells[i][j].align)) + align = cells[i][j].align; + var cellText = text({ + ctx: ctx, + left: leftPos + paddingH, + top: topPos + paddingV, + width: cellWidths[j] - 2 * paddingH, + height: cellHeights[i] - 2 * paddingV, + textArray: cells[i][j].text, + textAlign: align, + vertAlign: 'middle', + padding: 0.001, + box: cells[i][j].box, + sf: sf + //box:{type:'tight'} + }); + //console.log(cellText.tightRect[2],cellText.tightRect[3]); + leftPos += cellWidths[j]; + } + topPos += cellHeights[i]; + } + + // draw inner border + if (innerBorder.show == true) { + ctx.strokeStyle = innerBorder.color; + ctx.lineWidth = innerBorder.width; + if (!ctx.setLineDash) { + ctx.setLineDash = function () {} + } + ctx.setLineDash(innerBorder.dash); + var leftPos = left; + for (var i = 0; i < cellWidths.length - 1; i++) { + leftPos += cellWidths[i]; + ctx.beginPath(); + ctx.moveTo(leftPos, top); + ctx.lineTo(leftPos, top + totalHeight); + ctx.stroke(); + } + var topPos = top; + for (var i = 0; i < cellHeights.length - 1; i++) { + topPos += cellHeights[i]; + ctx.beginPath(); + ctx.moveTo(left, topPos); + ctx.lineTo(left + totalWidth, topPos); + ctx.stroke(); + } + } + + // draw outer border + if (outerBorder.show == true) { + ctx.strokeStyle = outerBorder.color; + ctx.lineWidth = outerBorder.width; + if (!ctx.setLineDash) { + ctx.setLineDash = function () {} + } + ctx.setLineDash(outerBorder.dash); + ctx.beginPath(); + ctx.strokeRect(left, top, totalWidth, totalHeight); + } + + ctx.restore(); + + return { + cell: cellDims, + xPos: horizPos, + yPos: vertPos + }; +} +function drawTable3(object) { + var sf = typeof object.sf !== 'undefined' ? object.sf : 1; + var ctx = object.ctx || object._ctx || object.context; + var left = object.left * sf; + var top = object.top * sf; + var cells = object.cells; + + var widths = clone(object.widths); + var heights = clone(object.heights); + if (sf !== 1) { + for (var w = 0; w < widths.length; w++) widths[w] = widths[w] * sf; + for (var h = 0; h < heights.length; h++) heights[h] = heights[h] * sf; + } + var minCellPadding = object.minCellPadding * sf || 0; + var paddingH = minCellPadding; + var paddingV = minCellPadding; + if (typeof object.paddingH == 'number') paddingH = object.paddingH * sf; + if (typeof object.paddingV == 'number') paddingV = object.paddingV * sf; + //var innerPaddingH = !un(obj.innerPaddingH) ? obj.innerPaddingH*sf : 0; + //var innerPaddingV = !un(obj.innerPaddingV) ? obj.innerPaddingV*sf : 0; + var horizAlign = object.horizAlign || object.align || 'center'; + var alpha = typeof object.alpha == 'number' ? object.alpha : 1; + var lineJoin = object.lineJoin || object.lineCap || 'round'; + var lineCap = object.lineCap || object.lineJoin || 'round'; + var outerBorder = {}; + if (typeof object.outerBorder == 'object') { + outerBorder.show = boolean(object.outerBorder.show, true); + outerBorder.width = object.outerBorder.width * sf || 4 * sf; + if (typeof object.outerBorder.color == 'undefined') { + outerBorder.color = colorA('#000', alpha); + } else { + outerBorder.color = colorA(object.outerBorder.color, alpha); + } + outerBorder.dash = object.outerBorder.dash || []; + outerBorder.radius = object.outerBorder.radius*sf || 0; + } else { + outerBorder.show = true; + outerBorder.width = 4 * sf; + outerBorder.color = colorA('#000', alpha); + outerBorder.dash = []; + outerBorder.radius = 0; + } + outerBorder.dash = enlargeDash(outerBorder.dash, sf); + var innerBorder = {}; + if (typeof object.innerBorder == 'object') { + innerBorder.show = boolean(object.innerBorder.show, true); + innerBorder.width = object.innerBorder.width * sf || 4 * sf; + if (typeof object.innerBorder.color == 'undefined') { + innerBorder.color = colorA('#000', alpha); + } else { + innerBorder.color = colorA(object.innerBorder.color, alpha); + } + innerBorder.dash = object.innerBorder.dash || []; + innerBorder.dash = enlargeDash(innerBorder.dash, sf); + } else { + innerBorder.show = false; + /*innerBorder.show = true; + innerBorder.width = 4 * sf; + innerBorder.color = colorA('#000', alpha); + innerBorder.dash = [20, 15]; + innerBorder.dash = enlargeDash(innerBorder.dash, sf);*/ + } + + var tableAlignHoriz = object.tableAlignHoriz || 'center'; // is the whole table centred on [left,top]? + var tableAlignVert = object.tableAlignVert || 'middle'; + if (!un(object.align)) { + tableAlignHoriz = object.align[0] == -1 ? 'left' : object.align[0] == 0 ? 'center' : 'right'; + tableAlignVert = object.align[1] == -1 ? 'top' : object.align[1] == 0 ? 'middle' : 'bottom'; + } + if (typeof object.text == 'object') { + var font = object.text.font || 'Arial'; + var fontSize = object.text.size || 28; + var textColor = object.text.color || '#000'; + } else { + var font = 'Arial'; + var fontSize = 28; + var textColor = '#000'; + } + var fracScale = object.fracScale; + var algPadding = object.algPadding; + + var totalWidth = arraySum(widths); + var totalHeight = arraySum(heights); + + ctx.save(); + ctx.lineCap = lineCap; + ctx.lineJoin = lineJoin; + + var cellDims = []; + + var horizPos = [left]; + for (var i = 0; i < widths.length; i++) { + horizPos.push(horizPos[horizPos.length - 1] + widths[i]) + } + + var vertPos = [top]; + for (var i = 0; i < heights.length; i++) { + vertPos.push(vertPos[vertPos.length - 1] + heights[i]) + } + + // color, inner border & text for cells + var topPos = top; + var cellTextMeasure = []; + for (var i = 0; i < cells.length; i++) { + var leftPos = left; + cellDims[i] = []; + var skipCells = 0; + cellTextMeasure[i] = []; + for (var j = 0; j < cells[i].length; j++) { + var cell = cells[i][j]; + if (skipCells > 0) { + skipCells--; + continue; + } + if (un(cell.colSpan)) { + var cellWidth = widths[j]; + } else { + var cellWidth = 0; + for (var k = j; k < Math.min(j + cell.colSpan, cells[i].length); k++) { + cellWidth += widths[k]; + } + skipCells = cell.colSpan - 1; + } + var cellPaddingH = def([cell.paddingH, cell.padding, paddingH]); + var cellPaddingV = def([cell.paddingV, cell.padding, paddingV]); + cellDims[i][j] = { + left: leftPos, + top: topPos, + width: cellWidth, + height: heights[i], + border: false, + offset: [-40, 0.5 * (heights[i]) - 20], + leftPoint: cellPaddingH, + }; + + var c1 = (typeof cell.color !== 'undefined' && cell.color !== 'none') ? true : false; + var c2 = (!un(cell.box) && cell.box.show == true) ? true : false; + var hl = cell.highlight; + /*if (!un(object.isInput)) { + if (draw.mode == 'interact') { + var selected = boolean(cell.toggle, false); + } else { + var selected = boolean(cell.ans, false); + } + if (!un(cell.selColors)) { + var isInputColor = selected ? cell.selColors[1] : cell.selColors[0]; + } else if (!un(object.isInput.selColors)) { + var isInputColor = selected ? object.isInput.selColors[1] : object.isInput.selColors[0]; + } else { + var isInputColor = selected ? '#66F' : '#CCF'; + } + */ + + if (c2 == true) { + var box = cell.box; + //var fillColor = isInputColor || box.fillColor || box.color || undefined; + var fillColor = box.fillColor || box.color || undefined; + var lineColor = box.borderColor || box.lineColor || undefined; + if (hl == true) { + if (!un(fillColor) && fillColor !== 'none') + fillColor = colorA(invertColor(fillColor), alpha); + if (!un(lineColor) && lineColor !== 'none') + lineColor = colorA(invertColor(lineColor), alpha); + } + var lineWidth = box.borderWidth || box.lineWidth || box.width || 3; + lineWidth = lineWidth * sf; + var dash = def([box.dash, []]); + var radius = box.borderRadius || box.radius || 0; + radius = radius * sf; + roundedRect(ctx, leftPos + cellPaddingH, topPos + cellPaddingV, cellWidth - 2 * cellPaddingH, heights[i] - 2 * cellPaddingV, radius, lineWidth, lineColor, fillColor, dash); + } else if (c1 == true) { + //var fillColor = isInputColor || cell.color; + var fillColor = cell.color; + if (hl == true) { + fillColor = colorA(invertColor(fillColor), alpha); + } else { + fillColor = colorA(fillColor, alpha); + } + /*ctx.fillStyle = fillColor; + ctx.globalCompositeOperation = 'destination-over'; // draw behind existing content + ctx.fillRect(leftPos,topPos,cellWidth,heights[i]); + ctx.globalCompositeOperation = 'source-over'; // default*/ + roundedRect(ctx, leftPos + cellPaddingH, topPos + cellPaddingV, cellWidth - 2 * cellPaddingH, heights[i] - 2 * cellPaddingV, 0, 0, 'none', fillColor); + } else { + if (hl == true) { + ctx.fillStyle = colorA(invertColor(mainCanvasFillStyle), alpha); + ctx.globalCompositeOperation = 'destination-over'; // draw behind existing content + ctx.fillRect(leftPos, topPos, cellWidth, heights[i]); + ctx.globalCompositeOperation = 'source-over'; // default + } + } + + if (innerBorder.show == true) { + ctx.strokeStyle = innerBorder.color; + ctx.lineWidth = innerBorder.width; + if (un(ctx.setLineDash)) { + ctx.setLineDash = function () {} + } + ctx.setLineDash(innerBorder.dash); + ctx.beginPath(); + if (i > 0) { + ctx.moveTo(leftPos, topPos); + ctx.lineTo(leftPos + cellWidth, topPos); + } + if (i < cells.length - 1) { + ctx.moveTo(leftPos, topPos + heights[i]); + ctx.lineTo(leftPos + cellWidth, topPos + heights[i]); + } + if (j > 0) { + ctx.moveTo(leftPos, topPos); + ctx.lineTo(leftPos, topPos + heights[i]); + } + if (j < cells[i].length - 1 && (un(cell.colSpan) || j + cell.colSpan < cells[i].length - 1)) { + ctx.moveTo(leftPos + cellWidth, topPos); + ctx.lineTo(leftPos + cellWidth, topPos + heights[i]); + } + ctx.stroke(); + } + + if (!un(cell.text)) { + var txt = clone(cell.text); + var align = [0, 0]; + if (tableAlignHoriz == 'left') align[0] = -1; + if (tableAlignHoriz == 'center') align[0] = 0; + if (tableAlignHoriz == 'right') align[0] = 1; + if (tableAlignVert == 'top') align[1] = -1; + if (tableAlignVert == 'middle') align[1] = 0; + if (tableAlignVert == 'bottom') align[1] = 1; + if (cell.align == 'left') align[0] = -1; + if (cell.align == 'center') align[0] = 0; + if (cell.align == 'right') align[0] = 1; + if (cell.vertAlign == 'top') align[1] = -1; + if (cell.vertAlign == 'middle') align[1] = 0; + if (cell.vertAlign == 'bottom') align[1] = 1; + if (typeof cell.align == 'object') align = cell.align; + var font2 = def([cell.font, font]); + var fontSize2 = def([cell.fontSize, fontSize]); + var textColor2 = def([cell.textColor, textColor]); + var italic2 = def([cell.italic, false]); + var bold2 = def([cell.bold, false]); + var paddingH2 = def([cell.paddingH, cell.padding, paddingH]); + var paddingV2 = def([cell.paddingV, cell.padding, paddingV]); + var fracScale = def([cell.fracScale, fracScale]); + var algPadding = def([cell.algPadding, algPadding]); + var marginLeft = def([cell.marginLeft, object.marginLeft, 0]); + var marginRight = def([cell.marginRight, object.marginRight, 0]); + var lineSpacingFactor = def([cell.lineSpacingFactor, object.lineSpacingFactor, 1.2]); + var lineSpacingStyle = def([cell.lineSpacingStyle, cell.spacingStyle, object.lineSpacingStyle, object.spacingStyle, "variable"]); + + var backgroundColor = typeof cell.color !== 'undefined' && cell.color !== 'none' ? cell.color : '#FFF'; + var box = clone(cell.box); + + if (!un(box) && typeof isInputColor !== 'undefined') box.color = isInputColor; + + cellTextMeasure[i][j] = text({ + ctx: ctx, + rect: [leftPos + paddingH2, topPos + paddingV2, cellWidth - 2 * paddingH2, heights[i] - 2 * paddingV2], + text: txt, + box: box, + sf: sf, + align: align, + font: font2, + fontSize: fontSize2, + color: textColor2, + italic: italic2, + bold: bold2, + selected: hl, + backgroundColor: backgroundColor, + fracScale:fracScale, + algPadding:algPadding, + marginLeft:marginLeft, + marginRight:marginRight, + lineSpacingFactor:lineSpacingFactor, + lineSpacingStyle:lineSpacingStyle + }); + } + + leftPos += cellWidth; + } + topPos += heights[i]; + } + + // draw outer border + if (outerBorder.show == true) { + /*ctx.strokeStyle = outerBorder.color; + ctx.lineWidth = outerBorder.width; + if (un(ctx.setLineDash)) { + ctx.setLineDash = function () {} + } + ctx.setLineDash(outerBorder.dash); + ctx.beginPath(); + ctx.strokeRect(left, top, totalWidth, totalHeight);*/ + var color = outerBorder.color || '#000'; + var width = outerBorder.width || 4; + var radius = outerBorder.radius || 0; + var dash = outerBorder.dash || [0,0]; + roundedRect(ctx,left,top,totalWidth,totalHeight,radius,width,color,'none',dash) + } + + ctx.restore(); + + return { + cellTextMeasure: cellTextMeasure, + cell: cellDims, + xPos: horizPos, + yPos: vertPos + }; +} +function createScrollTable(object) { + var left = object.left; + var top = object.top; + var z = object.z || object.zIndex || 2; + + var padding = 2; // padding for canvas + var fullRect = [0, 0, 100, 100]; + var visRect = [left, top, 100, 100]; + var topRowRect = [left, top, 100, 100]; + var scrollRect = [left, top, 25, 10]; + + var ctxVis = newctx({ + rect: visRect, + z: z + }); + var ctxTopRow = newctx({ + rect: topRowRect, + z: z + }); + var ctxInvis = newctx({ + rect: fullRect, + vis: false + }); + + var topRowFreeze = boolean(object.topRowFreeze, true); + + var scrollMax = 10; + var scrollDiff = 5; + + var scroll = createScroller({ + rect: scrollRect, + max: scrollMax, + zIndex: z, + funcMove: function (value) { + this.table.scrollPos = (value / this.table.scrollMax) * this.table.scrollDiff; + this.table.redraw(); + }, + funcStop: function (value) { + this.table.scrollPos = (value / this.table.scrollMax) * this.table.scrollDiff; + this.table.redraw(); + } + }); + + if (!un(object.funcMove)) { + ctxVis.canvas.style.pointerEvents = 'auto'; + ctxVis.data[6] = true; + ctxVis.data[106] = true; + addListenerMove(ctxVis.canvas, function (e) { + updateMouse(e); + var r = -1, + c = -1; + for (var x = 0; x < this.table.xPos.length - 1; x++) { + if (mouse.x >= this.table.xPos[x] + this.table.ctxVis.data[100] && mouse.x <= this.table.xPos[x + 1] + this.table.ctxVis.data[100]) { + c = x; + break; + } + } + if (this.table.topRowFreeze && mouse.y <= this.table.yPos[1] + this.table.ctxVis.data[101]) { + r = 0; + } else { + for (var y = 0; y < this.table.yPos.length - 1; y++) { + if (mouse.y + this.table.scrollPos >= this.table.yPos[y] + this.table.ctxVis.data[101] && mouse.y + this.table.scrollPos <= this.table.yPos[y + 1] + this.table.ctxVis.data[101]) { + r = y; + break; + } + } + } + this.table.funcMove(r, c); + }); + } else { + object.funcMove = function () {}; + } + if (!un(object.funcClick)) { + ctxVis.canvas.style.pointerEvents = 'auto'; + ctxVis.data[6] = true; + ctxVis.data[106] = true; + addListener(ctxVis.canvas, function (e) { + updateMouse(e); + var r = -1, + c = -1, + xProp = 0, + yProp = 0; + for (var x = 0; x < this.table.xPos.length - 1; x++) { + if (mouse.x >= this.table.xPos[x] + this.table.ctxVis.data[100] && mouse.x <= this.table.xPos[x + 1] + this.table.ctxVis.data[100]) { + c = x; + xProp = (mouse.x - (this.table.xPos[x] + this.table.ctxVis.data[100])) / (this.table.xPos[x + 1] - this.table.xPos[x]); + break; + } + } + if (this.table.topRowFreeze && mouse.y <= this.table.yPos[1] + this.table.ctxVis.data[101]) { + r = 0; + } else { + for (var y = 0; y < this.table.yPos.length - 1; y++) { + if (mouse.y + this.table.scrollPos >= this.table.yPos[y] + this.table.ctxVis.data[101] && mouse.y + this.table.scrollPos <= this.table.yPos[y + 1] + this.table.ctxVis.data[101]) { + r = y; + yProp = (mouse.y - (this.table.yPos[y] + this.table.ctxVis.data[101])) / (this.table.yPos[y + 1] - this.table.yPos[y]); + break; + } + } + } + this.table.funcClick(r, c, xProp, yProp); + }); + } else { + object.funcClick = function () {}; + } + + object.ctxVis = ctxVis; + object.ctxInvis = ctxInvis; + object.ctxTopRow = ctxTopRow; + object.padding = padding; + object.scroller = scroll; + object.scrollPos = 0; + object.scrollMax = scrollMax; + object.scrollDiff = scrollDiff; + object.topRowFreeze = topRowFreeze; + object.redraw = function () { + this.ctxVis.clearRect(0, 0, this.ctxVis.canvas.data[102], this.ctxVis.canvas.data[103]); + this.ctxVis.drawImage(this.ctxInvis.canvas, 0, -this.scrollPos); + }; + + object.scroller.table = object; + object.ctxVis.canvas.table = object; + + if (!un(object.additionalDraw)) { + object.additionalDraw(); + } + object.refreshCells = function () { + var sf = def([this.sf, 1]); + + var left = this.left * sf; + var top = this.top * sf; + var cells = this.cells; + var z = this.z || this.zIndex || 2; + + var minCellWidth = this.minCellWidth * sf || 80 * sf; + var maxCellWidth = this.maxCellWidth * sf || Math.max(1200 * sf, this.minCellWidth * sf); + var minCellHeight = this.minCellHeight * sf || 100 * sf; + var minCellPadding = this.minCellPadding * sf || 7 * sf; + var paddingH = minCellPadding; + var paddingV = minCellPadding; + if (typeof this.paddingH == 'number') + paddingH = this.paddingH * sf; + if (typeof this.paddingV == 'number') + paddingV = this.paddingV * sf; + var horizAlign = this.horizAlign || this.align || 'center'; + if (typeof this.text == 'this') { + var font = this.text.font || 'Arial'; + var fontSize = this.text.size || 32; + var textColor = this.text.color || '#000'; + } else { + var font = 'Arial'; + var fontSize = 32; + var textColor = '#000'; + } + if (typeof this.alpha == 'number') { + var alpha = this.alpha; + } else { + var alpha = 1; + } + var lineJoin = this.lineJoin || this.lineCap || 'round'; + var lineCap = this.lineCap || this.lineJoin || 'round'; + var outerBorder = {}; + if (typeof this.outerBorder == 'object') { + outerBorder.show = boolean(this.outerBorder.show, true); + outerBorder.width = this.outerBorder.width * sf || 4 * sf; + if (typeof this.outerBorder.color == 'undefined') { + outerBorder.color = colorA('#000', alpha); + } else { + outerBorder.color = colorA(this.outerBorder.color, alpha); + } + outerBorder.dash = this.outerBorder.dash || []; + } else { + outerBorder.show = true; + outerBorder.width = 4 * sf; + outerBorder.color = colorA('#000', alpha); + outerBorder.dash = []; + } + outerBorder.dash = enlargeDash(outerBorder.dash, sf); + var innerBorder = {}; + if (typeof this.innerBorder == 'object') { + innerBorder.show = boolean(this.innerBorder.show, true); + innerBorder.width = this.innerBorder.width * sf || 4 * sf; + if (typeof this.innerBorder.color == 'undefined') { + innerBorder.color = colorA('#000', alpha); + } else { + innerBorder.color = colorA(this.innerBorder.color, alpha); + } + innerBorder.dash = this.innerBorder.dash || []; + } else { + innerBorder.show = true; + innerBorder.width = 4 * sf; + innerBorder.color = colorA('#000', alpha); + innerBorder.dash = [20, 15]; + } + innerBorder.dash = enlargeDash(innerBorder.dash, sf); + var tableAlignHoriz = this.tableAlignHoriz || 'left'; // is the whole table centred on [left,top]? + var tableAlignVert = this.tableAlignVert || 'top'; + + var numRows = cells.length; + var numCols = 0; + for (var i = 0; i < cells.length; i++) { + numCols = Math.max(cells[i].length, numCols); + } + var cellHeights = []; + for (var i = 0; i < numRows; i++) { + cellHeights[i] = minCellHeight; + } + var cellWidths = []; + for (var j = 0; j < numCols; j++) { + cellWidths[j] = minCellWidth; + } + var totalWidth = 0; + var totalHeight = 0; + + var cellHeights = []; + for (var i = 0; i < numRows; i++) { + cellHeights[i] = minCellHeight; + } + var cellWidths = []; + for (var j = 0; j < numCols; j++) { + cellWidths[j] = minCellWidth; + } + var totalWidth = 0; + var totalHeight = 0; + + if (typeof hiddenCanvas == 'undefined') { + var hiddenCanvas = document.createElement('canvas'); + hiddenCanvas.width = mainCanvasWidth * sf; + hiddenCanvas.height = mainCanvasHeight * sf; + hiddenCanvas.ctx = hiddenCanvas.getContext('2d'); + } + + for (var r = 0; r < cells.length; r++) { + var maxHeight = minCellHeight; + for (var c = 0; c < cells[r].length; c++) { + if (typeof cells[r][c] == 'this') { + if (typeof cells[r][c].text !== 'this') { + cells[r][c].text = []; + } else { + cells[r][c].text = clone(cells[r][c].text); + } + if (typeof cells[r][c].color !== 'string') + cells[r][c].color = 'none'; + if (typeof cells[r][c].minWidth !== 'number') + cells[r][c].minWidth = 0; + if (typeof cells[r][c].minHeight !== 'number') + cells[r][c].minHeight = 0; + var font2 = font; + var fontSize2 = fontSize; + var textColor2 = textColor; + if (!un(cells[r][c].font)) + font2 = cells[r][c].font; + if (!un(cells[r][c].fontSize)) + fontSize2 = cells[r][c].fontSize; + if (!un(cells[r][c].textColor)) + textColor2 = cells[r][c].textColor; + if (un(cells[r][c].styled)) { + cells[r][c].text.unshift('<><><>'); + cells[r][c].styled = true; + } + //var dims = drawMathsText(ctx,cells[r][c].text,fontSize,0,0,false,[],horizAlign,'middle',text.color,'measure'); + //maxHeight = Math.max(dims[1]+2*minCellPadding,cells[r][c].minHeight,maxHeight); + //cellWidths[c] = Math.max(dims[0]+2*minCellPadding,cells[r][c].minWidth,cellWidths[c]); + + var cellText = text({ + ctx: hiddenCanvas.ctx, + left: 0, + top: 0, + width: maxCellWidth, + textArray: cells[r][c].text, + minTightWidth: 5, + minTightHeight: 5, + box: cells[r][c].box, + sf: sf + }); + maxHeight = Math.max(cellText.tightRect[3] + 2 * paddingV, cells[r][c].minHeight * sf, maxHeight); + cellWidths[c] = Math.max(cellText.tightRect[2] + 3 * paddingH, cells[r][c].minWidth * sf, cellWidths[c]); + } + } + cellHeights[r] = Math.max(maxHeight, cellHeights[r]); + totalHeight += cellHeights[r]; + } + for (var j = 0; j < cellWidths.length; j++) { + totalWidth += cellWidths[j]; + } + + if (tableAlignHoriz == 'center') { + left = left - totalWidth / 2; + } else if (tableAlignHoriz == 'right') { + left = left - totalWidth; + } + + if (tableAlignVert == 'middle') { + top = top - totalHeight / 2; + } else if (tableAlignVert == 'bottom') { + top = top - totalHeight; + } + + var maxHeight = this.maxHeight; + var padding = def([this.padding, this.outerBorder.width / 2]); // padding for canvas + if (totalHeight + 2 * padding < maxHeight) { + var hasScroll = true; + } else { + var hasScroll = false; + } + var fullRect = [0, 0, totalWidth + 2 * padding, totalHeight + 2 * padding]; + var visRect = [left, top, totalWidth + 2 * padding, maxHeight]; + var topRowRect = [left, top, totalWidth + 2 * padding, maxHeight]; + var scrollRect = [left + totalWidth + 4 * padding, top, 25, maxHeight]; + + var ctxInvis = this.ctxInvis; + var ctxVis = this.ctxVis; + var ctxTopRow = this.ctxTopRow; + ctxInvis.data[102] = fullRect[2]; + ctxInvis.canvas.width = fullRect[2]; + ctxInvis.data[103] = fullRect[3]; + ctxInvis.canvas.height = fullRect[3]; + ctxVis.data[100] = visRect[0]; + ctxVis.data[101] = visRect[1]; + ctxVis.data[102] = visRect[2]; + ctxVis.canvas.width = visRect[2]; + ctxVis.data[103] = visRect[3]; + ctxVis.canvas.height = visRect[3]; + ctxTopRow.data[100] = topRowRect[0]; + ctxTopRow.data[101] = topRowRect[1]; + ctxTopRow.data[102] = topRowRect[2]; + ctxTopRow.canvas.width = topRowRect[2]; + ctxTopRow.data[103] = topRowRect[3]; + ctxTopRow.canvas.height = topRowRect[3]; + resizeCanvas(ctxInvis.canvas, 0, 0, fullRect[2], fullRect[3]); + resizeCanvas(ctxVis.canvas, visRect[0], visRect[1], visRect[2], visRect[3]); + resizeCanvas(ctxTopRow.canvas, topRowRect[0], topRowRect[1], topRowRect[2], topRowRect[3]); + ctxInvis.clear(); + ctxVis.clear(); + ctxTopRow.clear(); + + var left = padding; + var top = padding; + + ctxInvis.save(); + ctxInvis.lineCap = lineCap; + ctxInvis.lineJoin = lineJoin; + + var cellDims = []; + + var horizPos = [left]; + for (var i = 0; i < cellWidths.length; i++) { + horizPos.push(horizPos[horizPos.length - 1] + cellWidths[i]) + } + + var vertPos = [top]; + for (var i = 0; i < cellHeights.length; i++) { + vertPos.push(vertPos[vertPos.length - 1] + cellHeights[i]) + } + + // write text to each cell + var topPos = top; + for (var i = 0; i < cells.length; i++) { + var leftPos = left; + cellDims[i] = []; + for (var j = 0; j < cells[i].length; j++) { + cellDims[i][j] = { + left: leftPos + 2, + top: topPos + 1, + width: cellWidths[j] - 9, + height: cellHeights[i] - 8, + border: false, + offset: [-40, 0.5 * (cellHeights[i] - 8) - 20], + fontSize: fontSize, + leftPoint: paddingH, + textColor: textColor, + textAlign: horizAlign, + fontSize: fontSize + }; + if (cells[i][j].highlight == true) { + if (typeof cells[i][j].color !== 'undefined' && cells[i][j].color !== 'none') { + ctxInvis.fillStyle = colorA(invertColor(cells[i][j].color), alpha); + } else { + ctxInvis.fillStyle = colorA(invertColor('#FFC'), alpha); + } + ctxInvis.fillRect(leftPos, topPos, cellWidths[j], cellHeights[i]); + } else if (typeof cells[i][j].color !== 'undefined' && cells[i][j].color !== 'none') { + ctxInvis.fillStyle = colorA(cells[i][j].color, alpha); + ctxInvis.fillRect(leftPos, topPos, cellWidths[j], cellHeights[i]); + } + var align = horizAlign; + if (!un(cells[i][j].align)) + align = cells[i][j].align; + var cellText = text({ + ctx: ctxInvis, + left: leftPos + paddingH, + top: topPos + paddingV, + width: cellWidths[j] - 2 * paddingH, + height: cellHeights[i] - 2 * paddingV, + textArray: cells[i][j].text, + textAlign: align, + vertAlign: 'middle', + padding: 0.001, + box: cells[i][j].box, + sf: sf + }); + /*console.log(cellText.tightRect[2],cellText.tightRect[3],{ + ctx:ctxInvis, + left:leftPos+paddingH, + top:topPos+paddingV, + width:cellWidths[j]-2*paddingH, + height:cellHeights[i]-2*paddingV, + textArray:cells[i][j].text, + textAlign:align, + vertAlign:'middle', + padding:0.001, + box:cells[i][j].box, + sf:sf + });*/ + leftPos += cellWidths[j]; + } + topPos += cellHeights[i]; + } + + // draw inner border + if (innerBorder.show == true) { + ctxInvis.strokeStyle = innerBorder.color; + ctxInvis.lineWidth = innerBorder.width; + if (!ctxInvis.setLineDash) { + ctxInvis.setLineDash = function () {} + } + ctxInvis.setLineDash(innerBorder.dash); + var leftPos = left; + for (var i = 0; i < cellWidths.length - 1; i++) { + leftPos += cellWidths[i]; + ctxInvis.beginPath(); + ctxInvis.moveTo(leftPos, top); + ctxInvis.lineTo(leftPos, top + totalHeight); + ctxInvis.stroke(); + } + var topPos = top; + for (var i = 0; i < cellHeights.length - 1; i++) { + topPos += cellHeights[i]; + ctxInvis.beginPath(); + ctxInvis.moveTo(left, topPos); + ctxInvis.lineTo(left + totalWidth, topPos); + ctxInvis.stroke(); + } + } + + // draw outer border + if (outerBorder.show == true) { + ctxInvis.strokeStyle = outerBorder.color; + ctxInvis.lineWidth = outerBorder.width; + if (!ctxInvis.setLineDash) { + ctxInvis.setLineDash = function () {} + } + ctxInvis.setLineDash(outerBorder.dash); + ctxInvis.beginPath(); + ctxInvis.strokeRect(left, top, totalWidth, totalHeight); + } + + ctxInvis.restore(); + + this.cell = cellDims; + this.xPos = horizPos; + this.yPos = vertPos; + + if (!un(this.additionalDraw)) { + this.additionalDraw(); + } + + //this.scroller.max = totalHeight/maxHeight; + //this.scroller.rect = scrollRect; + this.scroller.reposition(totalHeight / maxHeight, scrollRect); + setScrollerValue(this.scroller, 0, true); + this.scrollPos = 0; + this.scrollMax = scrollMax; + this.scrollDiff = totalHeight - maxHeight + 2 * padding; + + this.redraw(); + + if (totalHeight + 2 * padding > this.maxHeight) { + this.hasScroll = true; + //this.scrollPos = 0; + //this.scrollMax = totalHeight/maxHeight; + //this.scrollDiff = totalHeight-maxHeight+2*padding; + //this.scroller.max = totalHeight/maxHeight; + //this.scroller.value = 0; + //this.scroller = updateScrollerDims(this.scroller); + showScroller(this.scroller); + if (this.topRowFreeze) { + showObj(ctxTopRow.canvas); + ctxTopRow.data[3] = ctxTopRow.data[103] = padding + vertPos[1]; + ctxTopRow.canvas.width = ctxTopRow.data[102]; + ctxTopRow.canvas.height = ctxTopRow.data[103]; + resize(); + ctxTopRow.drawImage(ctxInvis.canvas, 0, 0); + } else { + hideObj(ctxTopRow.canvas); + } + } else { + this.hasScroll = false; + hideScroller(this.scroller); + hideObj(ctxTopRow.canvas); + } + } + object.refreshCells(); + + object.move = function (left, top) { + var relLeft = this.scroller.rect[0] - this.ctxVis.canvas.data[100]; + this.left = left; + this.top = top; + this.ctxVis.canvas.data[100] = left; + this.ctxVis.canvas.data[101] = top; + resizeCanvas3(this.ctxVis.canvas); + this.ctxTopRow.canvas.data[100] = left; + this.ctxTopRow.canvas.data[101] = top; + resizeCanvas3(this.ctxTopRow.canvas); + this.scroller.rect[0] = left + relLeft; + this.scroller.rect[1] = top; + this.scroller.reposition(); + } + + return object; +} +function drawScrollTable(object) { + /* EXAMPLE USAGE: + var j0001table1 = drawScrollTable({ + left:100, + top:150, + minCellWidth:80, + minCellHeight:50, + horizAlign:'center', + text:{font:'Arial',size:32,color:'#000'}, + outerBorder:{show:true,width:4,color:'#000'}, + innerBorder:{show:true,width:2,color:'#666'}, + cells:[ + [ // row 0{text:['<><>x'],color:'#CCF',minWidth:100,minHeight:70},{text:['<><>y'],color:'#CCF',minWidth:100,minHeight:70},{text:['<><><>z'],color:'#CCF',minWidth:100,minHeight:70}, + ], [ // row 1{},{text:['2']},{text:['<>3']}, + ], [ // row 2{text:['4']},{},{text:['<>6']}, + ], + ], + maxHeight:300, + moveFunc:function(r,c) {console.log(r,c)), + clickFunc:function(r,c) {console.log(r,c)), + padding:2 // def: outerBorder.width/2 + }); + */ + + if (typeof object.sf !== 'undefined') { + var sf = object.sf; + } else { + var sf = 1; + } + + var left = object.left * sf; + var top = object.top * sf; + var cells = object.cells; + var z = object.z || object.zIndex || 2; + + var minCellWidth = object.minCellWidth * sf || 80 * sf; + var maxCellWidth = object.maxCellWidth * sf || Math.max(1200 * sf, object.minCellWidth * sf); + var minCellHeight = object.minCellHeight * sf || 100 * sf; + var minCellPadding = object.minCellPadding * sf || 7 * sf; + var paddingH = minCellPadding; + var paddingV = minCellPadding; + if (typeof object.paddingH == 'number') + paddingH = object.paddingH * sf; + if (typeof object.paddingV == 'number') + paddingV = object.paddingV * sf; + var horizAlign = object.horizAlign || object.align || 'center'; + if (typeof object.text == 'object') { + var font = object.text.font || 'Arial'; + var fontSize = object.text.size || 32; + var textColor = object.text.color || '#000'; + } else { + var font = 'Arial'; + var fontSize = 32; + var textColor = '#000'; + } + if (typeof object.alpha == 'number') { + var alpha = object.alpha; + } else { + var alpha = 1; + } + var lineJoin = object.lineJoin || object.lineCap || 'round'; + var lineCap = object.lineCap || object.lineJoin || 'round'; + var outerBorder = {}; + if (typeof object.outerBorder == 'object') { + outerBorder.show = boolean(object.outerBorder.show, true); + outerBorder.width = object.outerBorder.width * sf || 4 * sf; + if (typeof object.outerBorder.color == 'undefined') { + outerBorder.color = colorA('#000', alpha); + } else { + outerBorder.color = colorA(object.outerBorder.color, alpha); + } + outerBorder.dash = object.outerBorder.dash || []; + } else { + outerBorder.show = true; + outerBorder.width = 4 * sf; + outerBorder.color = colorA('#000', alpha); + outerBorder.dash = []; + } + outerBorder.dash = enlargeDash(outerBorder.dash, sf); + var innerBorder = {}; + if (typeof object.innerBorder == 'object') { + innerBorder.show = boolean(object.innerBorder.show, true); + innerBorder.width = object.innerBorder.width * sf || 4 * sf; + if (typeof object.innerBorder.color == 'undefined') { + innerBorder.color = colorA('#000', alpha); + } else { + innerBorder.color = colorA(object.innerBorder.color, alpha); + } + innerBorder.dash = object.innerBorder.dash || []; + } else { + innerBorder.show = true; + innerBorder.width = 4 * sf; + innerBorder.color = colorA('#000', alpha); + innerBorder.dash = [20, 15]; + } + innerBorder.dash = enlargeDash(innerBorder.dash, sf); + var tableAlignHoriz = object.tableAlignHoriz || 'left'; // is the whole table centred on [left,top]? + var tableAlignVert = object.tableAlignVert || 'top'; + + var numRows = cells.length; + var numCols = 0; + for (var i = 0; i < cells.length; i++) { + numCols = Math.max(cells[i].length, numCols); + } + var cellHeights = []; + for (var i = 0; i < numRows; i++) { + cellHeights[i] = minCellHeight; + } + var cellWidths = []; + for (var j = 0; j < numCols; j++) { + cellWidths[j] = minCellWidth; + } + var totalWidth = 0; + var totalHeight = 0; + //calcTableDims(object); + + if (typeof hiddenCanvas == 'undefined') { + var hiddenCanvas = document.createElement('canvas'); + hiddenCanvas.width = mainCanvasWidth * sf; + hiddenCanvas.height = mainCanvasHeight * sf; + hiddenCanvas.ctx = hiddenCanvas.getContext('2d'); + } + + for (var r = 0; r < cells.length; r++) { + var maxHeight = minCellHeight; + for (var c = 0; c < cells[r].length; c++) { + if (typeof cells[r][c] == 'object') { + if (typeof cells[r][c].text !== 'object') { + cells[r][c].text = []; + } else { + cells[r][c].text = clone(cells[r][c].text); + } + if (typeof cells[r][c].color !== 'string') + cells[r][c].color = 'none'; + if (typeof cells[r][c].minWidth !== 'number') + cells[r][c].minWidth = 0; + if (typeof cells[r][c].minHeight !== 'number') + cells[r][c].minHeight = 0; + var font2 = font; + var fontSize2 = fontSize; + var textColor2 = textColor; + if (!un(cells[r][c].font)) + font2 = cells[r][c].font; + if (!un(cells[r][c].fontSize)) + fontSize2 = cells[r][c].fontSize; + if (!un(cells[r][c].textColor)) + textColor2 = cells[r][c].textColor; + if (un(cells[r][c].styled)) { + cells[r][c].text.unshift('<><><>'); + cells[r][c].styled = true; + } + //var dims = drawMathsText(ctx,cells[r][c].text,fontSize,0,0,false,[],horizAlign,'middle',text.color,'measure'); + //maxHeight = Math.max(dims[1]+2*minCellPadding,cells[r][c].minHeight,maxHeight); + //cellWidths[c] = Math.max(dims[0]+2*minCellPadding,cells[r][c].minWidth,cellWidths[c]); + + var cellText = text({ + ctx: hiddenCanvas.ctx, + left: 0, + top: 0, + width: maxCellWidth, + textArray: cells[r][c].text, + minTightWidth: 5, + minTightHeight: 5, + box: cells[r][c].box, + sf: sf + }); + maxHeight = Math.max(cellText.tightRect[3] + 2 * paddingV, cells[r][c].minHeight * sf, maxHeight); + cellWidths[c] = Math.max(cellText.tightRect[2] + 3 * paddingH, cells[r][c].minWidth * sf, cellWidths[c]); + } + } + cellHeights[r] = Math.max(maxHeight, cellHeights[r]); + totalHeight += cellHeights[r]; + } + for (var j = 0; j < cellWidths.length; j++) { + totalWidth += cellWidths[j]; + } + + if (tableAlignHoriz == 'center') { + left = left - totalWidth / 2; + } else if (tableAlignHoriz == 'right') { + left = left - totalWidth; + } + + if (tableAlignVert == 'middle') { + top = top - totalHeight / 2; + } else if (tableAlignVert == 'bottom') { + top = top - totalHeight; + } + + // now the table dims are known, create canvases + var maxHeight = object.maxHeight; + if (totalHeight + 2 * padding < maxHeight) { + var hasScroll = true; + } else { + var hasScroll = false; + } + var padding = def([object.padding, object.outerBorder.width / 2]); // padding for canvas + var fullRect = [0, 0, totalWidth + 2 * padding, totalHeight + 2 * padding]; + var visRect = [left, top, totalWidth + 2 * padding, maxHeight]; + var topRowRect = [left, top, totalWidth + 2 * padding, maxHeight]; + var scrollRect = [left + totalWidth + 4 * padding, top, 25, maxHeight]; + + var ctxVis = newctx({ + rect: visRect, + z: z + }); + var ctxTopRow = newctx({ + rect: topRowRect, + z: z + }); + var ctxInvis = newctx({ + rect: fullRect, + vis: false + }); + + var left = padding; + var top = padding; + + ctxInvis.save(); + ctxInvis.lineCap = lineCap; + ctxInvis.lineJoin = lineJoin; + + var cellDims = []; + + var horizPos = [left]; + for (var i = 0; i < cellWidths.length; i++) { + horizPos.push(horizPos[horizPos.length - 1] + cellWidths[i]) + } + + var vertPos = [top]; + for (var i = 0; i < cellHeights.length; i++) { + vertPos.push(vertPos[vertPos.length - 1] + cellHeights[i]) + } + + // write text to each cell + var topPos = top; + for (var i = 0; i < cells.length; i++) { + var leftPos = left; + cellDims[i] = []; + for (var j = 0; j < cells[i].length; j++) { + cellDims[i][j] = { + left: leftPos + 2, + top: topPos + 1, + width: cellWidths[j] - 9, + height: cellHeights[i] - 8, + border: false, + offset: [-40, 0.5 * (cellHeights[i] - 8) - 20], + fontSize: fontSize, + leftPoint: paddingH, + textColor: textColor, + textAlign: horizAlign, + fontSize: fontSize + }; + if (cells[i][j].highlight == true) { + if (typeof cells[i][j].color == 'undefined' || cells[i][j].color !== 'none') { + ctxInvis.fillStyle = colorA(invertColor(cells[i][j].color), alpha); + } else { + ctxInvis.fillStyle = colorA(invertColor('#FFC'), alpha); + } + ctxInvis.fillRect(leftPos, topPos, cellWidths[j], cellHeights[i]); + } else if (typeof cells[i][j].color == 'undefined' || cells[i][j].color !== 'none') { + ctxInvis.fillStyle = colorA(cells[i][j].color, alpha); + ctxInvis.fillRect(leftPos, topPos, cellWidths[j], cellHeights[i]); + } + var align = horizAlign; + if (!un(cells[i][j].align)) + align = cells[i][j].align; + var cellText = text({ + ctx: ctxInvis, + left: leftPos + paddingH, + top: topPos + paddingV, + width: cellWidths[j] - 2 * paddingH, + height: cellHeights[i] - 2 * paddingV, + textArray: cells[i][j].text, + textAlign: align, + vertAlign: 'middle', + padding: 0.001, + box: cells[i][j].box, + sf: sf + }); + //console.log(cellText.tightRect[2],cellText.tightRect[3]); + leftPos += cellWidths[j]; + } + topPos += cellHeights[i]; + } + + // draw inner border + if (innerBorder.show == true) { + ctxInvis.strokeStyle = innerBorder.color; + ctxInvis.lineWidth = innerBorder.width; + if (!ctxInvis.setLineDash) { + ctxInvis.setLineDash = function () {} + } + ctxInvis.setLineDash(innerBorder.dash); + var leftPos = left; + for (var i = 0; i < cellWidths.length - 1; i++) { + leftPos += cellWidths[i]; + ctxInvis.beginPath(); + ctxInvis.moveTo(leftPos, top); + ctxInvis.lineTo(leftPos, top + totalHeight); + ctxInvis.stroke(); + } + var topPos = top; + for (var i = 0; i < cellHeights.length - 1; i++) { + topPos += cellHeights[i]; + ctxInvis.beginPath(); + ctxInvis.moveTo(left, topPos); + ctxInvis.lineTo(left + totalWidth, topPos); + ctxInvis.stroke(); + } + } + + // draw outer border + if (outerBorder.show == true) { + ctxInvis.strokeStyle = outerBorder.color; + ctxInvis.lineWidth = outerBorder.width; + if (!ctxInvis.setLineDash) { + ctxInvis.setLineDash = function () {} + } + ctxInvis.setLineDash(outerBorder.dash); + ctxInvis.beginPath(); + ctxInvis.strokeRect(left, top, totalWidth, totalHeight); + } + + ctxInvis.restore(); + + var topRowFreeze = boolean(object.topRowFreeze, true); + + var scrollMax = totalHeight / maxHeight; + var scrollDiff = totalHeight - maxHeight + 2 * padding; + //console.log(scrollMax,scrollDiff); + + var scroll = createScroller({ + rect: scrollRect, + max: scrollMax, + zIndex: z, + funcMove: function (value) { + this.table.scrollPos = (value / this.table.scrollMax) * this.table.scrollDiff; + this.table.redraw(); + }, + funcStop: function (value) { + this.table.scrollPos = (value / this.table.scrollMax) * this.table.scrollDiff; + this.table.redraw(); + } + }); + + if (!un(object.funcMove)) { + ctxVis.canvas.style.pointerEvents = 'auto'; + ctxVis.data[6] = true; + ctxVis.data[106] = true; + addListenerMove(ctxVis.canvas, function (e) { + updateMouse(e); + var r = -1, + c = -1; + for (var x = 0; x < this.table.xPos.length - 1; x++) { + if (mouse.x >= this.table.xPos[x] + this.table.ctxVis.data[100] && mouse.x <= this.table.xPos[x + 1] + this.table.ctxVis.data[100]) { + c = x; + break; + } + } + if (this.table.topRowFreeze && mouse.y <= this.table.yPos[1] + this.table.ctxVis.data[101]) { + r = 0; + } else { + for (var y = 0; y < this.table.yPos.length - 1; y++) { + if (mouse.y + this.table.scrollPos >= this.table.yPos[y] + this.table.ctxVis.data[101] && mouse.y + this.table.scrollPos <= this.table.yPos[y + 1] + this.table.ctxVis.data[101]) { + r = y; + break; + } + } + } + this.table.funcMove(r, c); + }); + } else { + object.funcMove = function () {}; + } + if (!un(object.funcClick)) { + ctxVis.canvas.style.pointerEvents = 'auto'; + ctxVis.data[6] = true; + ctxVis.data[106] = true; + addListener(ctxVis.canvas, function (e) { + updateMouse(e); + var r = -1, + c = -1, + xProp = 0, + yProp = 0; + for (var x = 0; x < this.table.xPos.length - 1; x++) { + if (mouse.x >= this.table.xPos[x] + this.table.ctxVis.data[100] && mouse.x <= this.table.xPos[x + 1] + this.table.ctxVis.data[100]) { + c = x; + xProp = (mouse.x - (this.table.xPos[x] + this.table.ctxVis.data[100])) / (this.table.xPos[x + 1] - this.table.xPos[x]); + break; + } + } + if (this.table.topRowFreeze && mouse.y <= this.table.yPos[1] + this.table.ctxVis.data[101]) { + r = 0; + } else { + for (var y = 0; y < this.table.yPos.length - 1; y++) { + if (mouse.y + this.table.scrollPos >= this.table.yPos[y] + this.table.ctxVis.data[101] && mouse.y + this.table.scrollPos <= this.table.yPos[y + 1] + this.table.ctxVis.data[101]) { + r = y; + yProp = (mouse.y - (this.table.yPos[y] + this.table.ctxVis.data[101])) / (this.table.yPos[y + 1] - this.table.yPos[y]); + break; + } + } + } + this.table.funcClick(r, c, xProp, yProp); + }); + } else { + object.funcClick = function () {}; + } + + var returnObj = clone(object); + + returnObj.ctxVis = ctxVis; + returnObj.ctxInvis = ctxInvis; + returnObj.ctxTopRow = ctxTopRow; + returnObj.cell = cellDims; + returnObj.xPos = horizPos; + returnObj.yPos = vertPos; + returnObj.padding = padding; + returnObj.hasScroll = hasScroll; + returnObj.scroller = scroll; + returnObj.scrollPos = 0; + returnObj.scrollMax = scrollMax; + returnObj.scrollDiff = scrollDiff; + returnObj.topRowFreeze = topRowFreeze; + returnObj.redraw = function () { + this.ctxVis.clearRect(0, 0, this.ctxVis.canvas.data[102], this.ctxVis.canvas.data[103]); + this.ctxVis.drawImage(this.ctxInvis.canvas, 0, -this.scrollPos); + }; + + returnObj.scroller.table = returnObj; + returnObj.ctxVis.canvas.table = returnObj; + + if (!un(returnObj.additionalDraw)) { + returnObj.additionalDraw(); + } + + returnObj.redraw(); + + if (topRowFreeze) { + ctxTopRow.data[3] = ctxTopRow.data[103] = padding + vertPos[1]; + ctxTopRow.canvas.width = ctxTopRow.data[102]; + ctxTopRow.canvas.height = ctxTopRow.data[103]; + resizeCanvas2(ctxTopRow.canvas, ctxTopRow.data[102], ctxTopRow.data[103]); + resize(); + ctxTopRow.drawImage(ctxInvis.canvas, 0, 0); + } else { + hideObj(ctxTopRow.canvas); + } + + return returnObj; +} +function updateScrollTable(object) { // update previously drawn scrollTable with changed cell data + var sf = def([object.sf, 1]); + + var left = object.left * sf; + var top = object.top * sf; + var cells = object.cells; + var z = object.z || object.zIndex || 2; + + ///console.log(object); + + var minCellWidth = object.minCellWidth * sf || 80 * sf; + var maxCellWidth = object.maxCellWidth * sf || Math.max(1200 * sf, object.minCellWidth * sf); + var minCellHeight = object.minCellHeight * sf || 100 * sf; + var minCellPadding = object.minCellPadding * sf || 7 * sf; + var paddingH = minCellPadding; + var paddingV = minCellPadding; + if (typeof object.paddingH == 'number') + paddingH = object.paddingH * sf; + if (typeof object.paddingV == 'number') + paddingV = object.paddingV * sf; + var horizAlign = object.horizAlign || object.align || 'center'; + if (typeof object.text == 'object') { + var font = object.text.font || 'Arial'; + var fontSize = object.text.size || 32; + var textColor = object.text.color || '#000'; + } else { + var font = 'Arial'; + var fontSize = 32; + var textColor = '#000'; + } + if (typeof object.alpha == 'number') { + var alpha = object.alpha; + } else { + var alpha = 1; + } + var lineJoin = object.lineJoin || object.lineCap || 'round'; + var lineCap = object.lineCap || object.lineJoin || 'round'; + var outerBorder = {}; + if (typeof object.outerBorder == 'object') { + outerBorder.show = boolean(object.outerBorder.show, true); + outerBorder.width = object.outerBorder.width * sf || 4 * sf; + if (typeof object.outerBorder.color == 'undefined') { + outerBorder.color = colorA('#000', alpha); + } else { + outerBorder.color = colorA(object.outerBorder.color, alpha); + } + outerBorder.dash = object.outerBorder.dash || []; + } else { + outerBorder.show = true; + outerBorder.width = 4 * sf; + outerBorder.color = colorA('#000', alpha); + outerBorder.dash = []; + } + outerBorder.dash = enlargeDash(outerBorder.dash, sf); + var innerBorder = {}; + if (typeof object.innerBorder == 'object') { + innerBorder.show = boolean(object.innerBorder.show, true); + innerBorder.width = object.innerBorder.width * sf || 4 * sf; + if (typeof object.innerBorder.color == 'undefined') { + innerBorder.color = colorA('#000', alpha); + } else { + innerBorder.color = colorA(object.innerBorder.color, alpha); + } + innerBorder.dash = object.innerBorder.dash || []; + } else { + innerBorder.show = true; + innerBorder.width = 4 * sf; + innerBorder.color = colorA('#000', alpha); + innerBorder.dash = [20, 15]; + } + innerBorder.dash = enlargeDash(innerBorder.dash, sf); + var tableAlignHoriz = object.tableAlignHoriz || 'left'; // is the whole table centred on [left,top]? + var tableAlignVert = object.tableAlignVert || 'top'; + + var numRows = cells.length; + var numCols = 0; + for (var i = 0; i < cells.length; i++) { + numCols = Math.max(cells[i].length, numCols); + } + var cellHeights = []; + for (var i = 0; i < numRows; i++) { + cellHeights[i] = minCellHeight; + } + var cellWidths = []; + for (var j = 0; j < numCols; j++) { + cellWidths[j] = minCellWidth; + } + var totalWidth = 0; + var totalHeight = 0; + + var cellHeights = []; + for (var i = 0; i < numRows; i++) { + cellHeights[i] = minCellHeight; + } + var cellWidths = []; + for (var j = 0; j < numCols; j++) { + cellWidths[j] = minCellWidth; + } + var totalWidth = 0; + var totalHeight = 0; + + if (typeof hiddenCanvas == 'undefined') { + var hiddenCanvas = document.createElement('canvas'); + hiddenCanvas.width = mainCanvasWidth * sf; + hiddenCanvas.height = mainCanvasHeight * sf; + hiddenCanvas.ctx = hiddenCanvas.getContext('2d'); + } + + for (var r = 0; r < cells.length; r++) { + var maxHeight = minCellHeight; + for (var c = 0; c < cells[r].length; c++) { + if (typeof cells[r][c] == 'object') { + if (typeof cells[r][c].text !== 'object') { + cells[r][c].text = []; + } else { + cells[r][c].text = clone(cells[r][c].text); + } + if (typeof cells[r][c].color !== 'string') + cells[r][c].color = 'none'; + if (typeof cells[r][c].minWidth !== 'number') + cells[r][c].minWidth = 0; + if (typeof cells[r][c].minHeight !== 'number') + cells[r][c].minHeight = 0; + var font2 = font; + var fontSize2 = fontSize; + var textColor2 = textColor; + if (!un(cells[r][c].font)) + font2 = cells[r][c].font; + if (!un(cells[r][c].fontSize)) + fontSize2 = cells[r][c].fontSize; + if (!un(cells[r][c].textColor)) + textColor2 = cells[r][c].textColor; + if (un(cells[r][c].styled)) { + cells[r][c].text.unshift('<><><>'); + cells[r][c].styled = true; + } + //var dims = drawMathsText(ctx,cells[r][c].text,fontSize,0,0,false,[],horizAlign,'middle',text.color,'measure'); + //maxHeight = Math.max(dims[1]+2*minCellPadding,cells[r][c].minHeight,maxHeight); + //cellWidths[c] = Math.max(dims[0]+2*minCellPadding,cells[r][c].minWidth,cellWidths[c]); + + var cellText = text({ + ctx: hiddenCanvas.ctx, + left: 0, + top: 0, + width: maxCellWidth, + textArray: cells[r][c].text, + minTightWidth: 5, + minTightHeight: 5, + box: cells[r][c].box, + sf: sf + }); + maxHeight = Math.max(cellText.tightRect[3] + 2 * paddingV, cells[r][c].minHeight * sf, maxHeight); + cellWidths[c] = Math.max(cellText.tightRect[2] + 3 * paddingH, cells[r][c].minWidth * sf, cellWidths[c]); + } + } + cellHeights[r] = Math.max(maxHeight, cellHeights[r]); + totalHeight += cellHeights[r]; + } + for (var j = 0; j < cellWidths.length; j++) { + totalWidth += cellWidths[j]; + } + + if (tableAlignHoriz == 'center') { + left = left - totalWidth / 2; + } else if (tableAlignHoriz == 'right') { + left = left - totalWidth; + } + + if (tableAlignVert == 'middle') { + top = top - totalHeight / 2; + } else if (tableAlignVert == 'bottom') { + top = top - totalHeight; + } + + //update canvases + var maxHeight = object.maxHeight; + var padding = def([object.padding, object.outerBorder.width / 2]); // padding for canvas + var fullRect = [0, 0, totalWidth + 2 * padding, totalHeight + 2 * padding]; + var visRect = [left, top, totalWidth + 2 * padding, maxHeight]; + var topRowRect = [left, top, totalWidth + 2 * padding, maxHeight]; + var scrollRect = [left + totalWidth + 4 * padding, top, 25, maxHeight]; + + var ctxInvis = object.ctxInvis; + var ctxVis = object.ctxVis; + var ctxTopRow = object.ctxTopRow; + ctxInvis.clear(); + ctxVis.clear(); + ctxTopRow.clear(); + ctxInvis.data[102] = fullRect[2]; + ctxInvis.data[103] = fullRect[3]; + ctxInvis.canvas.width = fullRect[2]; + ctxInvis.canvas.height = fullRect[3]; + ctxVis.data[100] = visRect[0]; + ctxVis.data[101] = visRect[1]; + ctxVis.data[102] = visRect[2]; + ctxVis.data[103] = visRect[3]; + ctxVis.canvas.width = visRect[2]; + ctxVis.canvas.height = visRect[3]; + ctxTopRow.data[100] = topRowRect[0]; + ctxTopRow.data[101] = topRowRect[1]; + ctxTopRow.data[102] = topRowRect[2]; + ctxTopRow.data[103] = topRowRect[3]; + ctxTopRow.canvas.width = visRect[2]; + ctxTopRow.canvas.height = visRect[3]; + resize(); + + var left = padding; + var top = padding; + + ctxInvis.save(); + ctxInvis.lineCap = lineCap; + ctxInvis.lineJoin = lineJoin; + + var cellDims = []; + + var horizPos = [left]; + for (var i = 0; i < cellWidths.length; i++) { + horizPos.push(horizPos[horizPos.length - 1] + cellWidths[i]) + } + + var vertPos = [top]; + for (var i = 0; i < cellHeights.length; i++) { + vertPos.push(vertPos[vertPos.length - 1] + cellHeights[i]) + } + + // write text to each cell + var topPos = top; + for (var i = 0; i < cells.length; i++) { + var leftPos = left; + cellDims[i] = []; + for (var j = 0; j < cells[i].length; j++) { + cellDims[i][j] = { + left: leftPos + 2, + top: topPos + 1, + width: cellWidths[j] - 9, + height: cellHeights[i] - 8, + border: false, + offset: [-40, 0.5 * (cellHeights[i] - 8) - 20], + fontSize: fontSize, + leftPoint: paddingH, + textColor: textColor, + textAlign: horizAlign, + fontSize: fontSize + }; + if (cells[i][j].highlight == true) { + if (typeof cells[i][j].color == 'undefined' || cells[i][j].color !== 'none') { + ctxInvis.fillStyle = colorA(invertColor(cells[i][j].color), alpha); + } else { + ctxInvis.fillStyle = colorA(invertColor('#FFC'), alpha); + } + ctxInvis.fillRect(leftPos, topPos, cellWidths[j], cellHeights[i]); + } else if (typeof cells[i][j].color == 'undefined' || cells[i][j].color !== 'none') { + ctxInvis.fillStyle = colorA(cells[i][j].color, alpha); + ctxInvis.fillRect(leftPos, topPos, cellWidths[j], cellHeights[i]); + } + var align = horizAlign; + if (!un(cells[i][j].align)) + align = cells[i][j].align; + var cellText = text({ + ctx: ctxInvis, + left: leftPos + paddingH, + top: topPos + paddingV, + width: cellWidths[j] - 2 * paddingH, + height: cellHeights[i] - 2 * paddingV, + textArray: cells[i][j].text, + textAlign: align, + vertAlign: 'middle', + padding: 0.001, + box: cells[i][j].box, + sf: sf + }); + /*console.log(cellText.tightRect[2],cellText.tightRect[3],{ + ctx:ctxInvis, + left:leftPos+paddingH, + top:topPos+paddingV, + width:cellWidths[j]-2*paddingH, + height:cellHeights[i]-2*paddingV, + textArray:cells[i][j].text, + textAlign:align, + vertAlign:'middle', + padding:0.001, + box:cells[i][j].box, + sf:sf + });*/ + leftPos += cellWidths[j]; + } + topPos += cellHeights[i]; + } + + // draw inner border + if (innerBorder.show == true) { + ctxInvis.strokeStyle = innerBorder.color; + ctxInvis.lineWidth = innerBorder.width; + if (!ctxInvis.setLineDash) { + ctxInvis.setLineDash = function () {} + } + ctxInvis.setLineDash(innerBorder.dash); + var leftPos = left; + for (var i = 0; i < cellWidths.length - 1; i++) { + leftPos += cellWidths[i]; + ctxInvis.beginPath(); + ctxInvis.moveTo(leftPos, top); + ctxInvis.lineTo(leftPos, top + totalHeight); + ctxInvis.stroke(); + } + var topPos = top; + for (var i = 0; i < cellHeights.length - 1; i++) { + topPos += cellHeights[i]; + ctxInvis.beginPath(); + ctxInvis.moveTo(left, topPos); + ctxInvis.lineTo(left + totalWidth, topPos); + ctxInvis.stroke(); + } + } + + // draw outer border + if (outerBorder.show == true) { + ctxInvis.strokeStyle = outerBorder.color; + ctxInvis.lineWidth = outerBorder.width; + if (!ctxInvis.setLineDash) { + ctxInvis.setLineDash = function () {} + } + ctxInvis.setLineDash(outerBorder.dash); + ctxInvis.beginPath(); + ctxInvis.strokeRect(left, top, totalWidth, totalHeight); + } + + ctxInvis.restore(); + + object.cell = cellDims; + object.xPos = horizPos; + object.yPos = vertPos; + + if (!un(object.additionalDraw)) { + object.additionalDraw(); + } + + object.redraw(); + + if (totalHeight + 2 * padding > object.maxHeight) { + moveScroller(object.scroller, object.left + object.xPos.last(), object.top + object.yPos[0]); + object.hasScroll = true; + object.scrollPos = 0; + object.scrollMax = totalHeight / maxHeight; + object.scrollDiff = totalHeight - maxHeight + 2 * padding; + object.scroller.max = totalHeight / maxHeight; + object.scroller.value = 0; + object.scroller = updateScrollerDims(object.scroller); + showScroller(object.scroller); + if (object.topRowFreeze) { + showObj(ctxTopRow.canvas); + ctxTopRow.data[3] = ctxTopRow.data[103] = padding + vertPos[1]; + ctxTopRow.canvas.width = ctxTopRow.data[102]; + ctxTopRow.canvas.height = ctxTopRow.data[103]; + resizeCanvas3(ctxTopRow.canvas); + ctxTopRow.drawImage(ctxInvis.canvas, 0, 0); + } else { + hideObj(ctxTopRow.canvas); + } + } else { + object.hasScroll = false; + hideScroller(object.scroller); + hideObj(ctxTopRow.canvas); + } + + return object; +} +function moveScrollTable(object, left, top) { + var relLeft = object.scroller.rect[0] - object.ctxVis.canvas.data[100]; + object.left = left; + object.top = top; + object.ctxVis.canvas.data[100] = left; + object.ctxVis.canvas.data[101] = top; + resizeCanvas3(object.ctxVis.canvas); + object.ctxTopRow.canvas.data[100] = left; + object.ctxTopRow.canvas.data[101] = top; + resizeCanvas3(object.ctxTopRow.canvas); + object.scroller.rect[0] = left + relLeft; + object.scroller.rect[1] = top; + object.scroller.reposition(); +} +function showScrollTable(object, includeTopRow) { + showObj(object.ctxVis.canvas); + if (boolean(includeTopRow, true)) + showObj(object.ctxTopRow.canvas); + /*if (object.hasScroll == true)*/ + showScroller(object.scroller); +} +function hideScrollTable(object) { + hideObj(object.ctxVis.canvas); + hideObj(object.ctxTopRow.canvas); + hideScroller(object.scroller); +} + +function createSlider(object) { + if (typeof slider[pageIndex] == 'undefined') + slider[pageIndex] = []; + var id = object.id || slider[pageIndex].length; + if (typeof object.gridDetails == 'undefined') + object.gridDetails = {}; + var left = object.left || object.l || (object.gridDetails.left - 10); + var top = object.top || object.t || (object.gridDetails.top + object.gridDetails.height); + var width = object.width || object.w || (object.gridDetails.width + 20); + var height = object.height || object.h || 60; + var bottom = top + height; + var min, + max; + if (typeof object.min == 'number') { + min = object.min; + } else { + min = object.gridDetails.xMin; + } + if (typeof object.max == 'number') { + max = object.max; + } else { + max = object.gridDetails.xMax; + } + var vari = object.vari || "a"; + var linkedVar = object.linkedVar; + var varChangeListener = object.varChangeListener; + var onchange = object.onchange; + var startNum = min; + if (typeof object.startNum == 'number') + startNum = object.startNum; + var inc = object.inc || 1; + var label = true; + if (typeof object.label == 'boolean') + label = object.label; + var labelFont = object.labelFont || "24px Arial"; + var labelColor = object.labelColor || '#000'; + var visible = boolean(object.visible, true); + var zIndex = object.zIndex || 2; + var handleColor = object.handleColor || '#00F'; + var handleStyle = object.handleStyle || 'rect'; // rect or circle + var discrete = false; + if (typeof object.discrete == 'boolean') + discrete = object.discrete; + var stepNum = 1; + if (typeof object.stepNum == 'number') + stepNum = object.stepNum; + var snap = false; + if (typeof object.snap == 'boolean') + snap = object.snap; + var snapNum = 1; + if (typeof object.snapNum == 'number') + snapNum = object.snapNum; + var vert = boolean(object.vertical, false); + + if (vert == false) { + var sliderWidth = 20; + if (handleStyle == 'circle') + sliderWidth = 40; + var sliderHeight = 40; + } else { + var sliderHeight = 20; + if (handleStyle == 'circle') + sliderHeight = 40; + var sliderWidth = 40; + } + + var backCanvas = document.createElement('canvas'); + backCanvas.width = width; + backCanvas.height = height * 1.5; + backCanvas.setAttribute('position', 'absolute'); + backCanvas.setAttribute('cursor', 'auto'); + backCanvas.setAttribute('draggable', 'false'); + backCanvas.setAttribute('class', 'buttonClass'); + backCanvas.style.pointerEvents = 'none'; + backCanvas.style.zIndex = zIndex; + if (visible == true) + container.appendChild(backCanvas); + canvases[pageIndex].push(backCanvas); + var backCtx = backCanvas.getContext('2d'); + var backCanvasData = [left, top, width, height * 1.5, visible, false, false, zIndex]; + for (var i = 0; i < 8; i++) { + backCanvasData[100 + i] = backCanvasData[i]; + } + backCanvasData[130] = visible; + backCanvas.data = backCanvasData; + + backCtx.lineWidth = 5; + backCtx.strokeStyle = object.backColor || '#666'; + backCtx.lineCap = 'round'; + backCtx.lineJoin = 'round'; + backCtx.beginPath(); + if (vert == false) { + backCtx.moveTo(0.5 * sliderWidth, 0.5 * height); + backCtx.lineTo(width - 0.5 * sliderWidth, 0.5 * height); + } else { + backCtx.moveTo(0.5 * width, 0.5 * sliderHeight); + backCtx.lineTo(0.5 * width, height - 0.5 * sliderHeight); + } + backCtx.closePath(); + backCtx.stroke(); + if (!un(object.scale)) { + var step = def([object.scale.step, 1]); + var xInc = (width - 20) / ((max - min) / step); + var color = def([object.scale.color, backCtx.strokeStyle]); + backCtx.lineWidth = 2; + backCtx.strokeStyle = color; + backCtx.beginPath(); + var l = 10; + for (var i = min; i <= max; i += step) { + backCtx.moveTo(l, 0.5 * height); + backCtx.lineTo(l, 0.85 * height); + text({ + ctx: backCtx, + left: l - 50, + top: 0.85 * height, + width: 100, + height: height, + align: 'center', + color: color, + textArray: ['<>' + i] + }); + l += xInc; + } + backCtx.stroke(); + } + + var sliderCanvas = document.createElement('canvas'); + sliderCanvas.width = sliderWidth; + sliderCanvas.height = sliderHeight; + sliderCanvas.setAttribute('position', 'absolute'); + sliderCanvas.setAttribute('cursor', 'auto'); + sliderCanvas.setAttribute('draggable', 'false'); + sliderCanvas.setAttribute('class', 'buttonClass'); + sliderCanvas.style.zIndex = zIndex; + if (visible == true) + container.appendChild(sliderCanvas); + canvases[pageIndex].push(sliderCanvas); + var sliderCtx = sliderCanvas.getContext('2d'); + if (vert == false) { + var leftPos = left + (startNum - min) * (width - sliderWidth) / (max - min); + var sliderCanvasData = [leftPos, top + 0.5 * height - 0.5 * sliderHeight, sliderWidth, sliderHeight, visible, false, true, zIndex]; + } else { + var topPos = bottom - sliderHeight - (startNum - min) * (height - sliderHeight) / (max - min); + var sliderCanvasData = [left + 0.5 * width - 0.5 * sliderWidth, topPos, sliderWidth, sliderHeight, visible, false, true, zIndex]; + } + for (var i = 0; i < 8; i++) { + sliderCanvasData[100 + i] = sliderCanvasData[i]; + } + sliderCanvasData[130] = visible; + sliderCanvas.data = sliderCanvasData; + addListenerStart(sliderCanvas, sliderDragStart); + + sliderCtx.fillStyle = handleColor; + if (handleStyle == 'rect') { + sliderCtx.fillRect(0, 0, sliderWidth, sliderHeight); + } else if (handleStyle == 'circle') { + sliderCtx.beginPath(); + sliderCtx.arc(0.5 * sliderWidth, 0.5 * sliderHeight, 0.5 * sliderHeight - 2, 0, 2 * Math.PI); + sliderCtx.closePath(); + sliderCtx.fill(); + } + + var labelCanvas = document.createElement('canvas'); + labelCanvas.width = width; + labelCanvas.height = parseInt(labelFont) * 2; + labelCanvas.setAttribute('position', 'absolute'); + labelCanvas.setAttribute('cursor', 'auto'); + labelCanvas.setAttribute('draggable', 'false'); + labelCanvas.setAttribute('class', 'buttonClass'); + labelCanvas.style.pointerEvents = 'none'; + labelCanvas.style.zIndex = zIndex; + if (label == true && visible == true) { + container.appendChild(labelCanvas); + } + canvases[pageIndex].push(labelCanvas); + var labelCtx = labelCanvas.getContext('2d'); + var labelCanvasData = [left, top + height, width, height, visible, false, false, zIndex]; + for (var i = 0; i < 8; i++) { + labelCanvasData[100 + i] = labelCanvasData[i]; + } + labelCanvasData[130] = visible; + labelCanvas.data = labelCanvasData; + + if (label == true) { + labelCtx.fillStyle = labelColor; + labelCtx.font = "24px Arial"; + labelCtx.textAlign = 'center'; + labelCtx.textBaseline = 'middle'; + labelCtx.fillText(vari + " = " + startNum, 0.5 * width, parseInt(labelFont)); + } + + resize(); + + slider[pageIndex][id] = { + id: id, + backCanvas: backCanvas, + backctx: backCtx, + backData: backCanvasData, + sliderCanvas: sliderCanvas, + sliderctx: sliderCtx, + sliderData: sliderCanvasData, + labelCanvas: labelCanvas, + labelctx: labelCtx, + labelData: labelCanvasData, + left: left, + top: top, + width: width, + height: height, + bottom: bottom, + sliderWidth: sliderWidth, + sliderHeight: sliderHeight, + min: min, + max: max, + startNum: startNum, + currNum: startNum, + vari: vari, + linkedVar: linkedVar, + varChangeListener: varChangeListener, + onchange: onchange, + inc: inc, + label: label, + labelFont: labelFont, + labelColor: labelColor, + visible: visible, + zIndex: zIndex, + discrete: discrete, + stepNum: stepNum, + snap: snap, + snapNum: snapNum, + vert: vert + }; + + return slider[pageIndex][id]; +} +function showSlider(slider) { + showObj(slider.backCanvas, slider.backData); + showObj(slider.sliderCanvas, slider.sliderData); + showObj(slider.labelCanvas, slider.labelData); +} +function hideSlider(slider) { + hideObj(slider.backCanvas, slider.backData); + hideObj(slider.sliderCanvas, slider.sliderData); + hideObj(slider.labelCanvas, slider.labelData); +} +function setSliderValue(slider, value) { + var val = Math.min(Math.max(slider.min, value), slider.max); + if (slider.vert == false) { + var left = slider.left + ((val - slider.min) / (slider.max - slider.min)) * (slider.width - slider.sliderWidth); + slider.sliderData[100] = left; + } else { + var top = slider.bottom - slider.sliderHeight - ((val - slider.min) / (slider.max - slider.min)) * (slider.height - slider.sliderHeight); + slider.sliderData[101] = top; + } + slider.currNum = val; + if (slider.label == true) { + slider.labelctx.clearRect(0, 0, slider.width, mainCanvasHeight); + slider.labelctx.fillStyle = slider.labelColor; + slider.labelctx.font = slider.labelFont; + slider.labelctx.textAlign = 'center'; + slider.labelctx.textBaseline = 'middle'; + slider.labelctx.fillText(slider.vari + " = " + roundToNearest(val, 0.01), 0.5 * slider.width, parseInt(slider.labelFont)); + } + resizeCanvas2(slider.sliderCanvas, slider.sliderData[100], slider.sliderData[101]); + if (!un(slider.linkedVar)) + eval(slider.linkedVar + "=" + val); + if (!un(slider.varChangeListener)) + eval(slider.varChangeListener + "()"); + if (!un(slider.onchange)) + slider.onchange(val); +} +function sliderDragStart(e) { + for (var i = 0; i < slider[pageIndex].length; i++) { + if (slider[pageIndex][i].sliderCanvas == e.target) { + currSlider = i; + break; + } + } + removeListenerStart(e.target, sliderDragStart) + addListenerMove(window, sliderDragMove); + addListenerEnd(window, sliderDragStop); +} +function sliderDragMove(e) { + var s = slider[pageIndex][currSlider]; + if (s.vert == false) { + var left = Math.min(Math.max(mouse.x, s.left), s.left + s.width - s.sliderWidth); + slider[pageIndex][currSlider].sliderData[100] = left; + var val = s.min + (left - s.left) * (s.max - s.min) / (s.width - s.sliderWidth); + } else { + var top = Math.min(Math.max(mouse.y, s.top), s.bottom - s.sliderHeight); + slider[pageIndex][currSlider].sliderData[101] = top; + var val = s.min + (s.bottom - s.sliderHeight - top) * (s.max - s.min) / (s.height - s.sliderHeight); + } + if (s.discrete == true) { + val = roundToNearest(val, s.stepNum); + } + s.currNum = val; + if (!un(s.linkedVar)) + eval(s.linkedVar + "=" + val); + if (!un(s.varChangeListener)) + eval(s.varChangeListener + "()"); + if (!un(s.onchange)) + s.onchange(val); + if (s.label == true) { + s.labelctx.clearRect(0, 0, s.width, mainCanvasHeight); + s.labelctx.fillStyle = s.labelColor; + s.labelctx.font = s.labelFont; + s.labelctx.textAlign = 'center'; + s.labelctx.textBaseline = 'middle'; + s.labelctx.fillText(s.vari + " = " + roundToNearest(val, 0.01), 0.5 * s.width, parseInt(s.labelFont)); + } + resize(); +} +function sliderDragStop(e) { + removeListenerMove(window, sliderDragMove); + removeListenerEnd(window, sliderDragStop); + // snap slider to position + var s = slider[pageIndex][currSlider]; + if (s.snap == true) { + if (s.vert == false) { + var left = Math.min(Math.max(mouse.x, s.left), s.left + s.width - s.sliderWidth); + var val = s.min + (left - s.left) * (s.max - s.min) / (s.width - s.sliderWidth); + } else { + var top = Math.min(Math.max(mouse.y, s.top), s.bottom - s.sliderHeight); + var val = s.min + (s.bottom - s.sliderHeight - top) * (s.max - s.min) / (s.height - s.sliderHeight); + } + if (s.discrete == true) { + val = roundToNearest(val, s.stepNum); + } else if (s.snap == true) { + val = roundToNearest(val, s.snapNum); + } else { + val = roundToNearest(val, s.inc); + } + s.currNum = val; + eval(s.linkedVar + "=" + val); + eval(s.varChangeListener + "()"); + if (s.vert == false) { + var leftSnap = s.left + (val - s.min) * (s.width - s.sliderWidth) / (s.max - s.min); + slider[pageIndex][currSlider].sliderData[100] = leftSnap; + } else { + var topSnap = s.bottom - s.sliderHeight - (val - s.min) * (s.height - s.sliderHeight) / (s.max - s.min); + slider[pageIndex][currSlider].sliderData[101] = topSnap; + } + resize(); + } + addListenerStart(slider[pageIndex][currSlider].sliderCanvas, sliderDragStart) +} + +var currScroller; +function createScroller(obj) { + var rect = obj.rect; + var z = obj.zIndex || obj.z || 2; + var vis = boolean(obj.visible, boolean(obj.vis, true)); + var min = obj.min || 0; + var max = obj.max; + //var colors = obj.colors || ['#3FF','#00F','#333','#CCC','#333']; + var colors = obj.colors || { + buttons: '#3FF', + slider: '#00F', + border: '#333', + back: '#CCC', + buttonsBorder: '#333', + arrows: '#333' + }; + var sliderHeight = obj.sliderHeight || (rect[3] - rect[2] * 2) / (max - min); + var inc = obj.inc || 1; + var incDist = (rect[3] - 2 * rect[2] - sliderHeight) / ((max - min) / inc); + var sliderRect = [rect[0], rect[1] + rect[2], rect[2], sliderHeight]; + var radius = def([obj.radius, 0]); + var scroller = { + rect: rect, + zIndex: z, + min: min, + max: max, + value: min, + colors: colors, + sliderRect: sliderRect, + sliderHeight: sliderHeight, + inc: inc, + incDist: incDist, + radius: radius + }; + if (typeof obj.funcMove == 'function') + scroller.funcMove = obj.funcMove; + if (typeof obj.funcStop == 'function') + scroller.funcStop = obj.funcStop; + scroller.canvas = newctx({ + rect: scroller.rect, + pE: true, + z: scroller.zIndex + }).canvas; + scroller.canvas.scroller = scroller; + scroller.ctx = scroller.canvas.ctx; + scroller.slider = {}; + scroller.sliderCanvas = newctx({ + rect: sliderRect, + pE: true, + z: scroller.zIndex + 1 + }).canvas; + scroller.sliderCanvas.scroller = scroller; + scroller.sliderctx = scroller.sliderCanvas.ctx; + scroller.sliderctx.fillStyle = colors[1]; + scroller.sliderctx.fillRect(0, 0, sliderRect[2], sliderRect[3]); + scroller.reposition = function (newMax, newRect, sliderHeight) { + if (!un(newMax)) + this.max = newMax; + if (!un(newRect)) + this.rect = newRect; + this.canvas.data[100] = this.rect[0]; + this.canvas.data[101] = this.rect[1]; + this.canvas.data[102] = this.rect[2]; + this.canvas.data[103] = this.rect[3]; + resizeCanvas(this.canvas, this.rect[0], this.rect[1], this.rect[2], this.rect[3]); + this.canvas.width = this.rect[2]; + this.canvas.height = this.rect[3]; + if (!un(sliderHeight)) { + this.sliderHeight = sliderHeight; + } else { + this.sliderHeight = (this.rect[3] - this.rect[2] * 2) / (this.max - this.min); + } + this.sliderRect = [this.rect[0], this.rect[1] + this.rect[2], this.rect[2], this.sliderHeight]; + this.sliderCanvas.data[100] = this.sliderRect[0]; + this.sliderCanvas.data[101] = this.sliderRect[1]; + this.sliderCanvas.data[102] = this.sliderRect[2]; + this.sliderCanvas.data[103] = this.sliderRect[3]; + resizeCanvas(this.sliderCanvas, this.sliderRect[0], this.sliderRect[1], this.sliderRect[2], this.sliderRect[3]); + this.sliderCanvas.width = this.sliderRect[2]; + this.sliderCanvas.height = this.sliderRect[3]; + this.draw(); + this.value = 0; + this.sliderRect[1] = this.rect[1] + this.rect[2] + (this.rect[3] - 2 * this.rect[2] - this.sliderHeight) * ((this.value - this.min) / (this.max - this.min)); + resizeCanvas(this.sliderCanvas, this.sliderRect[0], this.sliderRect[1]); + } + scroller.draw = function () { + var ctx = this.ctx; + var w = this.rect[2]; + var h = this.rect[3]; + ctx.clearRect(0, 0, w, h); + var lineWidth = this.lineWidth || 4; + var radius = this.radius; + roundedRect(ctx, lineWidth / 2, lineWidth / 2, w - lineWidth, h - lineWidth, radius, lineWidth, this.colors.border, this.colors.back); + roundedRect(ctx, lineWidth / 2, lineWidth / 2, w - lineWidth, w - lineWidth, radius, lineWidth, this.colors.buttonsBorder, this.colors.buttons); + roundedRect(ctx, lineWidth / 2, h - w, w - lineWidth, w - lineWidth, radius, lineWidth, this.colors.buttonsBorder, this.colors.buttons); + ctx.fillStyle = this.colors.arrows; + ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; + ctx.beginPath(); + ctx.moveTo(w * 14 / 50, w * 34 / 50); + ctx.lineTo(w * 25 / 50, w * 16 / 50); + ctx.lineTo(w * 36 / 50, w * 34 / 50); + ctx.lineTo(w * 14 / 50, w * 34 / 50); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(w * 14 / 50, h - w + w * 16 / 50); + ctx.lineTo(w * 25 / 50, h - w + w * 34 / 50); + ctx.lineTo(w * 36 / 50, h - w + w * 16 / 50); + ctx.lineTo(w * 14 / 50, h - w + w * 16 / 50); + ctx.fill(); + this.sliderctx.fillStyle = this.colors.slider; + this.sliderctx.fillRect(0, 0, this.sliderRect[2], this.sliderHeight); + } + scroller.draw(); + addListener(scroller.canvas, function (e) { + updateMouse(e); + var scroller = this.scroller; + if (mouse.y < scroller.sliderRect[1]) { + scroller.update(-1); + } else if (mouse.y > scroller.sliderRect[1] + scroller.sliderRect[3]) { + scroller.update(1); + } + }); + addListenerStart(scroller.sliderCanvas, scrollerMoveStart); + scroller.update = function (change) { + var newValue = Math.min(this.max, Math.max(this.min, this.value + change)); + if (newValue == this.value) + return; + this.value = newValue; + this.sliderRect[1] = this.rect[1] + this.rect[2] + (this.rect[3] - 2 * this.rect[2] - this.sliderHeight) * ((this.value - this.min) / (this.max - this.min)); + resizeCanvas(this.sliderCanvas, this.sliderRect[0], this.sliderRect[1]); + if (typeof this.funcStop == 'function') { + this.funcStop(this.value); + } else if (typeof this.funcMove == 'function') { + this.funcMove(this.value); + } + } + return scroller; +} +function updateScrollerDims(s) { // update max + s.sliderHeight = (s.rect[3] - s.rect[2] * 2) / (s.max - s.min); + s.incDist = (s.rect[3] - 2 * s.rect[2] - s.sliderHeight) / ((s.max - s.min) / s.inc); + s.sliderRect[3] = s.sliderHeight; + s.sliderCanvas.data[100] = s.sliderRect[0]; + s.sliderCanvas.data[101] = s.sliderRect[1]; + s.sliderCanvas.data[102] = s.sliderRect[2]; + s.sliderCanvas.data[103] = s.sliderRect[3]; + resizeCanvas3(s.sliderCanvas); + setScrollerValue(s, s.value, true); + return s; +} +function scrollerMoveStart(e) { + updateMouse(e); + currScroller = e.target.scroller; + currScroller.dragStartPos = mouse.y; + currScroller.dragStartValue = currScroller.value; + currScroller.dragStartY = currScroller.sliderRect[1]; + currScroller.dragOffset = mouse.y - currScroller.sliderRect[1]; + currScroller.dragMinY = currScroller.rect[1] + currScroller.rect[2]; + currScroller.dragMaxY = currScroller.rect[1] + currScroller.rect[3] - currScroller.rect[2] - currScroller.sliderRect[3]; + addListenerMove(window, scrollerMoveMove); + addListenerEnd(window, scrollerMoveStop); +} +function scrollerMoveMove(e) { + updateMouse(e); + var dy = mouse.y - currScroller.dragStartPos; + var newY = Math.min(Math.max(currScroller.dragStartY + dy, currScroller.dragMinY), currScroller.dragMaxY); + if (newY == currScroller.sliderRect[1]) + return; + currScroller.sliderRect[1] = newY; + resizeCanvas(currScroller.sliderCanvas, currScroller.sliderRect[0], currScroller.sliderRect[1]); + currScroller.value = currScroller.min + (currScroller.max - currScroller.min) * (currScroller.sliderRect[1] - currScroller.dragMinY) / (currScroller.dragMaxY - currScroller.dragMinY); + if (typeof currScroller.funcMove == 'function') + currScroller.funcMove(currScroller.value); +}; +function scrollerMoveStop(e) { + if (typeof currScroller.funcStop == 'function') + currScroller.funcStop(currScroller.value); + currScroller = null; + removeListenerMove(window, scrollerMoveMove); + removeListenerEnd(window, scrollerMoveStop); +}; +function setScrollerValue(scroller, value, applyFunc) { + var newValue = Math.min(Math.max(scroller.min, value), scroller.max); + //if (newValue == scroller.value) return; + scroller.value = newValue; + scroller.sliderRect[1] = scroller.rect[1] + scroller.rect[2] + (scroller.rect[3] - 2 * scroller.rect[2] - scroller.sliderHeight) * ((scroller.value - scroller.min) / (scroller.max - scroller.min)); + resizeCanvas(scroller.sliderCanvas, scroller.sliderRect[0], scroller.sliderRect[1]); + scroller.update(0); + if (!applyFunc && applyFunc == false) + return; + if (typeof scroller.funcStop == 'function') { + scroller.funcStop(scroller.value); + } else if (typeof scroller.funcMove == 'function') { + scroller.funcMove(scroller.value); + } +} +function moveScroller(scroller, left, top) { + var relTop = scroller.sliderCanvas.data[101] - scroller.canvas.data[100]; + scroller.sliderRect[0] = left; + scroller.sliderRect[1] = top + relTop; + scroller.rect[0] = left; + scroller.rect[1] = top; + scroller.canvas.data[100] = left; + scroller.canvas.data[101] = top; + resizeCanvas3(scroller.canvas); + scroller.sliderCanvas.data[100] = left; + scroller.sliderCanvas.data[101] = top + relTop; + resizeCanvas3(scroller.sliderCanvas); +} +function hideScroller(scroller) { + if (un(scroller)) + return; + hideObj(scroller.canvas); + hideObj(scroller.sliderCanvas); +} +function showScroller(scroller) { + if (un(scroller)) + return; + showObj(scroller.canvas); + showObj(scroller.sliderCanvas); +} + +function drawArrow(object) { + var context = object.context || object.ctx; + var startX = object.startX; + var startY = object.startY; + var finX = object.finX || startX; + var finY = object.finY || startY; + + var doubleEnded; + if (typeof object.doubleEnded == 'boolean') { + doubleEnded = object.doubleEnded + } else { + doubleEnded = false + } + var arrowLength = object.arrowLength || 30; + var angleBetweenLinesRads = object.angleBetweenLinesRads || 0.5; + var fillArrow = boolean(object.fillArrow, false); + var showLine = boolean(object.showLine, true); + var dash = object.dash || []; + + context.save(); + context.strokeStyle = object.color || '#000'; + context.fillStyle = object.color || '#000'; + context.lineWidth = object.lineWidth || 4; + + if (showLine == true) { + //draw line + if (typeof context.setLineDash == 'undefined') + context.setLineDash = function () {}; + context.setLineDash(dash); + context.beginPath(); + context.moveTo(startX, startY); + context.lineTo(finX, finY); + context.stroke(); + } + + context.lineWidth = object.arrowLineWidth || object.lineWidth || 4; + context.beginPath(); + if (typeof context.setLineDash == 'undefined') + context.setLineDash = function () {}; + context.setLineDash([]); + context.lineJoin = 'round'; + context.lineCap = 'round'; + + var posX; + var posY; + var otherX; + var otherY; + var endToDraw = "fin"; + + do { + if (endToDraw == "fin") { + posX = finX; + posY = finY; + otherX = startX; + otherY = startY; + } else if (endToDraw == "start") { + posX = startX; + posY = startY; + otherX = finX; + otherY = finY; + } + + var nGradient = (-1 * (posY - otherY)) / (posX - otherX); + + var angleToHorizontal = Math.abs(Math.atan(nGradient)); + var remainingAngle = Math.PI / 2 - angleBetweenLinesRads - angleToHorizontal; + + var narrowX1, + narrowX2, + narrowY1, + narrowY2; + + if (nGradient == Infinity) { + //first half of arrow + narrowX1 = Math.sin(remainingAngle) * arrowLength; + narrowY1 = Math.cos(remainingAngle) * arrowLength; + context.moveTo(posX, posY); + context.lineTo(posX + narrowX1, posY + narrowY1); + + // second half of arrow + narrowX2 = Math.cos(angleBetweenLinesRads - angleToHorizontal) * arrowLength; + narrowY2 = Math.sin(angleBetweenLinesRads - angleToHorizontal) * arrowLength; + context.moveTo(posX, posY); + context.lineTo(posX + narrowX2, posY - narrowY2); + + if (fillArrow == true) + context.lineTo(posX + narrowX1, posY + narrowY1); + } else if (nGradient == -Infinity) { + //first half of arrow + narrowX1 = Math.sin(remainingAngle) * arrowLength; + narrowY1 = Math.cos(remainingAngle) * arrowLength; + context.moveTo(posX, posY); + context.lineTo(posX + narrowX1, posY - narrowY1); + + // second half of arrow + narrowX2 = Math.cos(angleBetweenLinesRads - angleToHorizontal) * arrowLength; + narrowY2 = Math.sin(angleBetweenLinesRads - angleToHorizontal) * arrowLength; + context.moveTo(posX, posY); + context.lineTo(posX + narrowX2, posY + narrowY2); + + if (fillArrow == true) + context.lineTo(posX + narrowX1, posY - narrowY1); + } else + + ///case 1 - arrow is pointing up and to the left + if (nGradient < 0 && posY < otherY) { + + //first half of arrow + narrowX1 = Math.sin(remainingAngle) * arrowLength; + narrowY1 = Math.cos(remainingAngle) * arrowLength; + context.moveTo(posX, posY); + context.lineTo(posX + narrowX1, posY + narrowY1); + + // second half of arrow + narrowX2 = Math.cos(angleBetweenLinesRads - angleToHorizontal) * arrowLength; + narrowY2 = Math.sin(angleBetweenLinesRads - angleToHorizontal) * arrowLength; + context.moveTo(posX, posY); + context.lineTo(posX + narrowX2, posY - narrowY2); + + if (fillArrow == true) + context.lineTo(posX + narrowX1, posY + narrowY1); + } else + + ///case 2 - arrow is pointing up and to the right + if (nGradient > 0 && posY < otherY) { + + //first half of arrow + narrowX1 = Math.sin(remainingAngle) * arrowLength; + narrowY1 = Math.cos(remainingAngle) * arrowLength; + context.moveTo(posX, posY); + context.lineTo(posX - narrowX1, posY + narrowY1); + + // second half of arrow + narrowX2 = Math.cos(angleBetweenLinesRads - angleToHorizontal) * arrowLength; + narrowY2 = Math.sin(angleBetweenLinesRads - angleToHorizontal) * arrowLength; + context.moveTo(posX, posY); + context.lineTo(posX - narrowX2, posY - narrowY2); + + if (fillArrow == true) + context.lineTo(posX - narrowX1, posY + narrowY1); + } else + + //gradient is 0 and pointing right + if (nGradient == 0 && posX > otherX) { + + //first half of arrow + narrowX1 = Math.sin(remainingAngle) * arrowLength + narrowY1 = Math.cos(remainingAngle) * arrowLength + context.moveTo(posX, posY) + context.lineTo(posX - narrowX1, posY + narrowY1) + + // second half of arrow + narrowX2 = Math.cos(angleBetweenLinesRads - angleToHorizontal) * arrowLength + narrowY2 = Math.sin(angleBetweenLinesRads - angleToHorizontal) * arrowLength + context.moveTo(posX, posY) + context.lineTo(posX - narrowX2, posY - narrowY2) + + if (fillArrow == true) + context.lineTo(posX - narrowX1, posY + narrowY1); + } else + + // gradient is 0 and pointing left + if (nGradient == 0 && posX < otherX) { + + //first half of arrow + narrowX1 = Math.sin(remainingAngle) * arrowLength + narrowY1 = Math.cos(remainingAngle) * arrowLength + context.moveTo(posX, posY) + context.lineTo(posX + narrowX1, posY + narrowY1) + + // second half of arrow + narrowX2 = Math.cos(angleBetweenLinesRads - angleToHorizontal) * arrowLength + narrowY2 = Math.sin(angleBetweenLinesRads - angleToHorizontal) * arrowLength + context.moveTo(posX, posY) + context.lineTo(posX + narrowX2, posY - narrowY2) + + if (fillArrow == true) + context.lineTo(posX + narrowX1, posY + narrowY1); + } else + + ///case 3 - arrow is pointing down and to the right + if (nGradient < 0 && posY > otherY) { + + angleBetweenLinesRads = Math.PI - angleBetweenLinesRads + remainingAngle = Math.PI / 2 - angleBetweenLinesRads - angleToHorizontal; + + //first half of arrow + narrowX1 = Math.sin(remainingAngle) * arrowLength + narrowY1 = Math.cos(remainingAngle) * arrowLength + context.moveTo(posX, posY) + context.lineTo(posX + narrowX1, posY + narrowY1) + + // second half of arrow + narrowX2 = Math.cos(angleBetweenLinesRads - angleToHorizontal) * arrowLength + narrowY2 = Math.sin(angleBetweenLinesRads - angleToHorizontal) * arrowLength + context.moveTo(posX, posY) + context.lineTo(posX + narrowX2, posY - narrowY2) + + if (fillArrow == true) + context.lineTo(posX + narrowX1, posY + narrowY1); + } else + + ///case 4 - arrow is pointing down and to the left + if (nGradient > 0 && posY > otherY) { + + angleBetweenLinesRads = Math.PI - angleBetweenLinesRads + remainingAngle = Math.PI / 2 - angleBetweenLinesRads - angleToHorizontal; + + //first half of arrow + narrowX1 = Math.sin(remainingAngle) * arrowLength + narrowY1 = Math.cos(remainingAngle) * arrowLength + context.moveTo(posX, posY) + context.lineTo(posX - narrowX1, posY + narrowY1) + + // second half of arrow + narrowX2 = Math.cos(angleBetweenLinesRads - angleToHorizontal) * arrowLength + narrowY2 = Math.sin(angleBetweenLinesRads - angleToHorizontal) * arrowLength + context.moveTo(posX, posY) + context.lineTo(posX - narrowX2, posY - narrowY2) + + if (fillArrow == true) + context.lineTo(posX - narrowX1, posY + narrowY1); + } + + if (doubleEnded == true && endToDraw == "fin") { + endToDraw = "start"; + } else { + endToDraw = "none"; + } + } while (endToDraw !== "none"); + + context.closePath(); + context.stroke(); + if (fillArrow == true) + context.fill(); + context.restore(); + +} +function drawVector(vectorCtx, lineEndPointX1, lineEndPointY1, lineEndPointX2, lineEndPointY2, arrowLength, angleBetweenLinesInRadians, opt_color, opt_lineWidth) { + + if (typeof opt_color == 'undefined') { + vectorCtx.strokeStyle = "#000"; + } + if (typeof opt_lineWidth == 'undefined') { + vectorCtx.lineWidth = 5; + } + + //draw line + + vectorCtx.beginPath(); + vectorCtx.moveTo(lineEndPointX1, lineEndPointY1); + vectorCtx.lineTo(lineEndPointX2, lineEndPointY2); + + //draw Arrow + var nMidpointX = (lineEndPointX1 + lineEndPointX2) / 2 + var nMidpointY = (lineEndPointY1 + lineEndPointY2) / 2 + + var nGradient = (-1 * (lineEndPointY2 - lineEndPointY1)) / (lineEndPointX2 - lineEndPointX1); + + var angleToHorizontal = Math.abs(Math.atan(nGradient)); + var remainingAngle = Math.PI / 2 - angleBetweenLinesInRadians - angleToHorizontal; + + var narrowX; + var narrowY; + + ///case 1 - arrow is pointing up and to the left + if (nGradient < 0 && lineEndPointY2 < lineEndPointY1) { + + //first half of arrow + + narrowX = Math.sin(remainingAngle) * arrowLength + narrowY = Math.cos(remainingAngle) * arrowLength + + vectorCtx.moveTo(nMidpointX, nMidpointY) + vectorCtx.lineTo(nMidpointX + narrowX, nMidpointY + narrowY) + + // second half of arrow + + narrowX = Math.cos(angleBetweenLinesInRadians - angleToHorizontal) * arrowLength + narrowY = Math.sin(angleBetweenLinesInRadians - angleToHorizontal) * arrowLength + + vectorCtx.moveTo(nMidpointX, nMidpointY) + vectorCtx.lineTo(nMidpointX + narrowX, nMidpointY - narrowY) + + } + + ///case 2 - arrow is pointing up and to the right + if (nGradient > 0 && lineEndPointY2 < lineEndPointY1) { + + //first half of arrow + + narrowX = Math.sin(remainingAngle) * arrowLength + narrowY = Math.cos(remainingAngle) * arrowLength + + vectorCtx.moveTo(nMidpointX, nMidpointY) + vectorCtx.lineTo(nMidpointX - narrowX, nMidpointY + narrowY) + + // second half of arrow + + narrowX = Math.cos(angleBetweenLinesInRadians - angleToHorizontal) * arrowLength + narrowY = Math.sin(angleBetweenLinesInRadians - angleToHorizontal) * arrowLength + + vectorCtx.moveTo(nMidpointX, nMidpointY) + vectorCtx.lineTo(nMidpointX - narrowX, nMidpointY - narrowY) + + } + + //gradient is 0 and pointing right + + + if (nGradient == 0 && lineEndPointX2 > lineEndPointX1) { + + //first half of arrow + + narrowX = Math.sin(remainingAngle) * arrowLength + narrowY = Math.cos(remainingAngle) * arrowLength + + vectorCtx.moveTo(nMidpointX, nMidpointY) + vectorCtx.lineTo(nMidpointX - narrowX, nMidpointY + narrowY) + + // second half of arrow + + narrowX = Math.cos(angleBetweenLinesInRadians - angleToHorizontal) * arrowLength + narrowY = Math.sin(angleBetweenLinesInRadians - angleToHorizontal) * arrowLength + + vectorCtx.moveTo(nMidpointX, nMidpointY) + vectorCtx.lineTo(nMidpointX - narrowX, nMidpointY - narrowY) + + } + + // gradient is - and pointing left + + if (nGradient == 0 && lineEndPointX2 < lineEndPointX1) { + + //first half of arrow + + narrowX = Math.sin(remainingAngle) * arrowLength + narrowY = Math.cos(remainingAngle) * arrowLength + + vectorCtx.moveTo(nMidpointX, nMidpointY) + vectorCtx.lineTo(nMidpointX + narrowX, nMidpointY + narrowY) + + // second half of arrow + + narrowX = Math.cos(angleBetweenLinesInRadians - angleToHorizontal) * arrowLength + narrowY = Math.sin(angleBetweenLinesInRadians - angleToHorizontal) * arrowLength + + vectorCtx.moveTo(nMidpointX, nMidpointY) + vectorCtx.lineTo(nMidpointX + narrowX, nMidpointY - narrowY) + + } + + ///case 3 - arrow is pointing down and to the right + if (nGradient < 0 && lineEndPointY2 > lineEndPointY1) { + + angleBetweenLinesInRadians = Math.PI - angleBetweenLinesInRadians + remainingAngle = Math.PI / 2 - angleBetweenLinesInRadians - angleToHorizontal; + + //first half of arrow + + narrowX = Math.sin(remainingAngle) * arrowLength + narrowY = Math.cos(remainingAngle) * arrowLength + + vectorCtx.moveTo(nMidpointX, nMidpointY) + vectorCtx.lineTo(nMidpointX + narrowX, nMidpointY + narrowY) + + // second half of arrow + + narrowX = Math.cos(angleBetweenLinesInRadians - angleToHorizontal) * arrowLength + narrowY = Math.sin(angleBetweenLinesInRadians - angleToHorizontal) * arrowLength + + vectorCtx.moveTo(nMidpointX, nMidpointY) + vectorCtx.lineTo(nMidpointX + narrowX, nMidpointY - narrowY) + + } + + ///case 4 - arrow is pointing down and to the left + if (nGradient > 0 && lineEndPointY2 > lineEndPointY1) { + + angleBetweenLinesInRadians = Math.PI - angleBetweenLinesInRadians + remainingAngle = Math.PI / 2 - angleBetweenLinesInRadians - angleToHorizontal; + + //first half of arrow + + narrowX = Math.sin(remainingAngle) * arrowLength + narrowY = Math.cos(remainingAngle) * arrowLength + + vectorCtx.moveTo(nMidpointX, nMidpointY) + vectorCtx.lineTo(nMidpointX - narrowX, nMidpointY + narrowY) + + // second half of arrow + + narrowX = Math.cos(angleBetweenLinesInRadians - angleToHorizontal) * arrowLength + narrowY = Math.sin(angleBetweenLinesInRadians - angleToHorizontal) * arrowLength + + vectorCtx.moveTo(nMidpointX, nMidpointY) + vectorCtx.lineTo(nMidpointX - narrowX, nMidpointY - narrowY) + + } + + vectorCtx.closePath(); + vectorCtx.stroke(); + +} +function measureAngle(object) { + // REQUIRED + var aPos = object.a; // array: [x,y] + var bPos = object.b; // array: [x,y] + var cPos = object.c; // array: [x,y] + + // OPTIONAL + var angleType = object.angleType || 'radians'; + + // Work out the angles seperately - measured in radians anti-clockwise from north + var m1 = (bPos[1] - aPos[1]) / (aPos[0] - bPos[0]); + var angle1 = (Math.PI / 2 - Math.atan(m1)); + if (aPos[0] <= bPos[0]) + angle1 += Math.PI; + if (aPos[0] == bPos[0]) { // if infinite gradient + if (aPos[1] < bPos[1]) { + angle1 = 0; + } else { + angle1 = Math.PI; + } + } + + var m2 = (cPos[1] - bPos[1]) / (bPos[0] - cPos[0]); + var angle2 = (Math.PI / 2 - Math.atan(m2)); + if (bPos[0] >= cPos[0]) + angle2 += Math.PI; + if (bPos[0] == cPos[0]) { // if infinite gradient + if (bPos[1] > cPos[1]) { + angle2 = 0; + } else { + angle2 = Math.PI; + } + } + + var angleDiff = angle2 - angle1; + if (angle1 > angle2) + angleDiff = angle2 - (angle1 - 2 * Math.PI); + if (angleType == 'degrees') + angleDiff = angleDiff * 180 / Math.PI; + + return angleDiff; +} +function drawAngle(obj) { + // REQUIRED + var ctx = obj.ctx || obj.context; + var aPos = obj.a; // array: [x,y] + var bPos = obj.b; // array: [x,y] + var cPos = obj.c; // array: [x,y] + + // OPTIONAL + if (typeof obj.sf !== 'undefined') { + var sf = obj.sf; + } else { + var sf = 1; + } + if (sf !== 1) { + aPos = [aPos[0] * sf, aPos[1] * sf]; + bPos = [bPos[0] * sf, bPos[1] * sf]; + cPos = [cPos[0] * sf, cPos[1] * sf]; + } + var labelCtx = obj.labelCtx || ctx; + var radius = obj.angleRadius * sf || obj.radius * sf || 35 * sf; + var forceRightAngle = boolean(obj.forceRightAngle, false); + var squareForRight = boolean(obj.squareForRight, true); + var labelIfRight = boolean(obj.labelIfRight, false); + + var drawLines = boolean(obj.drawLines, false); + var lineWidth = obj.lineWidth * sf || 4 * sf; + var armWidth = obj.armWidth * sf || lineWidth; + var lineColor = obj.lineColor || '#000'; + var armColor = obj.armColor || lineColor; + + var numOfCurves = obj.numOfCurves || 1; + var curveGap = obj.curveGap || 4 * sf + lineWidth; + + var drawCurve = boolean(obj.drawCurve, true); + var curveWidth = obj.curveWidth * sf || lineWidth; + var curveColor = obj.curveColor || lineColor; + + var fill = boolean(obj.fill, false); + var fillColor = obj.fillColor || '#CCF'; + if (fillColor == 'none') + fill = false; + + var label = obj.label || ['']; + var labelFont = obj.labelFont || 'Arial'; + var labelFontSize = obj.labelFontSize * sf || 30 * sf; + var labelColor = obj.labelColor || '#000'; + var labelRadius = obj.labelRadius * sf || radius * 1.1; + var labelBox = obj.labelBox || { + type: 'none' + }; + if (boolean(obj.labelBackFill, false) === true) { + labelBox = { + type: 'tight', + color: mainCanvasFillStyle, + borderColor: mainCanvasFillStyle, + padding: 0.1 + }; + } + + var labelMeasure = boolean(obj.labelMeasure, false); + var measureRoundTo = obj.measureRoundTo || 1; + var angleType = obj.angleType || 'degrees'; + + function lowerCaseTest(string) { + var upperCase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789lkhfdb'.split(''); + for (var i = 0; i < upperCase.length; i++) { + if (string.indexOf(upperCase[i]) > -1) + return false; + } + return true; + } + + // Work out the angles required - measured in radians anti-clockwise from north + var m1 = (bPos[1] - aPos[1]) / (aPos[0] - bPos[0]); + var angle1 = (Math.PI / 2 - Math.atan(m1)); + if (aPos[0] <= bPos[0]) + angle1 += Math.PI; + if (aPos[0] == bPos[0]) { // if infinite gradient + if (aPos[1] < bPos[1]) { + angle1 = 0; + } else { + angle1 = Math.PI; + } + } + + var m2 = (cPos[1] - bPos[1]) / (bPos[0] - cPos[0]); + var angle2 = (Math.PI / 2 - Math.atan(m2)); + if (bPos[0] >= cPos[0]) + angle2 += Math.PI; + if (bPos[0] == cPos[0]) { // if infinite gradient + if (bPos[1] > cPos[1]) { + angle2 = 0; + } else { + angle2 = Math.PI; + } + } + + var angleDiff = angle2 - angle1; + if (angle1 > angle2) angleDiff = angle2 - (angle1 - 2 * Math.PI); + + // test if right-angled + if (forceRightAngle == true || (squareForRight == true && roundToNearest(angleDiff * 180 / Math.PI, measureRoundTo) == 90)) { + var dAngle = angle1 + 0.5 * angleDiff; + if (angle1 > angle2) { + dAngle = (angle1 - 2 * Math.PI) + 0.5 * angleDiff; + } + var dPos = [bPos[0] + radius * Math.cos(dAngle - Math.PI / 2), bPos[1] + radius * Math.sin(dAngle - Math.PI / 2)]; + + // other vertices of the square + var abLength = Math.sqrt(Math.pow(bPos[0] - aPos[0], 2) + Math.pow(bPos[1] - aPos[1], 2)); + var aPos2 = [bPos[0] + (aPos[0] - bPos[0]) * radius / (abLength * Math.sqrt(2)), bPos[1] + (aPos[1] - bPos[1]) * radius / (abLength * Math.sqrt(2))]; + + var bcLength = Math.sqrt(Math.pow(bPos[0] - cPos[0], 2) + Math.pow(bPos[1] - cPos[1], 2)); + var cPos2 = [bPos[0] + (cPos[0] - bPos[0]) * radius / (bcLength * Math.sqrt(2)), bPos[1] + (cPos[1] - bPos[1]) * radius / (bcLength * Math.sqrt(2))]; + + if (fill == true) { + ctx.fillStyle = fillColor; + ctx.beginPath(); + ctx.moveTo(bPos[0], bPos[1]); + ctx.lineTo(aPos2[0], aPos2[1]); + ctx.lineTo(dPos[0], dPos[1]); + ctx.lineTo(cPos2[0], cPos2[1]); + ctx.closePath(); + ctx.fill(); + } + + if (drawLines == true) { + ctx.strokeStyle = armColor; + ctx.lineWidth = armWidth; + ctx.beginPath(); + ctx.joinCap = 'round'; + ctx.moveTo(aPos[0], aPos[1]); + ctx.lineTo(bPos[0], bPos[1]); + ctx.lineTo(cPos[0], cPos[1]); + ctx.stroke(); + } + + if (drawCurve == true) { + ctx.strokeStyle = curveColor; + ctx.lineWidth = curveWidth; + ctx.beginPath(); + ctx.moveTo(aPos2[0], aPos2[1]); + ctx.lineTo(dPos[0], dPos[1]); + ctx.lineTo(cPos2[0], cPos2[1]); + ctx.stroke(); + } + + if (labelIfRight == true) { + if (labelMeasure == true) { + var angleDiff2 = angleDiff; + if (angleType == 'degrees') + angleDiff2 = angleDiff2 * 180 / Math.PI; + angleDiff2 = roundToNearest(angleDiff2, measureRoundTo); + label = [String(angleDiff2)]; + if (angleType == 'degrees') { + label = [String(angleDiff2) + String.fromCharCode(0x00B0)] + }; + } + + // work out label position + // d is midPoint of ac + var dAngle = angle1 + 0.5 * angleDiff; + if (angle1 > angle2) { + dAngle = (angle1 - 2 * Math.PI) + 0.5 * angleDiff; + } + var dPos = [bPos[0] + 1.85 * radius * Math.cos(dAngle - Math.PI / 2), bPos[1] + 1.85 * radius * Math.sin(dAngle - Math.PI / 2)]; + + if (labelCtx == ctx) { + drawMathsText(labelCtx, label, labelFontSize, dPos[0], dPos[1] + 0.5 * labelFontSize, true, [], 'center', 'bottom'); + } else { + var labelCanvas = labelCtx.canvas; + var labelWidth = labelCanvas.width; + var labelHeight = labelCanvas.height; + //console.log(taskObject,pageIndex); + //var canvasNum = taskObject.indexOf(labelCanvas); + //var labelData = taskObjectData[canvasNum]; + + labelCtx.clearRect(0, 0, labelWidth, labelHeight); + + drawMathsText(labelCtx, label, labelFontSize, 0.5 * labelWidth, 0.5 * labelHeight + 0.5 * labelFontSize, true, [], 'center', 'bottom'); + labelCanvas.data[100] = dPos[0] - 0.5 * labelWidth; + labelCanvas.data[101] = dPos[1] - 0.5 * labelHeight; + resizeCanvas3(labelCanvas); + } + } else if (labelCtx !== ctx) { + var labelCanvas = labelCtx.canvas; + var labelWidth = labelCanvas.width; + var labelHeight = labelCanvas.height; + labelCtx.clearRect(0, 0, labelWidth, labelHeight); + } + + } else { + // points at the end of the arc drawn for the angle + var abLength = Math.sqrt(Math.pow(bPos[0] - aPos[0], 2) + Math.pow(bPos[1] - aPos[1], 2)); + var aPos2 = [bPos[0] + (aPos[0] - bPos[0]) * radius / abLength, bPos[1] + (aPos[1] - bPos[1]) * radius / abLength]; + + var bcLength = Math.sqrt(Math.pow(bPos[0] - cPos[0], 2) + Math.pow(bPos[1] - cPos[1], 2)); + var cPos2 = [bPos[0] + (cPos[0] - bPos[0]) * radius / bcLength, bPos[1] + (cPos[1] - bPos[1]) * radius / bcLength]; + + if (labelMeasure == true) { + var angleDiff2 = angleDiff; + if (angleType == 'degrees') + angleDiff2 = angleDiff2 * 180 / Math.PI; + angleDiff2 = roundToNearest(angleDiff2, measureRoundTo); + label = [String(angleDiff2)]; + if (angleType == 'degrees') { + label = [String(angleDiff2) + String.fromCharCode(0x00B0)] + }; + } + + // work out label position + // d is midPoint of ac + var dAngle = angle1 + 0.5 * angleDiff; + if (angle1 > angle2) { + dAngle = (angle1 - 2 * Math.PI) + 0.5 * angleDiff; + } + + var labelRadiusFactor = 1.5 + 0.11 * (removeTags(label[0]).length - 1); + var lowerCaseOnly = lowerCaseTest(removeTags(label[0])); + var lcHeightAdjust = 0; + if (lowerCaseOnly == true) + lcHeightAdjust = labelFontSize / 5; + + if (dAngle < Math.PI / 6 || dAngle > 11 * Math.PI / 6 || (dAngle > 5 * Math.PI / 6 && dAngle < 7 * Math.PI / 6)) { + labelRadiusFactor = labelRadiusFactor * 0.9; + } else if (dAngle < Math.PI / 4 || dAngle > 7 * Math.PI / 4 || (dAngle > 3 * Math.PI / 4 && dAngle < 5 * Math.PI / 4)) { + labelRadiusFactor = labelRadiusFactor * 0.95; + } + + var dPos = [bPos[0] + labelRadiusFactor * labelRadius * Math.cos(dAngle - Math.PI / 2), bPos[1] + labelRadiusFactor * labelRadius * Math.sin(dAngle - Math.PI / 2) - lcHeightAdjust]; + + /* draw a dot at dPos (for testing angle label position + ctx.fillStyle = '#F00'; + ctx.beginPath(); + ctx.arc(dPos[0],dPos[1],8,0,2*Math.PI); + ctx.closePath(); + ctx.fill(); + //*/ + + label.unshift('<><><>'); + + if (fill == true && boolean(obj.measureOnly, true) == true) { + ctx.fillStyle = fillColor; + ctx.beginPath(); + ctx.moveTo(bPos[0], bPos[1]); + ctx.lineTo(aPos2[0], aPos2[1]); + ctx.arc(bPos[0], bPos[1], radius, angle1 - 0.5 * Math.PI, angle2 - 0.5 * Math.PI); + ctx.lineTo(cPos2[0], cPos2[1]); + ctx.closePath(); + ctx.fill(); + } + + if (drawLines == true && boolean(obj.measureOnly, true) == true) { + ctx.strokeStyle = armColor; + ctx.lineWidth = armWidth; + ctx.joinCap = 'round'; + ctx.beginPath(); + ctx.moveTo(aPos[0], aPos[1]); + ctx.lineTo(bPos[0], bPos[1]); + ctx.lineTo(cPos[0], cPos[1]); + ctx.stroke(); + } + + if (drawCurve == true && boolean(obj.measureOnly, true) == true) { + ctx.strokeStyle = curveColor; + ctx.lineWidth = curveWidth; + if (numOfCurves == 1) { + ctx.beginPath(); + ctx.moveTo(aPos2[0], aPos2[1]); + ctx.arc(bPos[0], bPos[1], Math.abs(radius), angle1 - 0.5 * Math.PI, angle2 - 0.5 * Math.PI); + ctx.stroke(); + } else if (numOfCurves == 2) { + ctx.beginPath(); + ctx.arc(bPos[0], bPos[1], Math.abs(radius - curveGap / 2), angle1 - 0.5 * Math.PI, angle2 - 0.5 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(bPos[0], bPos[1], Math.abs(radius + curveGap / 2), angle1 - 0.5 * Math.PI, angle2 - 0.5 * Math.PI); + ctx.stroke(); + } else if (numOfCurves == 3) { + ctx.beginPath(); + ctx.arc(bPos[0], bPos[1], Math.abs(radius - curveGap), angle1 - 0.5 * Math.PI, angle2 - 0.5 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(bPos[0], bPos[1], Math.abs(radius), angle1 - 0.5 * Math.PI, angle2 - 0.5 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(bPos[0], bPos[1], Math.abs(radius + curveGap), angle1 - 0.5 * Math.PI, angle2 - 0.5 * Math.PI); + ctx.stroke(); + } + } + + if (labelCtx == ctx) { + if (boolean(obj.measureLabelOnly, false) == true) { + var angleLabelPos = [dPos[0] - 57.8 / 2, dPos[1] - 25, 57.8, 50]; + } else { + var angleLabelPos = text({ + ctx: labelCtx, + left: dPos[0] - 200, + top: dPos[1] - 200, + width: 400, + height: 400, + textArray: label, + align: 'center', + vertAlign: 'middle', + box: labelBox, + minTightWidth: 1, + minTightHeight: 1 + }).tightRect; + } + } else { + var labelCanvas = labelCtx.canvas; + var labelWidth = labelCanvas.width; + var labelHeight = labelCanvas.height; + //var canvasNum = taskObject[pageIndex].indexOf(labelCanvas); + //var labelData = taskObjectData[pageIndex][canvasNum]; + + labelCtx.clearRect(0, 0, labelWidth, labelHeight); + + drawMathsText(labelCtx, label, labelFontSize, 0.5 * labelWidth, 0.5 * labelHeight + 0.5 * labelFontSize, true, [], 'center', 'bottom'); + labelCanvas.data[100] = dPos[0] - 0.5 * labelWidth; + labelCanvas.data[101] = dPos[1] - 0.5 * labelHeight; + resizeCanvas3(labelCanvas); + //resize(); + } + } + return angleLabelPos; + +} +function getAngleMidAngle(obj) { + // Work out the angles required - measured in radians anti-clockwise from north + var m1 = (obj.b[1] - obj.a[1]) / (obj.a[0] - obj.b[0]); + var angle1 = (Math.PI / 2 - Math.atan(m1)); + if (obj.a[0] <= obj.b[0]) + angle1 += Math.PI; + if (obj.a[0] == obj.b[0]) { // if infinite gradient + if (obj.a[1] < obj.b[1]) { + angle1 = 0; + } else { + angle1 = Math.PI; + } + } + + var m2 = (obj.c[1] - obj.b[1]) / (obj.b[0] - obj.c[0]); + var angle2 = (Math.PI / 2 - Math.atan(m2)); + if (obj.b[0] >= obj.c[0]) + angle2 += Math.PI; + if (obj.b[0] == obj.c[0]) { // if infinite gradient + if (obj.b[1] > obj.c[1]) { + angle2 = 0; + } else { + angle2 = Math.PI; + } + } + + if (angle1 > angle2) { + var angleDiff = angle2 - (angle1 - 2 * Math.PI); + } else { + var angleDiff = angle2 - angle1; + } + + var dAngle = angle1 + 0.5 * angleDiff; + if (angle1 > angle2) { + dAngle = (angle1 - 2 * Math.PI) + 0.5 * angleDiff; + } + return dAngle - Math.PI / 2; +} +function getAngleLabelPos(obj,radius) { + var angle = getAngleMidAngle(obj); + return [obj.b[0]+radius*Math.cos(angle),obj.b[1]+radius*Math.sin(angle)] +} +function drawStar(obj) { + var ctx = obj.ctx; + var c = obj.center || obj.c; + var r = obj.radius || obj.r; + var p = obj.points || obj.p || 5; + var s = obj.step || obj.s || 2; + var vertices = []; + for (var i = 0; i < p; i++) { + var angle = -Math.PI / 2 + i * (2 * Math.PI) / p; + vertices.push([c[0] + r * Math.cos(angle), c[1] + r * Math.sin(angle)]); + } + ctx.moveTo(vertices[0][0], vertices[0][1]); + for (var i = p; i >= 0; i--) { + ctx.lineTo(vertices[(i * s) % p][0], vertices[(i * s) % p][1]); + } +} +function drawAnglesAroundPoint(obj) { + /* eg. + drawAnglesAroundPoint({ + ctx:ctx1, + center:[400,300], + points:[[300,300],[400,200],[450,250],[480,320]], + lineColor:'#000', + thickness:4, + angles:[{fill:true,fillColor:"#CFC",lineWidth:2,labelFontSize:25,labelMeasure:true,labelRadius:33,radius:30},{fill:false,fillColor:"#CFC",lineWidth:2,labelFontSize:25,labelMeasure:true,labelRadius:33,radius:30},{fill:true,fillColor:"#CFC",lineWidth:2,labelFontSize:25,labelMeasure:true,labelRadius:33,radius:30},{fill:false,fillColor:"#CFC",lineWidth:2,labelFontSize:25,labelMeasure:true,labelRadius:33,radius:30} + ] + }); + */ + + // required + var ctx = obj.ctx; + var points = obj.points; + var center = obj.center; + + // optional + var lineColor = obj.lineColor || obj.color || false; + var lineWidth = obj.lineWidth || obj.thickness || false; + var angles = obj.angles || []; + + ctx.save(); + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + ctx.lineWidth = lineWidth; + ctx.strokeStye = lineColor; + + var angleLabelPos = []; + + for (var i = 0; i < points.length; i++) { + if (typeof angles[i] == 'object' && angles[i] !== null) { + angles[i].ctx = ctx; + angles[i].b = center; + angles[i].a = points[i]; + if (i == points.length - 1) { + angles[i].c = points[0]; + } else { + angles[i].c = points[i + 1]; + } + angles[i].drawLines = false; + if (typeof angles[i].lineWidth == 'undefined') + angles[i].lineWidth = ctx.lineWidth; + if (typeof angles[i].lineColor == 'undefined') + angles[i].lineColor = ctx.lineWidth; + if (typeof angles[i].labelColor == 'undefined') + angles[i].labelColor = ctx.strokeStyle; + angleLabelPos[i] = drawAngle(angles[i]); + } + } + + ctx.lineWidth = lineWidth; + ctx.strokeStye = lineColor; + ctx.beginPath(); + for (var p = 0; p < points.length; p++) { + ctx.moveTo(center[0], center[1]); + ctx.lineTo(points[p][0], points[p][1]); + } + ctx.stroke(); + ctx.restore(); + + return angleLabelPos; +} +function drawCylinder(obj) { + var ctx = obj.ctx; + var pos = obj.pos; // position of centre of base + var h = obj.h || obj.height; + var r = obj.r || obj.radius; + + var direction = obj.direction || obj.dir || 'vert'; // 'vert', 'horiz1' or 'horiz2' + var angle = obj.angle || Math.PI / 6; // for 'horiz2' only + + var lineWidth = obj.lineWidth || obj.thickness || false; + var lineColor = obj.lineColor || obj.color || false; + var lineDash = obj.lineDash || []; + + ctx.save(); + if (lineWidth !== false) + ctx.lineWidth = lineWidth; + if (lineColor !== false) + ctx.strokeStyle = lineColor; + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + + var transparent = boolean(obj.transparent, true); + var backLineWidth = obj.lineWidth; + var backLineColor = obj.backLineWidth || getShades(ctx.strokeStyle)[8]; + var backLineDash = obj.backLineDash || [7, 10]; + + var fill = boolean(obj.fill, false); + if (typeof obj.fillColors !== 'undefined') { + var fillColors = obj.fillColors; + } else if (typeof obj.fillColor !== 'undefined') { + var fillColors = [obj.fillColor, obj.fillColor]; + } else { + var fillColors = ['#FCC', '#CFC']; + } + + ctx.translate(pos[0], pos[1]); + + if (direction == 'vert') { + ctx.scale(1, 0.4); + + // draw back of bottom ellipse + if (transparent == true && fill == false) { + ctx.save(); + ctx.lineWidth = backLineWidth; + ctx.strokeStyle = backLineColor; + ctx.setLineDash(backLineDash); + ctx.beginPath(); + ctx.arc(0, 0, r, Math.PI, 2 * Math.PI); + ctx.stroke(); + ctx.restore(); + } + + // draw front + ctx.beginPath(); + ctx.moveTo(r, 0); + ctx.arc(0, 0, r, 0, Math.PI); + ctx.lineTo(-r, -h / 0.4); + ctx.arc(0, -h / 0.4, r, Math.PI, 0, true); + ctx.lineTo(r, 0); + if (fill == true) { + ctx.fillStyle = fillColors[0]; + ctx.fill(); + } + ctx.stroke(); + + // draw top + ctx.beginPath(); + ctx.arc(0, -h / 0.4, r, 0, 2 * Math.PI); + if (fill == true && typeof fillColors[1] !== 'undefined' && fillColors[1] !== 'none' && fillColors[1] !== false) { + ctx.fillStyle = fillColors[1]; + ctx.fill(); + } + ctx.stroke(); + } else if (direction == 'horiz1') { + ctx.scale(0.4, 1); + + // draw back of right ellipse + if (transparent == true && fill == false) { + ctx.save(); + ctx.lineWidth = backLineWidth; + ctx.strokeStyle = backLineColor; + ctx.setLineDash(backLineDash); + ctx.beginPath(); + ctx.arc(0, 0, r, 0.5 * Math.PI, 1.5 * Math.PI); + ctx.stroke(); + ctx.restore(); + } + + // draw front + ctx.beginPath(); + ctx.moveTo(0, r); + ctx.arc(0, 0, r, 0.5 * Math.PI, 1.5 * Math.PI, true); + ctx.lineTo(-h / 0.4, -r); + ctx.arc(-h / 0.4, 0, r, 1.5 * Math.PI, 0.5 * Math.PI); + ctx.lineTo(0, r); + if (fill == true) { + ctx.fillStyle = fillColors[0]; + ctx.fill(); + } + ctx.stroke(); + + // draw left ellipse + ctx.beginPath(); + ctx.arc(-h / 0.4, 0, r, 0, 2 * Math.PI); + if (fill == true && typeof fillColors[1] !== 'undefined' && fillColors[1] !== 'none' && fillColors[1] !== false) { + ctx.fillStyle = fillColors[1]; + ctx.fill(); + } + ctx.stroke(); + } else if (direction == 'horiz2') { + var pos2 = [h * Math.cos(angle), -h * Math.sin(angle)]; + var angle1 = Math.PI / 2 - angle; + var angle2 = -angle - 0.5 * Math.PI; + var a = [r * Math.cos(angle1), r * Math.sin(angle1)]; + var b = [r * Math.cos(angle2), r * Math.sin(angle2)]; + var c = [pos2[0] + r * Math.cos(angle1), pos2[1] + r * Math.sin(angle1)]; + var d = [pos2[0] + r * Math.cos(angle2), pos2[1] + r * Math.sin(angle2)]; + + // draw back of right circle + if (transparent == true && fill == false) { + ctx.save(); + ctx.lineWidth = backLineWidth; + ctx.strokeStyle = backLineColor; + ctx.setLineDash(backLineDash); + ctx.beginPath(); + ctx.arc(pos2[0], pos2[1], r, angle1, angle2); + ctx.stroke(); + ctx.restore(); + } + + // front + ctx.beginPath(); + ctx.moveTo(d[0], d[1]); + ctx.arc(pos2[0], pos2[1], r, angle2, angle1); + ctx.lineTo(a[0], a[1]); + ctx.arc(0, 0, r, angle1, angle2, true); + ctx.lineTo(d[0], d[1]); + if (fill == true) { + ctx.fillStyle = fillColors[0]; + ctx.fill(); + } + ctx.stroke(); + + // front circle + ctx.beginPath(); + ctx.arc(0, 0, r, 0, 2 * Math.PI); + if (fill == true && typeof fillColors[1] !== 'undefined' && fillColors[1] !== 'none' && fillColors[1] !== false) { + ctx.fillStyle = fillColors[1]; + ctx.fill(); + } + ctx.stroke(); + } + + ctx.restore(); +} +function drawCuboid(obj) { + var ctx = obj.ctx; + var x = obj.x || obj.pos[0]; + var y = obj.y || obj.pos[1]; + var z = obj.z || obj.pos[2]; + var xd = obj.xd || obj.dims[0]; + var yd = obj.yd || obj.dims[1]; + var zd = obj.zd || obj.dims[2]; + + var labels = obj.labels; + var unitBaseVectors = obj.unitBaseVectors || [[Math.sqrt(3) / 2, -1 / 2], [-Math.sqrt(3) / 2, -1 / 2], [0, -1]]; + var unitLength = obj.unitLength || 15; + var baseVector = obj.baseVector || obj.baseVectors || [[unitLength * unitBaseVectors[0][0], unitLength * unitBaseVectors[0][1]], [unitLength * unitBaseVectors[1][0], unitLength * unitBaseVectors[1][1]], [unitLength * unitBaseVectors[2][0], unitLength * unitBaseVectors[2][1]]]; + var origin = obj.origin || [0, 700]; + var mag = obj.mag || 15; + + var lineWidth = obj.lineWidth || obj.thickness || false; + var lineColor = obj.lineColor || obj.color || false; + var lineDash = obj.lineDash || []; + + var showUnitCubes = boolean(obj.showUnitCubes, false); + + ctx.save(); + if (lineWidth !== false) + ctx.lineWidth = lineWidth; + if (lineColor !== false) + ctx.strokeStyle = lineColor; + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + + var transparent = boolean(obj.transparent, true); + var backLineWidth = obj.lineWidth; + var backLineColor = obj.backLineWidth || getShades(ctx.strokeStyle)[8]; + var backLineDash = obj.backLineDash || [7, 10]; + + var fill = boolean(obj.fill, false); + if (typeof obj.fillColors !== 'undefined') { + var fillColors = obj.fillColors + } else if (typeof obj.fillColor !== 'undefined') { + var fillColors = [obj.fillColor, obj.fillColor, obj.fillColor]; + } else { + var fillColors = ['#FCC', '#CFC', '#CCF']; + } + + var cuboid = [[x, y, z], [x + xd, y, z], [x + xd, y + yd, z], [x, y + yd, z], [x, y, z + zd], [x + xd, y, z + zd], [x + xd, y + yd, z + zd], [x, y + yd, z + zd]]; + var cuboidPos = []; + for (var v = 0; v < cuboid.length; v++) { + cuboidPos[v] = []; + + cuboidPos[v][0] = origin[0]; + cuboidPos[v][0] += cuboid[v][0] * baseVector[0][0]; + cuboidPos[v][0] += cuboid[v][1] * baseVector[1][0]; + cuboidPos[v][0] += cuboid[v][2] * baseVector[2][0]; + + cuboidPos[v][1] = origin[1]; + cuboidPos[v][1] += cuboid[v][0] * baseVector[0][1]; + cuboidPos[v][1] += cuboid[v][1] * baseVector[1][1]; + cuboidPos[v][1] += cuboid[v][2] * baseVector[2][1]; + } + + if (fill == true) { + ctx.fillStyle = fillColors[0]; + if (baseVector[0][0] * baseVector[2][1] - baseVector[0][1] * baseVector[2][0] > 0) { + ctx.beginPath(); + ctx.moveTo(cuboidPos[3][0], cuboidPos[3][1]); + ctx.lineTo(cuboidPos[2][0], cuboidPos[2][1]); + ctx.lineTo(cuboidPos[6][0], cuboidPos[6][1]); + ctx.lineTo(cuboidPos[7][0], cuboidPos[7][1]); + ctx.lineTo(cuboidPos[3][0], cuboidPos[3][1]); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } else { + ctx.beginPath(); + ctx.moveTo(cuboidPos[0][0], cuboidPos[0][1]); + ctx.lineTo(cuboidPos[1][0], cuboidPos[1][1]); + ctx.lineTo(cuboidPos[5][0], cuboidPos[5][1]); + ctx.lineTo(cuboidPos[4][0], cuboidPos[4][1]); + ctx.lineTo(cuboidPos[0][0], cuboidPos[0][1]); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } + + ctx.fillStyle = fillColors[1]; + if (baseVector[0][0] * baseVector[1][1] - baseVector[0][1] * baseVector[1][0] > 0) { + ctx.beginPath(); + ctx.moveTo(cuboidPos[0][0], cuboidPos[0][1]); + ctx.lineTo(cuboidPos[1][0], cuboidPos[1][1]); + ctx.lineTo(cuboidPos[2][0], cuboidPos[2][1]); + ctx.lineTo(cuboidPos[3][0], cuboidPos[3][1]); + ctx.lineTo(cuboidPos[0][0], cuboidPos[0][1]); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } else { + ctx.beginPath(); + ctx.moveTo(cuboidPos[4][0], cuboidPos[4][1]); + ctx.lineTo(cuboidPos[5][0], cuboidPos[5][1]); + ctx.lineTo(cuboidPos[6][0], cuboidPos[6][1]); + ctx.lineTo(cuboidPos[7][0], cuboidPos[7][1]); + ctx.lineTo(cuboidPos[4][0], cuboidPos[4][1]); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } + + ctx.fillStyle = fillColors[2]; + if (baseVector[2][0] * baseVector[1][1] - baseVector[2][1] * baseVector[1][0] > 0) { + ctx.beginPath(); + ctx.moveTo(cuboidPos[2][0], cuboidPos[2][1]); + ctx.lineTo(cuboidPos[1][0], cuboidPos[1][1]); + ctx.lineTo(cuboidPos[5][0], cuboidPos[5][1]); + ctx.lineTo(cuboidPos[6][0], cuboidPos[6][1]); + ctx.lineTo(cuboidPos[2][0], cuboidPos[2][1]); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } else { + ctx.beginPath(); + ctx.moveTo(cuboidPos[0][0], cuboidPos[0][1]); + ctx.lineTo(cuboidPos[3][0], cuboidPos[3][1]); + ctx.lineTo(cuboidPos[7][0], cuboidPos[7][1]); + ctx.lineTo(cuboidPos[4][0], cuboidPos[4][1]); + ctx.lineTo(cuboidPos[0][0], cuboidPos[0][1]); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } + } + + if (fill == false && transparent == true) { + ctx.save(); + ctx.lineWidth = backLineWidth; + ctx.strokeStyle = backLineColor; + ctx.setLineDash(backLineDash); + ctx.beginPath(); + ctx.moveTo(cuboidPos[1][0], cuboidPos[1][1]); + ctx.lineTo(cuboidPos[2][0], cuboidPos[2][1]); + ctx.moveTo(cuboidPos[2][0], cuboidPos[2][1]); + ctx.lineTo(cuboidPos[3][0], cuboidPos[3][1]); + ctx.moveTo(cuboidPos[2][0], cuboidPos[2][1]); + ctx.lineTo(cuboidPos[6][0], cuboidPos[6][1]); + ctx.stroke(); + ctx.restore(); + } + + ctx.beginPath(); + //base + ctx.moveTo(cuboidPos[0][0], cuboidPos[0][1]); + ctx.lineTo(cuboidPos[1][0], cuboidPos[1][1]); + ctx.moveTo(cuboidPos[0][0], cuboidPos[0][1]); + ctx.lineTo(cuboidPos[3][0], cuboidPos[3][1]); + //sides + ctx.moveTo(cuboidPos[0][0], cuboidPos[0][1]); + ctx.lineTo(cuboidPos[4][0], cuboidPos[4][1]); + ctx.moveTo(cuboidPos[1][0], cuboidPos[1][1]); + ctx.lineTo(cuboidPos[5][0], cuboidPos[5][1]); + ctx.moveTo(cuboidPos[3][0], cuboidPos[3][1]); + ctx.lineTo(cuboidPos[7][0], cuboidPos[7][1]); + //top + ctx.moveTo(cuboidPos[4][0], cuboidPos[4][1]); + ctx.lineTo(cuboidPos[5][0], cuboidPos[5][1]); + ctx.lineTo(cuboidPos[6][0], cuboidPos[6][1]); + ctx.lineTo(cuboidPos[7][0], cuboidPos[7][1]); + ctx.lineTo(cuboidPos[4][0], cuboidPos[4][1]); + ctx.closePath(); + ctx.stroke(); + + /* + ctx.fillStyle = '#FFC'; + ctx.fillRect(0.5*(cuboidPos[0][0]+cuboidPos[1][0])+8,0.5*(cuboidPos[0][1]+cuboidPos[1][1])-5,19,40); + ctx.fillRect(0.5*(cuboidPos[0][0]+cuboidPos[3][0])-8,0.5*(cuboidPos[0][1]+cuboidPos[3][1])-5,-19,40); + ctx.fillRect(0.5*(cuboidPos[3][0]+cuboidPos[7][0])-8,0.5*(cuboidPos[3][1]+cuboidPos[7][1])-5,-79,40); + */ + + if (showUnitCubes == true) { + ctx.beginPath(); + + for (var i = 1; i < xd; i++) { + var pos1 = interpolateTwoPoints(cuboidPos[0], cuboidPos[1], i / xd); + var pos2 = interpolateTwoPoints(cuboidPos[4], cuboidPos[5], i / xd); + ctx.moveTo(pos1[0], pos1[1]); + ctx.lineTo(pos2[0], pos2[1]); + + var pos1 = interpolateTwoPoints(cuboidPos[4], cuboidPos[5], i / xd); + var pos2 = interpolateTwoPoints(cuboidPos[7], cuboidPos[6], i / xd); + ctx.moveTo(pos1[0], pos1[1]); + ctx.lineTo(pos2[0], pos2[1]); + } + + for (var i = 1; i < yd; i++) { + var pos1 = interpolateTwoPoints(cuboidPos[3], cuboidPos[0], i / yd); + var pos2 = interpolateTwoPoints(cuboidPos[7], cuboidPos[4], i / yd); + ctx.moveTo(pos1[0], pos1[1]); + ctx.lineTo(pos2[0], pos2[1]); + + var pos1 = interpolateTwoPoints(cuboidPos[6], cuboidPos[5], i / yd); + var pos2 = interpolateTwoPoints(cuboidPos[7], cuboidPos[4], i / yd); + ctx.moveTo(pos1[0], pos1[1]); + ctx.lineTo(pos2[0], pos2[1]); + } + + for (var i = 1; i < zd; i++) { + var pos1 = interpolateTwoPoints(cuboidPos[0], cuboidPos[4], i / zd); + var pos2 = interpolateTwoPoints(cuboidPos[1], cuboidPos[5], i / zd); + ctx.moveTo(pos1[0], pos1[1]); + ctx.lineTo(pos2[0], pos2[1]); + + var pos1 = interpolateTwoPoints(cuboidPos[0], cuboidPos[4], i / zd); + var pos2 = interpolateTwoPoints(cuboidPos[3], cuboidPos[7], i / zd); + ctx.moveTo(pos1[0], pos1[1]); + ctx.lineTo(pos2[0], pos2[1]); + } + + ctx.stroke(); + } + + if (typeof labels !== 'undefined') { + text({ + ctx: ctx, + left: 0.5 * (cuboidPos[0][0] + cuboidPos[1][0]) + 10, + top: 0.5 * (cuboidPos[0][1] + cuboidPos[1][1]) - 5, + width: 200, + textArray: labels[0] + }); + + text({ + ctx: ctx, + left: 0.5 * (cuboidPos[0][0] + cuboidPos[3][0]) - 210, + top: 0.5 * (cuboidPos[0][1] + cuboidPos[3][1]) - 5, + width: 200, + align: 'right', + textArray: labels[1] + }); + + text({ + ctx: ctx, + left: 0.5 * (cuboidPos[3][0] + cuboidPos[7][0]) - 210, + top: 0.5 * (cuboidPos[3][1] + cuboidPos[7][1]) - 5, + width: 200, + align: 'right', + textArray: labels[2] + }); + } + ctx.restore(); +} +function drawTreeDiagram(obj) { + var ctx = obj.ctx; + var left = obj.left; + var top = obj.top; + var width = obj.width; + var height = obj.height; + var branches = obj.branches || [2, 2]; + + /* + var endLabelFontSize = obj.endLabelFontSize || obj.fontSize || width/20; + var midLabelFontSize = obj.midLabelFontSize || obj.fontSize || width/20 + var labels = obj.labels || [[['win','lose']],[['win','lose],['win','lose']]]; + var probabilities = obj.probabilities || [[[[1,2],[1,2]]],[[[1,3],[2,3]],[[3,4],[1,4]]]]; + var branchColors = obj.branchColors || [[['#000','#000']],[[['#F00','#00F'],['#000','#000']]]]; + var endLabelColors = obj.endLabelColors || [[['#000','#000']],[[['#F00','#00F'],['#000','#000']]]]; + var midLabelColors = obj.midLabelColors || [[['#000','#000']],[[['#F00','#00F'],['#000','#000']]]]; + var branchLineWidths = obj.branchLineWidths || [[[3]],[[3,3],[3,3]]]; + */ + + var labels = obj.labels || [['<>win'], ['<>draw'], ['<>lose']] + var probabilities = obj.probabilities || [['<>', ['frac', ['1'], ['4']]], ['<>', ['frac', ['1'], ['2']]], ['<>', ['frac', ['1'], ['4']]]]; + + var hiddenCanvas = document.createElement('canvas'); + var ctx2 = hiddenCanvas.getContext('2d'); + var labelTightRects = []; + var maxLabelWidth = 0; + for (var i = 0; i < labels.length; i++) { + labelTightRects[i] = text({ + ctx: ctx2, + textArray: labels[i], + measureOnly: true + }).tightRect; + maxLabelWidth = Math.max(maxLabelWidth, labelTightRects[i][2] + 15); + } + + var branchWidth = (width - maxLabelWidth * branches.length) / branches.length; + + ctx.save(); + ctx.lineWidth = 3; + ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; + + for (var i = 0; i < branches.length; i++) { // each vertical set of branches + var forkPoints = 1; + if (i > 0) { + for (var j = 0; j < i; j++) { + forkPoints = forkPoints * branches[j]; + } + } + var startX = left + i * branchWidth + i * maxLabelWidth; + var finX = startX + branchWidth; + var h = height / forkPoints; // height of surrouding rect for this fork point; + for (var j = 0; j < forkPoints; j++) { + var t = top + j * h; + var startY = t + 0.5 * h; + for (var k = 0; k < branches[i]; k++) { // each branch emanating from the fork point + if (branches[i] == 2) { + var finY = t + ((2 * k + 1) / 4) * h; + } else if (branches[i] == 3) { + var finY = t + ((2 * k + 1) / 6) * h; + } + //draw branch + ctx.beginPath(); + ctx.moveTo(startX, startY); + ctx.lineTo(finX, finY); + ctx.stroke(); + //draw endLabel + text({ + ctx: ctx, + textArray: labels[k], + left: finX, + top: finY - labelTightRects[k][3] / 2, + width: maxLabelWidth, + height: labelTightRects[k][3], + align: 'center', + horizAlign: 'middle', + box: { + type: 'none', + borderColor: '#00F', + padding: 0.001 + } + }); + //draw midLabel + var prob = probabilities[k]; + var measure = text({ + ctx: ctx2, + textArray: prob, + measureOnly: true + }).tightRect; + var l2 = (startX + finX) / 2 - measure[2] / 2; + if (k < branches[i] - 1) { + var t2 = (startY + finY) / 2 - measure[3] * 1.1; + } else { + var t2 = (startY + finY) / 2; ; + } + text({ + ctx: ctx, + textArray: prob, + left: l2, + top: t2, + width: measure[2], + height: measure[3], + align: 'center', + horizAlign: 'middle', + box: { + type: 'none', + borderColor: '#F00', + padding: 0.001 + } + }); + } + } + } + ctx.restore(); +} +function drawSpinner(context, center, radius, sectorAngles, sectorColors, arrowAngle, showArrow) { + if (typeof arrowAngle == 'undefined') + arrowAngle = -Math.PI / 4; + var show = boolean(showArrow, true); + + //work out angles in radians; + var vals = []; + var total = 0; + for (var val = 0; val < sectorAngles.length; val++) { + total += sectorAngles[val]; + } + var angles = [0]; // cumulative angle array + for (var val = 0; val < sectorAngles.length; val++) { + angles.push(angles[val] + (sectorAngles[val] / total) * 2 * Math.PI); + } + + context.lineWidth = 4; + context.strokeStyle = '#000'; + for (var val = 0; val < sectorAngles.length; val++) { + // draw a sector + context.fillStyle = sectorColors[val]; + context.beginPath(); + context.moveTo(center[0], center[1]); + context.arc(center[0], center[1], radius, angles[val], angles[val + 1]); + context.lineTo(center[0], center[1]); + context.closePath(); + context.fill(); + context.stroke(); + } + + if (show == true) { + context.strokeStyle = "#000"; + context.lineWidth = 4; + context.beginPath(); + context.moveTo(center[0], center[1]); + context.lineTo(center[0] + 0.75 * radius * Math.cos(arrowAngle), center[1] + 0.75 * radius * Math.sin(arrowAngle)); + context.stroke(); + drawArrow({ + context: context, + startX: center[0], + startY: center[1], + finX: center[0] + 0.75 * radius * Math.cos(arrowAngle), + finY: center[1] + 0.75 * radius * Math.sin(arrowAngle), + arrowLength: 13, + color: "#000000", + lineWidth: 4, + arrowLineWidth: 4, + fillArrow: true + }); + context.fillStyle = "#000"; + context.beginPath(); + context.arc(center[0], center[1], 5, 0, 2 * Math.PI); + context.fill(); + } +} +function vennDiagram(obj) { + var ctx = obj.ctx; + var l = obj.left || obj.l || 0; + var t = obj.top || obj.t || 0; + var w = obj.width || obj.w || 400; + var h = obj.height || obj.h || w * 0.65; + var radius = obj.radius || obj.r || w * 0.25; + var centerA = obj.centerA || [l + w * 0.35, t + h / 2]; + var centerB = obj.centerB || [l + w * 0.65, t + h / 2]; + var lineWidth = obj.lineWidth || 4; + var lineDash = obj.lineDash || []; + var strokeStyle = obj.strokeStyle || '#000'; + var colorA = obj.colorA || strokeStyle; + var colorB = obj.colorB || strokeStyle; + var labelA = obj.labelA || ['<>A']; + var labelB = obj.labelB || ['<>B']; + var fillStyle = obj.fillStyle || '#FCF'; + var shade = obj.shade || [false, false, false, false]; + + ctx.save(); + if (typeof ctx.setLineDash == 'undefined') + ctx.setLineDash = function () {}; + ctx.setLineDash(lineDash); + ctx.strokeStyle = strokeStyle; + ctx.lineWidth = lineWidth; + ctx.strokeRect(l, t, w, h); + + ctx.beginPath(); + ctx.strokeStyle = colorA; + ctx.arc(centerA[0], centerA[1], radius, 0, 2 * Math.PI); + ctx.stroke(); + + ctx.beginPath(); + ctx.strokeStyle = colorB; + ctx.arc(centerB[0], centerB[1], radius, 0, 2 * Math.PI); + ctx.stroke(); + + var xy = [centerA[0] - (radius * 1.25) * Math.cos(Math.PI / 4), centerA[1] - (radius * 1.25) * Math.cos(Math.PI / 4)]; + text({ + ctx: ctx, + textArray: labelA, + left: xy[0] - 100, + width: 200, + top: xy[1] - 100, + height: 200, + textAlign: 'center', + vertAlign: 'middle', + padding: 0.1, /*box:{type:'tight',color:mainCanvasFillStyle,borderColor:mainCanvasFillStyle}*/ + }); + + var xy = [centerB[0] + (radius * 1.25) * Math.cos(Math.PI / 4), centerB[1] - (radius * 1.25) * Math.cos(Math.PI / 4)]; + text({ + ctx: ctx, + textArray: labelB, + left: xy[0] - 100, + width: 200, + top: xy[1] - 100, + height: 200, + textAlign: 'center', + vertAlign: 'middle', + padding: 0.1, /*box:{type:'tight',color:mainCanvasFillStyle,borderColor:mainCanvasFillStyle}*/ + }); + + ctx.restore(); + + return { + ctx: ctx, + left: l, + top: t, + width: w, + height: h, + radius: radius, + centerA: centerA, + centerB: centerB, + lineWidth: lineWidth, + lineDash: lineDash, + strokeStyle: strokeStyle, + labelA: labelA, + labelB: labelB, + fillStyle: fillStyle, + shade: shade + }; +} +function vennDiagram3(obj) { + var ctx = obj.ctx; + var l = obj.left || obj.l || 0; + var t = obj.top || obj.t || 0; + var w = obj.width || obj.w || 400; + var h = obj.height || obj.h || w; + var radius = obj.radius || obj.r || w * 0.27; + var centerA = obj.centerA || [l + w * 0.5 + 0.6 * radius * Math.cos(Math.PI * (1 / 2 + 2 / 3)), t + h * 0.47 + 0.6 * radius * Math.sin(Math.PI * (1 / 2 + 2 / 3))]; + var centerB = obj.centerB || [l + w * 0.5 + 0.6 * radius * Math.cos(Math.PI * (1 / 2 + 4 / 3)), t + h * 0.47 + 0.6 * radius * Math.sin(Math.PI * (1 / 2 + 4 / 3))]; + var centerC = obj.centerC || [l + w * 0.5 + 0.6 * radius * Math.cos(Math.PI * (1 / 2)), t + h * 0.47 + 0.6 * radius * Math.sin(Math.PI * (1 / 2))]; + var lineWidth = obj.lineWidth || 4; + var lineDash = obj.lineDash || []; + var strokeStyle = obj.strokeStyle || '#000'; + var colorA = obj.colorA || strokeStyle; + var colorB = obj.colorB || strokeStyle; + var colorC = obj.colorC || strokeStyle; + var labelA = obj.labelA || ['<>A']; + var labelB = obj.labelB || ['<>B']; + var labelC = obj.labelC || ['<>C']; + var fillStyle = obj.fillStyle || '#FCF'; + var shade = obj.shade || [false, false, false, false, false, false, false, false]; + + ctx.save(); + if (typeof ctx.setLineDash == 'undefined') + ctx.setLineDash = function () {}; + ctx.setLineDash(lineDash); + ctx.strokeStyle = strokeStyle; + ctx.lineWidth = lineWidth; + ctx.strokeRect(l, t, w, h); + + ctx.beginPath(); + ctx.strokeStyle = colorA; + ctx.arc(centerA[0], centerA[1], radius, 0, 2 * Math.PI); + ctx.stroke(); + + ctx.beginPath(); + ctx.strokeStyle = colorB; + ctx.arc(centerB[0], centerB[1], radius, 0, 2 * Math.PI); + ctx.stroke(); + + ctx.beginPath(); + ctx.strokeStyle = colorC; + ctx.arc(centerC[0], centerC[1], radius, 0, 2 * Math.PI); + ctx.stroke(); + + var xy = [centerA[0] - (radius * 1.25) * Math.cos(Math.PI / 4), centerA[1] - (radius * 1.25) * Math.cos(Math.PI / 4)]; + text({ + ctx: ctx, + textArray: labelA, + left: xy[0] - 100, + width: 200, + top: xy[1] - 100, + height: 200, + textAlign: 'center', + vertAlign: 'middle', + padding: 0.1, /*box:{type:'tight',color:mainCanvasFillStyle,borderColor:mainCanvasFillStyle}*/ + }); + + var xy = [centerB[0] + (radius * 1.25) * Math.cos(Math.PI / 4), centerB[1] - (radius * 1.25) * Math.cos(Math.PI / 4)]; + text({ + ctx: ctx, + textArray: labelB, + left: xy[0] - 100, + width: 200, + top: xy[1] - 100, + height: 200, + textAlign: 'center', + vertAlign: 'middle', + padding: 0.1, /*box:{type:'tight',color:mainCanvasFillStyle,borderColor:mainCanvasFillStyle}*/ + }); + + var xy = [centerC[0] + (radius * 1.25) * Math.cos(Math.PI / 4), centerC[1] + (radius * 1.25) * Math.cos(Math.PI / 4)]; + text({ + ctx: ctx, + textArray: labelC, + left: xy[0] - 100, + width: 200, + top: xy[1] - 100, + height: 200, + textAlign: 'center', + vertAlign: 'middle', + padding: 0.1, /*box:{type:'tight',color:mainCanvasFillStyle,borderColor:mainCanvasFillStyle}*/ + }); + + ctx.restore(); + + return { + ctx: ctx, + left: l, + top: t, + width: w, + height: h, + radius: radius, + centerA: centerA, + centerB: centerB, + centerC: centerC, + lineWidth: lineWidth, + lineDash: lineDash, + strokeStyle: strokeStyle, + labelA: labelA, + labelB: labelB, + labelC: labelC, + fillStyle: fillStyle, + shade: shade + }; +} +function drawIsometricDotty(object) { + // required + var ctx = object.ctx || object.context; + + // options + var left = object.left || object.l || 0; + var top = object.top || object.t || 100; + var width = object.width || object.w || 1200; + var height = object.height || object.h || 700; + var spacingFactor = object.spacingFactor || 15; + var color = object.color || '#AAA'; + var origin = object.origin || [left + 0.5 * width, top + 0.5 * height]; + var radius = object.radius || 5; + + if (object.direction == 1) { + var baseVector = [ + [-5 * (1 / 2),5 * (Math.sqrt(3) / 2)], + [-5 * (1 / 2),-5 * (Math.sqrt(3) / 2)], + [5, 0] + ]; + } else { + var baseVector = [ + [5 * (Math.sqrt(3) / 2), -5 * (1 / 2)], + [-5 * (Math.sqrt(3) / 2), -5 * (1 / 2)], + [0, -5] + ]; + } + var points = []; + var x, + y; + + for (var i = 0; i < 2; i++) { + x = origin[0]; + y = origin[1]; + while (x >= left && y >= top && x <= left + width && y <= top + height) { + points.push([x, y]); + x += spacingFactor * baseVector[i][0]; + y += spacingFactor * baseVector[i][1]; + } + } + + for (var i = 0, lim = points.length; i < lim; i++) { + x = points[i][0]; + y = points[i][1]; + while (x >= left && y >= top && x <= left + width && y <= top + height) { + points.push([x, y]); + x += spacingFactor * baseVector[2][0]; + y += spacingFactor * baseVector[2][1]; + } + x = points[i][0]; + y = points[i][1]; + while (x >= left && y >= top && x <= left + width && y <= top + height) { + points.push([x, y]); + x -= spacingFactor * baseVector[2][0]; + y -= spacingFactor * baseVector[2][1]; + } + } + + // remove out-of-range and duplicate points + for (var i = points.length - 1; i >= 0; i--) { + for (var j = i - 1; j >= 0; j--) { + if (arraysEqual(points[i], points[j]) == true) { + points.splice(i, 1); + break; + } + } + } + + ctx.save(); + ctx.fillStyle = color; + for (var i = 0; i < points.length; i++) { + ctx.beginPath(); + ctx.arc(points[i][0], points[i][1], radius, 0, 2 * Math.PI); + ctx.fill(); + } + ctx.restore(); + return points; +} + +function drawSquareDotty(object) { + // required + var ctx = object.ctx || object.context; + + // options + var left = object.left || object.l || 0; + var top = object.top || object.t || 100; + var width = object.width || object.w || 1200; + var height = object.height || object.h || 700; + var spacingFactor = object.spacingFactor || 80; + var color = object.color || '#AAA'; + var origin = object.origin || [left + 0.5 * width, top + 0.5 * height]; + var radius = object.radius || 5; + + var points = []; + var x, + y; + + x = origin[0]; + y = origin[1]; + while (x >= left && y >= top && x <= left + width && y <= top + height) { + points.push([x, y]); + x += spacingFactor; + } + + x = origin[0] - spacingFactor; + while (x >= left && y >= top && x <= left + width && y <= top + height) { + points.push([x, y]); + x -= spacingFactor; + } + + for (var i = 0, lim = points.length; i < lim; i++) { + x = points[i][0]; + y = points[i][1] + spacingFactor; + while (x >= left && y >= top && x <= left + width && y <= top + height) { + points.push([x, y]); + y += spacingFactor; + } + y = points[i][1] - spacingFactor; + while (x >= left && y >= top && x <= left + width && y <= top + height) { + points.push([x, y]); + y -= spacingFactor; + } + } + + // remove duplicate points + for (var i = points.length - 1; i >= 0; i--) { + for (var j = i - 1; j >= 0; j--) { + if (arraysEqual(points[i], points[j]) == true) { + points.splice(i, 1); + break; + } + } + } + + ctx.save(); + ctx.fillStyle = color; + for (var i = 0; i < points.length; i++) { + ctx.beginPath(); + ctx.arc(points[i][0], points[i][1], radius, 0, 2 * Math.PI); + ctx.fill(); + } + ctx.restore(); + return points; +} +function drawNumberLine(object) { + var context = object.context; + var left = object.left; + var top = object.top; + var width = object.width; + var height = object.height; + var min = object.min; + var max = object.max; + var majorStep = object.majorStep; + var minorStep = object.minorStep; + + var minorYPos = [0.5, 0.75]; + if (typeof object.minorYPos == 'object') + minorYPos = object.minorYPos; + var majorYPos = [0.25, 0.75]; + if (typeof object.majorYPos == 'object') + majorYPos = object.majorYPos; + var minorWidth = object.minorWidth || 1.2; + var majorWidth = object.majorWidth || 2; + var minorColor = object.minorColor || '#CCC'; + var majorColor = object.majorColor || '#000'; + var lineWidth = object.lineWidth || 4; + var lineColor = object.lineColor || '#000'; + + var autoLabel = boolean(object.autoLabel, true); + var labels = object.labels; + var font = object.font || 'Arial'; + var fontSize = object.fontSize || 24; + var textColor = object.textColor || majorColor; + + var minorSpacing = (width * minorStep) / (max - min); + var majorSpacing = (width * majorStep) / (max - min); + + var x0 = left - (min * width) / (max - min); + + // draw minor markings + context.strokeStyle = minorColor; + context.lineWidth = minorWidth; + context.beginPath(); + var startValue = Math.abs(min % minorStep); + var axisPos = left; + while (axisPos - (left + width) <= 0.1) { + context.moveTo(axisPos, top + minorYPos[0] * height); + context.lineTo(axisPos, top + minorYPos[1] * height); + axisPos += minorSpacing; + } + context.stroke(); + // draw major markings + context.strokeStyle = majorColor; + context.lineWidth = majorWidth; + var startValue = Math.abs(min % majorStep); + var axisPos = left + startValue * majorSpacing; + var num = min + startValue; + var count = 0; + while (axisPos - (left + width) <= 0.1) { + context.moveTo(axisPos, top + majorYPos[0] * height); + context.lineTo(axisPos, top + majorYPos[1] * height); + if (autoLabel == true) { + text({ + context: context, + left: axisPos - 100, + width: 200, + top: top + 0.75 * height, + textArray: ['<><><><>' + num] + }) + } else if (typeof labels == 'object' && typeof labels[count] !== 'undefined') { + text({ + context: context, + left: axisPos - 100, + width: 200, + top: top + 0.75 * height, + textArray: ['<><><><>' + labels[count]] + }) + } + count++; + num += majorStep; + axisPos += majorSpacing; + } + context.stroke(); + context.strokeStyle = lineColor; + context.lineWidth = lineWidth; + context.moveTo(left, top + 0.5 * height); + context.lineTo(left + width, top + 0.5 * height); + context.stroke(); +} +function drawNumberLine2(obj) { + var ctx = obj.ctx || obj.context; + ctx.save(); + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + var vertical = boolean(obj.vertical, false); + + if (un(obj.rect)) + obj.rect = []; + var left = obj.left || obj.rect[0]; + var top = obj.top || obj.rect[1]; + var width = obj.width || obj.rect[2]; + var height = obj.height || obj.rect[3]; + if (vertical == true) { + if (width > height) { + var temp = width; + width = height; + height = temp; + } + } + var min = Math.min(obj.min, obj.max); + var max = Math.max(obj.min, obj.max); + if (min >= max) { + console.log('Check numberline min & max.'); + return; + } + var minorStep = obj.minorStep; + var majorStep = obj.majorStep; + + var scaleOffset = obj.scaleOffset || 15; + + var minorYPos = [0.5, 0.75]; + if (typeof obj.minorYPos == 'object') + minorYPos = obj.minorYPos; + var majorYPos = [0.25, 0.75]; + if (typeof obj.majorYPos == 'object') + majorYPos = obj.majorYPos; + var minorWidth = obj.minorWidth || 1.2; + var majorWidth = obj.majorWidth || 2; + var minorColor = obj.minorColor || '#CCC'; + var majorColor = obj.majorColor || '#000'; + var lineWidth = obj.lineWidth || 4; + var lineColor = obj.lineColor || '#000'; + var backColor = obj.backColor || mainCanvasFillStyle; + + var autoLabel = boolean(obj.autoLabel, true); + var labels = obj.labels; + var font = obj.font || 'Arial'; + var fontSize = obj.fontSize || 24; + var textColor = obj.textColor || majorColor; + + if (vertical == true) { + drawNumberlineVertical(); + } else { + drawNumberlineHorizontal(); + } + + ctx.restore(); + + function drawNumberlineHorizontal() { + if (typeof obj.arrows == 'number') { + left += obj.arrows; + width -= 2 * obj.arrows; + } + var minorSpacing = (width * minorStep) / (max - min); + var majorSpacing = (width * majorStep) / (max - min); + var x0 = left - (min * width) / (max - min); + + if (boolean(obj.showMinorPos, true) == true) { + ctx.strokeStyle = minorColor; + ctx.lineWidth = minorWidth; + ctx.beginPath(); + var xAxisPoint = x0 + minorSpacing; + while (Math.round(xAxisPoint) <= Math.round(left + width)) { + if (Math.round(xAxisPoint) >= Math.round(left)) { + ctx.moveTo(xAxisPoint, top + minorYPos[0] * height); + ctx.lineTo(xAxisPoint, top + minorYPos[1] * height); + //console.log(xAxisPoint); + } + xAxisPoint += minorSpacing; + } + var xAxisPoint = x0 - minorSpacing; + while (Math.round(xAxisPoint) >= Math.round(left)) { + if (Math.round(xAxisPoint) <= Math.round(left + width)) { + ctx.moveTo(xAxisPoint, top + minorYPos[0] * height); + ctx.lineTo(xAxisPoint, top + minorYPos[1] * height); + //console.log(xAxisPoint); + } + xAxisPoint -= minorSpacing; + } + ctx.closePath(); + ctx.stroke(); + } + + // draw major lines + ctx.strokeStyle = majorColor; + ctx.lineWidth = majorWidth; + ctx.beginPath(); + var xAxisPoint = x0; + while (Math.round(xAxisPoint) <= Math.round(left + width)) { + if (Math.round(xAxisPoint) >= Math.round(left)) { + ctx.moveTo(xAxisPoint, top + majorYPos[0] * height); + ctx.lineTo(xAxisPoint, top + majorYPos[1] * height); + } + xAxisPoint += majorSpacing; + } + var xAxisPoint = x0 - majorSpacing; + while (Math.round(xAxisPoint) >= Math.round(left)) { + if (Math.round(xAxisPoint) <= Math.round(left + width)) { + ctx.moveTo(xAxisPoint, top + majorYPos[0] * height); + ctx.lineTo(xAxisPoint, top + majorYPos[1] * height); + } + xAxisPoint -= majorSpacing; + } + ctx.closePath(); + ctx.stroke(); + + if (boolean(obj.showScales, true) == true) { + // draw axes numbers + ctx.font = fontSize + 'px Arial'; + ctx.textAlign = "center"; + ctx.textBaseline = "top"; + + var xAxisPoint = x0; + var major = 0; + var placeValue = Math.pow(10, Math.floor(Math.log(majorStep) / Math.log(10))); + while (roundToNearest(xAxisPoint, 0.001) <= roundToNearest(left + width, 0.001)) { + if (xAxisPoint >= left) { + var value = roundToNearest(major * majorStep, placeValue); + var axisValue = [String(value)]; + var textWidth = ctx.measureText(String(axisValue)).width; + ctx.fillStyle = backColor; + ctx.fillRect(xAxisPoint - textWidth / 2, top + 0.5 * height + scaleOffset - 1, textWidth, fontSize * 1.1); + var labelText = drawMathsText(ctx, axisValue, fontSize, xAxisPoint, top + 0.5 * height + scaleOffset + 0.5 * fontSize, true, [], 'center', 'middle', majorColor); + } + major += 1; + xAxisPoint += majorSpacing; + } + + var xAxisPoint = x0 - majorSpacing; + var major = -1; + while (roundToNearest(xAxisPoint, 0.001) >= roundToNearest(left, 0.001)) { + if (xAxisPoint < left + width) { + var value = roundToNearest(major * majorStep, placeValue); + var axisValue = [String(value)]; + var textWidth = ctx.measureText(String(axisValue)).width; + ctx.fillStyle = backColor; + ctx.fillRect(xAxisPoint - textWidth / 2, top + 0.5 * height + scaleOffset - 1, textWidth, fontSize * 1.1); + var labelText = drawMathsText(ctx, axisValue, fontSize, xAxisPoint, top + 0.5 * height + scaleOffset + 0.5 * fontSize, true, [], 'center', 'middle', majorColor); + } + major -= 1; + xAxisPoint -= majorSpacing; + } + } + + if (typeof obj.arrows == 'number') { + drawArrow({ + ctx: ctx, + startX: left - obj.arrows, + startY: top + 0.5 * height, + finX: left + width + obj.arrows, + finY: top + 0.5 * height, + doubleEnded: true, + color: lineColor, + lineWidth: lineWidth, + fillArrow: true, + arrowLength: 12 + }); + } else { + // draw line + ctx.beginPath(); + ctx.strokeStyle = lineColor; + ctx.lineWidth = lineWidth; + ctx.moveTo(left, top + 0.5 * height); + ctx.lineTo(left + width, top + 0.5 * height); + ctx.closePath(); + ctx.stroke(); + } + + } + function drawNumberlineVertical() { + if (typeof obj.arrows == 'number') { + top += obj.arrows; + height -= 2 * obj.arrows; + } + var minorSpacing = (height * minorStep) / (max - min); + var majorSpacing = (height * majorStep) / (max - min); + var y0 = top - (min * height) / (max - min); + + if (boolean(obj.showMinorPos, true) == true) { + ctx.strokeStyle = minorColor; + ctx.lineWidth = minorWidth; + ctx.beginPath(); + var yAxisPoint = y0 + minorSpacing; + while (Math.round(yAxisPoint) <= Math.round(top + height)) { + if (Math.round(yAxisPoint) >= Math.round(top)) { + ctx.moveTo(left + minorYPos[0] * width, yAxisPoint); + ctx.lineTo(left + minorYPos[1] * width, yAxisPoint); + } + yAxisPoint += minorSpacing; + } + var yAxisPoint = y0 - minorSpacing; + while (Math.round(yAxisPoint) >= Math.round(top)) { + if (Math.round(yAxisPoint) <= Math.round(top + height)) { + ctx.moveTo(left + minorYPos[0] * width, yAxisPoint); + ctx.lineTo(left + minorYPos[1] * width, yAxisPoint); + } + yAxisPoint -= minorSpacing; + } + ctx.closePath(); + ctx.stroke(); + } + + // draw major lines + ctx.strokeStyle = majorColor; + ctx.lineWidth = majorWidth; + ctx.beginPath(); + var yAxisPoint = y0; + while (Math.round(yAxisPoint) <= Math.round(top + height)) { + if (Math.round(yAxisPoint) >= Math.round(top)) { + ctx.moveTo(left + majorYPos[0] * width, yAxisPoint); + ctx.lineTo(left + majorYPos[1] * width, yAxisPoint); + } + yAxisPoint += majorSpacing; + } + var yAxisPoint = y0 - majorSpacing; + while (Math.round(yAxisPoint) >= Math.round(top)) { + if (Math.round(yAxisPoint) <= Math.round(top + height)) { + ctx.moveTo(left + majorYPos[0] * width, yAxisPoint); + ctx.lineTo(left + majorYPos[1] * width, yAxisPoint); + } + yAxisPoint -= majorSpacing; + } + ctx.closePath(); + ctx.stroke(); + + if (boolean(obj.showScales, true) == true) { + ctx.font = fontSize + 'px Arial'; + ctx.textBaseline = "middle"; + ctx.textAlign = "right"; + + // positive y numbers + var yAxisPoint = y0; + var major = 0; + while (roundToNearest(yAxisPoint, 0.001) >= roundToNearest(top, 0.001)) { + if (yAxisPoint <= top + height) { + var axisValue = Number(roundToNearest(major * majorStep, 0.00001)); + var textWidth = ctx.measureText(String(axisValue)).width + var labelText = drawMathsText(ctx, String(axisValue), fontSize, left + majorYPos[0] * width - 10, yAxisPoint - 2, true, [], 'right', 'middle', '#000'); + } + major += 1; + yAxisPoint -= majorSpacing; + } + + // negative y numbers + var yAxisPoint = y0 + majorSpacing; + var major = -1; + while (roundToNearest(yAxisPoint, 0.001) <= roundToNearest(top + height, 0.001)) { + if (yAxisPoint >= top) { + var axisValue = Number(roundToNearest(major * majorStep, 0.00001)); + var textWidth = ctx.measureText(String(axisValue)).width + var labelText = drawMathsText(ctx, String(axisValue), fontSize, left + majorYPos[0] * width - 10, yAxisPoint - 2, true, [], 'right', 'middle', '#000'); + } + major -= 1; + yAxisPoint += majorSpacing; + + } + } + + if (typeof obj.arrows == 'number') { + drawArrow({ + ctx: ctx, + startX: left + 0.5 * width, + startY: top - obj.arrows, + finX: left + 0.5 * width, + finY: top + height + obj.arrows, + doubleEnded: true, + color: lineColor, + lineWidth: lineWidth, + fillArrow: true, + arrowLength: 12 + }); + } else { + // draw line + ctx.beginPath(); + ctx.strokeStyle = lineColor; + ctx.lineWidth = lineWidth; + ctx.moveTo(left + 0.5 * width, top); + ctx.lineTo(left + 0.5 * width, top + height); + ctx.closePath(); + ctx.stroke(); + } + + } + +} + +function isPointBetweenAngles(center, p1, p2, p3, cw) { + var a1 = posToAngle(p1[0], p1[1], center[0], center[1]); + var a2 = posToAngle(p2[0], p2[1], center[0], center[1]); + var a3 = posToAngle(p3[0], p3[1], center[0], center[1]); + if (anglesInOrder(a1, a2, a3), cw) { + return false; + } else { + return true; + } +} +function anglesInOrder(a1, a2, a3, cw) { // test if three angles are in order + while (a1 < 0) + a1 += 2 * Math.PI; + while (a2 < 0) + a2 += 2 * Math.PI; + while (a3 < 0) + a3 += 2 * Math.PI; + while (a1 > 2 * Math.PI) + a1 -= 2 * Math.PI; + while (a2 > 2 * Math.PI) + a2 -= 2 * Math.PI; + while (a3 > 2 * Math.PI) + a3 -= 2 * Math.PI; + if (boolean(cw, true) == true) { + if ((a1 <= a2 && a2 <= a3) || //123 + (a2 <= a3 && a3 <= a1) || //231 + (a3 <= a1 && a1 <= a2)) { //312 + return true; + } else { + return false; + } + } else { + if ((a3 <= a2 && a2 <= a1) || //321 + (a2 <= a1 && a1 <= a3) || //213 + (a1 <= a3 && a3 <= a2)) { //132 + return true; + } else { + return false; + } + } +} + +function roundedRect(context, left, top, width, height, roundingSize, lineThickness, lineColor, fillColor, dash) { + context.save(); + if (lineThickness) { + context.lineWidth = lineThickness + }; + if (lineColor) { + context.strokeStyle = lineColor + }; + if (fillColor) { + context.fillStyle = fillColor + }; + if (typeof dash == 'undefined') + dash = []; + if (typeof dash == 'object') { + if (!context.setLineDash) { + context.setLineDash = function () {} + } + context.setLineDash(dash); + } + context.beginPath(); + context.moveTo(left + roundingSize, top); + context.lineTo(left + width - roundingSize, top); + context.arc(left + width - roundingSize, top + roundingSize, roundingSize, 1.5 * Math.PI, 2 * Math.PI); + context.lineTo(left + width, top + height - roundingSize); + context.arc(left + width - roundingSize, top + height - roundingSize, roundingSize, 0, 0.5 * Math.PI); + context.lineTo(left + roundingSize, top + height); + context.arc(left + roundingSize, top + height - roundingSize, roundingSize, 0.5 * Math.PI, Math.PI); + context.lineTo(left, top + roundingSize); + context.arc(left + roundingSize, top + roundingSize, roundingSize, Math.PI, 1.5 * Math.PI); + context.closePath(); + if (typeof lineColor == 'string' && lineColor !== 'none') + context.stroke(); + if (typeof fillColor == 'string' && fillColor !== 'none') + context.fill(); + context.restore(); +} +function roundedRect2(context, left, top, width, height, roundingSize, lineThickness, lineColor, fillColor, dash) { + context.save(); + if (lineThickness) + context.lineWidth = lineThickness; + if (lineColor) + context.strokeStyle = lineColor; + if (fillColor) + context.fillStyle = fillColor; + if (typeof dash == 'undefined') + dash = []; + if (typeof dash == 'object') { + if (!context.setLineDash) { + context.setLineDash = function () {} + } + context.setLineDash(dash); + } + context.beginPath(); + context.moveTo(left + roundingSize, top); + context.lineTo(left + width - roundingSize, top); + context.arc(left + width - roundingSize, top + roundingSize, roundingSize, 1.5 * Math.PI, 2 * Math.PI); + context.lineTo(left + width, top + height - roundingSize); + context.arc(left + width - roundingSize, top + height - roundingSize, roundingSize, 0, 0.5 * Math.PI); + context.lineTo(left + roundingSize, top + height); + context.arc(left + roundingSize, top + height - roundingSize, roundingSize, 0.5 * Math.PI, Math.PI); + context.lineTo(left, top + roundingSize); + context.arc(left + roundingSize, top + roundingSize, roundingSize, Math.PI, 1.5 * Math.PI); + context.closePath(); + if (typeof fillColor == 'string' && fillColor !== 'none') + context.fill(); + if (typeof lineColor == 'string' && lineColor !== 'none') + context.stroke(); + context.restore(); +} +function roundedRect3(context, left, top, width, height, roundingSize, lineThickness, lineColor, fillColor, dash) { + context.save(); + if (lineThickness) { + context.lineWidth = lineThickness + }; + if (lineColor) { + context.strokeStyle = lineColor + }; + if (fillColor) { + context.fillStyle = fillColor + }; + if (typeof dash == 'undefined') + dash = []; + if (typeof dash == 'object') { + if (!context.setLineDash) { + context.setLineDash = function () {} + } + context.setLineDash(dash); + } + context.beginPath(); + context.moveTo(left + roundingSize[0], top); + context.lineTo(left + width - roundingSize[1], top); + context.arc(left + width - roundingSize[1], top + roundingSize[1], roundingSize[1], 1.5 * Math.PI, 2 * Math.PI); + context.lineTo(left + width, top + height - roundingSize[2]); + context.arc(left + width - roundingSize[2], top + height - roundingSize[2], roundingSize[2], 0, 0.5 * Math.PI); + context.lineTo(left + roundingSize[3], top + height); + context.arc(left + roundingSize[3], top + height - roundingSize[3], roundingSize[3], 0.5 * Math.PI, Math.PI); + context.lineTo(left, top + roundingSize[0]); + context.arc(left + roundingSize[0], top + roundingSize[0], roundingSize[0], Math.PI, 1.5 * Math.PI); + context.closePath(); + if (typeof fillColor == 'string' && fillColor !== 'none') { + context.fill() + }; + context.stroke(); + context.restore(); +} +function drawPath(object) { + var ctx = object.ctx; + var path = object.path; + ctx.save(); + if (typeof object.lineColor !== 'undefined') { + ctx.strokeStyle = object.lineColor + }; + if (typeof object.lineWidth !== 'undefined') { + ctx.lineWidth = object.lineWidth + }; + ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; + ctx.beginPath(); + ctx.moveTo(path[0][0], path[0][1]); + for (var i = 1; i < path.length; i++) { + ctx.lineTo(path[i][0], path[i][1]); + } + if (typeof object.fillColor !== 'undefined' || object.closed == true) { + ctx.closePath() + }; + if (typeof object.fillColor !== 'undefined') { + ctx.fillStyle = object.fillColor; + ctx.fill(); + } + ctx.stroke(); + ctx.restore(); +} +function dashedLine(object) { + var context = object.context; + var startX = object.startX; + var startY = object.startY; + var finX = object.finX; + var finY = object.finY; + + if (startX > finX) { + var x1 = startX; + var y1 = startY; + var x2 = finX; + var y2 = finY; + startX = x2; + startY = y2; + finX = x1; + finY = y1; + } + + var dashSize = object.dashSize || 20; + var gapSize = object.gapSize || 10; + var color = object.color || '#000'; + var lineWidth = object.lineWidth || 2; + + var totalLength = Math.sqrt(Math.pow((finX - startX), 2) + Math.pow((finY - startY), 2)); + var numOfIncs = totalLength / (dashSize + gapSize); + + var dx = (finX - startX) / numOfIncs; + var dxDash = dx * dashSize / (dashSize + gapSize); + var dxGap = dx * gapSize / (dashSize + gapSize); + + var dy = (finY - startY) / numOfIncs; + var dyDash = dy * dashSize / (dashSize + gapSize); + var dyGap = dy * gapSize / (dashSize + gapSize); + + var xPos = startX; + var yPos = startY; + var incCount = 0; + + context.save(); + context.strokeStyle = color; + context.lineWidth = lineWidth; + context.beginPath(); + do { + context.moveTo(xPos, yPos); + xPos += dxDash; + yPos += dyDash; + context.lineTo(xPos, yPos); + xPos += dxGap; + yPos += dyGap; + incCount++; + } while (incCount <= numOfIncs - 1) + if ((startX < finX && xPos < finX) || (startX > finX && xPos > finX) || (startY < finY && yPos < finY) || (startY > finY && yPos > finY)) { + context.moveTo(xPos, yPos); + context.lineTo(finX, finY); + } + context.closePath(); + context.stroke(); + context.restore(); +} +function drawParallelArrow(object) { + // required + var context = object.context || object.ctx; + var startX = object.startX; + var startY = object.startY; + var finX = object.finX; + var finY = object.finY; + + // optional + var numOfArrows = object.numOfArrows || 1; + var arrowLength = object.arrowLength || 25; + var arrowAngle = object.arrowAngle || 0.5; + var color = object.color || '#000'; + var lineWidth = object.lineWidth || 2; + var fillArrow = boolean(object.fillArrow, false); + + context.save(); + if (numOfArrows == 1) { + + var fracPos = 0.5 + 0.5 * arrowLength * Math.cos(arrowAngle) / Math.sqrt(Math.pow((finX - startX), 2) + Math.pow((finY - startY), 2)) + drawArrow({ + context: context, + startX: startX, + startY: startY, + finX: startX + fracPos * (finX - startX), + finY: startY + fracPos * (finY - startY), + arrowLength: arrowLength, + angleBetweenLinesRads: arrowAngle, + color: color, + lineWidth: 0.1, + arrowLineWidth: lineWidth + }); + + } else if (numOfArrows == 2) { + + var fracPos1 = 0.5 + 0.75 * arrowLength * (Math.cos(arrowAngle) + 0.5) / Math.sqrt(Math.pow((finX - startX), 2) + Math.pow((finY - startY), 2)) + var fracPos2 = fracPos1 - 1 * arrowLength / Math.sqrt(Math.pow((finX - startX), 2) + Math.pow((finY - startY), 2)) + + drawArrow({ + context: context, + startX: startX, + startY: startY, + finX: startX + fracPos1 * (finX - startX), + finY: startY + fracPos1 * (finY - startY), + arrowLength: arrowLength, + angleBetweenLinesRads: arrowAngle, + color: color, + lineWidth: 0.1, + showLine: false, + arrowLineWidth: lineWidth + }); + drawArrow({ + context: context, + startX: startX, + startY: startY, + finX: startX + fracPos2 * (finX - startX), + finY: startY + fracPos2 * (finY - startY), + arrowLength: arrowLength, + angleBetweenLinesRads: arrowAngle, + color: color, + lineWidth: 0.1, + showLine: false, + arrowLineWidth: lineWidth + }); + } + context.restore(); +} +function labelLine(posA, posB, obj) { + var ctx = obj.ctx; + if (typeof hiddenCanvas == 'undefined') + hiddenCanvas = newcanvas({ + vis: false + }); + obj.ctx = hiddenCanvas.ctx; + obj.measureOnly = true; + var textDims = text(obj); + var w = textDims.tightRect[2]; + var h = textDims.tightRect[3]; + if (typeof obj.box == 'undefined' || obj.box.type !== 'tight') + w += 20; + var x1 = posA[0], + y1 = posA[1], + x2 = posB[0], + y2 = posB[1], + x4, + y4; + var x3 = (x1 + x2) / 2; + var y3 = (y1 + y2) / 2; + if (y1 == y2) { + x4 = x3 - w / 2; + if (x1 < x2) { + y4 = y3; + } else { + y4 = y3 - h; + } + } else if (x1 == x2) { + y4 = y3 - h / 2; + if (y1 < y2) { + x4 = x3 - w; + } else { + x4 = x3; + } + } else if (x1 < x2) { + if (y1 < y2) { + x4 = x3 - w; + y4 = y3; + } else { + x4 = x3; + y4 = y3; + } + } else if (x3 > x2) { + if (y3 < y2) { + x4 = x3 - w; + y4 = y3 - h; + } else { + + x4 = x3; + y4 = y3 - h; + } + } + obj.ctx = ctx; + obj.measureOnly = false; + obj.left = x4 - 10; + obj.top = y4 - 10; + obj.width = w + 20; + obj.height = h + 20; + obj.align = 'center'; + obj.vertAlign = 'middle'; + text(obj); +} +function drawDash(context, x1, y1, x2, y2, length) { + if (typeof length !== 'number') + length = 10; + var grad = - (x2 - x1) / (y2 - y1); + var midX = (x1 + x2) / 2; + var midY = (y1 + y2) / 2; + var end1X = midX - length * Math.cos(Math.atan(grad)); + var end1Y = midY - length * Math.sin(Math.atan(grad)); + var end2X = midX + length * Math.cos(Math.atan(grad)); + var end2Y = midY + length * Math.sin(Math.atan(grad)); + + context.beginPath(); + context.moveTo(end1X, end1Y); + context.lineTo(end2X, end2Y); + context.stroke(); +} +function drawDoubleDash(context, x1, y1, x2, y2, length, separation) { + if (typeof length !== 'number') + length = 10; + var sep = separation / 2 || context.lineWidth + 2 || 4; + var grad = - (x2 - x1) / (y2 - y1); + + // [unitX,unitY] is a unit vector in the direction of the line + var unitX = (x2 - x1) / Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); + var unitY = (y2 - y1) / Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); + + var midX = (x1 + x2) / 2; + var midY = (y1 + y2) / 2; + + var dashX1 = midX - sep * unitX; + var dashX2 = midX + sep * unitX; + var dashY1 = midY - sep * unitY; + var dashY2 = midY + sep * unitY; + + var end1X = dashX1 - length * Math.cos(Math.atan(grad)); + var end1Y = dashY1 - length * Math.sin(Math.atan(grad)); + var end2X = dashX1 + length * Math.cos(Math.atan(grad)); + var end2Y = dashY1 + length * Math.sin(Math.atan(grad)); + + var end3X = dashX2 - length * Math.cos(Math.atan(grad)); + var end3Y = dashY2 - length * Math.sin(Math.atan(grad)); + var end4X = dashX2 + length * Math.cos(Math.atan(grad)); + var end4Y = dashY2 + length * Math.sin(Math.atan(grad)); + + context.beginPath(); + context.moveTo(end1X, end1Y); + context.lineTo(end2X, end2Y); + context.moveTo(end3X, end3Y); + context.lineTo(end4X, end4Y); + context.stroke(); +} +function drawPrintIcon(ctx,rect,backColor) { + var w = rect[2]; + ctx.save(); + ctx.translate(rect[0],rect[1]); + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + ctx.strokeStyle = '#000'; + ctx.fillStyle = '#000'; + ctx.lineWidth = 0.1*w; + + + roundedRect(ctx,0,0.3*w,w,0.45*w,0.1*w,0.1*w,'#000','#000'); + ctx.beginPath(); + ctx.fillStyle = backColor; + ctx.fillRect(0.22*w,0.65*w,0.56*w,0.15*w); + + roundedRect(ctx,0.22*w,0,0.56*w,w,0.1*w,0.1*w,'#000'); + + roundedRect(ctx,0.35*w,0.70*w,0.3*w,0.05*w,0,0.01*w,'#000','#000'); + roundedRect(ctx,0.35*w,0.82*w,0.2*w,0.05*w,0,0.01*w,'#000','#000'); + + ctx.beginPath(); + ctx.fillStyle = backColor; + ctx.arc(0.86*w,0.4*w,0.085*w,0,2*Math.PI); + ctx.fill(); + + ctx.translate(-rect[0],-rect[1]); + ctx.restore(); +} + +CanvasRenderingContext2D.prototype.setStroke = function (obj) { + var lineWidth = obj.lineWidth || obj.width || obj.w || obj.thickness || this.lineWidth; + var strokeStyle = obj.color || obj.strokeStyle || obj.style || this.strokeStyle; + var dash = obj.dash || obj.lineDash || this.getLineDash(); + var lineCap = obj.lineCap || obj.cap || 'round'; + var lineJoin = obj.lineJoin || obj.join || obj.cap || 'round'; + this.lineWidth = lineWidth; + this.strokeStyle = strokeStyle; + this.setLineDash(dash); + this.lineCap = lineCap; + this.lineJoin = lineJoin; +} +CanvasRenderingContext2D.prototype.setFill = function (obj) { + var color = obj.color || this.fillStyle; + this.fillStyle = obj.color; +} +CanvasRenderingContext2D.prototype.path = function (pathArray, close, obj) { + if (typeof obj == 'undefined') + obj = {}; + this.beginPath(); + this.moveTo(pathArray[0][0], pathArray[0][1]); + for (var i = 1; i < pathArray.length; i++) { + this.lineTo(pathArray[i][0], pathArray[i][1]); + } + if (boolean(close, false) === true) { + this.lineTo(pathArray[0][0], pathArray[0][1]); + } + if (typeof obj.fill !== 'undefined') { + this.setFill(obj.fill); + this.fill(); + } + if (typeof obj.lineDec !== 'undefined') {} + if (typeof obj.intAngles !== 'undefined') { + if (typeof obj.intAngles.show == 'undefined') { + obj.intAngles.show = []; + for (var i = 0; i < pathArray.length; i++) + obj.intAngles.show.push(true); + } + var angle = { + ctx: this + }; + angle.radius = obj.intAngles.radius || obj.intAngles.r || undefined; + angle.squareForRight = boolean(obj.intAngles.squareForRight, true); + angle.labelIfRight = boolean(obj.intAngles.labelIfRight, false); + angle.drawLines = boolean(obj.intAngles.drawLines, false); + angle.lineWidth = obj.intAngles.lineWidth || obj.intAngles.width || obj.intAngles.w || undefined; + angle.lineColor = obj.intAngles.lineColor || obj.intAngles.color || undefined; + angle.drawCurve = boolean(obj.intAngles.drawCurve, true); + angle.curveWidth = obj.intAngles.curveWidth || angle.lineWidth; + angle.curveColor = obj.intAngles.curveColor || angle.lineColor; + if (typeof obj.intAngles.fill == 'string') { + angle.fill = true; + angle.fillColor = obj.intAngles.fill; + } else { + angle.fill = boolean(obj.intAngles.fill, false); + angle.fillColor = obj.intAngles.fillColor || undefined; + } + angle.label = obj.intAngles.label || obj.intAngles.text || undefined; + angle.labelFont = obj.intAngles.labelFont || obj.intAngles.font || undefined; + angle.labelFontSize = obj.intAngles.labelFontSize || obj.intAngles.fontSize || undefined; + angle.labelColor = obj.intAngles.labelColor || angle.lineColor || undefined; + angle.labelRadius = obj.intAngles.labelRadius || undefined; + angle.labelMeasure = boolean(obj.intAngles.labelMeasure, false); + angle.measureRoundTo = obj.intAngles.measureRoundTo || obj.intAngles.roundTo || undefined; + angle.angleType = obj.intAngles.angleType || undefined; + for (var i = 0; i < pathArray.length; i++) { + if (obj.intAngles.show[i] === true) { + if (i === 0) { + angle.a = pathArray[pathArray.length - 1] + } else { + angle.a = pathArray[i - 1] + }; + angle.b = pathArray[i]; + if (i === pathArray.length - 1) { + angle.c = pathArray[0] + } else { + angle.c = pathArray[i + 1] + }; + if (typeof obj.intAngles.r == 'object') + angle.radius = obj.intAngles.r[i]; + if (typeof obj.intAngles.radius == 'object') + angle.radius = obj.intAngles.radius[i]; + if (typeof obj.intAngles.squareForRight == 'object') + angle.squareForRight = boolean(obj.intAngles.squareForRight[i], true); + if (typeof obj.intAngles.labelIfRight == 'object') + angle.labelIfRight = boolean(obj.intAngles.labelIfRight[i], false); + if (typeof obj.intAngles.drawLines == 'object') + angle.drawLines = boolean(obj.intAngles.drawLines[i], false); + if (typeof obj.intAngles.lineWidth == 'object') + angle.lineWidth = obj.intAngles.lineWidth[i]; + if (typeof obj.intAngles.width == 'object') + angle.lineWidth = obj.intAngles.width[i]; + if (typeof obj.intAngles.w == 'object') + angle.lineWidth = obj.intAngles.w[i]; + if (typeof obj.intAngles.lineColor == 'object') + angle.lineColor = obj.intAngles.lineColor[i]; + if (typeof obj.intAngles.color == 'object') + angle.lineWidth = obj.intAngles.color[i]; + if (typeof obj.intAngles.drawCurve == 'object') + angle.drawCurve = boolean(obj.intAngles.drawCurve[i], true); + if (typeof obj.intAngles.lineWidth == 'object') + angle.curveWidth = obj.intAngles.lineWidth[i]; + if (typeof obj.intAngles.curveWidth == 'object') + angle.curveWidth = obj.intAngles.curveWidth[i]; + if (typeof obj.intAngles.curveColor == 'object') + angle.curveColor = obj.intAngles.curveColor[i]; + if (typeof obj.intAngles.fill == 'object') { + if (typeof obj.intAngles.fill[i] == 'string') { + angle.fill = true; + angle.fillColor = obj.intAngles.fill[i]; + } else { + angle.fill = boolean(obj.intAngles.fill[i], false); + if (typeof obj.intAngles.fillColor == 'object') + angle.fillColor = obj.intAngles.fillColor[i]; + } + } + if (typeof obj.intAngles.label == 'object') + angle.label = obj.intAngles.label[i]; + if (typeof obj.intAngles.text == 'object') + angle.label = obj.intAngles.text[i]; + if (typeof obj.intAngles.labelFont == 'object') + angle.labelFont = obj.intAngles.curveColor[i]; + if (typeof obj.intAngles.font == 'object') + angle.labelFont = obj.intAngles.font[i]; + if (typeof obj.intAngles.labelFontSize == 'object') + angle.labelFontSize = obj.intAngles.labelFontSize[i]; + if (typeof obj.intAngles.fontSize == 'object') + angle.labelFontSize = obj.intAngles.fontSize[i]; + if (typeof obj.intAngles.lineColor == 'object') + angle.labelColor = obj.intAngles.lineColor[i]; + if (typeof obj.intAngles.labelColor == 'object') + angle.labelColor = obj.intAngles.labelColor[i]; + if (typeof obj.intAngles.labelRadius == 'object') + angle.labelRadius = obj.intAngles.labelRadius[i]; + if (typeof obj.intAngles.labelMeasure == 'object') + angle.labelMeasure = boolean(obj.intAngles.labelMeasure[i], false); + if (typeof obj.intAngles.measureRoundTo == 'object') + angle.measureRoundTo = obj.intAngles.measureRoundTo[i]; + if (typeof obj.intAngles.roundTo == 'object') + angle.measureRoundTo = obj.intAngles.roundTo[i]; + drawAngle(angle); + } + } + } + if (typeof obj.vertexLabels !== 'undefined') { + if (typeof obj.vertexLabels.show == 'undefined') { + obj.vertexLabels.show = []; + for (var i = 0; i < pathArray.length; i++) + obj.vertexLabels.show.push(true); + } + var angle = { + ctx: this + }; + angle.labelIfRight = true; + angle.drawLines = false; + angle.drawCurve = false; + angle.fill = false; + angle.label = obj.vertexLabels.label || obj.vertexLabels.text || undefined; + angle.labelFont = obj.vertexLabels.labelFont || obj.vertexLabels.font || undefined; + angle.labelFontSize = obj.vertexLabels.labelFontSize || obj.vertexLabels.fontSize || undefined; + angle.labelColor = obj.vertexLabels.labelColor || angle.lineColor || undefined; + angle.labelRadius = obj.vertexLabels.labelRadius || obj.vertexLabels.radius || obj.vertexLabels.r || undefined; + angle.labelMeasure = false; + for (var i = 0; i < pathArray.length; i++) { + if (obj.vertexLabels.show[i] === true) { + if (i === 0) { + angle.c = pathArray[pathArray.length - 1] + } else { + angle.c = pathArray[i - 1] + }; + angle.b = pathArray[i]; + if (i === pathArray.length - 1) { + angle.a = pathArray[0] + } else { + angle.a = pathArray[i + 1] + }; + if (typeof obj.vertexLabels.label == 'object') + angle.label = obj.vertexLabels.label[i]; + if (typeof obj.vertexLabels.text == 'object') + angle.label = obj.vertexLabels.text[i]; + if (typeof obj.vertexLabels.labelFont == 'object') + angle.labelFont = obj.vertexLabels.curveColor[i]; + if (typeof obj.vertexLabels.font == 'object') + angle.labelFont = obj.vertexLabels.font[i]; + if (typeof obj.vertexLabels.labelFontSize == 'object') + angle.labelFontSize = obj.vertexLabels.labelFontSize[i]; + if (typeof obj.vertexLabels.fontSize == 'object') + angle.labelFontSize = obj.vertexLabels.fontSize[i]; + if (typeof obj.vertexLabels.lineColor == 'object') + angle.labelColor = obj.vertexLabels.lineColor[i]; + if (typeof obj.vertexLabels.labelColor == 'object') + angle.labelColor = obj.vertexLabels.labelColor[i]; + if (typeof obj.vertexLabels.radius == 'object') + angle.labelRadius = obj.vertexLabels.radius[i]; + if (typeof obj.vertexLabels.r == 'object') + angle.labelRadius = obj.vertexLabels.r[i]; + if (typeof obj.vertexLabels.labelRadius == 'object') + angle.labelRadius = obj.vertexLabels.labelRadius[i]; + drawAngle(angle); + } + } + } + if (typeof obj.edgeLabels !== 'undefined') { + if (typeof obj.edgeLabels.show == 'undefined') { + obj.edgeLabels.show = []; + for (var i = 0; i < pathArray.length; i++) + obj.edgeLabels.show.push(true); + } + var label = { + ctx: this + }; + label.font = obj.edgeLabels.font || undefined; + label.fontSize = obj.edgeLabels.fontSize || undefined; + label.width = 1200; + for (var i = 0; i < pathArray.length; i++) { + if (obj.edgeLabels.show[i] === true) { + var a = pathArray[i]; + var b = pathArray[(i + 1) % pathArray.length]; + label.textArray = obj.edgeLabels.text[i]; + labelLine(a, b, label); + } + } + } + if (typeof obj.stroke !== 'undefined') { + this.beginPath(); + this.moveTo(pathArray[0][0], pathArray[0][1]); + for (var i = 1; i < pathArray.length; i++) { + this.lineTo(pathArray[i][0], pathArray[i][1]); + } + if (boolean(close, false) === true) { + this.lineTo(pathArray[0][0], pathArray[0][1]); + } + this.setStroke(obj.stroke); + this.stroke(); + } +} +CanvasRenderingContext2D.prototype.rect2 = function (obj) { + var obj = clone(obj); + if (un(obj.sf)) + obj.sf = 1; + this.save(); + var line = true; + this.lineWidth = def([obj.lineWidth, obj.thickness, this.lineWidth]) * obj.sf; + this.strokeStyle = def([obj.lineColor, obj.color, obj.strokeStyle, this.strokeStyle]); + if (obj.lineColor == 'none' || obj.color == 'none' || obj.strokeStyle == 'none') + line = false; + var fill = false; + if (!un(obj.fillColor) && obj.fillColor !== 'none') { + fill = true; + this.fillStyle = obj.fillColor; + } else if (!un(obj.fillStyle) && obj.fillStyle !== 'none') { + fill = true; + this.fillStyle = obj.fillStyle; + } + var dash = enlargeDash(def([obj.dash, this.getLineDash(), []]), this.sf); + if (!this.setLineDash) { + this.setLineDash = function () {} + } + this.setLineDash(dash); + if (!un(obj.rect)) { + obj.left = obj.rect[0]; + obj.top = obj.rect[1]; + obj.width = obj.rect[2]; + obj.height = obj.rect[3]; + } + var left = (obj.left || obj.l) * obj.sf; + var top = (obj.top || obj.t) * obj.sf; + var width = (obj.width || obj.w) * obj.sf; + var height = (obj.height || obj.h) * obj.sf; + this.beginPath(); + if (!un(obj.radius)) { + var radius = obj.radius * obj.sf; + this.moveTo(left + radius, top); + this.lineTo(left + width - radius, top); + this.arc(left + width - radius, top + radius, radius, 1.5 * Math.PI, 2 * Math.PI); + this.lineTo(left + width, top + height - radius); + this.arc(left + width - radius, top + height - radius, radius, 0, 0.5 * Math.PI); + this.lineTo(left + radius, top + height); + this.arc(left + radius, top + height - radius, radius, 0.5 * Math.PI, Math.PI); + this.lineTo(left, top + radius); + this.arc(left + radius, top + radius, radius, Math.PI, 1.5 * Math.PI); + this.closePath(); + if (fill == true) + this.fill(); + if (line == true) + this.stroke(); + } else { + if (fill == true) + this.fillRect(left, top, width, height); + if (line == true) + this.strokeRect(left, top, width, height); + } + this.restore(); +} +CanvasRenderingContext2D.prototype.clear = function () { + this.clearRect(0, 0, this.data[102], this.data[103]); +} +HTMLCanvasElement.prototype.setLeft = function (left) { + this.data[100] = left; + resizeCanvas2(this, this.data[100], this.data[101]); +} +HTMLCanvasElement.prototype.setTop = function (top) { + this.data[101] = top; + resizeCanvas2(this, this.data[100], this.data[101]); +} +HTMLCanvasElement.prototype.setPos = function (left, top) { + this.data[100] = left; + this.data[101] = top; + resizeCanvas2(this, this.data[100], this.data[101]); +} +HTMLCanvasElement.prototype.setWidth = function (width) { + this.data[102] = width; + resizeCanvas(this, this.data[100], this.data[101], this.data[102], this.data[103]); +} +HTMLCanvasElement.prototype.setHeight = function (height) { + this.data[103] = height; + resizeCanvas(this, this.data[100], this.data[101], this.data[102], this.data[103]); +} +HTMLCanvasElement.prototype.setDims = function (width, height) { + this.data[102] = width; + this.data[103] = height; + resizeCanvas(this, this.data[100], this.data[101], this.data[102], this.data[103]); +} +HTMLCanvasElement.prototype.setVis = function (vis) { + if (typeof vis == 'undefined') { + this.data[104] = !this.data[104]; + } else { + this.data[104] = vis; + } + if (this.data[104] == true) { + showObj(this); + } else { + hideObj(this); + } +} +HTMLCanvasElement.prototype.setPE = function (point) { + if (typeof point == 'undefined') { + this.data[106] = !this.data[106]; + } else { + this.data[106] = point; + } + if (this.data[106] == true) { + this.style.pointerEvents = 'auto'; + } else { + this.style.pointerEvents = 'none'; + } +} +HTMLCanvasElement.prototype.setZ = function (z) { + this.data[107] = z; + this.style.zIndex = z; +} +HTMLCanvasElement.prototype.setCursor = function (cursor) { + this.style.cursor = cursor || 'pointer'; +} +HTMLCanvasElement.prototype.setOpacity = function (opacity) { + this.style.opacity = opacity; +} + +function playButton(left, top, width, func, options) { + //visible,zIndex,fillColor,lineColor,lineWidth,radiusx + if (typeof options == 'undefined') + var options = {}; + var zIndex = options.zIndex || 2; + var button = createCanvas(left, top, width, width, boolean(options.visible, true), false, true, zIndex); + button.lineColor = options.lineColor || '#000'; + button.lineWidth = options.lineWidth || 4; + button.fillColor = options.fillColor || '#3FF'; + button.radius = options.radius || 8; + button.direction = 'right'; + if (options.dir == 'left') + button.direction = 'left'; + button.width = width; + button.left = left; + button.top = top; + button.draw = function () { + var ctx = this.ctx; + roundedRect2(ctx, this.lineWidth / 2, this.lineWidth / 2, this.width - this.lineWidth, this.width - this.lineWidth, this.radius, this.lineWidth, this.lineColor, this.fillColor); + ctx.fillStyle = this.lineColor; + ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; + ctx.beginPath(); + if (this.direction == 'right') { + ctx.moveTo(this.width * 16 / 50, this.width * 14 / 50); + ctx.lineTo(this.width * 34 / 50, this.width * 25 / 50); + ctx.lineTo(this.width * 16 / 50, this.width * 36 / 50); + ctx.lineTo(this.width * 16 / 50, this.width * 14 / 50); + } else { + ctx.moveTo(this.width * 34 / 50, this.width * 14 / 50); + ctx.lineTo(this.width * 16 / 50, this.width * 25 / 50); + ctx.lineTo(this.width * 34 / 50, this.width * 36 / 50); + ctx.lineTo(this.width * 34 / 50, this.width * 14 / 50); + } + ctx.fill(); + } + button.draw(); + if (typeof func !== 'undefined') { + addListener(button, func); + } + return button; +} +function drawCalcAllowedButton(ctx, l, t, size, allowed, backColor) { + var w = size || 20; + var h = size || 20; + var color = backColor || '#FFC'; + ctx.save(); + ctx.strokeStyle = '#000'; + ctx.fillStyle = '#000'; + roundedRect(ctx, l, t, w, h, 2, 3, '#000', color); + roundedRect(ctx, l + 0.3 * w, t + 0.2 * h, w * 0.4, h * 0.6, 1, 3, '#000', '#000'); + roundedRect(ctx, l + 0.35 * w, t + 0.25 * h, w * 0.3, h * 0.15, 0.01, 3, '#000', color); + for (var i = 0; i < 3; i++) { + for (var j = 0; j < 3; j++) { + roundedRect(ctx, l + 0.35 * w + 0.12 * j * w, t + 0.45 * h + 0.12 * i * h, w * 0.08, h * 0.08, 0.01, 3, '#000', color); + } + } + if (boolean(allowed, true) == false) { + ctx.lineWidth = 0.1 * w; + ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; + ctx.beginPath(); + ctx.strokeStyle = '#F00'; + ctx.moveTo(l + 0.85 * w, t + 0.15 * h); + ctx.lineTo(l + 0.15 * w, t + 0.85 * h); + ctx.stroke(); + } + ctx.restore(); +} +function drawCalcAllowedButton2(ctx, l, t, size, allowed, backColor, calcColor) { + var w = size || 20; + var h = size || 20; + var color = backColor || '#FFC'; + var color2 = calcColor || '#333'; + ctx.save(); + ctx.strokeStyle = color2; + ctx.fillStyle = color2; + roundedRect(ctx, l, t, w, h, 2, 3, '#000', color); + roundedRect(ctx, l + 0.3 * w, t + 0.2 * h, w * 0.4, h * 0.6, 1, 3, color2, color2); + roundedRect(ctx, l + 0.35 * w, t + 0.25 * h, w * 0.3, h * 0.15, 0.01, 3, color2, color); + for (var i = 0; i < 3; i++) { + for (var j = 0; j < 3; j++) { + roundedRect(ctx, l + 0.35 * w + 0.12 * j * w, t + 0.45 * h + 0.12 * i * h, w * 0.08, h * 0.08, 0.01, 3, color2, color); + } + } + if (boolean(allowed, true) == false) { + ctx.lineWidth = 0.08 * w; + ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; + ctx.beginPath(); + ctx.strokeStyle = '#900'; + ctx.arc(l + 0.5 * w, t + 0.5 * h, 0.45 * w, 0, 2 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.lineWidth = 0.12 * w; + ctx.moveTo(l + 0.5 * w + (1 / Math.sqrt(2)) * 0.42 * w, t + 0.5 * h - (1 / Math.sqrt(2)) * 0.42 * h); + ctx.lineTo(l + 0.5 * w - (1 / Math.sqrt(2)) * 0.42 * w, t + 0.5 * h + (1 / Math.sqrt(2)) * 0.42 * h); + //ctx.moveTo(l+0.15*w,t+0.15*h); + //ctx.lineTo(l+0.85*w,t+0.85*h); + ctx.stroke(); + + } + ctx.restore(); +} +function drawRefreshButton(ctx, l, t, size, backColor) { + var w = size || 20; + var h = size || 20; + var color = backColor || '#FFC'; + ctx.save(); + ctx.strokeStyle = '#000'; + ctx.fillStyle = '#000'; + ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; + roundedRect(ctx, l, t, w, h, 8, 8, '#000', color); + ctx.lineWidth = 0.08 * w; + ctx.beginPath(); + ctx.arc(l + 0.5 * w, t + 0.5 * h, 0.22 * w, -1.8 * Math.PI, -0.2 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + var l2 = l + 0.77 * w; + var t2 = t + 0.47 * h; + var arrowLength = 0.28 * w; + ctx.moveTo(l2, t2); + ctx.lineTo(l2 + arrowLength * Math.sin(1.06 * Math.PI), t2 + arrowLength * Math.cos(1.06 * Math.PI)); + ctx.lineTo(l2 + arrowLength * Math.cos(1.03 * Math.PI), t2 - arrowLength * Math.sin(1.03 * Math.PI)); + ctx.lineTo(l2, t2); + ctx.fill(); + ctx.restore(); +} +function drawRangeLabel(ctx, l, t, w, h, direction) { + var dir = 'bottom'; // default; + if (typeof direction == 'string') + dir = direction; + switch (dir) { + case 'bottom': + var p1 = [l, t]; + var p2 = [l + w / 2, t + h]; + var p3 = [l + w, t]; + var c1 = [l + w * (1 / 12), t + h * 1.3]; + var c2 = [l + w * (13 / 40), t + h * (-0.7)]; + var c3 = [l + w * (27 / 40), t + h * (-0.7)]; + var c4 = [l + w * (11 / 12), t + h * 1.3]; + break; + case 'top': + var p1 = [l, t + h]; + var p2 = [l + w / 2, t]; + var p3 = [l + w, t + h]; + var c1 = [l + w * (1 / 12), t + h - h * 1.3]; + var c2 = [l + w * (13 / 40), t + h - h * (-0.7)]; + var c3 = [l + w * (27 / 40), t + h - h * (-0.7)]; + var c4 = [l + w * (11 / 12), t + h - h * 1.3]; + break; + case 'right': + var p1 = [l, t]; + var p2 = [l + w, t + h / 2]; + var p3 = [l, t + h]; + var c1 = [l + w * 1.3, t + h * (1 / 12)]; + var c2 = [l + w * (-0.7), t + h * (13 / 40)]; + var c3 = [l + w * (-0.7), t + h * (27 / 40)]; + var c4 = [l + w * 1.3, t + h * (11 / 12)]; + break; + case 'left': + var p1 = [l + w, t]; + var p2 = [l, t + h / 2]; + var p3 = [l + w, t + h]; + var c1 = [l + w - w * 1.3, t + h * (1 / 12)]; + var c2 = [l + w - w * (-0.7), t + h * (13 / 40)]; + var c3 = [l + w - w * (-0.7), t + h * (27 / 40)]; + var c4 = [l + w - w * 1.3, t + h * (11 / 12)]; + break; + } + ctx.beginPath(); + ctx.moveTo(p1[0], p1[1]); + ctx.bezierCurveTo(c1[0], c1[1], c2[0], c2[1], p2[0], p2[1]); + ctx.bezierCurveTo(c3[0], c3[1], c4[0], c4[1], p3[0], p3[1]); + ctx.stroke(); +} + +var JSONfn = {}; +(function () { + JSONfn.stringify = function (obj) { + return JSON.stringify(obj, function (key, value) { + return (typeof value === 'function') ? value.toString() : value; + }); + } + JSONfn.parse = function (str) { + return JSON.parse(str, function (key, value) { + if (typeof value != 'string') + return value; + return (value.substring(0, 8) == 'function') ? eval('(' + value + ')') : value; + }); + } +} + ()); + +function replaceAll(string, find, replace) { + return string.replace(new RegExp(escapeRegExp(find), 'g'), replace); +} +function escapeRegExp(string) { + return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); +} +function removeSpaces(string, opt_mathsInput) { + string = string.replace(/\s/g, ""); + if (typeof opt_mathsInput !== 'undefined') + setMathsInputText(opt_mathsInput, string, 0); + return string; +} +function stringTo2dArray(string, elemType) { + if (!elemType) + elemType = 'string'; + var start = string.indexOf("["); + + // replace commas with semi-colons inside array (level 1) + var bracket = 0; + for (j = start; j < string.length; j++) { + if (string.charAt(j) == "[") + bracket++; + if (string.charAt(j) == "]") + bracket--; + if (string.charAt(j) == "," && bracket == 1) { + string = string.slice(0, j) + ";" + string.slice(j + 1); + } + } + + var bracket = 0; + var fin; + for (j = start; j < string.length; j++) { + if (string.charAt(j) == "[") + bracket++; + if (string.charAt(j) == "]") + bracket--; + if (bracket == 0) { + fin = j + 1; + break; + } + } + + string = string.slice(start + 1, fin - 1); + var array = string.split(";"); + + for (j = 0; j < array.length; j++) { + var str = array[j]; + array[j] = array[j].slice(1, -1); + array[j] = array[j].split(','); + for (k = 0; k < array[j].length; k++) { + if (elemType == 'number') + array[j][k] = Number(array[j][k]); + } + } + + return array; +} +function textArrayReplace(textArray, findStr, replaceStr) { + if (typeof replaceStr == 'object') { + return textArrayReplace2(textArray, findStr, replaceStr); + } + replaceStr = String(replaceStr); + for (var i = 0; i < textArray.length; i++) { + if (typeof textArray[i] == 'string') { + textArray[i] = textStringReplace(textArray[i], findStr, replaceStr); + } else if (typeof textArray[i] == 'object') { + textArray[i] = textArrayReplace(textArray[i], findStr, replaceStr); + } + } + return textArray; +} +function textStringReplace(string, findStr, replaceStr) { + var re = new RegExp(findStr, "gi"); + return string.replace(re, replaceStr); +} +function textArrayReplace2(textArray, findStr, replacement) { + // replace string with array + for (var i = 0; i < textArray.length; i++) { + if (typeof textArray[i] == 'string') { + var pos = textArray[i].indexOf(findStr); + if (pos > -1) { + var newElems = clone(replacement); + newElems.unshift(textArray[i].slice(0, pos)); + newElems.push(textArray[i].slice(pos + findStr.length)); + textArray.splice(i, 1); + textArray.splice.apply(textArray, [i, 0].concat(newElems)); + break; + } + } else if (typeof textArray[i] == 'object') { + textArray[i] = textArrayReplace2(textArray[i], findStr, replacement); + } + } + return textArray; +} + +function getArrayCount(testArray, testValue) { + var count = 0; + for (var j = 0; j < testArray.length; j++) { + if (testArray[j] == testValue) { + count++; + } + } + return count; +} +function getArrayLessThanCount(testArray, testValue) { + var count = 0; + for (var j = 0; j < testArray.length; j++) { + if (testArray[j] < testValue) { + count++; + } + } + return count; +} +function shuffleArray(array) { + var newArray = []; + do { + var randomPos = Math.floor(Math.random() * array.length); + newArray.push(array[randomPos]); + array.splice(randomPos, 1); + } while (array.length > 0); + return newArray; +} +function buildArray(array, dim0, dim1, dim2, dim3, dim4, dim5) { + if ((typeof dim0 !== 'undefined') && (typeof array[dim0] == 'undefined')) { + array[dim0] = []; + } + if ((typeof dim1 !== 'undefined') && (typeof array[dim0][dim1] == 'undefined')) { + array[dim0][dim1] = []; + } + if ((typeof dim2 !== 'undefined') && (typeof array[dim0][dim1][dim2] == 'undefined')) { + array[dim0][dim1][dim2] = []; + } + if ((typeof dim3 !== 'undefined') && (typeof array[dim0][dim1][dim2][dim3] == 'undefined')) { + array[dim0][dim1][dim2][dim3] = []; + } + if ((typeof dim4 !== 'undefined') && (typeof array[dim0][dim1][dim2][dim3][dim4] == 'undefined')) { + array[dim0][dim1][dim2][dim3][dim4] = []; + } + if ((typeof dim5 !== 'undefined') && (typeof array[dim0][dim1][dim2][dim3][dim4][dim5] == 'undefined')) { + array[dim0][dim1][dim2][dim3][dim4][dim5] = []; + } +} +function arraySum(array) { + var sum = 0; + if (!un(array)) { + for (var a = 0, aMax = array.length; a < aMax; a++) { + sum += Number(array[a]); + } + } + return sum; +} +Array.prototype.alphanumSort = function (caseInsensitive) { + for (var z = 0, t; t = this[z]; z++) { + this[z] = [], + 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) { + this[z][++y] = ""; + n = m; + } + this[z][y] += j; + } + } + + this.sort(function (a, b) { + for (var x = 0, aa, bb; (aa = a[x]) && (bb = b[x]); x++) { + if (caseInsensitive) { + aa = aa.toLowerCase(); + bb = bb.toLowerCase(); + } + if (aa !== bb) { + var c = Number(aa), + d = Number(bb); + if (c == aa && d == bb) { + return c - d; + } else + return (aa > bb) ? 1 : -1; + } + } + return a.length - b.length; + }); + + for (var z = 0; z < this.length; z++) + this[z] = this[z].join(""); +} +function compareVersion(data0, data1, levels) { + function getVersionHash(version) { + var value = 0; + version = version.split(".").map(function (a) { + var n = parseInt(a); + var letter = a.replace(n, ""); + if (letter) { + return n + letter[0].charCodeAt() / 0xFF; + } else { + return n; + } + }); + for (var i = 0; i < version.length; ++i) { + if (levels === i) + break; + value += version[i] / 0xFF * Math.pow(0xFF, levels - i + 1); + } + return value; + }; + var v1 = getVersionHash(data0); + var v2 = getVersionHash(data1); + return v1 === v2 ? -1 : v1 > v2 ? 0 : 1; +}; + +function mouseHitRect(l, t, w, h) { + var x = mouse.x; + var y = mouse.y; + if (x >= l && x <= (l + w) && y >= t && y <= (t + h)) { + return true; + } else { + return false; + } +} +function hitTestMouseOver(obj) { + if (un(obj)) + return; + var objBoundingRect = obj.getBoundingClientRect(); + var x = xCanvasToWindow(mouse.x); + var y = yCanvasToWindow(mouse.y); + if (x > objBoundingRect.left && x < objBoundingRect.right && y > objBoundingRect.top && y < objBoundingRect.bottom) { + return true; + } else { + return false; + } +} +function hitTestTwoObjects(obj1, obj2) { + var obj1BoundingRect = obj1.getBoundingClientRect(); + var obj2BoundingRect = obj2.getBoundingClientRect(); + if (obj1BoundingRect.left < obj2BoundingRect.right && obj1BoundingRect.top < obj2BoundingRect.bottom && obj1BoundingRect.right > obj2BoundingRect.left && obj1BoundingRect.bottom > obj2BoundingRect.top) { + return true + } else { + if (obj1BoundingRect.right > obj2BoundingRect.left && obj1BoundingRect.top < obj2BoundingRect.bottom && obj1BoundingRect.left < obj2BoundingRect.right && obj1BoundingRect.bottom > obj2BoundingRect.top) { + return true; + } else { + if (obj2BoundingRect.left < obj1BoundingRect.right && obj2BoundingRect.top < obj1BoundingRect.bottom && obj2BoundingRect.right > obj1BoundingRect.left && obj2BoundingRect.bottom > obj1BoundingRect.top) { + return true + } else { + if (obj2BoundingRect.right > obj1BoundingRect.left && obj2BoundingRect.top < obj1BoundingRect.bottom && obj2BoundingRect.left < obj1BoundingRect.right && obj2BoundingRect.bottom > obj1BoundingRect.top) { + return true; + } else { + return false + } + } + } + } +} +function hitTestRect(obj, left, top, width, height) { + var objBoundingRect = obj.getBoundingClientRect(); + var right = left + width; + var bottom = top + height; + left = xCanvasToWindow(left); + right = xCanvasToWindow(right); + top = yCanvasToWindow(top); + bottom = yCanvasToWindow(bottom); + if (objBoundingRect.left < right && objBoundingRect.top < bottom && objBoundingRect.right > left && objBoundingRect.bottom > top) { + return true; + } else { + if (objBoundingRect.right > left && objBoundingRect.top < bottom && objBoundingRect.left < right && objBoundingRect.bottom > top) { + return true; + } else { + if (left < objBoundingRect.right && top < objBoundingRect.bottom && right > objBoundingRect.left && bottom > objBoundingRect.top) { + return true; + } else { + if (right > objBoundingRect.left && top < objBoundingRect.bottom && left < objBoundingRect.right && bottom > objBoundingRect.top) { + return true; + } else { + return false; + } + } + } + } +} +function hitTestRect2(obj, left, top, width, height) { // tests if center of obj is in rect + var objBoundingRect = obj.getBoundingClientRect(); + var objX = objBoundingRect.left + 0.5 * (objBoundingRect.right - objBoundingRect.left); + var objY = objBoundingRect.top + 0.5 * (objBoundingRect.bottom - objBoundingRect.top); + var right = left + width; + var bottom = top + height; + left = xCanvasToWindow(left); + right = xCanvasToWindow(right); + top = yCanvasToWindow(top); + bottom = yCanvasToWindow(bottom); + if (objX < right && objY < bottom && objX > left && objY > top) { + return true; + } else { + return false; + } +} +function hitTestTwoRects(rect1, rect2) { + var xHit = false; + var yHit = false; + if ( + (rect2[0] <= rect1[0] && rect2[0] + rect2[2] >= rect1[0]) || + (rect2[0] <= rect1[0] + rect1[2] && rect2[0] + rect2[2] >= rect1[0] + rect1[2]) || + (rect2[0] >= rect1[0] && rect2[0] + rect2[2] <= rect1[0] + rect1[2])) { + xHit = true; + }; + if ( + (rect2[1] <= rect1[1] && rect2[1] + rect2[3] >= rect1[1]) || + (rect2[1] <= rect1[1] + rect1[3] && rect2[1] + rect2[3] >= rect1[1] + rect1[3]) || + (rect2[1] >= rect1[1] && rect2[1] + rect2[3] <= rect1[1] + rect1[3])) { + yHit = true; + }; + return (xHit && yHit); +} +function hitTestMouseOverRect(left, top, width, height) { // tests if mouse is in rect - REQUIRES mouse coords to have been updated + if (mouse.x < left + width && mouse.y < top + height && mouse.x > left && mouse.y > top) { + return true; + } else { + return false; + } +} +function hitTestCircle(obj, centreX, centreY, radius) { + var objBoundingRect = obj.getBoundingClientRect(); + centreX = xCanvasToWindow(centreX); + centreY = yCanvasToWindow(centreY); + if ((window.innerWidth / window.innerHeight) > (12 / 7)) { + radius = (radius / canvas.height) * window.innerHeight; + } else { + radius = (radius / canvas.width) * window.innerWidth; + } + var testPoint = []; + testPoint[0] = Math.pow((objBoundingRect.left - centreX), 2) + Math.pow((objBoundingRect.top - centreY), 2); + testPoint[1] = Math.pow((objBoundingRect.left - centreX), 2) + Math.pow((objBoundingRect.bottom - centreY), 2); + testPoint[2] = Math.pow((objBoundingRect.right - centreX), 2) + Math.pow((objBoundingRect.top - centreY), 2); + testPoint[3] = Math.pow((objBoundingRect.right - centreX), 2) + Math.pow((objBoundingRect.bottom - centreY), 2); + testPoint[4] = Math.pow((objBoundingRect.left - centreX), 2) + Math.pow(((objBoundingRect.top + objBoundingRect.bottom) / 2 - centreY), 2); + testPoint[5] = Math.pow((objBoundingRect.right - centreX), 2) + Math.pow(((objBoundingRect.top + objBoundingRect.bottom) / 2 - centreY), 2); + testPoint[6] = Math.pow(((objBoundingRect.left + objBoundingRect.right) / 2 - centreX), 2) + Math.pow((objBoundingRect.top - centreY), 2); + testPoint[7] = Math.pow(((objBoundingRect.left + objBoundingRect.right) / 2 - centreX), 2) + Math.pow((objBoundingRect.bottom - centreY), 2); + + if (getArrayLessThanCount(testPoint, Math.pow(radius, 2)) > 0) { + return true + } else { + return false + } +} +function hitTestCircle2(obj, centreX, centreY, radius) { // this one just requires the center of the object to be within the circle + var objBoundingRect = obj.getBoundingClientRect(); + centreX = xCanvasToWindow(centreX); + centreY = yCanvasToWindow(centreY); + if ((window.innerWidth / window.innerHeight) > (12 / 7)) { + radius = (radius / canvas.height) * window.innerHeight; + } else { + radius = (radius / canvas.width) * window.innerWidth; + } + var objX = objBoundingRect.left + 0.5 * objBoundingRect.width; + var objY = objBoundingRect.top + 0.5 * objBoundingRect.height; + if (Math.pow((objX - centreX), 2) + Math.pow((objY - centreY), 2) <= Math.pow(radius, 2)) { + return true + } else { + return false + } +} +function hitTestMouseOverPolygon(verticesArray) { + var x = mouse.x; + var y = mouse.y; + + // split n-agon into (n-2) triangles and test if (x,y) is in each triangle + var x1 = verticesArray[0][0]; + var y1 = verticesArray[0][1]; + + for (var i = 1; i <= verticesArray.length - 2; i++) { + var x2 = verticesArray[i][0]; + var y2 = verticesArray[i][1]; + var x3 = verticesArray[i + 1][0]; + var y3 = verticesArray[i + 1][1]; + // work out the barycentric coordinates for the triangle + // iff all are positive, (x, y) is in the triangle + var alpha = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3)) / ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3)); + var beta = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3)) / ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3)); + var gamma = 1 - alpha - beta; + if (alpha > 0 && beta > 0 && gamma > 0) { + return true; + } + } + return false; +} +function hitTestPolygon(point, verticesArray, includePerimeter) { + var x = point[0]; + var y = point[1]; + + // split n-agon into (n-2) triangles and test if (x,y) is in each triangle + var x1 = verticesArray[0][0]; + var y1 = verticesArray[0][1]; + + for (var i = 1; i <= verticesArray.length - 2; i++) { + var x2 = verticesArray[i][0]; + var y2 = verticesArray[i][1]; + var x3 = verticesArray[i + 1][0]; + var y3 = verticesArray[i + 1][1]; + // work out the barycentric coordinates for the triangle + // iff all are positive, (x, y) is in the triangle + var alpha = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3)) / ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3)); + var beta = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3)) / ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3)); + var gamma = 1 - alpha - beta; + if (boolean(includePerimeter, true) == true) { + if (alpha >= 0 && beta >= 0 && gamma >= 0) + return true; + } else { + if (alpha > 0 && beta > 0 && gamma > 0) + return true; + } + } + return false; +} +function hitTestPolygon2(point,pos) { + //https://stackoverflow.com/questions/8721406/how-to-determine-if-a-point-is-inside-a-2d-convex-polygon/23223947#23223947 + var result = false; + for (var i = 0, j = pos.length - 1; i < pos.length; j = i++) { + if ((pos[i][1] > point[1]) != (pos[j][1] > point[1]) && + (point[0] < (pos[j][0] - pos[i][0]) * (point[1] - pos[i][1]) / (pos[j][1]-pos[i][1]) + pos[i][0])) { + result = !result; + } + } + return result; +} +function hitTestPolygonBoundary(point,pos,tol) { + if (un(tol)) tol = 0.01; + for (var p = 0; p < pos.length; p++) { + var pos1 = pos[p]; + var pos2 = pos[(p+1)%pos.length]; + if (isPointOnLineSegment(point, pos1, pos2, tol) == true) return true; + } + return false; +} + +function motionPath(canvasData, startX, startY, finX, finY, rateType, rate) { // eg. rateType='time', rate=2000 (time in ms to complete motion) or rateType='speed', rate=200 (speed of motion in (canvas) pixels per second) + var xDistance = finX - startX; + var yDistance = finY - startY; + var distance = Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2)); + var time; + if (rateType == 'speed') { + time = 1000 * distance / rate + }; + if (rateType == 'time') { + time = rate + }; + var dx = xDistance * 40 / time; + var dy = yDistance * 40 / time; + var frameCount = 0; + var totalFrames = Math.floor(time / 40); + var motion = setInterval(function () { + canvasData[100] += dx; + canvasData[101] += dy; + resize(); + frameCount++; + if (frameCount >= totalFrames) { + canvasData[100] = finX; + canvasData[101] = finY; + resize(); + clearInterval(motion) + }; + }, 40); +} +function motionResize(canvasData, newWidth, newHeight, time, coeX, coeY) { + //if not present, the coe will default to the centre of the object + if (!coeX) { + coeX = canvasData[100] + 0.5 * canvasData[102] + }; + if (!coeY) { + coeY = canvasData[101] + 0.5 * canvasData[103] + }; + var dw = (newWidth - canvasData[102]) * 40 / time; + var dh = (newHeight - canvasData[103]) * 40 / time; + var dx = 0.5 * (canvasData[102] - newWidth) * 40 / time; + var dy = 0.5 * (canvasData[103] - newHeight) * 40 / time; + var finX = canvasData[100] + 0.5 * (canvasData[102] - newWidth); + var finY = canvasData[101] + 0.5 * (canvasData[103] - newHeight); + var frameCount = 0; + var totalFrames = Math.floor(time / 40); + var motion = setInterval(function () { + canvasData[100] += dx; + canvasData[101] += dy; + canvasData[102] += dw; + canvasData[103] += dh; + resize(); + frameCount++; + if (frameCount >= totalFrames) { + canvasData[100] = finX; + canvasData[101] = finY; + canvasData[102] = newWidth; + canvasData[103] = newHeight; + resize(); + clearInterval(motion) + }; + }, 40); +} + +function ceiling(number, toNearest) { + // get the place value of toNearest + var decPointPos; + if (String(toNearest).indexOf('.') !== -1) { + decPointPos = String(toNearest).indexOf('.'); + } else { + decPointPos = String(toNearest).length - 1; + } + var placeValue; + for (ii = 0; ii < String(toNearest).length; ii++) { + if (String(toNearest).charAt(ii) !== "0" && String(toNearest).charAt(ii) !== ".") { + placeValue = decPointPos - ii; + } + } + // divide number and toNearesr by 10^placevalue + number = number / (Math.pow(10, placeValue)); + toNearest = toNearest / (Math.pow(10, placeValue)); + // divide number by toNearest + number = number / toNearest; + number = Math.ceil(number); + number = number * toNearest; + number = number * (Math.pow(10, placeValue)); + number = Math.round(number * 1000000000) / 1000000000; + return number; +} +function truncate(number, decPlaces) { + return + (Math.floor(number * Math.pow(10, decPlaces)) / Math.pow(10, decPlaces)).toFixed(decPlaces); +} +function round2(number, decPlaces) { + return + (Math.round(number * Math.pow(10, decPlaces)) / Math.pow(10, decPlaces)).toFixed(decPlaces); +} +function roundToNearest(number, toNearest) { + var testLog = Math.log(toNearest) / Math.log(10); + if (Math.abs(testLog - Math.round(testLog)) < 0.000001) { // powers of 10 + return round(number, toNearest); + } else { + // get the place value of toNearest + var decPointPos; + if (String(toNearest).indexOf('.') !== -1) { + decPointPos = String(toNearest).indexOf('.'); + } else { + decPointPos = String(toNearest).length - 1; + } + var placeValue; + for (var i = 0; i < String(toNearest).length; i++) { + if (String(toNearest).charAt(i) !== "0" && String(toNearest).charAt(i) !== ".") { + placeValue = decPointPos - i; + } + } + number = number / (Math.pow(10, placeValue)); + toNearest = toNearest / (Math.pow(10, placeValue)); + number = number / toNearest; + number = Math.round(number); + number = number * toNearest; + number = number * (Math.pow(10, placeValue)); + number = Math.round(number * 1000000000) / 1000000000; + return number; + } +} +function roundSF(number, sigFigs, showAllZeros) { + if (un(sigFigs)) sigFigs = 1; + var negative = number < 0 ? true : false; + number = Math.abs(number); + var pv = Math.floor(Math.log(number)/Math.log(10)) - (sigFigs-1); + var toNearest = Math.pow(10,pv); + var rounded = String(roundToNearest(number,toNearest)); + + var sfCount = 0; // check number of sf in answer + var dpPassed = false; + for (var d = 0; d < rounded.length; d++) { + if (rounded[d] == '-') continue; + if (rounded[d] == '.') { + if (sfCount >= sigFigs) { + rounded = rounded.slice(0,d); + break; + } else { + dpPassed = true; + continue; + } + } + if (sfCount == 0 && rounded[d] == '0') continue; + sfCount++; + if (dpPassed == true && sfCount == sigFigs) { + rounded = rounded.slice(0,d+1); + break; + } + } + if (negative == true) rounded = '-'+rounded; + return rounded; +} +function round(number, toNearest, returnAsString) { // toNearest must be a power of 10 + var sign = number < 0 ? '-' : ''; + var str = String(Math.abs(number)); + var decPos = str.indexOf('.') > -1 ? str.indexOf('.') : str.length; + var digits = []; + for (var n = 0; n < str.length; n++) { + if (isNaN(Number(str[n]))) + continue; + digits.push(Number(str[n])); + } + var roundPV = Math.round(Math.log(toNearest) / Math.log(10)); + var roundPos = Math.round(decPos - roundPV - 1); + if (roundPos >= digits.length) { + var addDigits = roundPos - digits.length; + for (var p = 0; p <= addDigits; p++) + digits.push(0); + } else { + if (roundPos == -1) { + if (digits[0] >= 5) { + digits.unshift(1); + roundPos++; + decPos++; + } else { + return 0; + } + } else if (digits[roundPos + 1] >= 5) { + if (digits[roundPos] < 9) { + digits[roundPos]++; + } else { + digits[roundPos] = 0; + var done = false; + var pos = roundPos - 1; + var count = 0; + while (done == false && count < 50) { + count++ + if (pos == -1) { + digits.unshift(1); + roundPos++; + decPos++; + done = true; + } else if (digits[pos] < 9) { + digits[pos]++; + done = true; + } else { + digits[pos] = 0; + pos--; + } + } + } + } + for (var p = roundPos + 1; p < digits.length; p++) + digits[p] = 0; + } + var str2 = sign; + for (var p = 0; p < digits.length; p++) { + if (p == decPos) { + if (roundPV < 0) { + str2 += '.'; + } else { + break; + } + } + if (p == roundPos + 1 && p > decPos) + break; + str2 += String(digits[p]); + } + if (Number(str2) == 0) + str2 = '0'; + + if (boolean(returnAsString, false) == true) { + return str2; + } else { + return Number(str2); + } + //console.log(sign,str,toNearest,decPos,digits,roundPV,roundPos,str2); +} + +function nthroot(x, n) { + try { + var negate = n % 2 == 1 && x < 0; + if (negate) + x = -x; + var possible = Math.pow(x, 1 / n); + n = Math.pow(possible, n); + if (Math.abs(x - n) < 1 && (x > 0 == n > 0)) + return negate ? -possible : possible; + } catch (e) {} +} +function hcf(x, y) { + x = Math.abs(x); + y = Math.abs(y); + // Apply Euclid's algorithm to the two numbers. + while (Math.max(x, y) % Math.min(x, y) != 0) { + if (x > y) { + x %= y; + } else { + y %= x; + } + } + // When the while loop finishes the minimum of x and y is the HCF. + return Math.min(x, y); +} +function ran(minNum, maxNum) { + return Math.floor((maxNum - minNum + 1) * Math.random()) + minNum; +} + +function clickToHide(canvas, canvasctx, canvasData, font, textColor) { + if (!font) { + font = '12px Arial' + }; + if (!textColor) { + textColor = '#333' + }; + canvasctx.font = font; + canvasctx.fillStyle = textColor; + canvasctx.textBaseline = 'bottom'; + canvasctx.textAlign = 'center'; + canvasctx.fillText('Click to close', canvasData[2] / 2, canvasData[3] - 3); + canvas.style.pointerEvents = 'auto'; + canvas.onclick = function () { + hideObj(canvas, canvasData); + } +} + +// rotates coordinates about the origin (or a given center) and rounds to nearest integer +function rotateCoords(x, y, degrees, opt_direction, opt_centerX, opt_centerY, opt_round) { + var round = true; + if (typeof opt_round == 'boolean') { + round = opt_round + } + var direction = opt_direction || 'anti-cw'; // use 'cw' or 'anti-cw' + var centerX = opt_centerX || 0; //optional centre of rotation + var centerY = opt_centerY || 0; + // convert to anti-cw and radians + if (direction == 'cw') + degrees = 360 - degrees; + var angleRads = degrees / 180 * Math.PI; + // transpose to origin + x -= centerX; + y -= centerY; + // apply matrix + if (round == true) { + var newX = Math.round(Math.cos(angleRads) * x - Math.sin(angleRads) * y); + var newY = Math.round(Math.sin(angleRads) * x + Math.cos(angleRads) * y); + } else { + var newX = Math.cos(angleRads) * x - Math.sin(angleRads) * y; + var newY = Math.sin(angleRads) * x + Math.cos(angleRads) * y; + } + // un-transpose + newX += centerX; + newY += centerY; + return { + x: newX, + y: newY + }; +} + +var variArray = ['a', 'b', 'c', 'd', 'g', 'h', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'y', 'y']; +function equivExpressions(string1, string2) { // checks if two algebraic expressions (as strings in x only) are equivalent + if (string1 == '' || string2 == '') { + return false; + } + var equivalent = true; + // select 100 * 2 random numbers of different magnitudes + for (mag = 0; mag < 10; mag++) { + for (num = 0; num < 10; num++) { + var x = Math.random() * Math.pow(10, mag); + var val1 = eval(string1); + var val2 = eval(string2); + // get percentage error between values + var pError = 0; + if (val1 !== 0) { + pError = Math.abs((val1 - val2) / val1); + } else if (val2 !== 0) { + pError = Math.abs((val1 - val2) / val2); + } + if (pError > 0.00001) { + equivalent = false; + } + x = -1 * x; + val1 = eval(string1); + val2 = eval(string2); + pError = 0; + if (val1 !== 0) { + pError = Math.abs((val1 - val2) / val1); + } else if (val2 !== 0) { + pError = Math.abs((val1 - val2) / val2); + } + if (pError > 0.00001) { + equivalent = false; + } + } + } + return equivalent +} + +function buildQuadraticExp(d, e, f, g, h, vari) { + if (un(h)) + h = 1; + var a = h * d * f; + var b = h * (d * g + e * f); + var c = h * e * g; + return buildQuadratic(a, b, c, d, e, f, g, h, vari); +} +function buildQuadratic(a, b, c, d, e, f, g, h, vari) { + // ax^2 + bx + c = h(dx + e)(fx + g) + if (!vari) { + var variNum = ran(0, 50); + vari = 'x'; + if (variNum < 19) + vari = variArray[variNum]; + } + var p, + q, + r, + exp, + fac, + compSq, + disc, + qText, + rText, + root1Text, + root2Text, + vertexText, + expJS, + factors = [], + factorisedForms = [], + expandedTerms = [], + expandedForms = []; + + // create the expanded form text + var aSign = ''; + var aFirstSign = ''; + var aTerm = String(Math.abs(a)) + vari; + var aTermJS = String(Math.abs(a)) + '*Math.pow(' + vari + ',2)'; + if (a > 0) + aSign = ' + '; + if (a < 0) + aSign = ' - '; + if (a < 0) + aFirstSign = '-'; + if (a == 1 || a == -1) + aTerm = vari; + if (a == 1) + aTermJS = 'Math.pow(' + vari + ',2)'; + if (a == -1) + aTermJS = '-Math.pow(' + vari + ',2)'; + if (a == 0) + aTerm = ''; + + var bSign = ''; + var bFirstSign = ''; + var bTerm = String((Math.abs(b))) + vari; + var bTermJS = String((Math.abs(b))) + '*' + vari; + if (b > 0) + bSign = ' + '; + if (b < 0) + bSign = ' - '; + if (b < 0) + bFirstSign = '-'; + if (b == 1 || b == -1) + bTerm = vari; + if (b == 1 || b == -1) + bTermJS = vari; + if (b == -1) + bTermJS = vari; + if (b == 0) + bTerm = ''; + if (b == 0) + bTermJS = ''; + + var cSign = ''; + var cFirstSign = ''; + var cTerm = String(Math.abs(c)); + var cTermJS = String(Math.abs(c)); + if (c > 0) + cSign = ' + '; + if (c < 0) + cSign = ' - '; + if (c < 0) + cFirstSign = '-'; + if (c == 0) + cTermJS = ''; + if (c == 0) + cTerm = ''; + + var expTermsMathsText = [[aFirstSign, aTerm], [bFirstSign, bTerm], [cFirstSign, cTerm]]; + var expFormsMathsText = [ + [aFirstSign + aTerm, ['power', false, '2'], bSign, bTerm, cSign, cTerm], + [aFirstSign + aTerm, ['power', false, '2'], cSign, cTerm, bSign, bTerm], + [bFirstSign + bTerm + aSign + aTerm, ['power', false, '2'], cSign + cTerm], + [bFirstSign + bTerm + cSign + cTerm + aSign + aTerm, ['power', false, '2']], + [cFirstSign + cTerm + aSign + aTerm, ['power', false, '2'], bSign + bTerm], + [cFirstSign + cTerm + bSign + bTerm + aSign + aTerm, ['power', false, '2']] + ]; + var expTermsJS = [aTermJS, bTermJS, cTermJS]; + var expFormsJS = [ + aFirstSign + aTermJS + bSign + bTermJS + cSign + cTermJS, + aFirstSign + aTermJS + cSign + cTermJS + bSign + bTermJS, + bFirstSign + bTermJS + aSign + aTermJS + cSign + cTermJS, + bFirstSign + bTermJS + cSign + cTermJS + aSign + aTermJS, + cFirstSign + cTermJS + aSign + aTermJS + bSign + bTermJS, + cFirstSign + cTermJS + bSign + bTermJS + aSign + aTermJS, + ] + + if (a > 0 || (a < 0 && b < 0 && c < 0)) { + exp = expFormsMathsText[0]; + expJS = expFormsJS[0]; + } else if (c > 0) { + exp = expFormsMathsText[5]; + expJS = expFormsJS[5]; + } else { + exp = expFormsMathsText[3]; + expJS = expFormsJS[3]; + } + + // create the factorised forms + var factors = constructFactors(h, d, e, f, g, vari); + var factorisedForms = constructFactorisedForms(factors); + + var hFactors = []; + for (var hF = h; hF > 1; hF--) { + if (h % hF == 0) + hFactors.push(hF); + } + + var compositeFactors = []; + var partiallyFactorisedForms = []; + + //construct sets of factors; + + if (typeof d !== 'undefined' && typeof e !== 'undefined' && typeof f !== 'undefined' && typeof g !== 'undefined') { + if (d < 0) { + bracket1 = '(' + e + " - "; + var dText = Math.abs(d); + if (d == -1) + dText = ''; + bracket1 += dText + vari + ')' + } else { + var eSign = " + "; + if (e < 0) { + eSign = " - " + }; + var dText = d; + if (d == 1) + dText = ""; + if (d == -1) + dText = "-"; + bracket1 = "(" + dText + vari + eSign + Math.abs(e) + ")"; + } + + if (f < 0) { + bracket2 = '(' + g + " - "; + var fText = Math.abs(f); + if (f == -1) + fText = ''; + bracket2 += fText + vari + ')' + } else { + var gSign = " + "; + if (g < 0) { + gSign = " - " + }; + var fText = f; + if (f == 1) + fText = ""; + if (f == -1) + fText = "-"; + bracket2 = "(" + fText + vari + gSign + Math.abs(g) + ")"; + } + + fac = bracket1 + bracket2; + if (Math.random() < 0.5) + fac = bracket2 + bracket1; + if (bracket1 == bracket2) { + fac = bracket1 + String.fromCharCode(0x00B2) + }; + + if (e == 0) { + var dText = d; + if (d == 1) + dText = ''; + if (d == -1) + dText = '-'; + bracket1 = dText + vari; + + } + + if (g == 0) { + var fText = f; + if (f == 1) + fText = ''; + if (f == -1) + fText = '-'; + bracket2 = fText + vari; + fac = bracket2 + bracket1; + } + + if (typeof h !== 'undefined') { + var hTerm = h; + if (h == 1) + hTerm = ''; + if (h == -1) + hTerm = '-'; + fac = hTerm + fac; + + } + } + + // create the completed square form text + p = a; + q = b / (2 * a); + r = c - (b * b) / (4 * a); + compSq = ''; + if (p > 0 || p < 0 && r <= 0) { + var pTerm = String(p); + if (p == 1) + pTerm = ''; + if (p == -1) + pTerm = '-'; + var qSign = " + "; + if (q < 0) + qSign = " - "; + var qTerm = toMathsText(b, 2 * a); + var rSign = " + "; + if (r < 0) + rSign = " - "; + var rTerm = toMathsText(4 * a * c - b * b, 4 * a); + if (r == 0) { + rSign = ''; + rTerm = ''; + } + compSq = [pTerm + '(' + vari + qSign, qTerm, ')', ['pow', '', '2'], rSign, rTerm]; + } else if (p < 0 && r > 0) { // r - p(x +- q)^2 + var rTerm = toMathsText(4 * a * c - b * b, 4 * a); + var qSign = " + "; + if (q < 0) + qSign = " - "; + var qTerm = toMathsText(b, 2 * a); + var pTerm = toMathsText(p); + if (p == -1) + pTerm = ''; + compSq = [rTerm, ' - ' + pTerm + '(' + vari + qSign, qTerm, ')', ['pow', '', '2']]; + } + + // makes qText, rText and vertexText + if (q < 0) { + qText = ["-", qTerm]; + if (r < 0) { + rText = ["-", rTerm]; + vertexText = ['(', qTerm, ', -', rTerm, ')']; + } else { + rText = rTerm; + vertexText = ['(', qTerm, ', ', rTerm, ')']; + } + } else { + qText = qTerm; + if (r < 0) { + rText = ["-", rTerm]; + vertexText = ['(-', qTerm, ', -', rTerm, ')']; + } else { + rText = rTerm; + vertexText = ['(-', qTerm, ', ', rTerm, ')']; + } + } + + disc = b * b - 4 * a * c; + + // solves ax^2 + bx + c = 0 + var root1 = (-1 * b + Math.pow(b * b - 4 * a * c, 0.5)) / (2 * a); + var root2 = (-1 * b - Math.pow(b * b - 4 * a * c, 0.5)) / (2 * a); + + // makes root1text and root2text + // if non-surd solutions + if (Math.pow(b * b - 4 * a * c, 0.5) == Math.round(Math.pow(b * b - 4 * a * c, 0.5))) { + var root1sign = ''; + if ((-1 * b + Math.pow(b * b - 4 * a * c, 0.5)) / (2 * a) < 0) + root1sign = '-'; + root1text = [root1sign, toMathsText((-1 * b + Math.pow(b * b - 4 * a * c, 0.5)), (2 * a))]; + var root2sign = ''; + if ((-1 * b - Math.pow(b * b - 4 * a * c, 0.5)) / (2 * a) < 0) + root2sign = '-'; + root2text = [root2sign, toMathsText((-1 * b - Math.pow(b * b - 4 * a * c, 0.5)), (2 * a))]; + } else { + var surd = simpSurd(b * b - 4 * a * c, 0.5); + root1text = [toMathsText((-1 * b), (2 * a)), ' + ', toMathsText(1, (2 * a)), surd.mathsText]; + root2text = [toMathsText((-1 * b), (2 * a)), ' - ', toMathsText(1, (2 * a)), surd.mathsText]; + } + + return { + exp: exp, + fac: fac, + compSq: compSq, + disc: disc, + a: a, + b: b, + c: c, + d: d, + e: e, + f: f, + g: g, + h: h, + p: p, + q: q, + r: r, + vari: vari, + root1: root1, + root2: root2, + qText: qText, + rText: rText, + vertexText: vertexText, + root1text: root1text, + root2text: root2text, + factors: factors, + factorisedForms: factorisedForms, + expTermsMathsText: expTermsMathsText, + expFormsMathsText: expFormsMathsText, + expTermsJS: expTermsJS, + expFormsJS: expFormsJS, + expJS: expJS + } +} +function quadratic(level, vari) { + if (!vari) { + var variNum = ran(0, 50); + vari = 'x'; + if (variNum < 19) + vari = variArray[variNum]; + } + // quadratic is (dx + e)(fx + g) = ax^2 + bx + c + var a, + b, + c, + d, + e, + f, + g, + h, + p, + q, + r, + exp, + fac, + compSq, + disc, + qText, + rText, + root1Text, + root2Text, + vertexText, + expJS; + var factors = []; + var factorisedForms = []; + var expandedTerms = []; + var expandedForms = []; + switch (level) { + case 1: + // (x +- e)(x +- g) || dx(fx +- g) + do { + d = 1; + e = ran(-10, 10); + f = 1; + g = ran(-10, 10); + } while (g == 0) + + if (e == 0) { + d = ran(1, 3); + do { + f = ran(1, 3); + } while (hcf(f, g) !== 1) + } + + a = d * f; + b = d * g + e * f; + c = e * g; + h = 1; + + break; + case 2: + // h(x +- e)(x +- g), where h > 1 + do { + d = 1; + e = ran(-10, 10); + f = 1; + g = ran(-10, 10); + } while (e == 0 || g == 0) + + a = d * f; + b = d * g + e * f; + c = e * g; + h = [2, 2, 3, 4, 5, 10][ran(0, 5)]; + break; + case 3: + // h(dx +- e)(fx +- g) where d = 1 or f = 1 but not both + do { + d = ran(1, 6); + e = ran(-10, 10); + f = ran(1, 6); + g = ran(-10, 10); + h = Math.max(1, ran(-15, 5)); + } while (e == 0 || g == 0 || (d == 1 && f == 1) || (d !== 1 && f !== 1) || hcf(d, e) > 1 || hcf(f, g) > 1) + + a = h * d * f; + b = h * (d * g + e * f); + c = h * e * g; + + break; + case 4: + // h(dx +- e)(fx +- g) where d >= 1 and f >= 1 + do { + d = ran(1, 6); + e = ran(-10, 10); + f = ran(1, 6); + g = ran(-10, 10); + h = Math.max(1, ran(-15, 5)); + } while (e == 0 || g == 0 || (d == 1 && f == 1) || hcf(d, e) > 1 || hcf(f, g) > 1) + + a = h * d * f; + b = h * (d * g + e * f); + c = h * e * g; + + break; + case 5: + // h(e - dx)(fx +- g) + do { + d = ran(-6, -1); + e = ran(1, 10); + f = ran(1, 6); + g = ran(-10, 10); + h = Math.max(1, ran(-15, 5)); + } while (g == 0 || hcf(d, e) > 1 || hcf(f, g) > 1) + + a = h * d * f; + b = h * (d * g + e * f); + c = h * e * g; + + break; + case 6: + // +-h(+-dx +- e)(+-fx +- g) where d >= 1 and f >= 1 + do { + d = ran(-3, 3); + e = ran(-10, 10); + f = ran(-3, 3); + g = ran(-10, 10); + h = ran(-3, 3); + } while (e == 0 || g == 0 || d == 0 || f == 0 || h == 0 || (d == 1 && f == 1) || hcf(d, e) > 1 || hcf(f, g) > 1) + + if (d < 0 && e < 0) { + d = -d; + e = -e; + h = -h; + } + if (f < 0 && g < 0) { + f = -f; + g = -g; + h = -h; + } + + a = h * d * f; + b = h * (d * g + e * f); + c = h * e * g; + + break; + case 7: + // generates a quadratic ax^2 +- bx +- c that doesn't factorise + do { + a = ran(1, 5); + b = ran(-100, 100); + c = ran(-100, 100); + var disc = Math.pow(b * b - 4 * a * c, 0.5); + } while (disc == Math.round(disc)); + h = 1; + + break; + case 8: + // generates a quadratic +-ax^2 +- bx +- c that doesn't factorise + do { + a = ran(-10, 10); + b = ran(-100, 100); + c = ran(-100, 100); + var disc = Math.pow(b * b - 4 * a * c, 0.5); + } while (a == 0 || disc == Math.round(disc) || (b == 0 && c == 0)); + h = 1; + + break; + + } + + return buildQuadratic(a, b, c, d, e, f, g, h, vari); +} +function sketchQuad(context, quad, gridLeft, gridTop, gridWidth, gridHeight, xMin, xMax, yMin, yMax, yIntLabel, xIntLabel, vertexLabel, opt_fontSize) { + // draws a sketch of the graph of a quadratic + if (typeof yIntLabel !== 'boolean') + yIntLabel = false; + if (typeof xIntLabel !== 'boolean') + xIntLabel = false; + if (typeof vertexLabel !== 'boolean') + vertexLabel = false; + if (quad.q == 0 && vertexLabel == true) + yIntLabel = true; + var fontSize = opt_fontSize || (Math.min(gridWidth, gridHeight) / 13); + + if (quad.a > 0) { + // need to solve ax^2 + bx + c = yMax + var maxVal = solveQuad(quad.a, quad.b, quad.c - yMax); + } else { + // need to solve ax^2 + bx + c = yMin + var maxVal = solveQuad(quad.a, quad.b, quad.c - yMin); + } + var xStart = Math.max(xMin, maxVal.x2); + var yStart = quad.a * xStart * xStart + quad.b * xStart + quad.c; + var xFin = Math.min(xMax, maxVal.x1); + var yFin = quad.a * xFin * xFin + quad.b * xFin + quad.c; + + var controlPoint = getBezierCurveForQuad(quad.a, quad.b, quad.c, xStart, yStart, xFin, yFin); + + var minPoint = { + x: -1 * quad.q, + y: quad.r + } + + var gridSta = convertGridCoordsToCanvas(xStart, yStart, gridLeft, gridTop, gridWidth, gridHeight, xMin, xMax, yMin, yMax); + var gridFin = convertGridCoordsToCanvas(xFin, yFin, gridLeft, gridTop, gridWidth, gridHeight, xMin, xMax, yMin, yMax); + var gridCon = convertGridCoordsToCanvas(controlPoint.x, controlPoint.y, gridLeft, gridTop, gridWidth, gridHeight, xMin, xMax, yMin, yMax); + var gridMin = convertGridCoordsToCanvas(minPoint.x, minPoint.y, gridLeft, gridTop, gridWidth, gridHeight, xMin, xMax, yMin, yMax); + var gridRoot1 = convertGridCoordsToCanvas(quad.root1, 0, gridLeft, gridTop, gridWidth, gridHeight, xMin, xMax, yMin, yMax); + var gridRoot2 = convertGridCoordsToCanvas(quad.root2, 0, gridLeft, gridTop, gridWidth, gridHeight, xMin, xMax, yMin, yMax); + var gridYInt = convertGridCoordsToCanvas(0, quad.c, gridLeft, gridTop, gridWidth, gridHeight, xMin, xMax, yMin, yMax); + + context.lineWidth = 1; + context.strokeStyle = '#666'; + context.beginPath(); + context.moveTo(gridLeft, gridTop + 0.5 * gridHeight); + context.lineTo(gridLeft + gridWidth, gridTop + 0.5 * gridHeight); + context.moveTo(gridLeft + gridWidth * 0.5, gridTop); + context.lineTo(gridLeft + gridWidth * 0.5, gridTop + gridHeight); + context.stroke(); + context.strokeStyle = '#000'; + context.beginPath() + context.moveTo(gridSta.x, gridSta.y); + context.quadraticCurveTo(gridCon.x, gridCon.y, gridFin.x, gridFin.y); + if (vertexLabel == true) { + context.moveTo(gridMin.x - 5, gridMin.y - 5); + context.lineTo(gridMin.x + 5, gridMin.y + 5); + context.moveTo(gridMin.x - 5, gridMin.y + 5); + context.lineTo(gridMin.x + 5, gridMin.y - 5); + } + context.stroke(); + + context.font = "14px Arial"; + context.textBaseline = "middle"; + context.fillStyle = "#000"; + context.textAlign = 'center'; + if (quad.disc == 0) { + if (xIntLabel == true || vertexLabel == true) + drawMathsText(context, quad.root1text, fontSize, gridRoot1.x, gridRoot1.y + 15, true, '', 'center', 'middle', '#000', 'draw', '#000'); + if (yIntLabel == true) + drawMathsText(context, [String(quad.c)], fontSize, gridYInt.x + 5, gridYInt.y, true, '', 'left', 'middle', '#000', 'draw', '#000'); + + } else if (quad.a > 0) { + if (xIntLabel == true) + drawMathsText(context, quad.root1text, fontSize, gridRoot1.x + 4, gridRoot1.y + 12, true, '', 'left', 'middle', '#000', 'draw'); + if (xIntLabel == true) + drawMathsText(context, quad.root2text, fontSize, gridRoot2.x - 4, gridRoot1.y + 12, true, '', 'right', 'middle', '#000', 'draw'); + if (quad.b !== 0) { + var vertexY = gridMin.y + 20; + if (xIntLabel == true && vertexY - (gridTop + 0.5 * gridHeight) < 40) + vertexY = gridTop + 0.5 * gridHeight + 40; + if (vertexLabel == true) + drawMathsText(context, quad.vertexText, fontSize, gridMin.x, vertexY, true, '', 'center', 'middle', '#000', 'draw'); + } + if (quad.c !== 0) { + if (quad.q <= 0) { + if (quad.b !== 0) { + if (yIntLabel == true) + drawMathsText(context, [String(quad.c)], fontSize, gridYInt.x - 5, gridYInt.y, true, '', 'right', 'middle', '#000', 'draw'); + } else { + if (yIntLabel == true) + drawMathsText(context, [String(quad.c)], fontSize, gridYInt.x - 5, gridYInt.y + 15, true, '', 'right', 'middle', '#000', 'draw'); + } + } else { + if (quad.b !== 0) { + if (yIntLabel == true) + drawMathsText(context, [String(quad.c)], fontSize, gridYInt.x + 5, gridYInt.y, true, '', 'left', 'middle', '#000', 'draw'); + } else { + if (yIntLabel == true) + drawMathsText(context, [String(quad.c)], fontSize, gridYInt.x + 5, gridYInt.y + 15, true, '', 'left', 'middle', '#000', 'draw'); + } + } + } + } else { + // quads with a < 0 + + } +} +function constructFactors(z0, z1, z2, z3, z4, vari) { + // z0 is h + var hSign = '+'; + var hOppSign = '-'; + var hFirstSign = ''; + var hOppFirstSign = '-'; + if (z0 < 0) { + hSign = '-'; + hOppSign = '+'; + hFirstSign = '-'; + hOppFirstSign = ''; + } + var hTerm = String(Math.abs(z0)); + if (z0 == 1 || z0 == -1) + hTerm = ''; + + // z1 is d (ie.dx) + var dSign = '+'; + var dOppSign = '-'; + var dFirstSign = ''; + var dOppFirstSign = '-'; + if (z1 < 0) { + dSign = '-'; + dOppSign = '+'; + dFirstSign = '-'; + dOppFirstSign = ''; + } + var dTerm = String(Math.abs(z1)) + vari; + if (z1 == 1 || z1 == -1) + dTerm = vari; + + // z2 is e + var eSign = '+'; + var eOppSign = '-'; + var eFirstSign = ''; + var eOppFirstSign = '-'; + if (z2 < 0) { + eSign = '-'; + eOppSign = '+'; + eFirstSign = '-'; + eOppFirstSign = ''; + } + var eTerm = String(Math.abs(z2)); + if (z2 == 0) { + eSign = ''; + eOppSign = ''; + eFirstSign = ''; + eOppFirstSign = ''; + eTerm = ''; + } + + // z3 is f (ie.fx) + var fSign = '+'; + var fOppSign = '-'; + var fFirstSign = ''; + var fOppFirstSign = '-'; + if (z3 < 0) { + fSign = '-'; + fOppSign = '+'; + fFirstSign = '-'; + fOppFirstSign = ''; + } + var fTerm = String(Math.abs(z3)) + vari; + if (z3 == 1 || z3 == -1) + fTerm = vari; + + // z4 is g + var gSign = '+'; + var gOppSign = '-'; + var gFirstSign = ''; + var gOppFirstSign = '-'; + if (z4 < 0) { + gSign = '-'; + gOppSign = '+'; + gFirstSign = '-'; + gOppFirstSign = ''; + } + var gTerm = String(Math.abs(z4)); + if (z4 == 0) { + gSign = ''; + gOppSign = ''; + gFirstSign = ''; + gOppFirstSign = ''; + gTerm = ''; + } + + return [ + + [hFirstSign + hTerm, dFirstSign + dTerm + eSign + eTerm, fFirstSign + fTerm + gSign + gTerm], + [hFirstSign + hTerm, eFirstSign + eTerm + dSign + dTerm, fFirstSign + fTerm + gSign + gTerm], + [hFirstSign + hTerm, dFirstSign + dTerm + eSign + eTerm, gFirstSign + gTerm + fSign + fTerm], + [hFirstSign + hTerm, eFirstSign + eTerm + dSign + dTerm, gFirstSign + gTerm + fSign + fTerm], + + [hOppFirstSign + hTerm, dOppFirstSign + dTerm + eOppSign + eTerm, fFirstSign + fTerm + gSign + gTerm], + [hOppFirstSign + hTerm, eOppFirstSign + eTerm + dOppSign + dTerm, fFirstSign + fTerm + gSign + gTerm], + [hOppFirstSign + hTerm, dOppFirstSign + dTerm + eOppSign + eTerm, gFirstSign + gTerm + fSign + fTerm], + [hOppFirstSign + hTerm, eOppFirstSign + eTerm + dOppSign + dTerm, gFirstSign + gTerm + fSign + fTerm], + + [hOppFirstSign + hTerm, dFirstSign + dTerm + eSign + eTerm, fOppFirstSign + fTerm + gOppSign + gTerm], + [hOppFirstSign + hTerm, eFirstSign + eTerm + dSign + dTerm, fOppFirstSign + fTerm + gOppSign + gTerm], + [hOppFirstSign + hTerm, dFirstSign + dTerm + eSign + eTerm, gOppFirstSign + gTerm + fOppSign + fTerm], + [hOppFirstSign + hTerm, eFirstSign + eTerm + dSign + dTerm, gOppFirstSign + gTerm + fOppSign + fTerm], + + [hFirstSign + hTerm, dOppFirstSign + dTerm + eOppSign + eTerm, fOppFirstSign + fTerm + gOppSign + gTerm], + [hFirstSign + hTerm, eOppFirstSign + eTerm + dOppSign + dTerm, fOppFirstSign + fTerm + gOppSign + gTerm], + [hFirstSign + hTerm, dOppFirstSign + dTerm + eOppSign + eTerm, gOppFirstSign + gTerm + fOppSign + fTerm], + [hFirstSign + hTerm, eOppFirstSign + eTerm + dOppSign + dTerm, gOppFirstSign + gTerm + fOppSign + fTerm], + + ] + +} +function constructFactorisedForms(factorsArray) { + var returnArray = []; + for (var set = 0; set < factorsArray.length; set++) { + var fac = [factorsArray[set][0], factorsArray[set][1], factorsArray[set][2]]; + var facBrac = ['(' + fac[0] + ')', '(' + fac[1] + ')', '(' + fac[2] + ')']; + var bracReqFirst = [false, true, true]; + var bracReq = [true, true, true]; + + for (br = 0; br < fac.length; br++) { + if (fac[br] == '-') + facBrac[br] = '(-1)'; + // if the factor doesn't contain + or -, no bracket required + if (fac[br].indexOf('-') == -1 && fac[br].indexOf('+') == -1) + bracReq[br] = false; + } + + var first; + var second; + var third; + + for (br1 = 0; br1 < 3; br1++) { + first = facBrac[br1]; + if (bracReqFirst[br1] == false) + first = fac[br1]; + for (br2 = 0; br2 < 3; br2++) { + if (br2 !== br1) { + second = facBrac[br2]; + if (bracReq[br2] == false) + second = fac[br2]; + for (br3 = 0; br3 < 3; br3++) { + if (br3 !== br1 && br3 !== br2) { + third = facBrac[br3]; + if (bracReq[br3] == false) + third = fac[br3]; + + if (returnArray.indexOf(first + second + third) == -1) { + returnArray.push(first + second + third); + } + if (first == second && second == third && first !== '') { + if (returnArray.indexOf('Math.pow(' + first + ',3)') == -1) + returnArray.push('Math.pow(' + first + ',3)'); + } + if (first == second && first !== '') { + if (returnArray.indexOf('Math.pow(' + first + ',2)' + second) == -1) + returnArray.push('Math.pow(' + first + ',2)' + second); + } + if (second == third && second !== '') { + if (returnArray.indexOf(first + 'Math.pow(' + second + ',2)') == -1) + returnArray.push(first + 'Math.pow(' + second + ',2)'); + } + } + } + } + } + } + } + return returnArray; +} +function toMathsText(num, num2) { + if (typeof num2 == 'undefined') { + num = Math.abs(num); + if (num == Math.round(num)) { + return String(num); + } else { + // if num is negative? + var frac = decToFrac(num); + return ['frac', String(frac.num), String(frac.denom)]; + } + } else { + if (num == 0) + return '0'; + var pos = true; + if (num / num2 < 0) + pos = false; + num = Math.abs(num) + num2 = Math.abs(num2); + var divisor = hcf(num, num2); + num = num / divisor; + num2 = num2 / divisor; + if (num2 == 1) { + if (pos == true) { + return String(num); + } else { + return "-" + String(num); + } + } else { + if (pos == true) { + return ['frac', [String(num)], [String(num2)]]; + } else { + return ['-', ['frac', [String(num)], [String(num2)]]]; + } + } + } +} +function addFracs(num1, denom1, num2, denom2) { + var denom = denom1 * denom2 / hcf(denom1, denom2); + var num = num1 * (denom / denom1) + num2 * (denom / denom2); + //console.log(num,denom); + return toMathsText(num, denom); +} +function simplifyFrac(object, textArrayOrObject) { + var textArray = boolean(textArrayOrObject, true); + + var positive = true; + if (object.num / object.denom < 0) + positive = false; + if (object.num == 0) { + if (textArray == true) { + return ["0"]; + } else { + return { + num: 0, + denom: 1 + }; + } + } + while (Math.round(object.num) !== object.num || Math.round(object.denom) !== object.denom) { + object.num = object.num * 10; + object.denom = object.denom * 10; + } + + var num = Math.abs(object.num); + var denom = Math.abs(object.denom); + var multOfPi = false; + if (typeof object.multOfPi == 'boolean') + multOfPi = object.multOfPi; + var newNum = num / hcf(num, denom); + var newDenom = denom / hcf(num, denom); + + if (textArray == true) { // if returning a textArray + if (multOfPi == false) { + if (newDenom == 1) { + if (positive == true) { + return String(newNum); + } else { + return "-" + String(newNum); + } + } else { + if (positive == true) { + return ['frac', [String(newNum)], [String(newDenom)]] + } else { + return ['-', ['frac', [String(newNum)], [String(newDenom)]]] + } + } + } else { + if (newDenom == 1) { + if (positive == true) { + if (newNum == 1) { + return String.fromCharCode(0x03C0); + } else { + return String(newNum) + String.fromCharCode(0x03C0); + } + } else { + if (newNum == 1) { + return "-" + String.fromCharCode(0x03C0); + } else { + return "-" + String(newNum) + String.fromCharCode(0x03C0); + } + } + } else { + if (positive == true) { + if (newNum == 1) { + return ['frac', [String.fromCharCode(0x03C0)], [String(newDenom)]]; + } else { + return ['frac', [String(newNum) + String.fromCharCode(0x03C0)], [String(newDenom)]]; + } + } else { + if (newNum == 1) { + return ["-", ['frac', [String.fromCharCode(0x03C0)], [String(newDenom)]]]; + } else { + return ["-", ['frac', [String(newNum) + String.fromCharCode(0x03C0)], [String(newDenom)]]]; + } + } + } + } + } else { // if returning an object + if (positive == true) { + return { + num: newNum, + denom: newDenom + }; + } else { + return { + num: -1 * newNum, + denom: newDenom + }; + } + } +} +function simplifyFrac2(frac) { + var positive = true; + if (frac[0] / frac[1] < 0) + positive = false; + if (frac[0] == 0) + return [0, 1]; + while (Math.round(frac[0]) !== frac[0] || Math.round(frac[1]) !== frac[1]) { + frac[0] = frac[0] * 10; + frac[1] = frac[1] * 10; + } + + var num = Math.abs(frac[0]); + var denom = Math.abs(frac[1]); + var newNum = num / hcf(num, denom); + var newDenom = denom / hcf(num, denom); + if (positive == true) { + return [newNum, newDenom]; + } else { + return [-1 * newNum, newDenom]; + } +} +function addFracs2(frac1, frac2) { + var denom = frac1[1] * frac2[1] / hcf(frac1[1], frac2[1]); + var num = frac1[0] * (denom / frac1[1]) + frac2[0] * (denom / frac2[1]); + return [num, denom]; +} +function simpSurd(c) { + //takes sqrt(c) and returns simplified: a*sqrt(b) + var a, + b, + mathsTextSurd; + + for (fac = 1; fac <= c; fac++) { + if (Math.sqrt(c / fac) == Math.round(Math.sqrt(c / fac))) { + a = Math.sqrt(c / fac); + b = fac; + break; + } + } + + var aTerm = String(a); + mathsTextSurd = [String(a), ['sqrt', [String(b)]]]; + if (a == 1 && b > 1) + mathsTextSurd = [['sqrt', [String(b)]]] + if (b == 1) + mathsTextSurd = [String(a)]; + + return { + a: a, + b: b, + mathsText: mathsTextSurd + }; +} +function primeFactors(num) { + var primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131]; + if (num == 1) + return ['1']; + if (num > 131) + return ['']; // too big! + if (primes.indexOf(num) > -1) + return [String(num)]; + var primeFactors = []; + do { + for (var i = 0; i < primes.length; i++) { + if (num % primes[i] == 0) { + primeFactors.push(primes[i]); + num = num / primes[i]; + break; + } + } + } while (num > 1); + var returnArray = []; + for (var i = 0; i < primeFactors.length; i++) { + var repeated = 1; + for (var j = i + 1; j < primeFactors.length; j++) { + if (primeFactors[i] == primeFactors[j]) + repeated++; + } + if (i > 0) + returnArray.push(String.fromCharCode(0x00D7)) + returnArray.push(String(primeFactors[i])); + if (repeated > 1) { + returnArray.push(['power', false, [String(repeated)]]); + i += repeated - 1; + } + } + return returnArray; +} +function cuberoot(x) { + var y = Math.pow(Math.abs(x), 1 / 3); + return x < 0 ? -y : y; +} +function solveCubic(a, b, c, d) { + if (Math.abs(a) < 1e-8) { // Quadratic case, ax^2+bx+c=0 + a = b; + b = c; + c = d; + if (Math.abs(a) < 1e-8) { // Linear case, ax+b=0 + a = b; + b = c; + if (Math.abs(a) < 1e-8) // Degenerate case + return []; + return [-b / a]; + } + + var D = b * b - 4 * a * c; + if (Math.abs(D) < 1e-8) + return [-b / (2 * a)]; + else if (D > 0) + return [(-b + Math.sqrt(D)) / (2 * a), (-b - Math.sqrt(D)) / (2 * a)]; + return []; + } + + // Convert to depressed cubic t^3+pt+q = 0 (subst x = t - b/3a) + var p = (3 * a * c - b * b) / (3 * a * a); + var q = (2 * b * b * b - 9 * a * b * c + 27 * a * a * d) / (27 * a * a * a); + var roots; + + if (Math.abs(p) < 1e-8) { // p = 0 -> t^3 = -q -> t = -q^1/3 + roots = [cuberoot(-q)]; + } else if (Math.abs(q) < 1e-8) { // q = 0 -> t^3 + pt = 0 -> t(t^2+p)=0 + roots = [0].concat(p < 0 ? [Math.sqrt(-p), -Math.sqrt(-p)] : []); + } else { + var D = q * q / 4 + p * p * p / 27; + if (Math.abs(D) < 1e-8) { // D = 0 -> two roots + roots = [-1.5 * q / p, 3 * q / p]; + } else if (D > 0) { // Only one real root + var u = cuberoot(-q / 2 - Math.sqrt(D)); + roots = [u - p / (3 * u)]; + } else { // D < 0, three roots, but needs to use complex numbers/trigonometric solution + var u = 2 * Math.sqrt(-p / 3); + var t = Math.acos(3 * q / p / u) / 3; // D < 0 implies p < 0 and acos argument in [-1..1] + var k = 2 * Math.PI / 3; + roots = [u * Math.cos(t), u * Math.cos(t - k), u * Math.cos(t - 2 * k)]; + } + } + + // Convert back from depressed cubic + for (var i = 0; i < roots.length; i++) + roots[i] -= b / (3 * a); + + return roots; +} + +function hitAndAlign(object, firstArray, secondArray, offsetX, offsetY) { + //this function aligns a dragged object to the x and y position of another object + //first array is draggable buttons + //second array is the things they are going to hit + //this works for buttons + if (typeof offsetX == 'undefined') { + offsetX = 0 + } else { + offsetX = Number(offsetX) + } + if (typeof offsetY == 'undefined') { + offsetY = 0 + } else { + offsetY = Number(offsetY) + } + var alreadyHitMe = false + for (var j = 0; j < firstArray.length; j++) { + + if (hitTestTwoObjects(object, firstArray[j]) == true && object !== firstArray[j]) { + alreadyHitMe = true + } + + } + + for (var j = 0; j < secondArray.length; j++) { + + if (hitTestTwoObjects(object, secondArray[j]) == true && alreadyHitMe == false) { + + var buttonArray = eval(String(taskTag) + 'button') + var buttonArrayData = eval(String(taskTag) + 'buttonData') + + var butNum = buttonArray.indexOf(object) + var butNum2 = buttonArray.indexOf(secondArray[j]) + + buttonArrayData[butNum][100] = buttonArrayData[butNum2][100] + Number(offsetX) + buttonArrayData[butNum][101] = buttonArrayData[butNum2][101] + Number(offsetY) + resize() + } + + } + +} +function hitAndAlignImages(object, firstArray, secondArray, offsetX, offsetY) { + + if (typeof offsetX == 'undefined') { + offsetX = 0 + } else { + offsetX = Number(offsetX) + } + if (typeof offsetY == 'undefined') { + offsetY = 0 + } else { + offsetY = Number(offsetY) + } + + var alreadyHitMe = false + for (var j = 0; j < firstArray.length; j++) { + + if (hitTestTwoObjects(object, firstArray[j]) == true && object !== firstArray[j]) { + alreadyHitMe = true + } + + } + + for (var j = 0; j < secondArray.length; j++) { + + if (hitTestTwoObjects(object, secondArray[j]) == true && alreadyHitMe == false) { + + var buttonArray = eval(String(taskTag) + 'imageCanvas') + var buttonArrayData = eval(String(taskTag) + 'imageCanvasData') + + var butNum = buttonArray.indexOf(object) + var butNum2 = buttonArray.indexOf(secondArray[j]) + + buttonArrayData[butNum][100] = buttonArrayData[butNum2][100] + Number(offsetX) + buttonArrayData[butNum][101] = buttonArrayData[butNum2][101] + Number(offsetY) + resize() + } + + } + +} +function shuffleImagePositions(arrayOfObjects) { + + var buttonArray = eval(String(taskTag) + 'imageCanvas') + var buttonArrayData = eval(String(taskTag) + 'imageCanvasData') + + currentX = [] + currentY = [] + + //get current x and y positions of all objects in array + + for (n201i = 0; n201i < arrayOfObjects.length; n201i++) { + var object = arrayOfObjects[n201i] + + var butNum = buttonArray.indexOf(object) + + currentX[n201i] = buttonArrayData[butNum][100] + currentY[n201i] = buttonArrayData[butNum][101] + + } + var mixedNumbers = [] + for (n201i = 0; n201i < arrayOfObjects.length; n201i++) { + mixedNumbers[n201i] = n201i + } + mixedNumbers = shuffleArray(mixedNumbers) + + for (n201i = 0; n201i < arrayOfObjects.length; n201i++) { + + //arrayOfObjects = shuffleArray(arrayOfObjects) + //choose random object from array + + var objectN = arrayOfObjects[mixedNumbers[n201i]] + var butNumN = buttonArray.indexOf(objectN) + + buttonArrayData[butNumN][100] = currentX[n201i] + buttonArrayData[butNumN][101] = currentY[n201i] + //arrayOfObjects = arrayOfObjects.splice(ran,1) + } + + resize() +} +function shuffleObjectPositions(arrayOfObjects) { + + var buttonArray = eval(String(taskTag) + 'button') + var buttonArrayData = eval(String(taskTag) + 'buttonData') + + currentX = [] + currentY = [] + + //get current x and y positions of all objects in array + + for (n201i = 0; n201i < arrayOfObjects.length; n201i++) { + var object = arrayOfObjects[n201i] + + var butNum = buttonArray.indexOf(object) + + currentX[n201i] = buttonArrayData[butNum][100] + currentY[n201i] = buttonArrayData[butNum][101] + + } + var mixedNumbers = [] + for (n201i = 0; n201i < arrayOfObjects.length; n201i++) { + mixedNumbers[n201i] = n201i + } + mixedNumbers = shuffleArray(mixedNumbers) + + for (n201i = 0; n201i < arrayOfObjects.length; n201i++) { + + //arrayOfObjects = shuffleArray(arrayOfObjects) + //choose random object from array + + var objectN = arrayOfObjects[mixedNumbers[n201i]] + var butNumN = buttonArray.indexOf(objectN) + + buttonArrayData[butNumN][100] = currentX[n201i] + buttonArrayData[butNumN][101] = currentY[n201i] + //arrayOfObjects = arrayOfObjects.splice(ran,1) + } + + resize() +} + +// array sorting function for key values +// use is: array = array.sort(keySort('/*key*/')); +function keySort(key, desc) { + return function (a, b) { + return desc ? ~~(a[key] < b[key]) : ~~(a[key] > b[key]); + } +} +function arrayMin(array) { + var min = array[0]; + for (var i = 1; i < array.length; i++) { + min = Math.min(min, array[i]); + } + return min; +} +function arrayMax(array) { + var max = array[0]; + for (var i = 1; i < array.length; i++) { + max = Math.max(max, array[i]); + } + return max; +} +function cloneArray(array) { + var newArray = []; + for (var i = 0; i < array.length; i++) { + newArray[i] = array[i].slice(0); + } + return newArray +} + +function hexToRgb(color) { + if (color.indexOf('rgba') > -1) { // if an rgba string + var result = color.substring(5, color.length - 1).replace(/ /g, '').split(','); + return result ? { + r: parseInt(result[0]), + g: parseInt(result[1]), + b: parseInt(result[2]) + } + : null; + } else if (color.indexOf('rgb') > -1) { // if an rgb string + var result = color.substring(5, color.length - 1).replace(/ /g, '').split(','); + return result ? { + r: parseInt(result[0]), + g: parseInt(result[1]), + b: parseInt(result[2]) + } + : null; + } else if (color.indexOf('#') > -1) { // if a hex string + // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") + var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + color = color.replace(shorthandRegex, function (m, r, g, b) { + return r + r + g + g + b + b; + }); + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color); + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + } + : null; + } else { + return color; + } +} +function colorA(color, alpha) { + var colorRGB = hexToRgb(color); + return "rgba(" + colorRGB.r + "," + colorRGB.g + "," + colorRGB.b + "," + alpha + ")"; +} +function invertColor(color) { + if (/^(#)((?:[A-Fa-f0-9]{3}){1,2})$/i.test(color) == true) + color = hexToRgb(color); // if hex, change to rgba + color.r = 255 - color.r; + color.g = 255 - color.g; + color.b = 255 - color.b; + var a = 1; + if (typeof color.a == 'number') + a = color.a; + return "rgba(" + color.r + "," + color.g + "," + color.b + "," + a + ")"; +} +function getShades(color, invert) { + var invert = boolean(invert, false); + if (invert == true) { + var color2 = hexToRgb(invertColor(color)); + } else { + var color2 = hexToRgb(color); + } + var r = color2.r; + var g = color2.g; + var b = color2.b; + + //var rgb = []; + var hex = []; + + if (r == 0 && g == 0 && b == 0) { // if the color is black, switch it to white + r = 255; + g = 255; + b = 255; + } + + // get brightest shade of rgb + if (Math.max(r, g, b) < 255) { + var m = 255 / Math.max(r, g, b); + r = r * m; + g = g * m; + b = b * m; + } + + for (var i = 0; i < 16; i++) { + var r2 = roundToNearest((i / 15) * r, 1); + var g2 = roundToNearest((i / 15) * g, 1); + var b2 = roundToNearest((i / 15) * b, 1); + //rgb[i] = "rgb("+String(r2)+","+String(g2)+","+String(b2)+")"; + var r3 = decToHex2Digits(r2); + var g3 = decToHex2Digits(g2); + var b3 = decToHex2Digits(b2); + hex[i] = "#" + r3 + g3 + b3; + } + + return hex; +} +function decToHex2Digits(num) { + num2 = num.toString(16); + if (num2.length == 1) { + num2 = "0" + num2; + } else if (num2.indexOf('.') == 1) { + num2 = "0" + num2.slice(0, 1); + } else { + num2 = num2.slice(0, 2); + } + return num2; +} +function testShades(ctx, color, invert) { + var colors = getShades(color, invert); + var ctx = draw.drawctx; + ctx.strokeStyle = '#000'; + for (var i = 0; i < 16; i++) { + ctx.fillStyle = colors[i]; + ctx.fillRect(50 + i * 50, 50, 50, 50); + ctx.strokeRect(50 + i * 50, 50, 50, 50); + } +} +function getColor(r, g, b, a) { + if (un(a)) + a = 1; + return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; +} +function getScaleColor(value,max,saturation) { + if (un(saturation)) saturation = 1; + var perc = Math.round((value / max) * 100); + var h = Math.floor(perc * 1.2); + var s = saturation; + var v = 1; + return hsv2rgb(h, s, v); +} +function hsv2rgb (h, s, v) { + // adapted from http://schinckel.net/2012/01/10/hsv-to-rgb-in-javascript/ + var rgb, i, data = []; + if (s === 0) { + rgb = [v,v,v]; + } else { + h = h / 60; + i = Math.floor(h); + data = [v*(1-s), v*(1-s*(h-i)), v*(1-s*(1-(h-i)))]; + switch(i) { + case 0: + rgb = [v, data[2], data[0]]; + break; + case 1: + rgb = [data[1], v, data[0]]; + break; + case 2: + rgb = [data[0], v, data[2]]; + break; + case 3: + rgb = [data[0], data[1], v]; + break; + case 4: + rgb = [data[2], data[0], v]; + break; + default: + rgb = [v, data[0], data[1]]; + break; + } + } + return '#' + rgb.map(function(x){ + return ("0" + Math.round(x*255).toString(16)).slice(-2); + }).join(''); +}; + +function collapseLines(lineArray) { + // collapses lines in an array such as: + // [ [[x1,y1],[x2,y2]], [[x3,y3],[x4,y4]], [[x5,y5],[x6,y6]] ] + do { + var joinFound = false; + for (var i = 0; i < lineArray.length; i++) { + if (joinFound == false) { + for (var j = i + 1; j < lineArray.length; j++) { + var x1 = lineArray[i][0][0]; + var y1 = lineArray[i][0][1]; + var x2 = lineArray[i][1][0]; + var y2 = lineArray[i][1][1]; + var x3 = lineArray[j][0][0]; + var y3 = lineArray[j][0][1]; + var x4 = lineArray[j][1][0]; + var y4 = lineArray[j][1][1]; + var m1 = (y2 - y1) / (x2 - x1); + var m2 = (y4 - y3) / (x4 - x3); + // if gradients are equal and point from line 1 is on line 2 + //console.log('x1,y1,x2,y2,x3,y3,x4,y4'); + //console.log('grad/grad: ',mMax/mMin); + //console.log('pointOnLine: ',isPointOnLine([x1,y1],[x3,y3],[x4,y4],3.5)); + if (((Math.abs(m1) == 'Infinity' && Math.abs(m2) == 'Infinity') || Math.abs(m1 - m2) < 0.0001) && isPointOnLine([x1, y1], [x3, y3], [x4, y4], 0.25) == true) { + // if one of the points is between the two points on the other line + if ((x1 >= Math.min(x3, x4) && x1 <= Math.max(x3, x4) && y1 >= Math.min(y3, y4) && y1 <= Math.max(y3, y4)) || (x2 >= Math.min(x3, x4) && x2 <= Math.max(x3, x4) && y2 >= Math.min(y3, y4) && y2 <= Math.max(y3, y4)) || (x3 >= Math.min(x1, x2) && x3 <= Math.max(x1, x2) && y3 >= Math.min(y1, y2) && y3 <= Math.max(y1, y2)) || (x4 >= Math.min(x1, x2) && x4 <= Math.max(x1, x2) && y4 >= Math.min(y1, y2) && y4 <= Math.max(y1, y2))) { + var xMin = Math.min(x1, x2, x3, x4); + var xMax = Math.max(x1, x2, x3, x4); + if (xMin == x1) + var yMin = y1; + if (xMin == x2) + var yMin = y2; + if (xMin == x3) + var yMin = y3; + if (xMin == x4) + var yMin = y4; + if (xMax == x1) + var yMax = y1; + if (xMax == x2) + var yMax = y2; + if (xMax == x3) + var yMax = y3; + if (xMax == x4) + var yMax = y4; + if (xMin == xMax) { + var yMin = Math.min(y1, y2, y3, y4); + var yMax = Math.max(y1, y2, y3, y4); + } + joinFound = true; + lineArray[i][0] = [xMin, yMin]; + lineArray[i][1] = [xMax, yMax]; + lineArray.splice(j, 1); + break; + } + } + } + } + } + } while (joinFound == true); + return lineArray; +} + +function drawBarChart(object) { + /* EXAMPLE USAGE: + drawBarChart({ + ctx:j324buttonctx[0], + left:200, + top:240, + width:500, + height:370, + data:[{value:['0'], freq:17},{value:['1'], freq:6},{value:['2'], freq:3},{value:['3'], freq:1},{value:['4'], freq:2},{value:['5'], freq:0},{value:['6'], freq:1}, + ], + barWidth:[2,1], // bar width to gap width ratio + barColor:'#FCF', + barOutlineColor:'#000', + barOutlineWidth:4, + xLabel:['number of days'], + yLabel:['frequency'], + title:['Number of days absent in a month for a class'], + titlePos:[450,260,200,90], + yMinor:{show:true,step:1,color:'#AAA',width:1,dash:[3,5]}, + yMajor:{show:true,step:2,color:'#888',width:2,dash:[]}, + yMin:0, + yMax:18, + }); + */ + + var ctx = object.ctx || object.context; + var left = object.left; + var top = object.top; + var width = object.width; + var height = object.height; + var data = object.data; + + var barWidth = object.barWidth || [2, 1]; + var barColor = object.barColor || '#FCF'; + var barOutlineColor = object.barOutlineColor || '#000'; + var barOutlineWidth = object.barOutlineWidth || 2; + var xLabel = object.xLabel || ['']; + var yLabel = object.yLabel || ['']; + var title = object.title || ['']; + var titlePos = object.titlePos || [left + 0.5 * width, top + 0.03 * height, 0.4 * width, 0.2 * height]; + + if (typeof object.yMinor == 'object') { + var yMinorShow = boolean(object.yMinor.show, false); + var yMinorStep = object.yMinor.step || 1; + var yMinorColor = object.yMinor.color || '#AAA'; + var yMinorWidth = object.yMinor.width || 1; + var yMinorDash = object.yMinor.dash || []; + } else { + var yMinorShow = false; + var yMinorStep = 1; + var yMinorColor = '#AAA'; + var yMinorWidth = 1; + var yMinorDash = []; + } + + if (typeof object.yMajor == 'object') { + var yMajorShow = boolean(object.yMajor.show, false); + var yMajorStep = object.yMajor.step || 1; + var yMajorColor = object.yMajor.color || '#888'; + var yMajorWidth = object.yMajor.width || 2; + var yMajorDash = object.yMajor.dash || []; + } else { + var yMajorShow = false; + var yMajorStep = 1; + var yMajorColor = '#888'; + var yMajorWidth = 2; + var yMajorDash = []; + } + + if (object.yMin == 'number') { + var yMin = object.yMin; + } else { + var yMin = 0; + } + + if (object.yMax == 'number') { + var yMax = object.yMax; + } else { + var yMax = 0; + for (var i = 0; i < data.length; i++) { + yMax = Math.max(yMax, data[i].freq + 1); + } + var steps = Math.floor(yMax / yMajorStep); + while (yMax % yMajorStep > 0 || yMax / yMajorStep < steps + 1) { + yMax++; + } + } + + // work out the spacing for minor and major steps + var yMinorSpacing = (height * yMinorStep) / (yMax - yMin); + var yMajorSpacing = (height * yMajorStep) / (yMax - yMin); + + // work out the coordinates of the origin + var x0 = left; + var y0 = top + (yMax * height) / (yMax - yMin); + + // work out the actual display position of the origin (ie. at the edge if it is off the grid) + var x0DisplayPos = left; + var y0DisplayPos = top + height; + + ctx.save(); + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + if (yMinorShow == true) { + ctx.beginPath(); + ctx.strokeStyle = yMinorColor; + ctx.lineWidth = yMinorWidth; + if (!ctx.setLineDash) { + ctx.setLineDash = function () {} + } + ctx.setLineDash(yMinorDash); + var yAxisPoint = y0 - yMinorSpacing; + while (yAxisPoint > top) { + if (yAxisPoint < top + height) { + ctx.moveTo(left, yAxisPoint); + ctx.lineTo(left + width, yAxisPoint); + } + yAxisPoint -= yMinorSpacing; + } + var yAxisPoint = y0 + yMinorSpacing; + while (yAxisPoint <= top + height) { + if (yAxisPoint >= top) { + ctx.moveTo(left, yAxisPoint); + ctx.lineTo(left + width, yAxisPoint); + } + yAxisPoint += yMinorSpacing; + } + ctx.stroke(); + } + + if (yMajorShow == true) { + ctx.beginPath(); + ctx.strokeStyle = yMajorColor; + ctx.lineWidth = yMajorWidth; + if (!ctx.setLineDash) { + ctx.setLineDash = function () {} + } + ctx.setLineDash(yMajorDash); + var yAxisPoint = y0 - yMajorSpacing; + while (yAxisPoint > top - 0.00001) { + if (yAxisPoint < (top + height + 0.00001)) { + ctx.moveTo(left, yAxisPoint); + ctx.lineTo(left + width, yAxisPoint); + } + yAxisPoint -= yMajorSpacing; + } + var yAxisPoint = y0 + yMajorSpacing; + while (yAxisPoint < top + height + 0.00001) { + if (yAxisPoint > top - 0.00001) { + ctx.moveTo(left, yAxisPoint); + ctx.lineTo(left + width, yAxisPoint); + } + yAxisPoint += yMajorSpacing; + } + ctx.stroke(); + } + + // draw axes + ctx.beginPath(); + ctx.strokeStyle = '#000'; + ctx.lineWidth = 3; + if (!ctx.setLineDash) { + ctx.setLineDash = function () {} + } + ctx.setLineDash([]); + // if neccesary, draw x-Axis + if (y0 >= top && y0 <= top + height) { + ctx.moveTo(left, y0); + ctx.lineTo(left + width, y0); + } + ctx.stroke(); + ctx.beginPath(); + ctx.strokeStyle = '#000'; + ctx.lineWidth = 3; + // if neccesary, draw y-Axis + if (x0 >= left && x0 <= left + width) { + ctx.moveTo(x0, top); + ctx.lineTo(x0, top + height); + } + ctx.stroke(); + + // draw yAxes numbers + ctx.font = '24px Arial'; + ctx.textAlign = "center"; + ctx.textBaseline = "top"; + ctx.lineWidth = 2; + ctx.strokeStyle = yMajorColor; + ctx.textBaseline = "middle"; + ctx.textAlign = "right"; + ctx.fillStyle = '#000'; + var fontSize = 24; + + // positive y numbers + var yAxisPoint = y0; + var major = 0; + var placeValue = Math.pow(10, Math.floor(Math.log(yMajorStep) / Math.log(10))); + while (yAxisPoint >= top - 0.0001) { + if (yAxisPoint <= top + height) { + var axisValue = Number(roundSF(major * yMajorStep, 5)); + var textWidth = ctx.measureText(String(axisValue)).width + ctx.clearRect(x0DisplayPos - textWidth - 16, yAxisPoint - fontSize * 0.5, textWidth + 3, fontSize); + ctx.beginPath(); + ctx.moveTo(x0DisplayPos, yAxisPoint); + ctx.lineTo(x0DisplayPos - 8, yAxisPoint); + ctx.stroke(); + wrapText(ctx, String(axisValue), x0DisplayPos - 7, yAxisPoint - 2, 50, 40, fontSize + 'px Arial'); + } + major += 1; + yAxisPoint -= yMajorSpacing; + } + // negative y numbers + var yAxisPoint = y0 + yMajorSpacing; + var major = -1; + while (yAxisPoint <= top + height + 0.0001) { + if (yAxisPoint >= top) { + var axisValue = Number(roundSF(major * yMajorStep, 5)); + var textWidth = ctx.measureText(String(axisValue)).width + ctx.clearRect(x0DisplayPos - textWidth - 16, yAxisPoint - fontSize * 0.5, textWidth + 3, fontSize); + ctx.beginPath(); + ctx.moveTo(x0DisplayPos, yAxisPoint); + ctx.lineTo(x0DisplayPos - 8, yAxisPoint); + ctx.stroke(); + wrapText(ctx, String(axisValue), x0DisplayPos - 7, yAxisPoint - 2, 50, 40, fontSize + 'px Arial'); + } + major -= 1; + yAxisPoint += yMajorSpacing; + } + + // draw axes + ctx.strokeStyle = '#000'; + ctx.lineWidth = 4; + ctx.beginPath(); + ctx.moveTo(left, top); + ctx.lineTo(left, top + height); + ctx.lineTo(left + width, top + height); + ctx.stroke(); + + var ySpacing = height / (yMax - yMin); + var xSpacing = width / (data.length * barWidth[0] + (data.length + 1) * barWidth[1]); + var l = []; + + ctx.beginPath(); + for (var i = 0; i < data.length; i++) { + l[i] = left + i * barWidth[0] * xSpacing + (i + 1) * barWidth[1] * xSpacing; + drawMathsText(ctx, data[i].value, 24, l[i] + 0.5 * xSpacing * barWidth[0], top + height + 5, false, [], 'center', 'top', '#000'); + //ctx.moveTo(l[i]+0.5*xSpacing*barWidth[0],top+height); + //ctx.lineTo(l[i]+0.5*xSpacing*barWidth[0],top+height+5); + } + ctx.stroke(); + + ctx.lineWidth = barOutlineWidth; + ctx.strokeStyle = barOutlineColor; + ctx.fillStyle = barColor; + ctx.beginPath(); + + for (var i = 0; i < data.length; i++) { + var t = top + height - ySpacing * data[i].freq; + var h = ySpacing * data[i].freq; + ctx.fillRect(l[i], t, xSpacing * barWidth[0], h); + ctx.strokeRect(l[i], t, xSpacing * barWidth[0], h); + } + + if (arraysEqual(xLabel, ['']) == false) { + text({ + context: ctx, + textArray: ['<><><><>' + xLabel], + left: left + width - 200, + width: 200, + top: top + height + 30, + }); + } + + if (arraysEqual(yLabel, ['']) == false) { + text({ + context: ctx, + textArray: ['<><><><>' + yLabel], + left: left - 50 - 200, + width: 200, + top: top + height * 0.03, + }); + } + + if (arraysEqual(title, ['']) == false) { + text({ + context: ctx, + textArray: ['<><><><>' + title], + left: titlePos[0], + top: titlePos[1], + width: titlePos[2], + maxHeight: titlePos[3], + box: { + color: '#FFC', + dash: [], + border: '#000', + borderWidth: 3, + radius: 5, + pos: [titlePos[0] - 10, titlePos[1] - 5, titlePos[2] + 12, titlePos[3] + 10] + } + // box.pos may need tweaking when text has been improved + }); + } +} + +function isNode(o) { //Returns true if it is a DOM node + return ( + typeof Node === "object" ? o instanceof Node : + o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string"); +} +function isElement(o) { //Returns true if it is a DOM element + return ( + typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2 + o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string"); +} + +/***************************/ +/* VECTOR GEOMETRY */ +/***************************/ + +function getVectorAB(a, b) { + return [b[0] - a[0], b[1] - a[1]]; +} +function getUnitVector(vector) { + var mag = Math.sqrt(Math.pow(vector[0], 2) + Math.pow(vector[1], 2)); + return [vector[0] / mag, vector[1] / mag]; +} +function setVectorMag(vector, mag) { + var unit = getUnitVector(vector); + return [unit[0] * mag, unit[1] * mag]; +} +function getDist(a, b) { + return Math.sqrt(Math.pow(b[0] - a[0], 2) + Math.pow(b[1] - a[1], 2)); +} +function getPerpVector(vector, dir) { + if (un(dir)) + dir = 1; + if (dir == 1) { + return [-1 * vector[1], vector[0]]; + } else { + return [vector[1], -1 * vector[0]]; + } +} +function pointAddVector(point, vector, scalarMult) { + if (un(scalarMult)) + scalarMult = 1; + return [point[0] + scalarMult * vector[0], point[1] + scalarMult * vector[1]]; +} +function getVectorMag(vector) { + return Math.sqrt(Math.pow(vector[0], 2) + Math.pow(vector[1], 2)); +} +function getVectorAngle(vector) { + var angle = Math.atan(vector[1] / vector[0]); + if (vector[0] >= 0 && vector[1] >= 0) + return angle; + if (vector[0] >= 0 && vector[1] < 0) + return angle + 2 * Math.PI; + if (vector[0] < 0) + return angle + Math.PI; +} +function angleToVector(angle, mag) { + if (un(mag)) mag = 1; + return [mag*Math.cos(angle),mag*Math.sin(angle)]; +} +function rotateVector(vector, angle) { + var mag = Math.sqrt(Math.pow(vector[0], 2) + Math.pow(vector[1], 2)); + var theta = getVectorAngle(vector) + angle; + return [mag * Math.cos(theta), mag * Math.sin(theta)]; +} +function getVectorLinesIntersection(p1, v1, p2, v2) { + var lambda = (p1[1] * v2[0] + p2[0] * v2[1] - p1[0] * v2[1] - p2[1] * v2[0]) / (v1[0] * v2[1] - v1[1] * v2[0]); + return pointAddVector(p1, v1, lambda); +} +function getMidpoint(p1, p2) { + return [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2]; +} +function getFootOfPerp(p, v, q) { // foot of perp from point q to line p + kv + var lambda = (q[0] * v[0] + q[1] * v[1] - p[0] * v[0] - p[1] * v[1]) / (v[0] * v[0] + v[1] * v[1]); + return pointAddVector(p, v, lambda); +} +function getPerpDist(p, v, q) { // shortest dist from point q to line p + kv + var foot = getFootOfPerp(p, v, q); + return getDist(q, foot); +} + +var vector = { // generalised 3d versions + getVectorAB: function (a, b) { + if (a.length !== b.length) return null; + var v = []; + for (var i = 0; i < a.length; i++) v[i] = b[i] - a[i]; + return v; + }, + scalarMult: function (a, k) { + var r = []; + for (var i = 0; i < a.length; i++) + r[i] = a[i] * k; + return r; + }, + dotProduct: function (a, b) { + if (a.length !== b.length) + return null; + var p = 0; + for (var i = 0; i < a.length; i++) + p += b[i] * a[i]; + return p; + }, + crossProduct: function (a, b) { + if (a.length !== 3 || b.length !== 3) + return null; + return [ + a[1] * b[2] - a[2] * b[1], + a[2] * b[0] - a[0] * b[2], + a[0] * b[1] - a[1] * b[0] + ]; + }, + getMagnitude: function (a) { + var m = 0; + for (var i = 0; i < a.length; i++) + m += a[i] * a[i]; + return Math.sqrt(m); + }, + getUnitVector: function (a) { + var u = []; + var m = vector.getMagnitude(a); + for (var i = 0; i < a.length; i++) + u[i] = a[i] / m; + return u; + }, + setMagnitude: function (a, m) { + var b = []; + var unitVector = vector.getUnitVector(a); + for (var i = 0; i < a.length; i++) + b[i] = unitVector[i] * m; + return b; + }, + addVectors: function (a, b, k) { + if (a.length !== b.length) + return null; + if (un(k)) + k = 1; + var r = []; + for (var i = 0; i < a.length; i++) + r[i] = a[i] + b[i] * k; + return r; + }, + getAngleBetweenVectors: function (a, b) { + if (a.length !== b.length) + return null; + var dot = vector.dotProduct(a, b); + var mag1 = vector.getMagnitude(a); + var mag2 = vector.getMagnitude(b); + return Math.acos(dot / (mag1 * mag2)); + } +}; + +/***************************/ +/* POLYGONS */ +/***************************/ + +function drawPolygon(obj) { + // required + var ctx = obj.ctx; + var points = obj.points; + + // optional + var fillColor = obj.fillColor || obj.fillStyle || false; + var lineColor = obj.lineColor || obj.color || obj.strokeStyle || false; + var lineWidth = obj.lineWidth || obj.thickness || false; + var closed = boolean(obj.closed, true); + var lineDecoration = obj.lineDecoration || []; + var angles = obj.angles || []; + var outerAngles = obj.outerAngles || []; + var exteriorAngles = obj.exteriorAngles || []; + var clockwise = boolean(obj.clockwise, true) + var sf = obj.sf || 1; + var calcTextSnapPos = boolean(obj.calcTextSnapPos, false) + + //console.log(ctx,points,lineColor,lineWidth,closed); + + var angleLabelPos = []; + var outerAngleLabelPos = []; + var exteriorAngleLabelPos = []; + var prismPoints = []; + var textSnapPos = []; + + ctx.save(); + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + + ctx.beginPath(); + + if (!un(obj.solidType)) { + if (obj.solidType == 'prism') { + var prismVector = obj.prismVector || [40, -40]; + var vectorAngle = getVectorAngle(prismVector); + var points2 = []; + var points2Vis = []; + var angles2 = []; + for (var p = 0; p < points.length; p++) { + points2[p] = pointAddVector(points[p], prismVector); + prismPoints[p] = points2[p]; + var prev = p - 1; + if (prev < 0) + prev = obj.points.length - 1; + var next = p + 1; + if (next > obj.points.length - 1) + next = 0; + angles2[p] = [ + posToAngle(obj.points[prev][0], obj.points[prev][1], obj.points[p][0], obj.points[p][1]), + posToAngle(obj.points[next][0], obj.points[next][1], obj.points[p][0], obj.points[p][1]) + ]; + if (anglesInOrder(angles2[p][1], vectorAngle, angles2[p][0]) == true) { + points2Vis[p] = false; + } else { + points2Vis[p] = true; + } + } + + if (closed == true && fillColor !== false && fillColor !== 'none') { + for (var p = 0; p < points.length; p++) { // fill polygons + var next = (p + 1) % (points.length); + ctx.moveTo(points[p][0], points[p][1]); + ctx.lineTo(points2[p][0], points2[p][1]); + ctx.lineTo(points2[next][0], points2[next][1]); + ctx.lineTo(points[next][0], points[next][1]); + ctx.closePath(); + ctx.fillStyle = fillColor; + ctx.fill(); + } + } + + for (var p = 0; p < points.length; p++) { // draw lines + ctx.save(); + if (points2Vis[p] == false || points2Vis[(p + 1) % (points.length)] == false) { + if (obj.solidShowAllLines == false) continue; + ctx.strokeStyle = getShades(ctx.strokeStyle)[12]; + } + ctx.beginPath(); + ctx.moveTo(points2[p][0], points2[p][1]); + ctx.lineTo(points2[(p + 1) % (points.length)][0], points2[(p + 1) % (points.length)][1]); + ctx.stroke(); + ctx.restore(); + } + + for (var p = 0; p < points.length; p++) { // draw lines + ctx.save(); + if (points2Vis[p] == false) { + if (obj.solidShowAllLines == false) continue; + ctx.strokeStyle = getShades(ctx.strokeStyle)[12]; + } + ctx.beginPath(); + ctx.moveTo(points[p][0], points[p][1]); + ctx.lineTo(points2[p][0], points2[p][1]); + ctx.stroke(); + ctx.restore(); + } + + } + } + + if (closed == true && fillColor !== false && fillColor !== 'none') { + ctx.moveTo(points[0][0], points[0][1]); + for (var i = 1; i < points.length; i++) { + ctx.lineTo(points[i][0], points[i][1]); + } + ctx.closePath(); + ctx.fillStyle = fillColor; + ctx.fill(); + } + + for (var i = 0; i < points.length; i++) { + var p1 = i == 0 ? points.last() : points[i-1]; + var p2 = points[i]; + var p3 = points[(i+1)%points.length]; + + var a1 = getAngleFromAToB(p2,p1); + var a2 = getAngleFromAToB(p2,p3); + if (a2 > a1) { + var a3 = (a1+a2)/2; + } else { + var a3 = (a1+a2+2*Math.PI)/2; + while (a3 > 2*Math.PI) { + a3 -= 2*Math.PI; + } + } + var n = Math.PI/8; + var align = [0,0]; + if (a3 < n || a3 >= 15*n) { + align = [-1,0]; + } else if (a3 < 3*n) { + align = [-1,-1]; + } else if (a3 < 5*n) { + align = [0,-1]; + } else if (a3 < 7*n) { + align = [1,-1]; + } else if (a3 < 9*n) { + align = [1,0]; + } else if (a3 < 11*n) { + align = [1,1]; + } else if (a3 < 13*n) { + align = [0,1]; + } else if (a3 < 15*n) { + align = [-1,1]; + } + + var vector = angleToVector(a3,5); + var labelPos = pointAddVector(p2,vector); + + if (calcTextSnapPos == true) textSnapPos.push({type:'polygonVertex',pos:labelPos,align:align,angle:a3,vertex:i,polygon:obj}); + + // side labels + var mid = midpoint(p2[0],p2[1],p3[0],p3[1]); + var ang = a2-0.5*Math.PI; + while (ang < 0) ang += 2*Math.PI; + + var align = [0,0]; + if (ang < n || ang >= 15*n) { + align = [-1,0]; + } else if (ang < 3*n) { + align = [-1,-1]; + } else if (ang < 5*n) { + align = [0,-1]; + } else if (ang < 7*n) { + align = [1,-1]; + } else if (ang < 9*n) { + align = [1,0]; + } else if (ang < 11*n) { + align = [1,1]; + } else if (ang < 13*n) { + align = [0,1]; + } else if (ang < 15*n) { + align = [-1,1]; + } + + var margin = align.indexOf(0) > -1 ? 10 : 5; + var vector = angleToVector(ang,margin); + var labelPos = pointAddVector(mid,vector); + + if (calcTextSnapPos == true) textSnapPos.push({type:'polygonSide',pos:labelPos,align:align,angle:ang,vertex:i,polygon:obj}); + } + + for (var i = 0; i < points.length; i++) { + if (typeof angles[i] == 'object' && angles[i] !== null) { + angle = clone(angles[i]); + } else { + continue; + angle = { + labelMeasure: true, + measureLabelOnly: true, + measureOnly: true + }; + } + angle.ctx = ctx; + angle.b = points[i]; + if (clockwise == false) { + angle.a = points[(i + 1) % (points.length)]; + if (i == 0) { + angle.c = points[points.length - 1]; + } else { + angle.c = points[i - 1]; + } + } else { + angle.c = points[(i + 1) % (points.length)]; + if (i == 0) { + angle.a = points[points.length - 1]; + } else { + angle.a = points[i - 1]; + } + } + angle.drawLines = false; + if (typeof angle.lineWidth == 'undefined') + angle.lineWidth = ctx.lineWidth; + if (typeof angle.lineColor == 'undefined') + angle.lineColor = ctx.lineWidth; + if (typeof angle.labelColor == 'undefined') + angle.labelColor = ctx.strokeStyle; + angleLabelPos[i] = drawAngle(angle); + } + + if (obj.anglesMode == 'outer' && (un(obj.solidType) || obj.solidType !== 'prism')) { + for (var i = 0; i < outerAngles.length; i++) { + if (typeof outerAngles[i] == 'object' && outerAngles[i] !== null) { + outerAngles[i].ctx = ctx; + outerAngles[i].b = points[i]; + if (clockwise == true) { + outerAngles[i].a = points[(i + 1) % (points.length)]; + if (i == 0) { + outerAngles[i].c = points[points.length - 1]; + } else { + outerAngles[i].c = points[i - 1]; + } + } else { + outerAngles[i].c = points[(i + 1) % (points.length)]; + if (i == 0) { + outerAngles[i].a = points[points.length - 1]; + } else { + outerAngles[i].a = points[i - 1]; + } + } + outerAngles[i].drawLines = false; + if (typeof outerAngles[i].lineWidth == 'undefined') + outerAngles[i].lineWidth = ctx.lineWidth; + if (typeof outerAngles[i].lineColor == 'undefined') + outerAngles[i].lineColor = ctx.lineWidth; + if (typeof outerAngles[i].labelColor == 'undefined') + outerAngles[i].labelColor = ctx.strokeStyle; + outerAngleLabelPos[i] = drawAngle(outerAngles[i]); + } + } + } + + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + + if (lineColor !== false) { + ctx.strokeStyle = lineColor; + } + if (lineWidth !== false) { + ctx.lineWidth = lineWidth; + } + + if (obj.anglesMode == 'exterior' && (un(obj.solidType) || obj.solidType !== 'prism')) { + for (var i = 0; i < exteriorAngles.length; i++) { + if (typeof exteriorAngles[i] == 'object') { + exteriorAngleLabelPos[i] = []; + var ext = exteriorAngles[i]; + if (!un(ext.a1) && ext.a1 !== null) { + ext.a1.ctx = ctx; + ext.a1.c = points[(i + 1) % (points.length)]; + ext.a1.b = points[i]; + ext.a1.a = ext.line2.pos; + ext.a1.drawLines = false; + if (un(ext.a1.lineWidth)) + ext.a1.lineWidth = ctx.lineWidth; + if (un(ext.a1.lineColor)) + ext.a1.lineColor = ctx.lineWidth; + if (un(ext.a1.labelColor)) + ext.a1.labelColor = ctx.strokeStyle; + exteriorAngleLabelPos[i][0] = drawAngle(ext.a1); + } + if (!un(ext.a2) && ext.a2 !== null) { + ext.a2.ctx = ctx; + ext.a2.c = ext.line2.pos; + ext.a2.b = points[i]; + ext.a2.a = ext.line1.pos; + ext.a2.drawLines = false; + if (un(ext.a2.lineWidth)) + ext.a2.lineWidth = ctx.lineWidth; + if (un(ext.a2.lineColor)) + ext.a2.lineColor = ctx.lineWidth; + if (un(ext.a2.labelColor)) + ext.a2.labelColor = ctx.strokeStyle; + exteriorAngleLabelPos[i][1] = drawAngle(ext.a2); + } + if (!un(ext.a3) && ext.a3 !== null) { + ext.a3.ctx = ctx; + ext.a3.c = ext.line1.pos; + ext.a3.b = points[i]; + if (i > 0) { + ext.a3.a = points[i - 1]; + } else { + ext.a3.a = points[points.length - 1]; + } + ext.a3.drawLines = false; + if (un(ext.a3.lineWidth)) + ext.a3.lineWidth = ctx.lineWidth; + if (un(ext.a3.lineColor)) + ext.a3.lineColor = ctx.lineWidth; + if (un(ext.a3.labelColor)) + ext.a3.labelColor = ctx.strokeStyle; + exteriorAngleLabelPos[i][2] = drawAngle(ext.a3); + } + if (boolean(ext.line1.show, false)) { + ctx.save(); + ctx.lineWidth = lineWidth; + ctx.moveTo(points[i][0], points[i][1]); + ctx.lineTo(ext.line1.pos[0], ext.line1.pos[1]); + ctx.stroke(); + ctx.restore(); + } + if (boolean(ext.line2.show, false)) { + ctx.save(); + ctx.lineWidth = lineWidth; + ctx.moveTo(points[i][0], points[i][1]); + ctx.lineTo(ext.line2.pos[0], ext.line2.pos[1]); + ctx.stroke(); + ctx.restore(); + } + } + } + } + + ctx.beginPath(); + ctx.moveTo(points[0][0], points[0][1]); + for (var i = 1; i < points.length; i++) { + ctx.lineTo(points[i][0], points[i][1]); + } + if (closed == true) { + ctx.closePath(); + } + ctx.lineWidth = lineWidth; + if (obj.strokeStyle !== 'none') + ctx.stroke(); + + for (var i = 0; i < lineDecoration.length; i++) { + if (typeof lineDecoration[i] == 'object' && lineDecoration[i] !== null) { + switch (lineDecoration[i].type) { + case 'dash': + var dashLength = lineDecoration[i].length || 8 * sf; + var number = lineDecoration[i].number || 1; + if (number == 1) { + drawDash(ctx, points[i][0], points[i][1], points[(i + 1) % (points.length)][0], points[(i + 1) % (points.length)][1], dashLength); + } else if (number == 2) { + drawDoubleDash(ctx, points[i][0], points[i][1], points[(i + 1) % (points.length)][0], points[(i + 1) % (points.length)][1], dashLength); + } + break; + case 'arrow': + var arrowLength = lineDecoration[i].length || 12 * sf; + var number = lineDecoration[i].number || 1; + var direction = lineDecoration[i].direction || 1; + ctx.save(); + if (direction == 1) { + drawParallelArrow({ + context: ctx, + startX: points[i][0], + startY: points[i][1], + finX: points[(i + 1) % (points.length)][0], + finY: points[(i + 1) % (points.length)][1], + arrowLength: arrowLength, + lineWidth: ctx.lineWidth, + numOfArrows: number, + color: ctx.strokeStyle + }); + } else if (direction == -1) { + drawParallelArrow({ + context: ctx, + finX: points[i][0], + finY: points[i][1], + startX: points[(i + 1) % (points.length)][0], + startY: points[(i + 1) % (points.length)][1], + arrowLength: arrowLength, + lineWidth: ctx.lineWidth, + numOfArrows: number, + color: ctx.strokeStyle + }); + } + ctx.restore(); + break; + default: + break; + }; + } + } + + ctx.restore(); + return { + angleLabelPos: angleLabelPos, + outerAngleLabelPos: outerAngleLabelPos, + exteriorAngleLabelPos: exteriorAngleLabelPos, + prismPoints: prismPoints, + textSnapPos: textSnapPos + }; +} +function checkLinesForPolygon(lines, allowSelfIntersect) { + if (typeof allowSelfIntersect == 'undefined') allowSelfIntersect = false; + if (lines.length < 3) return false; + var lines = clone(lines); + lines = reduceLineSegments(lines); + + // remove lines with zero length + for (var l = lines.length - 1; l >= 0; l--) { + if (arraysEqual(lines[l][0], lines[l][1])) lines.splice(l, 1); + } + + var gradients = []; + for (var l = 0; l < lines.length; l++) { + var line = lines[l]; + gradients[l] = (line[1][1] - line[0][1]) / (line[1][0] - line[0][0]); + } + + function pointsEqual(p1, p2) { + if (p1[0] == p2[0] && p1[1] == p2[1]) return true; + return false; + } + + //console.clear(); + //console.log('lines:',clone(lines),lines.length); + //console.log('gradients:',gradients); + // test for overlapping parallel lines + for (var a = 0; a < lines.length - 1; a++) { + var lineA = lines[a]; + for (var b = a + 1; b < lines.length; b++) { + var lineB = lines[b]; + if (gradients[a] !== gradients[b]) continue; + var test1 = isPointOnLineSegment(lineB[0], lineA[0], lineA[1]); + var test2 = isPointOnLineSegment(lineB[1], lineA[0], lineA[1]); + var test3 = (pointsEqual(lineA[0], lineB[0]) || pointsEqual(lineA[0], lineB[1]) || pointsEqual(lineA[1], lineB[0]) || pointsEqual(lineA[1], lineB[1])) ? true : false; + if (test1 == false && test2 == false && test3 == false) continue; + + //console.log('a:',a,lineA[0],lineA[1],'b:',b,lineB[0],lineB[1],'gradient:',gradients[a],gradients[b]); //console.log(pointsEqual(lineA[0],lineB[0]),pointsEqual(lineA[0],lineB[1]),pointsEqual(lineA[1],lineB[0]),pointsEqual(lineA[1],lineB[1])); + //console.log(test1,test2,test3); + + // lines overlap - find most extreme of the 4 points + var pos = lineA.concat(lineB); + var min = pos[0]; + var max = pos[0]; + if (gradients[a] == Infinity || gradients[a] == -Infinity) { + for (var p = 1; p < pos.length; p++) { + if (pos[p][1] < min[1]) + min = pos[p]; + if (pos[p][1] > max[1]) + max = pos[p]; + } + } else { + for (var p = 1; p < pos.length; p++) { + if (pos[p][0] < min[0]) + min = pos[p]; + if (pos[p][0] > max[0]) + max = pos[p]; + } + } + lines[a] = [min, max]; + lines.splice(b, 1); + } + } + + var pos = []; + //console.log('reduced lines:'); + for (var l = 0; l < lines.length; l++) { + var line = lines[l]; + //console.log('('+line[0][0]+','+line[0][1]+')','('+line[1][0]+','+line[1][1]+')'); + var found = [false, false]; + for (var p = 0; p < pos.length; p++) { + if (pos[p].pos[0] == line[0][0] && pos[p].pos[1] == line[0][1]) { + pos[p].count++; + found[0] = true; + } + if (pos[p].pos[0] == line[1][0] && pos[p].pos[1] == line[1][1]) { + pos[p].count++; + found[1] = true; + } + } + if (found[0] == false) + pos.push({ + pos: clone(line[0]), + count: 1 + }); + if (found[1] == false) + pos.push({ + pos: clone(line[1]), + count: 1 + }); + //console.log(found,clone(pos)); + } + //console.log('pos:',pos); + + for (var p = 0; p < pos.length; p++) { + if (pos[p].count !== 2) return false; + } + + var polygon = [lines.shift()]; + while (lines.length > 0) { + var pos = polygon.last()[1]; + var found = false; + for (var l = 0; l < lines.length; l++) { + var line = lines[l]; + if (arraysEqual(line[0], pos)) { + polygon.push(line); + lines.splice(l, 1); + found = true; + break; + } else if (arraysEqual(line[1], pos)) { + polygon.push(line.reverse()); + lines.splice(l, 1); + found = true; + break; + } + } + //console.log(clone(polygon),clone(lines),found); + if (found == false) + return false; + } + for (var p = 0; p < polygon.length; p++) polygon[p] = polygon[p][0]; + //console.log('polygon:',polygon); + //if (allowSelfIntersect == false && polygonSelfIntersect2(polygon) == false) return false; + + return polygon; +} +/*function reduceLineSegments(lines) { + var lines = clone(lines); + + // remove lines with zero length + for (var l = lines.length - 1; l >= 0; l--) { + if (pointsEqual(lines[l][0], lines[l][1])) lines.splice(l, 1); + } + + var gradients = []; + for (var l = 0; l < lines.length; l++) { + var line = lines[l]; + gradients[l] = (line[1][1] - line[0][1]) / (line[1][0] - line[0][0]); + } + + function pointsEqual(p1, p2) { + if (p1[0] == p2[0] && p1[1] == p2[1]) return true; + return false; + } + + // test for overlapping parallel lines + for (var a = 0; a < lines.length - 1; a++) { + var lineA = lines[a]; + for (var b = a + 1; b < lines.length; b++) { + var lineB = lines[b]; + if (gradients[a] !== gradients[b]) continue; + var test1 = isPointOnLineSegment(lineB[0], lineA[0], lineA[1]); + var test2 = isPointOnLineSegment(lineB[1], lineA[0], lineA[1]); + var test3 = (pointsEqual(lineA[0], lineB[0]) || pointsEqual(lineA[0], lineB[1]) || pointsEqual(lineA[1], lineB[0]) || pointsEqual(lineA[1], lineB[1])) ? true : false; + if (test1 == false && test2 == false && test3 == false) continue; + + // lines overlap - find most extreme of the 4 points + var pos = lineA.concat(lineB); + var min = pos[0]; + var max = pos[0]; + if (gradients[a] == Infinity || gradients[a] == -Infinity) { + for (var p = 1; p < pos.length; p++) { + if (pos[p][1] < min[1]) + min = pos[p]; + if (pos[p][1] > max[1]) + max = pos[p]; + } + } else { + for (var p = 1; p < pos.length; p++) { + if (pos[p][0] < min[0]) + min = pos[p]; + if (pos[p][0] > max[0]) + max = pos[p]; + } + } + lines[a] = [min, max]; + lines.splice(b, 1); + } + } + + return lines; +}*/ +function reduceLineSegments(lines) { + var keepGoing = true; + while (keepGoing === true) { + keepGoing = false; + for (var l1 = 0; l1 < lines.length-1; l1++) { + var line1 = lines[l1]; + for (var l2 = l1+1; l2 < lines.length; l2++) { + var line2 = lines[l2]; + var x1 = line1[0][0]; + var y1 = line1[0][1]; + var x2 = line1[1][0]; + var y2 = line1[1][1]; + var x3 = line2[0][0]; + var y3 = line2[0][1]; + var x4 = line2[1][0]; + var y4 = line2[1][1]; + + if ((x1 === x2 && x1 === x3 && x1 === x4) || (Math.abs((y2 - y1) / (x2 - x1) - (y4 - y3) / (x4 - x3))) < 0.01) { // vertical or gradients equal + // if one of the points is between the two points on the other line + if ((x1 >= Math.min(x3,x4) && x1 <= Math.max(x3,x4) && y1 >= Math.min(y3,y4) && y1 <= Math.max(y3,y4)) || + (x2 >= Math.min(x3,x4) && x2 <= Math.max(x3,x4) && y2 >= Math.min(y3,y4) && y2 <= Math.max(y3,y4)) || + (x3 >= Math.min(x1,x2) && x3 <= Math.max(x1,x2) && y3 >= Math.min(y1,y2) && y3 <= Math.max(y1,y2)) || + (x4 >= Math.min(x1,x2) && x4 <= Math.max(x1,x2) && y4 >= Math.min(y1,y2) && y4 <= Math.max(y1,y2))) { + var x5 = Math.min(x1,x2,x3,x4); + var x6 = Math.max(x1,x2,x3,x4); + var y5 = x5 === x6 ? Math.min(y1,y2,y3,y4) : x5 === x1 ? y1 : x5 === x2 ? y2 : x5 === x3 ? y3 : y4; + var y6 = x5 === x6 ? Math.max(y1,y2,y3,y4) : x6 === x1 ? y1 : x6 === x2 ? y2 : x6 === x3 ? y3 : y4; + lines[l1] = [[x5,y5],[x6,y6]]; + lines.splice(l2, 1); + l2--; + keepGoing = true; + } + } + } + } + } + return lines; +} +function polygonConvexTest(polygon) { + var polygon = clone(polygon); + if (polygonClockwiseTest(polygon) == false) + polygon.reverse(); + for (var p = 0; p < polygon.length; p++) { + var angle = measureAngle({ + a: polygon[p], + b: polygon[(p + 1) % polygon.length], + c: polygon[(p + 2) % polygon.length] + }); + if (angle > Math.PI) + return false; + } + return true; +} +function polygonClockwiseTest(pos) { + if (pos.length < 3) + return null; + var sum = (pos[0][0] - pos.last()[0]) * (pos[0][1] + pos.last()[1]); + for (var i = 0; i < pos.length - 1; i++) { + sum += (pos[i + 1][0] - pos[i][0]) * (pos[i + 1][1] + pos[i][1]); + } + if (sum > 0) + return true; + return false; +}; +function getPolygonSideLabelPoints(polygon, gap) { + if (un(gap)) + gap = 25; + var points = []; + for (var i = 0; i < polygon.length; i++) { + var j = (i + 1) % polygon.length; + var mid = [(polygon[i][0] + polygon[j][0]) / 2, (polygon[i][1] + polygon[j][1]) / 2]; + var vec = getVectorAB(polygon[i], polygon[j]); + var perp = getPerpVector(vec); + var perpDist = setVectorMag(perp, gap); + var point = pointAddVector(mid, perpDist); + points.push(point); + } + return points; +} +function drawRegularPolygon(obj) { + var ctx = obj.ctx; + var c = obj.center || obj.c; + var r = obj.radius || obj.r; + var p = obj.points || obj.p || 3; + var s = obj.step || obj.s || 1; + var startAngle = -Math.PI / 2; + if (typeof obj.startAngle == 'number') + startAngle = obj.startAngle; + var vertices = []; + for (var i = 0; i < p; i++) { + var angle = startAngle + i * (2 * Math.PI) / p; + vertices.push([c[0] + r * Math.cos(angle), c[1] + r * Math.sin(angle)]); + } + ctx.moveTo(vertices[0][0], vertices[0][1]); + for (var i = p; i >= 0; i--) { + ctx.lineTo(vertices[(i * s) % p][0], vertices[(i * s) % p][1]); + } +} +function drawRegularPolygonEllipse(ctx, obj) { + var c = obj.center; + var rX = obj.radiusX; + var rY = obj.radiusY; + var p = obj.points; + var s = obj.step || 1; + var startAngle = -Math.PI / 2; + if (typeof obj.startAngle == 'number') + startAngle = obj.startAngle; + + var vertices = []; + for (var i = 0; i < p; i++) { + var angle = startAngle + i * (2 * Math.PI) / p; + vertices.push([c[0] + rX * Math.cos(angle), c[1] + rY * Math.sin(angle), angle]); + } + + ctx.beginPath(); + ctx.moveTo(vertices[0][0], vertices[0][1]); + for (var i = p; i >= 0; i--) { + ctx.lineTo(vertices[(i * s) % p][0], vertices[(i * s) % p][1]); + } + ctx.stroke(); + + return vertices; +} +function polygonIntersections(p1, p2) { + var intersections = []; + for (var i = 0; i < p1.length; i++) { + var line1 = [p1[i], p1[(i + 1) % p1.length]]; + for (var j = 0; j < p2.length; j++) { + var line2 = [p2[j], p2[(j + 1) % p2.length]]; + if (lineSegmentsIntersectionTest(line1, line2)) { + intersections.push({ + edges: [i, j], + intersection: linesIntersection(line1, line2) + }); + } + } + } + if (intersections.length == 0) + return false; + return intersections +} +function isPolygonInPolygon(p1, p2, includePerimeter) { + if (hitTestPolygon2(p1[0], p2) == false) return false; + for (var i = 0; i < p1.length; i++) { + var line1 = [p1[i], p1[(i + 1) % p1.length]]; + for (var j = 0; j < p2.length; j++) { + var line2 = [p2[j], p2[(j + 1) % p2.length]]; + if (lineSegmentsIntersectionTest(line1, line2) == true) { + if (includePerimeter == false) return false; + var p3 = isPointOnLineSegment(line1[0],line2[0],line2[1]); + var p4 = isPointOnLineSegment(line1[1],line2[0],line2[1]); + if (p3 == false && p4 == false) return false; + if (p3 == true && p4 == true) continue; // edge is part of edge + if (p3 == true && p4 == false && hitTestPolygon2(line1[1],p2) == false) return false; + if (p3 == false && p4 == true && hitTestPolygon2(line1[0],p2) == false) return false; + } + } + } + return true; +} +function polygonsIntersectionPolygon(p1, p2) { + var p = []; // polygon of intersecting region + + // add all edge-edge intersections + for (var i = 0; i < p1.length; i++) { + var line1 = [p1[i], p1[(i + 1) % p1.length]]; + for (var j = 0; j < p2.length; j++) { + var line2 = [p2[j], p2[(j + 1) % p2.length]]; + if (lineSegmentsIntersectionTest(line1, line2)) { + p.push(linesIntersection(line1, line2)); + } + } + } + + // get all vertices of p1 that are inside p2 + for (var i = 0; i < p1.length; i++) { + if (hitTestPolygon(p1[i], p2, false)) + p.push(p1[i]); + } + + // get all vertices of p1 that are inside p2 + for (var i = 0; i < p2.length; i++) { + if (hitTestPolygon(p2[i], p1, false)) + p.push(p2[i]); + } + + // get centre of p + var center = [0, 0]; + for (var i = 0; i < p.length; i++) { + center[0] += p[i][0]; + center[1] += p[i][1]; + } + center[0] = center[0] / p.length; + center[1] = center[1] / p.length; + + // order p + p.sort(function (a, b) { + var a1 = getAngleFromAToB(center, a); + var a2 = getAngleFromAToB(center, b); + return a2 - a1; + }); + + console.log(p, polygonClockwiseTest(p)); + + return p; +} +function polygonArea(verticesArray) { + verticesArray.push(verticesArray[0]); + var area = 0; + for (var i = 0; i < verticesArray.length - 1; i++) { + area += (verticesArray[i][0] * verticesArray[i + 1][1]) - (verticesArray[i][1] * verticesArray[i + 1][0]); + } + area = 0.5 * Math.abs(area); + return area; +} +function polygonCountSides(verticesArray) { + // collapses polygon (ie. finds any three points that are on a straight line and reduces to single line) + // returns number of sides/vertices of collapsed polygon + var verticesNum = verticesArray.length - 1; // given number of vertices + if (verticesNum < 3) + return verticesNum; + verticesArray.push(verticesArray[0]); + verticesArray.push(verticesArray[1]); + var verticesCount = 0; // return number of vertices + for (var i = 0; i <= verticesNum; i++) { + var point1 = verticesArray[i]; + var point2 = verticesArray[i + 1]; + var point3 = verticesArray[i + 2]; + var m1 = (point2[1] - point1[1]) / (point2[0] - point1[0]); + var m2 = (point3[1] - point2[1]) / (point3[0] - point2[0]); + var error = false; + if (point1[0] == point2[0] && point1[1] == point2[1]) + error = true; + if (point1[0] == point3[0] && point1[1] == point3[1]) + error = true; + if (point3[0] == point2[0] && point3[1] == point2[1]) + error = true; + if (m1 !== m2 && error == false) + verticesCount++; + } + verticesArray.splice(-2, 2); + logMe("verticesArray:", verticesArray, "polygon"); + return verticesCount; +} +function polygonSelfIntersect(verticesArray) { + // vertices array such as [[], [], [], []] + verticesArray = clone(verticesArray); + verticesArray.push(verticesArray[0]); // duplicate first vertex to last vertex + var segmentArray = []; + var events = []; + + // create segments array [[x1,y1,x2,y2],[...],...] where x1 <= x2 + for (var i = 0; i < verticesArray.length - 1; i++) { + segmentArray.push([verticesArray[i][0], verticesArray[i][1], verticesArray[i + 1][0], verticesArray[i + 1][1]]); + + // choose left-most end of segment to be 'start' + var order; + if (verticesArray[i][0] < verticesArray[i + 1][0]) { + order = 1; + } else if (verticesArray[i][0] > verticesArray[i + 1][0]) { + order = 2; + } else { + if (verticesArray[i][1] < verticesArray[i + 1][1]) { + order = 1; + } else { + order = 2; + } + } + + if (order == 1) { + events.push([verticesArray[i][0], verticesArray[i][1], i, 'start']); + events.push([verticesArray[i + 1][0], verticesArray[i + 1][1], i, 'end']); + } else { + events.push([verticesArray[i][0], verticesArray[i][1], i, 'end']); + events.push([verticesArray[i + 1][0], verticesArray[i + 1][1], i, 'start']); + } + } + + // sort the events by x value + events.sort(function (a, b) { + return a[0] - b[0] + }); + + var sweepLine = []; + + for (var j = 0; j < events.length; j++) { + if (events[j][3] == 'start') { + sweepLine.push([events[j][0], events[j][1], events[j][2]]); // x-value, y-value, lineSegmentId + sweepLine.sort(function (a, b) { + return a[1] - b[1] + }); // sort sweepLine by y-value + } else if (events[j][3] == 'end') { + // get position in sweepline (pos1) and segment id (seg1) + var pos1, + seg1; + for (var k = 0; k < sweepLine.length; k++) { + if (sweepLine[k][2] == events[j][2]) { + pos1 = k; + seg1 = segmentArray[k]; + break; + } + } + // if not the lowest line, check for intersection with line below + if (pos1 > 0) { + var pos2 = sweepLine[pos1 - 1][2]; + var seg2 = segmentArray[pos2]; + if (intersects(seg1[0], seg1[1], seg1[2], seg1[3], seg2[0], seg2[1], seg2[2], seg2[3]) == true) + return true; + } + if (pos1 < sweepLine.length - 1) { + var pos2 = sweepLine[pos1 + 1][2]; + var seg2 = segmentArray[pos2]; + if (intersects(seg1[0], seg1[1], seg1[2], seg1[3], seg2[0], seg2[1], seg2[2], seg2[3]) == true) + return true; + } + } + } + + return false; +} +function polygonSelfIntersect2(polygon) { + var edges = []; + for (var i = 0; i < polygon.length; i++) + edges.push([polygon[i], polygon[(i + 1) % polygon.length]]); + for (var i = 0; i < edges.length - 1; i++) { + for (var j = i + 1; j < edges.length; j++) { + if ( + arraysEqual(edges[i][0], edges[j][0]) || + arraysEqual(edges[i][0], edges[j][1]) || + arraysEqual(edges[i][1], edges[j][0]) || + arraysEqual(edges[i][1], edges[j][1])) { + continue; + } + if (intersects(edges[i][0][0], edges[i][0][1], edges[i][1][0], edges[i][1][1], edges[j][0][0], edges[j][0][1], edges[j][1][0], edges[j][1][1]) == true) { + return true; + } + } + } + return false; +} +function findMinPolygon(edgeVertices, lines, point) { + var edges = []; + var lines2 = []; + + edgeVertices.push(edgeVertices[0].slice()); + // split edges into smaller vectors according to intersection points with lines + for (var i = 0; i < edgeVertices.length - 1; i++) { + edges[i] = [{ + point: edgeVertices[i].slice(0), + dist: 0 + }, { + point: edgeVertices[i + 1].slice(0), + dist: dist(edgeVertices[i][0], edgeVertices[i][1], edgeVertices[i + 1][0], edgeVertices[i + 1][1]) + } + ]; + for (var j = 0; j < lines.length; j++) { + //console.log('edgeVertices:',edgeVertices[i][0],edgeVertices[i][1],edgeVertices[i+1][0],edgeVertices[i+1][1]); + // console.log('lineVertices:',lines[j][0][0],lines[j][0][1],lines[j][1][0],lines[j][1][1]); + // console.log('intersects2:',intersects2(lines[j][0][0],lines[j][0][1],lines[j][1][0],lines[j][1][1],edgeVertices[i][0],edgeVertices[i][1],edgeVertices[i+1][0],edgeVertices[i+1][1])); + if (intersects2(lines[j][0][0], lines[j][0][1], lines[j][1][0], lines[j][1][1], edgeVertices[i][0], edgeVertices[i][1], edgeVertices[i + 1][0], edgeVertices[i + 1][1])) { + + var int = intersection(edgeVertices[i][0], edgeVertices[i][1], edgeVertices[i + 1][0], edgeVertices[i + 1][1], lines[j][0][0], lines[j][0][1], lines[j][1][0], lines[j][1][1]); + + // check that the intersection point is not one of the end points of the edge + if ((int[0] == edgeVertices[i][0] && int[1] == edgeVertices[i][1]) || (int[0] == edgeVertices[i + 1][0] && int[1] == edgeVertices[i + 1][1])) { + //console.log('corner'); + } else { + edges[i].push({ + point: int, + dist: dist(edges[i][0].point[0], edges[i][0].point[1], int[0], int[1]) + }); + } + + if (typeof lines2[j] == 'undefined') { + lines2[j] = [{ + point: int.slice(0), + dist: 0 + } + ]; + } else { + lines2[j].push({ + point: int.slice(0), + dist: dist(int[0], int[1], lines2[j][0].point[0], lines2[j][0].point[1]) + }); + } + + } + } + } + for (var i = 0; i < lines.length - 1; i++) { + for (var j = i + 1; j < lines.length; j++) { + var int = intersection(lines[i][0][0], lines[i][0][1], lines[i][1][0], lines[i][1][1], lines[j][0][0], lines[j][0][1], lines[j][1][0], lines[j][1][1]); + if (hitTestPolygon(int, edgeVertices)) { + lines2[i].push({ + point: int.slice(0), + dist: dist(int[0], int[1], lines2[i][0].point[0], lines2[i][0].point[1]) + }); + lines2[j].push({ + point: int.slice(0), + dist: dist(int[0], int[1], lines2[j][0].point[0], lines2[j][0].point[1]) + }); + } + } + } + + for (var i = 0; i < edges.length; i++) { + edges[i].sortOn('dist'); + } + for (var i = 0; i < lines2.length; i++) { + lines2[i].sortOn('dist'); + } + + //console.log('edges:',edges.slice(0)); + //console.log('lines2:',lines2.slice(0)); + + // find duplicate points in lines2 and remove + for (var i = 0; i < lines2.length; i++) { + for (var j = 1; j < lines2[i].length; ) { + if (lines2[i][j - 1].dist == lines2[i][j].dist) { + lines2[i].splice(j, 1); + } else { + j++; + } + } + } + + var vectors = []; + + for (var i = 0; i < edges.length; i++) { + for (var j = 0; j < edges[i].length - 1; j++) { + vectors.push([edges[i][j].point.slice(0), edges[i][j + 1].point.slice(0)]); + } + } + + for (var i = 0; i < lines2.length; i++) { + for (var j = 0; j < lines2[i].length - 1; j++) { + vectors.push([lines2[i][j].point.slice(0), lines2[i][j + 1].point.slice(0)]); + } + } + + //console.log('vectors:',vectors.slice(0)); + //console.log('point:',point); + + var minDist = []; + // work out whish vector is closest to the point + for (var i = 0; i < vectors.length; i++) { + minDist[i] = distancePointToLineSegment(point, vectors[i][0], vectors[i][1]); + } + //console.log('minDist:',minDist); + + // start the polygon vertices array + var vertices = []; + var currVector = vectors[minDist.indexOf(minDist.min())]; + + // put the closest vector vertices into the array + if (measureAngle({ + a: point, + b: currVector[0], + c: currVector[1] + }) < measureAngle({ + a: point, + b: currVector[1], + c: currVector[0] + })) { + vertices.push(currVector[0].slice(0), currVector[1].slice(0)); + } else { + vertices.push(currVector[1].slice(0), currVector[0].slice(0)); + } + vectors.splice(minDist.indexOf(minDist.min()), 1); + //console.log('first vertices:',vertices[0],vertices[1]); + + // until the path is completed + do { + var currPoint = vertices[vertices.length - 1].slice(0); + // find all poss vectors from the current point + var possVectors = []; + var possVectorsAngle = []; + var possVectorsIndex = []; + for (var i = 0; i < vectors.length; i++) { + if (arraysEqual(vectors[i][0], currPoint)) { + //console.log('---possVector:',vectors[i].slice(0)); + possVectors.push(vectors[i].slice(0)); + possVectorsIndex.push(i); + possVectorsAngle.push(measureAngle({ + a: vertices[vertices.length - 2], + b: vertices[vertices.length - 1], + c: vectors[i][1] + })); + //console.log('angle:',measureAngle({a:vertices[vertices.length-2],b:vertices[vertices.length-1],c:vectors[i][1]})); + } else if (arraysEqual(vectors[i][1], currPoint)) { + //console.log('+++possVector:',[vectors[i][1].slice(0),vectors[i][0].slice(0)]); + possVectors.push([vectors[i][1].slice(0), vectors[i][0].slice(0)]); + possVectorsIndex.push(i); + possVectorsAngle.push(measureAngle({ + a: vertices[vertices.length - 2], + b: vertices[vertices.length - 1], + c: vectors[i][0] + })); + //console.log('angle:',measureAngle({a:vertices[vertices.length-2],b:vertices[vertices.length-1],c:vectors[i][0]})); + } + } + //console.log('possVectors:',possVectors); + //console.log('possVectorsAngle:',possVectorsAngle); + //console.log('possVectorsIndex:',possVectorsIndex); + // chose the vector with the smallest angle + var selVectorPos = possVectorsAngle.indexOf(possVectorsAngle.min()); + vertices.push(possVectors[selVectorPos][1].slice(0)); + selVectorPos = possVectorsIndex[selVectorPos]; + vectors.splice(selVectorPos, 1); + + //console.log('vertex:',vertices[vertices.length-1]); + } while (arraysEqual(vertices[0], vertices[vertices.length - 1]) == false); + + //console.log('vertices:',vertices); + + return vertices; +} +function samePolygons(verticesArray1, verticesArray2) { + var a = verticesArray1; + var b = verticesArray2; + if (a.length !== b.length) + return false; + for (var i = 0; i < a.length; i++) { + var found = false; + for (var j = 0; j < b.length; j++) { + if (roundToNearest(a[i][0], 0.0001) == roundToNearest(b[j][0], 0.0001) && roundToNearest(a[i][1], 0.0001) == roundToNearest(b[j][1], 0.0001)) { + found = true; + break; + } + } + if (found == false) + return false; + } + return true; +} +function polygonCongruenceTest(p1, p2, mode) { // mode 0 = any congruent shape, 1 = rotations allowed but not reflections, 2 = no rotations or reflectons allowed + if (typeof mode == 'undefined') + mode = 0 + if (p1.length !== p2.length) + return false; + var p1 = clone(p1); + var p2 = clone(p2); + if (polygonClockwiseTest(p1) == false) + p1.reverse(); + if (polygonClockwiseTest(p2) == false) + p2.reverse(); + if (mode == 2) { // test edge vectors are congruent + var vectors1 = [], + vectors2 = []; + for (var p = 0; p < p1.length; p++) { + var b = p1[p]; + var c = p == p1.length - 1 ? p1[0] : p1[p + 1]; + vectors1[p] = [c[0] - b[0], c[1] - b[1]]; + var b = p2[p]; + var c = p == p2.length - 1 ? p2[0] : p2[p + 1]; + vectors2[p] = [c[0] - b[0], c[1] - b[1]]; + } + //console.log(vectors1,vectors2); + + for (var p = 0; p < p1.length; p++) { + if (arraysEqual(vectors1[0], vectors2[p])) { + //console.log(p,vectors1[0],vectors2[p]); + var match = true; + for (var q = 1; q < p2.length; q++) { + var r = (p + q) % p2.length; + //console.log(q,vectors1[q],vectors2[r]); + if (arraysEqual(vectors1[q], vectors2[r]) == false) { + match = false; + break; + } + } + if (match == true) + return true; + } + } + return false; + } else { // test edge lengths and angles are congruent + var lengths1 = [], + lengths2 = []; + var angles1 = [], + angles2 = []; + for (var p = 0; p < p1.length; p++) { + var a = p == 0 ? p1.last() : p1[p - 1]; + var b = p1[p]; + var c = p == p1.length - 1 ? p1[0] : p1[p + 1]; + lengths1[p] = dist(b[0], b[1], c[0], c[1]); + angles1[p] = measureAngle({ + a: a, + b: b, + c: c + }); + } + for (var p = 0; p < p2.length; p++) { + var a = p == 0 ? p2.last() : p2[p - 1]; + var b = p2[p]; + var c = p == p2.length - 1 ? p2[0] : p2[p + 1]; + lengths2[p] = dist(b[0], b[1], c[0], c[1]); + angles2[p] = measureAngle({ + a: a, + b: b, + c: c + }); + } + + for (var p = 0; p < p1.length; p++) { + if (Math.abs(lengths1[0] - lengths2[p]) < 0.001 && Math.abs(angles1[0] - angles2[p]) < 0.001) { + var match = true; + for (var q = 1; q < p2.length; q++) { + var r = (p + q) % p2.length; + if (Math.abs(lengths1[q] - lengths2[r]) > 0.001 || Math.abs(angles1[q] - angles2[r]) > 0.001) { + match = false; + break; + } + } + if (match == true) + return true; + if (mode == 0) { // check for a match in reverse vertices direction ie. a reflection + for (var q = 1; q < p2.length; q++) { + var r = p - q; + if (r < 0) + r = p2.length + r; + if (Math.abs(lengths1[q] - lengths2[r]) > 0.001 || Math.abs(angles1[q] - angles2[r]) > 0.001) { + match = false; + break; + } + } + if (match == true) + return true; + } + } + } + return false; + } +} +function polygonGetCenter(polygon) { + var totals = [0,0]; + for (var p = 0; p < polygon.length; p++) { + totals[0] += polygon[p][0]; + totals[1] += polygon[p][1]; + if (polygon[p].length == 3) { + if (un(totals[2])) totals[2] = 0; + totals[2] += polygon[p][2]; + } + } + for (var t = 0; t < totals.length; t++) { + totals[t] /= polygon.length; + } + return totals; +} + +function lineSegmentsIntersectionTest(line1, line2) { + return intersects(line1[0][0], line1[0][1], line1[1][0], line1[1][1], line2[0][0], line2[0][1], line2[1][0], line2[1][1]); +} +function linesIntersection(line1, line2) { + return intersection(line1[0][0], line1[0][1], line1[1][0], line1[1][1], line2[0][0], line2[0][1], line2[1][0], line2[1][1]); +} +function intersects(a, b, c, d, p, q, r, s) { + // returns true iff the line segemnt from (a,b)->(c,d) intersects with the line segment from (p,q)->(r,s) + var det, + gamma, + lambda; + det = (c - a) * (s - q) - (r - p) * (d - b); + if (det === 0) { + return false; + } else { + lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / det; + gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / det; + return (0 < lambda && lambda < 1) && (0 < gamma && gamma < 1); + } +}; +function intersects2(a, b, c, d, p, q, r, s) { + // returns true iff the infinite line though (a,b) & (c,d) intersects with line segment (p,q)->(r,s) + var int = intersection(a, b, c, d, p, q, r, s); + return isPointOnLineSegment(int, [p, q], [r, s]); +}; +function intersection(aX, aY, bX, bY, cX, cY, dX, dY) { // finds and returns the intersection point of the lines AB and CD + if (aX instanceof Array) { + var a = aX, b = aY, c = bX, d = bY; + aX = a[0]; + aY = a[1]; + bX = b[0]; + bY = b[1]; + cX = c[0]; + cY = c[1]; + dX = d[0]; + dY = d[1]; + } + if (aX !== bX && cX !== dX) { + var m1 = (bY - aY) / (bX - aX); // gradient of AB; + var m2 = (dY - cY) / (dX - cX); // gradient of CD; + if (m1 == m2) + return 'parallel'; + var xIntersection = ((aY - aX * m1) - (cY - cX * m2)) / (m2 - m1); + var yIntersection = aY + m1 * (xIntersection - aX); + return [xIntersection, yIntersection]; + } else if (aX == bX && cX == dX) { + return 'parallel'; + } else if (aX == bX) { + // if AB is vertical + var m2 = (dY - cY) / (dX - cX); // gradient of CD; + var xIntersection = aX; + var yIntersection = cY + m2 * (xIntersection - cX); + return [xIntersection, yIntersection]; + } else if (cX == dX) { + // if CD is vertical + var m1 = (bY - aY) / (bX - aX); // gradient of AB; + var xIntersection = cX; + var yIntersection = aY + m1 * (xIntersection - aX); + return [xIntersection, yIntersection]; + } +} +function dist(x1, y1, x2, y2) { + if (x1 instanceof Array && y1 instanceof Array) return (dist(x1[0],x1[1],y1[0],y1[1])); + return Math.pow(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2), 0.5); +} +function midpoint(x1, y1, x2, y2) { + return [0.5 * (x1 + x2), 0.5 * (y1 + y2)]; +} +function collapsePath(path) { + var path1 = path.slice(0); + + do { + var joinFound = false; + for (var i = path1.length - 1; i >= 0; i--) { + if (joinFound == false) { + for (var j = i - 1; j >= 0; j--) { + if (path1[i].obj[0].type == path1[j].obj[0].type) { + if (path1[i].type == 'pen') {} + else if (path1[i].obj[0].type == 'line') { + var x1 = path1[i].obj[0].startPos[0]; + var y1 = path1[i].obj[0].startPos[1]; + var x2 = path1[i].obj[0].finPos[0]; + var y2 = path1[i].obj[0].finPos[1]; + var x3 = path1[j].obj[0].startPos[0]; + var y3 = path1[j].obj[0].startPos[1]; + var x4 = path1[j].obj[0].finPos[0]; + var y4 = path1[j].obj[0].finPos[1]; + var m1 = (y2 - y1) / (x2 - x1); + var m2 = (y4 - y3) / (x4 - x3); + if (Math.abs(m1) > Math.abs(m2)) { + var mMax = m1; + var mMin = m2; + } else { + var mMax = m2; + var mMin = m1; + } + // if gradients are equal and point from line 1 is on line 2 + //console.log('grad/grad: ',mMax/mMin); + //console.log('pointOnLine: ',isPointOnLine([x1,y1],[x3,y3],[x4,y4],3.5)); + if (((mMin >= 0 && mMax >= 0) || (mMin < 0 && mMax < 0)) && Math.abs(mMax / mMin) < 1.1 && isPointOnLine([x1, y1], [x3, y3], [x4, y4], 3.5) == true) { + // if one of the points is between the two points on the other line + if ((x1 >= Math.min(x3, x4) && x1 <= Math.max(x3, x4) && y1 >= Math.min(y3, y4) && y1 <= Math.max(y3, y4)) || (x2 >= Math.min(x3, x4) && x2 <= Math.max(x3, x4) && y2 >= Math.min(y3, y4) && y2 <= Math.max(y3, y4)) || (x3 >= Math.min(x1, x2) && x3 <= Math.max(x1, x2) && y3 >= Math.min(y1, y2) && y3 <= Math.max(y1, y2)) || (x4 >= Math.min(x1, x2) && x4 <= Math.max(x1, x2) && y4 >= Math.min(y1, y2) && y4 <= Math.max(y1, y2))) { + var xMin = Math.min(x1, x2, x3, x4); + var xMax = Math.max(x1, x2, x3, x4); + if (xMin == x1) + var yMin = y1; + if (xMin == x2) + var yMin = y2; + if (xMin == x3) + var yMin = y3; + if (xMin == x4) + var yMin = y4; + if (xMax == x1) + var yMax = y1; + if (xMax == x2) + var yMax = y2; + if (xMax == x3) + var yMax = y3; + if (xMax == x4) + var yMax = y4; + joinFound = true; + path1[i].startPos = [xMin, yMin]; + path1[i].finPos = [xMax, yMax]; + path1.splice(j, 1); + break; + } + } + } else if (path1[i].obj[0].type == 'arc') { + if (Math.abs(path1[i].obj[0].center[0] - path1[j].obj[0].center[0]) < 2 && Math.abs(path1[i].obj[0].center[1] - path1[j].obj[0].center[1]) < 2 && Math.abs(path1[i].obj[0].radius - path1[j].obj[0].radius) < 2) { + if ((path1[i].obj[0].startAngle >= path1[j].obj[0].startAngle && path1[i].obj[0].startAngle <= path1[j].obj[0].finAngle) || + /* if min angle of first is between min & max angle of second*/ + (path1[i].obj[0].finAngle >= path1[j].obj[0].startAngle && path1[i].obj[0].finAngle <= path1[j].obj[0].finAngle) || + /* if max angle of first is between min & max angle of second*/ + (path1[j].obj[0].startAngle >= path1[i].obj[0].startAngle && path1[j].obj[0].startAngle <= path1[i].obj[0].finAngle) || + /* if min angle of second is between min & max angle of first*/ + (path1[j].obj[0].finAngle >= path1[i].obj[0].startAngle && path1[j].obj[0].finAngle <= path1[i].obj[0].finAngle) || + /* if max angle of second is between min & max angle of first*/ + (path1[i].obj[0].startAngle <= path1[j].obj[0].startAngle && path1[i].obj[0].finAngle >= path1[j].obj[0].finAngle) || + /* if second is contained within first*/ + (path1[j].obj[0].startAngle <= path1[i].obj[0].startAngle && path1[j].obj[0].finAngle >= path1[i].obj[0].finAngle)) + /* if first is contained within second*/ + { + joinFound = true; + path1[i].obj[0].startAngle = Math.min(path1[i].obj[0].startAngle, path1[j].obj[0].startAngle); + path1[i].obj[0].finAngle = Math.max(path1[i].obj[0].finAngle, path1[j].obj[0].finAngle); + path1.splice(j, 1); + break; + } + } + } + } + } + } + } + } while (joinFound == true); + + return path1; +} +function getIntersectionPoints(path) { + // currently only handles line/line, line/arc and arc/arc intersections + var intPoints = []; + for (var i = 0; i < path.length; i++) { + if (typeof path[i].obj == 'undefined') + continue; + for (var j = 0; j < path[i].obj.length; j++) { + var obj1 = path[i].obj[j]; + if (obj1.drawing == true) + continue; + for (var k = i; k < path.length; k++) { + for (var l = 0; l < path[k].obj.length; l++) { + if (i == k && j == l) + continue; + var obj2 = path[k].obj[l]; + if (obj2.drawing == true) + continue; + if (obj1.type == 'line' && obj2.type == 'line') { + var int = intersection(obj1.startPos[0], obj1.startPos[1], obj1.finPos[0], obj1.finPos[1], obj2.startPos[0], obj2.startPos[1], obj2.finPos[0], obj2.finPos[1]); + if (isPointOnLineSegment(int, obj1.startPos, obj1.finPos) == true) + intPoints.push(int); + } else if (obj1.type == 'line' && obj2.type == 'arc') { + var int = lineCircleIntersections(obj1.startPos, obj1.finPos, obj2.center, obj2.radius); + for (var m = 0; m < int.length; m++) { + if (isPointOnLineSegment(int[m], obj1.startPos, obj1.finPos) == true && isAngleInPath(obj2, obj2.center, obj2.radius, int[m][2], 1) == true) + intPoints.push(int[m]); + } + } else if (obj1.type == 'arc' && obj2.type == 'line') { + var int = lineCircleIntersections(obj2.startPos, obj2.finPos, obj1.center, obj1.radius); + for (var m = 0; m < int.length; m++) { + if (isPointOnLineSegment(int[m], obj2.startPos, obj2.finPos) == true && isAngleInPath(obj1, obj1.center, obj1.radius, int[m][2], 1) == true) + intPoints.push(int[m]); + } + } else if (obj1.type == 'arc' && obj2.type == 'arc') { + var int = circleIntersections(obj1.center[0], obj1.center[1], obj1.radius, obj2.center[0], obj2.center[1], obj2.radius); + for (var m = 0; m < int.length; m++) { + var a = isPointOnArc(int[m], obj1); + var b = isPointOnArc(int[m], obj2); + if (a == true && b == true) + intPoints.push(int[m]); + } + } + } + } + } + } + return intPoints; +} +function getEndPoints(path) { + // currently only handles pen, line and arc paths + var endPoints = []; + for (var i = 0; i < path.length; i++) { + if (typeof path[i].obj == 'undefined') + continue; + for (var j = 0; j < path[i].obj.length; j++) { + if (path[i].obj[j].drawing == true) + continue; + switch (path[i].obj[j].type) { + case 'line': + endPoints.push(path[i].obj[j].startPos, path[i].obj[j].finPos); + break; + case 'arc': + var arcEnds = getEndPointsOfArc(path[i].obj[j]); + for (var k = 0; k < arcEnds.length; k++) { + endPoints.push(arcEnds[k]); + } + break; + } + } + } + return endPoints; +} +function isAngleInPath(arcPath, center, radius, angle, tolerance) { + if (dist(arcPath.center[0], arcPath.center[1], center[0], center[1]) > tolerance) + return false; + if (Math.abs(arcPath.radius - radius) > tolerance) + return false; + var startAngle = arcPath.startAngle; + var finAngle = arcPath.finAngle; + if (arcPath.clockwise == true) { + if (startAngle < angle && finAngle > angle) + return true; + if (startAngle + 2 * Math.PI < angle && finAngle + 2 * Math.PI > angle) + return true; + if (startAngle - 2 * Math.PI < angle && finAngle - 2 * Math.PI > angle) + return true; + } else { + if (startAngle < finAngle) + startAngle += 2 * Math.PI; + if (startAngle > angle && finAngle < angle) + return true; + if (startAngle + 2 * Math.PI > angle && finAngle + 2 * Math.PI < angle) + return true; + if (startAngle - 2 * Math.PI > angle && finAngle - 2 * Math.PI < angle) + return true; + } + return false; +} +function doesArcIncludeAngle(arc, angle) { + while (angle < 0) { + angle += 2 * Math.PI; + } + angle = angle % (2 * Math.PI); + if (arc.clockwise == false) { + if (arc.finAngle > arc.startAngle) { + if (angle <= arc.startAngle || angle >= arc.finAngle) + return true; + } else { + if (angle >= arc.finAngle && angle <= arc.startAngle) + return true; + } + } else { + if (arc.finAngle > arc.startAngle) { + if (angle >= arc.startAngle || angle <= arc.finAngle) + return true; + } else { + if (angle <= arc.finAngle && angle >= arc.startAngle) + return true; + } + } + return false; +} +function distancePointToPath(point, path) { + if (path.length > 1) { + var closestDist = distancePointToLineSegment(point, path[0], path[1]); + for (var i = 1; i < path.length - 1; i++) { + closestDist = Math.min(closestDist, distancePointToLineSegment(point, path[i], path[i + 1])); + } + return closestDist; + } else if (path.length == 1) { + return dist(point[0], point[1], path[0][0], path[0][1]); + } else { + return 100000000000; + } + +} +function isPointOnLine(point, linePos1, linePos2, tolerance) { + if (un(tolerance)) + tolerance = 0.001; + var closestPos = closestPointOnLine(point, linePos1, linePos2); + var distance = dist(closestPos[0], closestPos[1], point[0], point[1]); + if (distance <= tolerance) + return true; + return false; +} +function closestPointOnLine(point, linePos1, linePos2) { + var dirVector = [linePos2[0] - linePos1[0], linePos2[1] - linePos1[1]]; + var lambda = ((point[0] - linePos1[0]) * dirVector[0] + (point[1] - linePos1[1]) * dirVector[1]) / (Math.pow(dirVector[0], 2) + Math.pow(dirVector[1], 2)); + return [linePos1[0] + lambda * dirVector[0], linePos1[1] + lambda * dirVector[1]]; +} +function distancePointToLine(point, linePos1, linePos2) { + var closest = closestPointOnLine(point, linePos1, linePos2); + return dist(point[0], point[1], closest[0], closest[1]); +} +function closestPointOnLineSegment(point, linePos1, linePos2) { + var dirVector = [linePos2[0] - linePos1[0], linePos2[1] - linePos1[1]]; + var lambda = ((point[0] - linePos1[0]) * dirVector[0] + (point[1] - linePos1[1]) * dirVector[1]) / (Math.pow(dirVector[0], 2) + Math.pow(dirVector[1], 2)); + if (lambda <= 0) { + return linePos1; + } else if (lambda >= 1) { + return linePos2; + } else { + return [linePos1[0] + lambda * dirVector[0], linePos1[1] + lambda * dirVector[1]]; + } +} +function distancePointToLineSegment(point, linePos1, linePos2) { + var closest = closestPointOnLineSegment(point, linePos1, linePos2); + return dist(point[0], point[1], closest[0], closest[1]); +} +function checkPathForLine(path, x1, y1, isEnd1, x2, y2, isEnd2, tolerance) { + for (var i = 0; i < path.length; i++) { + for (var k = 0; k < path[i].obj.length; k++) { + if (path[i].obj[k].type == 'line' && lineCheck(x1, y1, isEnd1, x2, y2, isEnd2, tolerance, path[i].obj[k].startPos[0], path[i].obj[k].startPos[1], path[i].obj[k].finPos[0], path[i].obj[k].finPos[1]) == true) + return true; + } + } + return false; +} +function isPointOnLineSegment(point, linePos1, linePos2, tolerance) { + if (un(tolerance)) tolerance = 0.001; + return distancePointToLineSegment(point, linePos1, linePos2) < tolerance; +} +/*function isPointOnLineSegment(point,linePos1,linePos2) { +var dirVector = [linePos2[0]-linePos1[0],linePos2[1]-linePos1[1]]; +var lambda = ((point[0]-linePos1[0])*dirVector[0]+(point[1]-linePos1[1])*dirVector[1])/(Math.pow(dirVector[0],2)+Math.pow(dirVector[1],2)); +if (lambda >= 0 && lambda <= 1) { +return true; +} else { +return false; +} +}*/ +function isPointInSector(point, dims) { + if (dist(point[0], point[1], dims[0], dims[1]) > dims[2]) + return false; + var a1 = dims[3]; + var a2 = getAngleTwoPoints([dims[0], dims[1]], point); + var a3 = dims[4]; + return anglesInOrder(a1, a2, a3); +} + +function sameLine(line1, line2) { + return (isPointOnLine(line1[0], line2[0], line2[1], 0) && isPointOnLine(line1[1], line2[0], line2[1], 0)); +} + +function lineCheck(x1, y1, isEnd1, x2, y2, isEnd2, tolerance, xTest1, yTest1, xTest2, yTest2) { + //console.log(x1,y1,isEnd1,x2,y2,isEnd2,tolerance,xTest1,yTest1,xTest2,yTest2); + // if points should be ends of the line, test if they are + if (isEnd1 == true && dist(x1, y1, xTest1, yTest1) > tolerance && dist(x1, y1, xTest2, yTest2) > tolerance) { + return false; + } + if (isEnd2 == true && dist(x2, y2, xTest1, yTest1) > tolerance && dist(x2, y2, xTest2, yTest2) > tolerance) { + return false; + } + // test if the points are on the line, with the given tolerance + if (isPointOnLine([x1, y1], [xTest1, yTest1], [xTest2, yTest2], tolerance) == false || isPointOnLine([x2, y2], [xTest1, yTest1], [xTest2, yTest2], tolerance) == false) { + return false; + } + // test if the line has been drawn far enough + var mag = dist(x1, y1, x2, y2); + var minPos1 = [x1 + 2 * tolerance / mag * (x2 - x1), y1 + 2 * tolerance / mag * (y2 - y1)]; + var minPos2 = [x2 + 2 * tolerance / mag * (x1 - x2), y2 + 2 * tolerance / mag * (y1 - y2)]; + + /* + console.log('pos1:'+x1+','+y1); + console.log('pos2:'+x2+','+y2); + console.log('minPos1:'+minPos1[0]+','+minPos1[1]); + console.log('minPos2:'+minPos2[0]+','+minPos2[1]); + console.log('testPos1:'+xTest1+','+yTest1); + console.log('testPos2:'+xTest2+','+yTest2); + console.log('dist to min pos, dist to actual pos:'); + console.log('pos1,testPos1:',dist(minPos1[0],minPos1[1],xTest1,yTest1),dist(x1,y1,xTest1,yTest1),dist(minPos1[0],minPos1[1],xTest1,yTest1)>dist(x1,y1,xTest1,yTest1)); + console.log('pos2,testPos2:',dist(minPos2[0],minPos2[1],xTest2,yTest2),dist(x2,y2,xTest2,yTest2),dist(minPos2[0],minPos2[1],xTest2,yTest2)>dist(x2,y2,xTest2,yTest2)); + console.log('pos1,testPos2:',dist(minPos1[0],minPos1[1],xTest2,yTest2),dist(x1,y1,xTest2,yTest2),dist(minPos1[0],minPos1[1],xTest2,yTest2)>dist(x1,y1,xTest2,yTest2)); + console.log('pos2,testPos1:',dist(minPos2[0],minPos2[1],xTest1,yTest1),dist(x2,y2,xTest1,yTest1),dist(minPos2[0],minPos2[1],xTest1,yTest1)>dist(x2,y2,xTest1,yTest1)); + //*/ + + // if both points are closer to minPos than to either of the actual points + if ((dist(minPos1[0], minPos1[1], xTest1, yTest1) > dist(x1, y1, xTest1, yTest1) + && dist(minPos1[0], minPos1[1], xTest2, yTest2) > dist(x1, y1, xTest2, yTest2)) + + || (dist(minPos2[0], minPos2[1], xTest1, yTest1) > dist(x2, y2, xTest1, yTest1) + && dist(minPos2[0], minPos2[1], xTest2, yTest2) > dist(x2, y2, xTest2, yTest2))) { + return false; + } + return true; +} +function getAngleTwoPoints(pos1, pos2) { // returns the angle in rad from pos1 to pos2 + var m = (pos2[1] - pos1[1]) / (pos2[0] - pos1[0]); + var a = Math.atan(m); + if (pos1[1] == pos2[1]) { // horizontal + if (pos1[0] < pos2[0]) { + return 0; + } else { + return Math.PI; + } + } + if (pos1[0] == pos2[0]) { // vertical + if (pos1[1] < pos2[1]) { + return Math.PI / 2; + } else { + return 3 * Math.PI / 2; + } + } + if (m > 0) { + if (pos2[0] > pos1[0]) { + return a; + } else { + return a + Math.PI; + } + } else { + if (pos2[0] > pos1[0]) { + return a; + } else { + return a + Math.PI; + } + } +} +function interpolateTwoPoints(pos1, pos2, proportion) { + if (typeof proportion == 'undefined') + proportion = 0.5; + return [pos1[0] + proportion * (pos2[0] - pos1[0]), pos1[1] + proportion * (pos2[1] - pos1[1])]; +} +function angleToPos(angle, centerX, centerY, radius, opt_angleType) { + if (opt_angleType == 'degrees') + angle = angle * Math.PI / 180; + return [centerX + Math.cos(angle) * radius, centerY - Math.sin(angle) * radius]; +} +function posToAngle(posX, posY, centerX, centerY, radius) { + if (un(radius)) + radius = dist(posX, posY, centerX, centerY); + var cosTheta = (posX - centerX) / radius; + var sinTheta = (posY - centerY) / radius; + if (sinTheta >= 0) + return Math.acos(cosTheta); + if (sinTheta < 0) + return 2 * Math.PI - Math.acos(cosTheta); +} +function extendLine(pos1, pos2, extLength) { + var m = (pos2[1] - pos1[1]) / (pos2[0] - pos1[0]); + var angle = Math.atan(m); + if (pos1[1] == pos2[1]) { // horizontal + if (pos1[0] < pos2[0]) { + return [pos2[0] + extLength, pos2[1]]; + } else { + return [pos2[0] - extLength, pos2[1]]; + } + } + if (pos1[0] == pos2[0]) { // vertical + if (pos1[1] < pos2[1]) { + return [pos2[0], pos2[1] + extLength]; + } else { + return [pos2[0], pos2[1] - extLength]; + } + } + if (m > 0) { + if (pos2[0] >= pos1[0]) { + return ([pos2[0] + extLength * Math.cos(angle), pos2[1] + extLength * Math.sin(angle)]); + } else { + return ([pos2[0] - extLength * Math.cos(angle), pos2[1] - extLength * Math.sin(angle)]); + } + } else { + if (pos2[0] <= pos1[0]) { + return ([pos2[0] - extLength * Math.cos(angle), pos2[1] - extLength * Math.sin(angle)]); + } else { + return ([pos2[0] + extLength * Math.cos(angle), pos2[1] + extLength * Math.sin(angle)]); + } + } +} + +function isPointOnEllipse(point, center, radiusX, radiusY, tolerance) { + if (typeof tolerance == 'undefined') { + if (roundToNearest(Math.pow((point[0] - center[0]) / radiusX, 2) + Math.pow((point[1] - center[1]) / radiusY, 2), 0.0001) == 1) { + return true; + } else { + return false; + } + } else { + var max = Math.pow((point[0] - center[0]) / (radiusX + tolerance), 2) + Math.pow((point[1] - center[1]) / (radiusY + tolerance), 2); + var min = Math.pow((point[0] - center[0]) / (radiusX - tolerance), 2) + Math.pow((point[1] - center[1]) / (radiusY - tolerance), 2); + if (Math.max(min, max) >= 1 && Math.min(min, max) <= 1) { + return true; + } else { + return false; + } + } +} +function isPointInEllipse(point, center, radiusX, radiusY) { + if (roundToNearest(Math.pow((point[0] - center[0]) / radiusX, 2) + Math.pow((point[1] - center[1]) / radiusY, 2), 0.0001) <= 1) { + return true; + } else { + return false; + } +} +function isPointInRect(point, left, top, width, height) { + if (point[0] >= left && point[0] <= left + width && point[1] >= top && point[1] <= top + height) { + return true; + } else { + return false; + } +} +function distPointToRect(point, left, top, width, height) { + if (isPointInRect(point, left, top, width, height) == true) + return 0; + return Math.min( + distancePointToLineSegment(point, [left, top], [left + width, top]), + distancePointToLineSegment(point, [left + width, top], [left + width, top + height]), + distancePointToLineSegment(point, [left + width, top + height], [left, top + height]), + distancePointToLineSegment(point, [left, top + height], [left + width, top])) +} +function lineCircleIntersections(linePoint1, linePoint2, circleCenter, circleRadius) { + var a = linePoint1[0]; + var b = linePoint1[1]; + var c = linePoint2[0]; + var d = linePoint2[1]; + var p = circleCenter[0]; + var q = circleCenter[1]; + var r = circleRadius; + + var m = (d - b) / (c - a); + var s = m * m + 1; + var t = -2 * p + 2 * m * (-m * a + b - q); + var u = p * p + (-m * a + b - q) * (-m * a + b - q) - r * r; + var discrim = t * t - 4 * s * u; + if (discrim < 0) { + return []; + } else if (discrim == 0) { + var x = (-t + Math.sqrt(discrim)) / (2 * s); + var y = m * (x - a) + b; + if (x >= p) { + if (y >= q) { + var angle = Math.atan((y - q) / (x - p)); + } else { + var angle = 2 * Math.PI + Math.atan((y - q) / (x - p)); + } + } else { + var angle = Math.PI + Math.atan((y - q) / (x - p)); + } + return [[x, y, angle]]; + } else { + var x1 = (-t + Math.sqrt(discrim)) / (2 * s); + var y1 = m * (x1 - a) + b; + if (x1 >= p) { + if (y1 >= q) { + var angle1 = Math.atan((y1 - q) / (x1 - p)); + } else { + var angle1 = 2 * Math.PI + Math.atan((y1 - q) / (x1 - p)); + } + } else { + var angle1 = Math.PI + Math.atan((y1 - q) / (x1 - p)); + } + var x2 = (-t - Math.sqrt(discrim)) / (2 * s); + var y2 = m * (x2 - a) + b; + if (x2 >= p) { + if (y2 >= q) { + var angle2 = Math.atan((y2 - q) / (x2 - p)); + } else { + var angle2 = 2 * Math.PI + Math.atan((y2 - q) / (x2 - p)); + } + } else { + var angle2 = Math.PI + Math.atan((y2 - q) / (x2 - p)); + } + return [[x1, y1, angle1], [x2, y2, angle2]]; + } +} +function circleIntersections(x0, y0, r0, x1, y1, r1) { + var a, + dx, + dy, + d, + h, + rx, + ry; + var x2, + y2; + /* dx and dy are the vertical and horizontal distances between the circle centers. */ + dx = x1 - x0; + dy = y1 - y0; + /* Determine the straight-line distance between the centers. */ + d = Math.sqrt((dy * dy) + (dx * dx)); + /* Check for solvability. */ + if (d > (r0 + r1)) { + /* no solution. circles do not intersect. */ + return []; + } + if (d < Math.abs(r0 - r1)) { + /* no solution. one circle is contained in the other */ + return []; + } + /* 'point 2' is the point where the line through the circle intersection points crosses the line between the circle centers. */ + /* Determine the distance from point 0 to point 2. */ + a = ((r0 * r0) - (r1 * r1) + (d * d)) / (2 * d); + /* Determine the coordinates of point 2. */ + x2 = x0 + (dx * a / d); + y2 = y0 + (dy * a / d); + /* Determine the distance from point 2 to either of the intersection points. */ + h = Math.sqrt((r0 * r0) - (a * a)); + /* Now determine the offsets of the intersection points from point 2. */ + rx = -dy * (h / d); + ry = dx * (h / d); + /* Determine the absolute intersection points. */ + var xi = x2 + rx; + var xii = x2 - rx; + var yi = y2 + ry; + var yii = y2 - ry; + return [[xi, yi], [xii, yii]]; +} +function isPointOnArc(point, arcPath, tolerance) { + if (!tolerance) + tolerance = 0.00001; + var rad = dist(point[0], point[1], arcPath.center[0], arcPath.center[1]); + if (rad < arcPath.radius - tolerance || rad > arcPath.radius + tolerance) + return false; + if (Math.abs(arcPath.startAngle - arcPath.finAngle) + 0.00001 >= 2 * Math.PI) + return true; + var angle = getAngleFromAToB(arcPath.center, point); + var angle1 = simplifyAngle(arcPath.startAngle); + var angle2 = simplifyAngle(arcPath.finAngle); + var a = arcPath.clockwise; + var b = angle1 < angle2; + var c = angle >= angle1 && angle <= angle2; + /* + if (a && b && c) return true; + if (a && b && !c) return false; + if (a && !b && c) return true; + if (a && !b && !c) return false; + if (!a && b && c) return false; + if (!a && b && !c) return true; + if (!a && !b && c) return false; + if (!a && !b && !c) return true; + /*/ + if (a == true) { + if (b == true) { + if (c == true) { + return true; + } else { + return false; + } + } else { + if (c == true) { + return true; + } else { + return false; + } + } + } else { + if (b == true) { + if (c == true) { + return false; + } else { + return true; + } + } else { + if (c == true) { + return false; + } else { + return true; + } + } + } + //*/ +} + +function simplifyAngle(angle) { + while (angle < 0) { + angle += (2 * Math.PI) + }; + angle = angle % (2 * Math.PI); + return angle; +} +function getAngleFromAToB(a, b) { + // angle as measured anticlockwise from positive x-direction (upside down on canvas) - A is in the centre + var angle = Math.atan((b[1] - a[1]) / (b[0] - a[0])); + if (b[0] >= a[0] && b[1] >= a[1]) + return angle; + if (b[0] >= a[0] && b[1] < a[1]) + return angle + 2 * Math.PI; + if (b[0] < a[0]) + return angle + Math.PI; +} +function getEndPointsOfArc(arcPath) { + // if closed circle, return empty + if (Math.abs(arcPath.startAngle - arcPath.finAngle) % (2 * Math.PI) == 0) + return []; + return [ + [arcPath.center[0] + arcPath.radius * Math.cos(arcPath.startAngle), arcPath.center[1] + arcPath.radius * Math.sin(arcPath.startAngle)], + [arcPath.center[0] + arcPath.radius * Math.cos(arcPath.finAngle), arcPath.center[1] + arcPath.radius * Math.sin(arcPath.finAngle)] + ] +} +function vectorFromAToB(a, b) { + return [b[0] - a[0], b[1] - a[1]]; +} + +function snapToPoints(point, snapPoints, tolerance) { + var minDist = []; + for (var i = 0; i < snapPoints.length; i++) { + minDist.push(dist(point[0], point[1], snapPoints[i][0], snapPoints[i][1])); + } + if (arrayMin(minDist) < tolerance) { + return snapPoints[minDist.indexOf(arrayMin(minDist))].slice(0); + } else { + return point.slice(0); + } +} +function snapToGrid(point, snapGrid) { + //snapGrid should contain: {left,top,width,height,dx,dy} +} + +function getBezierPoints(p1, p2, p3, density) { + if (!density) + density = 10; + var length = quadraticBezierLength(p1, p2, p3); + var points = [p1]; + for (var i = density; i < length; i += density) { + points.push(getQuadraticCurvePoint(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1], i / length)); + } + points.push(p3); + return points; +} +function getQBezierValue(t, p1, p2, p3) { // called by getQuadraticCurvePoint, src: http://jsfiddle.net/QA6VG/ + var iT = 1 - t; + return iT * iT * p1 + 2 * iT * t * p2 + t * t * p3; +} +function getQuadraticCurvePoint(startX, startY, cpX, cpY, endX, endY, position) { // called by getBezierPoints, src: http://jsfiddle.net/QA6VG/ + return [getQBezierValue(position, startX, cpX, endX), getQBezierValue(position, startY, cpY, endY)]; +} +function quadraticBezierLength(p0, p1, p2) { // called by getBezierPoints, src: https://gist.github.com/tunght13488/6744e77c242cc7a94859 + function Point(x, y) { + this.x = x; + this.y = y; + } + var a = new Point( + p0[0] - 2 * p1[0] + p2[0], + p0[1] - 2 * p1[1] + p2[1]); + var b = new Point( + 2 * p1[0] - 2 * p0[0], + 2 * p1[1] - 2 * p0[1]); + var A = 4 * (a.x * a.x + a.y * a.y); + var B = 4 * (a.x * b.x + a.y * b.y); + var C = b.x * b.x + b.y * b.y; + + var Sabc = 2 * Math.sqrt(A + B + C); + var A_2 = Math.sqrt(A); + var A_32 = 2 * A * A_2; + var C_2 = 2 * Math.sqrt(C); + var BA = B / A_2; + + return (A_32 * Sabc + A_2 * B * (Sabc - C_2) + (4 * C * A - B * B) * Math.log((2 * A_2 + BA + Sabc) / (BA + C_2))) / (4 * A_32); +} + +function drawTextBox(canvas, canvasctx, canvasData, backgroundColor, borderColor, borderWidth, font, textColor, textAlign, textLine1, textLine2) { + var polygon = false; + if (typeof canvasData[41] !== 'undefined') + polygon = true; + canvasctx.clearRect(0, 0, canvasData[2], canvasData[3]); + canvasctx.fillStyle = backgroundColor; + canvasctx.strokeStyle = borderColor; + canvasctx.lineWidth = borderWidth; + if (polygon == true) { + canvasctx.beginPath(); + canvasctx.moveTo(canvasData[41][0][0], canvasData[41][0][1]); + for (j = 0; j < canvasData[41].length; j++) { + if (j > 0) + canvasctx.lineTo(canvasData[41][j][0], canvasData[41][j][1]); + } + canvasctx.closePath(); + canvasctx.fill(); + canvasctx.stroke(); + } else { + if (backgroundColor !== '') + canvasctx.fillRect(0, 0, canvasData[2], canvasData[3]); + if (borderColor !== '') + canvasctx.strokeRect(0, 0, canvasData[2], canvasData[3]); + } + canvasctx.font = font; + canvasctx.fillStyle = textColor; + canvasctx.textAlign = textAlign; + canvasctx.textBaseline = "middle"; + if (!textLine2) { + canvasctx.fillText(textLine1, 0.5 * canvasData[2], 0.5 * canvasData[3]); + } else { + canvasctx.fillText(textLine1, 0.5 * canvasData[2], 0.3 * canvasData[3]); + canvasctx.fillText(textLine2, 0.5 * canvasData[2], 0.7 * canvasData[3]); + } +} +function drawMathsTextBox(canvasctx, canvasData, textArray, object) { + // optional object can contain: backColor, algText, fontSize, borderColor, borderWidth, textColor, textAlign + if (!object) { + var fontSize = 0.45 * canvasData[3]; + var horizAlign = 'center'; + var algText = true; + var textColor = '#000'; + var backgroundColor = '#6F9'; + var borderColor = '#000'; + var borderWidth = 4; + } else { + var fontSize = object.fontSize || 0.45 * canvasData[3]; + var horizAlign = object.textAlign || 'center'; + var algText = true; + if (typeof object.algText == 'boolean') + algText = object.algText; + var textColor = object.textColor || '#000'; + var backgroundColor = object.backColor || '#6F9'; + var borderColor = object.borderColor || '#000'; + var borderWidth = object.borderWidth || 4; + } + var horizPos = 0.5 * canvasData[2]; + if (horizAlign == 'left') + horizPos = 5; + if (horizAlign == 'right') + horizPos = canvasData[2] - 5; + canvasctx.clearRect(0, 0, canvasData[2], canvasData[3]); + canvasctx.fillStyle = backgroundColor; + canvasctx.strokeStyle = borderColor; + canvasctx.lineWidth = borderWidth; + canvasctx.fillRect(0, 0, canvasData[2], canvasData[3]); + canvasctx.strokeRect(borderWidth * 0.5, borderWidth * 0.5, canvasData[2] - borderWidth, canvasData[3] - borderWidth); + drawMathsText(canvasctx, textArray, fontSize, horizPos, 0.47 * canvasData[3], algText, [], horizAlign, 'middle', textColor, 'draw'); +} +function wrapText(context, text, x, y, maxWidth, lineHeight, font, fillStyle) { + context.font = font; + context.fillStyle = fillStyle; + var words = text.split(' '); + var line = ''; + for (var n = 0; n < words.length; n++) { + var testLine = line + words[n] + ' '; + var metrics = context.measureText(testLine); + var testWidth = metrics.width; + if (testWidth > maxWidth && n > 0) { + context.fillText(line, x, y); + line = words[n] + ' '; + y += lineHeight; + } else { + line = testLine; + } + } + context.fillText(line, x, y); +} +function drawFrac(context, font, x, y, horizAlign, numerator, denominator) { + context.save(); + context.font = font; + var fractionWidth = Math.max(context.measureText(numerator).width, context.measureText(denominator).width); + context.textAlign = 'center'; + context.textBaseline = 'bottom'; + switch (horizAlign) { + case 'right': + context.fillText(numerator, x - 2 - 0.5 * fractionWidth, y - 5); + context.textBaseline = 'top'; + context.fillText(denominator, x - 2 - 0.5 * fractionWidth, y + 5); + context.strokeStyle = textColor; + context.lineWidth = 3; + context.moveTo(x - 4 - fractionWidth, y); + context.lineTo(x, y); + break; + case 'center': + context.fillText(numerator, x, y - 5); + context.textBaseline = 'top'; + context.fillText(denominator, x, y + 5); + context.strokeStyle = textColor; + context.lineWidth = 3; + context.moveTo(x - 2 - 0.5 * fractionWidth, y); + context.lineTo(x + 2 + 0.5 * fractionWidth, y); + break; + default: + context.fillText(numerator, x + 2 + 0.5 * fractionWidth, y - 5); + context.textBaseline = 'top'; + context.fillText(denominator, x + 2 + 0.5 * fractionWidth, y + 5); + context.strokeStyle = textColor; + context.lineWidth = 3; + context.moveTo(x, y); + context.lineTo(x + 4 + fractionWidth, y); + break; + } + context.stroke(); + context.restore(); +} + +function generateNormalData(mean, sd, n) { + var normal = gaussian(mean, sd); + var data = []; + var groups = []; + for (var i = 0; i < 8; i++) + groups[i] = { + min: roundToNearest(mean + (i - 4) * sd, 0.00001), + max: roundToNearest(mean + (i - 3) * sd, 0.00001), + frequency: 0 + }; + for (var i = 0; i < n; i++) { + var value = normal() + data.push(value); + for (var g = 0; g < 8; g++) { + if (value >= groups[g].min && value <= groups[g].max) { + groups[g].frequency++; + break; + } + } + } + return { + data: data, + grouped: groups + }; +} +// returns a gaussian random function with the given mean and stdev. +function gaussian(mean, stdev) { + var y2; + var use_last = false; + return function () { + var y1; + if (use_last) { + y1 = y2; + use_last = false; + } else { + var x1, + x2, + w; + do { + x1 = 2.0 * Math.random() - 1.0; + x2 = 2.0 * Math.random() - 1.0; + w = x1 * x1 + x2 * x2; + } while (w >= 1.0); + w = Math.sqrt((-2.0 * Math.log(w)) / w); + y1 = x1 * w; + y2 = x2 * w; + use_last = true; + } + + var retval = mean + stdev * y1; + if (retval > 0) + return retval; + return -retval; + } +} +function phi(x) { // for normal distribution + var a1 = 0.254829592; + var a2 = -0.284496736; + var a3 = 1.421413741; + var a4 = -1.453152027; + var a5 = 1.061405429; + var p = 0.3275911; + + var sign = 1 + if (x < 0) + sign = -1 + x = Math.abs(x) / Math.sqrt(2) + + // A&S formula 7.1.26 + var t = 1 / (1 + p * x) + var y = 1 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x) + + return 0.5 * (1 + sign * y) +} +function enlargeDash(dash, sf) { + if (typeof sf == 'undefined') + return dash; + for (var i = 0; i < dash.length; i++) { + dash[i] = dash[i] * sf; + } + return dash; +} + +function numberToString(n, commaForThousand) { + var decPart = roundToNearest(n - Math.floor(n), 0.00001); + var str = Math.floor(n).toString(); + if (str.length < 4 || (str.length == 4 && boolean(commaForThousand, false) == false)) {} + else { + var count = 0; + for (var i = str.length; i >= 1; i--) { + if (count == 3) { + str = str.slice(0, i) + ',' + str.slice(i); + count = 0; + } + count++; + } + } + if (decPart > 0) { + var str2 = String(decPart); + str = str + str2.slice(1); + } + return str; +} +function stringToNumber(str) { + var str = replaceAll(str, ',', ''); + var str = replaceAll(str, ' ', ''); + return parseInt(str); +} +function numberToWords(n, capFirstLetter) { + var string = n.toString(), + units, + tens, + scales, + start, + end, + chunks, + chunksLen, + chunk, + ints, + i, + word, + words, + and = 'and'; + if (parseInt(string) === 0) + return 'zero'; + + units = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen']; + + tens = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety']; + + scales = ['', 'thousand', 'million', 'billion', 'trillion', 'quadrillion', 'quintillion', 'sextillion', 'septillion', 'octillion', 'nonillion', 'decillion', 'undecillion', 'duodecillion', 'tredecillion', 'quatttuor-decillion', 'quindecillion', 'sexdecillion', 'septen-decillion', 'octodecillion', 'novemdecillion', 'vigintillion', 'centillion']; + + start = string.length; /* Split user arguemnt into 3 digit chunks from right to left */ + chunks = []; + while (start > 0) { + end = start; + chunks.push(string.slice((start = Math.max(0, start - 3)), end)); + } + + chunksLen = chunks.length; /* Check if function has enough scale words to be able to stringify the user argument */ + if (chunksLen > scales.length) { + return ''; + } + + words = []; /* Stringify each integer in each chunk */ + for (i = 0; i < chunksLen; i++) { + chunk = parseInt(chunks[i]); + if (chunk) { + + /* Split chunk into array of individual integers */ + ints = chunks[i].split('').reverse().map(parseFloat); + + /* If tens integer is 1, i.e. 10, then add 10 to units integer */ + if (ints[1] === 1) { + ints[0] += 10; + } + + if (i > 0) + words.push(','); + + /* Add scale word if chunk is not zero and array item exists */ + if ((word = scales[i])) { + words.push(word); + } + + /* Add unit word if array item exists */ + if ((word = units[ints[0]])) { + words.push(word); + } + + /* Add tens word if array item exists */ + if ((word = tens[ints[1]])) { + words.push(word); + } + + /* Add 'and' string after units or tens integer if: */ + if (ints[0] || ints[1]) { + + /* Chunk has a hundreds integer or chunk is the first of multiple chunks */ + if (ints[2] || !i && chunksLen) { + words.push(and); + } + + } + + /* Add hundreds word if array item exists */ + if ((word = units[ints[2]])) { + words.push(word + ' hundred'); + } + + } + + } + + var str = words.reverse().join(' '); + str = replaceAll(str, ' ,', ','); + str = replaceAll(str, ', and', ' and'); + if (str.slice(0, 4) == 'and ') + str = str.slice(4); + if (str.slice(-1) == ',') + str = str.slice(0, -1); + if (boolean(capFirstLetter, true)) + str = str.charAt(0).toUpperCase() + str.slice(1); + return str; +} diff --git a/tools/i2/_style.css b/tools/i2/_style.css new file mode 100644 index 0000000..ec60650 --- /dev/null +++ b/tools/i2/_style.css @@ -0,0 +1,186 @@ +@charset "utf-8"; +/* CSS Document */ + +@media screen { + html, body { + width: 100%; + height: 100%; + margin: auto auto auto auto; + background-color: #9C9AFF; + border: 0; + text-align: center; + overflow: hidden; /* Disable scrollbars */ + } +} +@media print { + size: auto; + margin: 0mm; +} + +@font-face { + font-family: 'Hobo'; + src: url('fonts/hobo-webfont.woff') format('woff'), + url('fonts/hobo-webfontd41d.eot?#iefix') format('embedded-opentype'), + url('fonts/hobo-webfont.eot'), + url('fonts/hobo-webfont.ttf') format('truetype'), + url('fonts/hobo-webfont.svg#hoboregular') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'segoePrint'; + src:url('fonts/segeo-print-webfont.woff') format('woff'), + url('fonts/segeo-print-webfont.ttf') format('truetype'), + url('fonts/segeo-print-webfont.svg#SegoePrintRegular') format('svg'), + url('fonts/segeo-print-webfont-2.svg#SegoePrintRegular') format('svg'), + url('fonts/segeo-print-webfont.eot'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'smileyMonster'; + src:url('fonts/smileymonster.woff') format('woff'), + url('fonts/smileymonster.ttf') format('truetype'), + url('fonts/smileymonster.svg#Smiley-Monster') format('svg'), + url('fonts/smileymonster.eot'), + url('fonts/smileymonsterd41d.eot?#iefix') format('embedded-opentype'); + font-weight: normal; + font-style: normal; +} + +.wrapper { + display: table; + padding: 0; + width: 100%; + height: 100%; + position: absolute; +} +.container { + display: table-cell; + vertical-align: middle; + user-select: none; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + -o-user-select: none; +} +.canvas-container { + position: relative; + margin: 0 auto; +} +#canvas { + width: 100%; + height: auto; + position: relative; + tabindex: 1; + touch-action: none; + -ms-touch-action: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-tap-highlight-color: transparent; +} +#inactiveBox { + position: absolute; + z-index: 900000; + left: 0; + top: 0; + -ms-touch-action: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-tap-highlight-color: transparent; +} +.holderButton { + position: absolute; + z-index: 800000; + left: 0; + top: 0; + touch-action: none; + -ms-touch-action: none; + cursor: pointer; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-tap-highlight-color: transparent; +} +.hideMe { + overflow: hidden; + visibility: hidden; + height: 0; + width: 0; +} +#videoMask { + position: absolute; + z-index: 510; + cursor: auto; + tabindex: 2; + touch-action: none; + -ms-touch-action: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-tap-highlight-color: transparent; +} +.buttonClass { + position: absolute; + z-index: 2; + cursor: pointer; + tabindex: 2; + touch-action: none; + -ms-touch-action: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-tap-highlight-color: transparent; +} +.drawDivCanvas { + position: absolute; + z-index: 2; + cursor: pointer; + tabindex: 2; + left:0px; + top:0px; + width:100%; + touch-action: none; + -ms-touch-action: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-tap-highlight-color: transparent; +} +.page-div { + position: relative; + margin-bottom: 10px; +} +.keyClass { + position: absolute; + z-index: 800000; + cursor: auto; + border-radius: 5px; + tabindex: 2; + touch-action: none; + -ms-touch-action: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-tap-highlight-color: transparent; +} +.inputClass { + position: absolute; + z-index: 2; + font-size: 42px; + text-align: center; + margin: 0; + width: 50px; + height: 50px; + cursor: text; + touch-action: none; + -ms-touch-action: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-tap-highlight-color: transparent; +} +.gridCanvasClass { + position: absolute; + z-index: 2; + tabindex: 2; + -ms-touch-action: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-tap-highlight-color: transparent; +} +.videoClass { + position: absolute; + z-index: 500; + cursor: auto; + tabindex: 2; + touch-action: none; + -ms-touch-action: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-tap-highlight-color: transparent; +} \ No newline at end of file diff --git a/tools/i2/_text2.js b/tools/i2/_text2.js new file mode 100644 index 0000000..ee72766 --- /dev/null +++ b/tools/i2/_text2.js @@ -0,0 +1,925 @@ +//js + +var defaultTags = { + font:'Arial', + fontSize:20, + bold:false, + italic:false, + color:'#000', + selected:false, + backColor:'none', + align:[0,0], + lineSpacingFactor:1.2, + lineSpacingStyle:'variable' +} +function updateTagsFromText(arr,startTags) { + var tags = clone(startTags); + if (typeof arr == 'string') { + var str = arr.slice(0); + while (str.indexOf('<<') > -1) { + str = str.slice(str.indexOf('<<')); + var type = str.slice(2,str.indexOf(':')); + var value = str.slice(str.indexOf(':')+1,str.indexOf('>>')); + if (type == 'align') { + if (value == 'left') tags.align[0] = -1; + if (value == 'center') tags.align[0] = 0; + if (value == 'right') tags.align[0] = 1; + } else { + if (!isNaN(Number(value))) value = Number(value); + if (value == 'true') value = true; + if (value == 'false') value = false; + tags[type] = value; + } + str = str.slice(str.indexOf('>>')+2); + } + } else if (typeof arr == 'object') { + for (var i = 0; i < arr.length; i++) { + tags = updateTagsFromText(arr[i],tags); + } + } + return tags; +} +function getAlignFromText(arr,fallback) { + var align = fallback; + if (typeof arr == 'string') { + var str = arr.slice(0); + if (str.indexOf('< -1) { + str = str.slice(str.indexOf('<>')); + } + } else if (typeof arr == 'object') { + for (var i = 0; i < arr.length; i++) { + align = getAlignFromText(arr[i],align); + } + } + if (align == 'left') align = -1; + if (align == 'center') align = 0; + if (align == 'right') align = 1; + return align; +} + +function splitTextByTags(textStr) { + // find markup tags and split text + var splitPoints = [0]; + for (var ch1 = 0; ch1 < textStr.length; ch1++) { + if (textStr.slice(ch1).indexOf('<<') == 0 && textStr.slice(ch1).indexOf('<<<') !== 0) { + for (var ch2 = ch1; ch2 < textStr.length; ch2++) { + if (textStr.slice(ch2).indexOf('>>') == 0) { + splitPoints.push(ch1,ch2+2); + break; + } + } + } + } + splitPoints.push(textStr.length); + var splitText = []; + for (var ch = 0; ch < splitPoints.length-1; ch++) { + splitText.push(textStr.slice(splitPoints[ch],splitPoints[ch+1])) + } + return splitText; +} +function textArrayCombineAdjacentText(arr) { + for (var gg = arr.length - 1; gg >= 0; gg--) { + if (typeof arr[gg] == 'object') { + textArrayCombineAdjacentText(arr[gg]); + } else { + if (gg < arr.length - 1 && typeof arr[gg] == 'string' && typeof arr[gg+1] == 'string') { + arr[gg] += arr[gg+1]; + arr.splice(gg+1,1);; + } + } + } + return arr; +} +function textTagGetTypeValue(tagStr) { + if (tagStr.indexOf('<<') == -1) { + return null; + } else if (tagStr.indexOf('<<') > 0) { + tagStr = tagStr.slice(tagStr.indexOf('<<')) + } + var type = tagStr.slice(2,tagStr.indexOf(':')); + var value = tagStr.slice(tagStr.indexOf(':')+1,tagStr.indexOf('>>')); + if (!isNaN(Number(value))) value = Number(value); + if (value == 'false') value = false; + if (value == 'true') value = true; + return {type:type,value:value}; +} +function simplifyText(arr) { + var arr = textArrayCombineAdjacentText(arr); + var arr = textArrayRemoveRedundantTagsA(arr); + var arr = textArrayRemoveRedundantTagsB(arr); + var arr = textArrayRemoveRedundantAlignTags(arr); + return arr; +} +function textArrayRemoveRedundantTagsA(arr) { + // removes tags of the same type with no characters between them + var tags = {bold:null,italic:null,fontSize:null,font:null,color:null,backColor:null,selected:null}; + var char = {bold:true,italic:true,fontSize:true,font:true,color:true,backColor:true,selected:true}; + + if (typeof arr == 'object') { + arr = arrayHandler(arr); + } else if (typeof arr == 'string') { + arr = stringHandler(arr); + } + + function arrayHandler(arr) { + for (var l = arr.length - 1; l >= 0; l--) { + if (typeof arr[l] == 'string') { + arr[l] = stringHandler(arr[l]); + } else if (typeof arr[l] == 'object') { + for (var prop in char) char[prop] = true; + arr[l] = arrayHandler(arr[l]); + } + } + return arr; + } + + function stringHandler(str) { + var split = splitTextByTags(str); + for (var j = split.length - 1; j >= 0; j--) { + if (split[j].indexOf('<<') == 0) { + var tag = textTagGetTypeValue(split[j]); + if (tag.type == 'align') continue; + if (char[tag.type] == false) { + split.splice(j,1); + } else { + tags[tag.type] = tag.value; + char[tag.type] = false; + } + } else if (split[j].length > 0) { + for (var prop in char) char[prop] = true; + } + } + var str = ''; + for (var j = 0; j < split.length; j++) str += split[j]; + return str; + } + return arr; +} +function textArrayRemoveRedundantTagsB(arr) { + // removes redundant repeated tags + var tags = {bold:null,italic:null,fontSize:null,font:null,color:null,backColor:null,selected:null}; + + if (typeof arr == 'object') { + arr = arrayHandler(arr); + } else if (typeof arr == 'string') { + arr = stringHandler(arr); + } + + function arrayHandler(arr) { + for (var l = 0; l < arr.length; l++) { + if (typeof arr[l] == 'string') { + arr[l] = stringHandler(arr[l]); + } else if (typeof arr[l] == 'object') { + arr[l] = arrayHandler(arr[l]); + } + } + return arr; + } + + function stringHandler(str) { + var split = splitTextByTags(str); + for (var j = 0; j < split.length; j++) { + if (split[j].indexOf('<<') == 0) { + var tag = textTagGetTypeValue(split[j]); + if (tag.type == 'align') continue; + if (tags[tag.type] == tag.value) { + split.splice(j,1); + } else { + tags[tag.type] = tag.value; + } + } + } + var str = ''; + for (var j = 0; j < split.length; j++) str += split[j]; + return str; + } + return arr; +} +function textArrayRemoveRedundantAlignTags(arr) { + var alignSet = false; + + if (typeof arr == 'object') { + arr = arrayHandler(arr); + } else if (typeof arr == 'string') { + arr = stringHandler(arr); + } + + function arrayHandler(arr) { + for (var l = 0; l < arr.length; l++) { + if (typeof arr[l] == 'string') { + arr[l] = stringHandler(arr[l]); + } else if (typeof arr[l] == 'object') { + arr[l] = arrayHandler(arr[l]); + } + } + return arr; + } + + function stringHandler(str) { + var split = splitTextByTags(str); + for (var j = 0; j < split.length; j++) { + if (split[j].indexOf('< -1) { + alignSet = false; + } + } + var str = ''; + for (var j = 0; j < split.length; j++) str += split[j]; + return str; + } + return arr; +} +function textArrayReplace(arr,find,replace) { + //if (find == '<
>') console.log(clone(arr),arr,find,replace); + if (typeof arr == 'string') { + arr = replaceAll(arr,find,replace); + } else if (typeof arr == 'object') { + for (var i = 0; i < arr.length; i++) { + arr[i] = textArrayReplace(arr[i],find,replace); + } + } + return arr; +} +function textArrayFontSizeAdjust(arr,sf) { + if (typeof arr == 'string') { + for (var c = 0; c < arr.length; c++) { + var sliced = arr.slice(c); + if (sliced.indexOf('<>'); + var after = sliced.slice(d+2); + var num = arr.slice(c+11); + num = num.slice(0,num.indexOf('>>')); + num = Number(num); + if (!isNaN(num)) { + num = Math.round(num*sf); + arr = before+'<>'+after; + } + } + } + } else if (arr instanceof Array) { + for (var i = 0; i < arr.length; i++) { + arr[i] = textArrayFontSizeAdjust(arr[i],sf); + } + } else if (typeof arr == 'object') { + for (var i in arr) { + arr[i] = textArrayFontSizeAdjust(arr[i],sf); + } + } + return arr; +} +function textArrayFind(arr,str,found,depth) { + if (un(found)) found = false; + if (un(depth)) depth = 0; + if (typeof arr == 'string') { + if (arr.indexOf(str) > -1) found = true; + } else if (typeof arr == 'object') { + for (var i = 0; i < arr.length; i++) { + if (depth > 0 && i === 0 && ['frac','pow','sqrt','root','power','sub','subs','subscript','sin','cos','tan','log','ln','sin-1','cos-1','tan-1','logBase','abs','exp','int1','sigma1','sigma2','int2','recurring','vectorArrow','bar','hat','colVector2d','colVector3d','mixedNum','lim'].indexOf(arr[i]) > -1) continue; + found = textArrayFind(arr[i],str,found,depth+1); + } + } + return found; +} +function textArrayRemoveDefaultTags(arr,tags) { //also removes redundant tags at end + if (un(tags)) tags = defaultTags; + var sp = splitTextByTags(arr[0]); + arr[0] = ''; + var stop = false; + for (var s = 0; s < sp.length; s++) { + if (sp[s].length == 0) continue; + if (stop == true || (sp[s].indexOf('<<') !== 0)) { + arr[0] += sp[s]; + stop = true; + } else { + var type = sp[s].slice(2,sp[s].indexOf(':')); + var value = sp[s].slice(sp[s].indexOf(':')+1,-2); + if (!isNaN(Number(value))) value = Number(value); + if (value == 'true') value = true; + if (value == 'false') value = false; + if (tags[type] !== value || type == 'align') arr[0] += sp[s]; + } + } + + if (typeof arr[arr.length-1] == 'string') { + var sp = splitTextByTags(arr[arr.length-1]); + arr[arr.length-1] = ''; + var stop = false; + for (var s = sp.length-1; s >= 0; s--) { + if (sp[s].length == 0) continue; + if (stop == true || (sp[s].indexOf('<<') !== 0)) { + arr[arr.length-1] = sp[s] + arr[arr.length-1]; + stop = true; + } else { + var type = sp[s].slice(2,sp[s].indexOf(':')); + if (type == 'align') arr[arr.length-1] = sp[s] + arr[arr.length-1]; + } + } + } + return arr; +} +function textArrayCheckIfEmpty(arr) { + for (var i = 0; i < arr.length; i++) { + if (typeof arr[i] == 'object') return false; + var sp = splitTextByTags(arr[i]); + for (var s = 0; s < sp.length; s++) { + if (sp[s].length == 0 || sp[s].indexOf('<<') == 0) continue; + return false; + } + } + return true; +} +function removeTags(elem) { + if (typeof elem == 'string') { + //remove markup tags + for (var char = elem.length-1; char > -1; char--) { + if (elem.slice(char).indexOf('>>') == 0 && elem.slice(char-1).indexOf('>>>') !== 0) { + for (var char2 = char-2; char2 > -1; char2--) { + if (elem.slice(char2).indexOf('<<') == 0) { + elem = elem.slice(0,char2) + elem.slice(char+2); + char = char2; + break; + } + } + } + } + } else { + for (var i = 0; i < elem.length; i++) { + elem[i] = removeTags(elem[i]); + } + } + return elem; +} +function textArrayGetStartTags(arr) { + var str = (typeof arr == 'string') ? arr : arr[0]; + + var splitPoints = [0]; + for (var c = 0; c < str.length; c++) { + if (str.slice(c).indexOf('<<') == 0 && str.slice(c).indexOf('<<<') !== 0) { + for (var c2 = c; c2 < str.length; c2++) { + if (str.slice(c2).indexOf('>>') == 0) { + splitPoints.push(c,c2+2); + break; + } + } + } + } + splitPoints.push(str.length); + var tags = ''; + for (var c = 0; c < splitPoints.length-1; c++) { + var subStr = str.slice(splitPoints[c],splitPoints[c+1]); + if (subStr.indexOf('<<') == 0) { + tags += subStr; + } else if (subStr !== '') { + break; + } + } + + return tags; +} +function textArrayToLowerCase(arr) { + if (typeof arr == 'string') { + arr = arr.toLowerCase(); + } else if (typeof arr == 'object') { + for (var i = 0; i < arr.length; i++) { + arr[i] = textArrayToLowerCase(arr[i]); + } + } + return arr; +} + +function removeTagsOfType(textArray,tagType) { + var stringHandler = function(string) { + for (var j = string.length - 1; j >= 0; j--) { + var slice = string.slice(j); + if (slice.indexOf('<<'+tagType+':') == 0) { + string = string.slice(0,j)+string.slice(j+slice.indexOf('>>')+2); + } + } + return string; + } + + var arrayHandler = function(array) { + for (var l = array.length - 1; l >= 0; l--) { + if (typeof array[l] == 'string') { + array[l] = stringHandler(array[l]); + } else { + array[l] = arrayHandler(array[l]); + } + } + return array; + } + + if (typeof textArray == 'object') { + return arrayHandler(textArray); + } else if (typeof textArray == 'string') { + return stringHandler(textArray); + } +} +function getDateString() { + var monthNames = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; + //var dayNames = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]; + //var monthNames = ["January","February","March","April","May","June","July","August","September","October","November","December"]; + //var dayNames = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]; + var dateNow = new Date(); + var dateNum = dateNow.getDate(); + var dateString = String(dateNum) + ([1,21,31].includes(dateNum) ? 'st' : [2,22].includes(dateNum) ? 'nd' : [3,23].includes(dateNum) ? 'rd' : 'th'); + //dateString = dayNames[dateNow.getDay()]+' '+dateString+' '+monthNames[dateNow.getMonth()]; + return dateString+' '+monthNames[dateNow.getMonth()]; +} + +function text(obj) { + // Required + var ctx = obj.context || obj.ctx; + ctx.beginPath(); + var textArray = obj.textArray || obj.text || []; + if (typeof textArray == 'number') { + textArray = [String(textArray)]; + } else if (typeof textArray == 'string') { + textArray = [textArray]; + } + textArray = textArrayReplace(textArray,'<
>',br); + + if (typeof mode !== 'undefined' && mode == 'present') textArray = textArrayReplace(clone(textArray),'{date}',getDateString()); + + var measureOnly = boolean(obj.measureOnly,false); + //var sf = obj.sf || 1; + var sf = 1; + + if (!un(obj.tags)) { + var startTags = clone(obj.tags); + } else { + var startTags = clone(defaultTags); + for (var key in startTags) { + if (key !== 'align'); + startTags[key] = def([obj[key],defaultTags[key]]); + } + } + var tabWidth = obj.tabWidth || 20; + + // Text Placing, Spacing & Alignment + if (un(obj.rect)) { + obj.rect = [ + def([obj.left,obj.l,0]), + def([obj.top,obj.t,0]), + def([obj.maxWidth,obj.width,obj.w,ctx.canvas.width]), + def([obj.maxHeight,obj.height,obj.h,ctx.canvas.height]) + ]; + } + var left = obj.rect[0]; + var top = obj.rect[1]; + var maxWidth = obj.rect[2]; + var maxHeight = obj.rect[3]; + var leftLoose = left; + var maxWidthLoose = maxWidth; + if (!un(obj.marginLeft)) { + left += obj.marginLeft; + maxWidth -= obj.marginLeft; + } + if (!un(obj.marginRight)) { + maxWidth -= obj.marginRight; + } + + if (typeof obj.align !== 'object') { + var align = clone(defaultTags.align); + align[0] = def([obj.align,obj.textAlign,obj.horizAlign,-1]); + align[1] = def([obj.vertAlign,-1]); + if (align[0] == 'left') align[0] = -1; + if (align[0] == 'center') align[0] = 0; + if (align[0] == 'right') align[0] = 1; + if (align[1] == 'top') align[1] = -1; + if (align[1] == 'middle') align[1] = 0; + if (align[1] == 'bottom') align[1] = 1; + } else { + var align = obj.align; + } + var allowSpaces = boolean(obj.allowSpaces,false); + + var minTightWidth = obj.minTightWidth || 50; + var minTightHeight = obj.minTightHeight || 50; + + var fracScale = obj.fracScale || obj._fracScale || 0.7; + var algPadding = obj.algPadding || obj._algPadding || 'default'; + + //var alignEquals = boolean(obj.alignEquals, false); + //var equalsCenter = obj.equalsLeft || left + 0.5 * maxWidth; + + var lineSpacingFactor = obj.lineSpacingFactor || 1.2; + var lineSpacingStyle = obj.lineSpacingStyle || obj.spacingStyle || 'variable'; + + var box = {}; + var padding = obj.padding || 0; + if (un(obj.box) || obj.box.type == 'none') { + box.type = 'none'; + } else { + box.type = obj.box.type || 'loose'; + box.color = obj.box.color || obj.box.backgroundColor || '#FFC'; + box.borderColor = obj.box.borderColor || obj.borderColor || '#000'; + box.borderWidth = obj.box.borderWidth || obj.borderWidth || 4*sf; + box.dash = obj.box.dash || []; + box.radius = obj.box.rounded || obj.box.radius || 0; + box.dir = obj.box.dir || 'right'; + box.arrowWidth = obj.box.arrowWidth || 40; + var padding = obj.box.padding || obj.padding || 10*sf; + } + var tags = clone(startTags); + + var backgroundColor = obj.backgroundColor || '#FFF'; // determines selection color + if (box.type !== 'none' && !un(box.color) && box.color !== 'none') backgroundColor = box.color; + + //console.log(ctx.canvas,textArray,align,obj.rect); + + var words = [[]]; + var arr = clone(textArray); + for (var i = 0; i < arr.length; i++) { + if (typeof arr[i] == 'string') { + do { + var s = arr[i].indexOf(" "); + var t = arr[i].indexOf(tab); + var b = arr[i].indexOf(br); + if (s == -1 && t == -1 && b == -1) { + words[words.length-1].push(arr[i]); + arr[i] = ''; + } else if (b > -1 && (t == -1 || b < t) && (s == -1 || b < s)) { + words[words.length-1].push(arr[i].slice(0,b),br); + arr[i] = arr[i].slice(b+1); + if (arr[i].length == 0 && i == arr.length-1) words[words.length-1].push(''); + } else if (s > -1 && (t == -1 || s < t)) { + words[words.length-1].push(arr[i].slice(0,s)," "); + arr[i] = arr[i].slice(s+1); + } else if (t > -1 && (s == -1 || t < s)) { + words[words.length-1].push(arr[i].slice(0,t),tab); + arr[i] = arr[i].slice(t+1); + } + } while (arr[i].length > 0); + } else { + words[words.length-1].push(arr[i]); + } + } + //console.log(words); + + var subWords = [[]]; + for (var l = 0; l < words.length; l++) { + for (var w = 0; w < words[l].length; w++) { + var sub = words[l][w]; + if (sub == br || sub == " " || sub == tab) { + subWords.push([sub],[]); + } else { + subWords[subWords.length-1].push(sub); + } + } + } + + if (obj.log) console.log(words); + + var lines = []; + var line; + newLine(); + function newLine() { + line = {width:0,height:tags.fontSize,words:[],text:[]}; + lines.push(line); + } + + for (var w = 0; w < subWords.length; w++) { + var subWord = subWords[w]; + subWords[w] = {text:subWords[w],width:0,tags:clone(tags)}; + if (arraysEqual(subWord,[br])) { + line.hardBreak = true; + newLine(); + } else if (arraysEqual(subWord,[tab])) { + if (w > 1 && subWords[w-1].text.length == 1 && subWords[w-2].tab == true) { + var dw = tabWidth; + } else { + var dw = (Math.ceil(line.width/tabWidth))*tabWidth - line.width; + } + line.words.push(subWords[w]); + line.text = line.text.concat(subWords[w].text); + subWords[w].width = dw; + subWords[w].tab = true; + line.width += dw; + if (line.width > maxWidth) newLine(); + } else if (subWord.length == 0) { + + } else { + var measure = drawMathsText(ctx,subWords[w].text,tags.fontSize,left,top,false,[],align,'middle',tags.color,'measure',tags.backColor,tags.bold,tags.italic,tags.font,tags.selected,sf,'none',fracScale,algPadding); + subWords[w].width = measure[0]; + subWords[w].height = measure[1]; + + tags = updateTagsFromText(subWords[w].text,tags); + + if (line.width > 0 && line.width + subWords[w].width > maxWidth) newLine(); + + //if (allowSpaces == true || line.width > 0 || !arraysEqual(subWord,[" "])) { + line.words.push(subWords[w]); + line.text = line.text.concat(subWords[w].text); + line.width += subWords[w].width; + line.height = Math.max(line.height,subWords[w].height); + //} + + } + } + + if (obj.log) console.log(lines); + + //console.log('subWords:',subWords); + + var maxLineHeight = 0; + var totalLineHeight = 0; + + for (var l = 0; l < lines.length; l++) { + var line = lines[l]; + // test for spaces at the end of line and remove + /*if (allowSpaces == false) { + for (var e = line.words.length-1; e >= 0; e--) { + var word = line.words[e]; + if (arraysEquals(word[e],[' ']) { + + } else { + + } + if (typeof elem == 'string' && elem.length > 0) { + var sub = splitTextByTags(elem); + console.log(sub); + + if (elem[elem.length-1] == " ") { + elem = elem.slice(0,elem.length-1); + // get the width of this space and reduce line width + var font = line.font; + var fontSize = line.fontSize; + var bold = line.bold; + var italic = line.italic; + // work forwards through line to get the styling at the point of the space + for (var elem2 = 0; elem2 < eleme; elem2++) { + if (typeof line.text[elem2] == 'string' && line.text[elem2].indexOf('<<') == 0) { + markupTag(line.text[elem2]); + } + } + var styledText = styleElement(" "); + var spaceWidth = drawMathsText(ctx, styledText, fontSize, left, top, false, [], align, 'middle', color, 'measure')[0]; + line.width -= spaceWidth; + } + break; + } else if (typeof elem == 'object') { + break; + } + } + }*/ + + var alignFromText = getAlignFromText(line.text); + if (!un(alignFromText)) { + line.align = alignFromText; + } else if (l > 0 && lines[l-1].hardBreak !== true) { + line.align = lines[l-1].align; + } else { + line.align = align[0]; + } + + //if (lines.length > 3) console.log('line'+l+':',line); + + // fix x + if (line.align == -1) line.x = left + padding; + if (line.align == 0) line.x = left + 0.5 * maxWidth; + if (line.align == 1) line.x = left + maxWidth - padding; + + // sort relative vertical spacing (assuming variable) + if (l > 0) { + line.relY = lines[l-1].relY + (0.5 * lines[l-1].height + 0.5 * line.height) * lineSpacingFactor; + } else { + line.relY = 0.5 * line.height * lineSpacingFactor; + } + maxLineHeight = Math.max(maxLineHeight, line.height); + totalLineHeight += line.height * lineSpacingFactor; + } + + if (lineSpacingStyle == 'fixed') { + for (var l = 0; l < lines.length; l++) { + lines[l].relY = (l + 0.5) * maxLineHeight * lineSpacingFactor; + } + totalLineHeight = lines.length * maxLineHeight * lineSpacingFactor; + } + + // work out where the top of the text will actually be + var topPos = top + padding; + + if (align[1] == 0) { + topPos = top + 0.5 * maxHeight - 0.5 * totalLineHeight; + } else if (align[1] == 1) { + topPos = top + maxHeight - padding - totalLineHeight; + } + + /* + if (alignEquals == true) { + var leftOfEqualsWidth = []; + var rightOfEqualsWidth = []; + var equalsWidth = []; + for (var line = 0; line < textLine.length; line++) { + var leftOfEquals = []; + var rightOfEquals = []; + var equalsFound = false; + // locate equals sign in each line + for (var elem = 0; elem < textLine[line].text.length; elem++) { + if (equalsFound == true) { + rightOfEquals.push(textLine[line].text[elem]); + } else { + if (typeof textLine[line].text[elem] == 'string') { + var equalsPos; + for (var pos = 0; pos < textLine[line].text[elem].length; pos++) { + if (textLine[line].text[elem][pos] == "=") { + equalsPos = pos; + equalsFound = true; + } + } + if (equalsFound == true) { + leftOfEquals.push(textLine[line].text[elem].slice(0, equalsPos)); + rightOfEquals.push(textLine[line].text[elem].slice(equalsPos+1)); + } else { + leftOfEquals.push(textLine[line].text[elem]); + } + } else { + leftOfEquals.push(textLine[line].text[elem]); + } + } + } + if (equalsFound == true) { + // measure left of equals sign + var styledLine1 = leftOfEquals.slice(0); + styledLine1.unshift('<><><><><><>'); + leftOfEqualsWidth[line] = drawMathsText(ctx, styledLine1, fontSize, x, y, false, [], textLine[line].align, 'middle', '#000', 'measure')[0]; + + // measure equals sign + styledLine1.push("="); + equalsWidth[line] = drawMathsText(ctx, styledLine1, fontSize, x, y, false, [], textLine[line].align, 'middle', '#000', 'measure')[0] - leftOfEqualsWidth[line]; + + // measure right of equals sign + styledLine1 = styledLine1.concat(rightOfEquals); + rightOfEqualsWidth[line] = drawMathsText(ctx, styledLine1, fontSize, x, y, false, [], textLine[line].align, 'middle', '#000', 'measure')[0] - equalsWidth[line] - leftOfEqualsWidth[line]; + + // loop through rows and set x value accordingly + textLine[line].alignEquals = true; + textLine[line].align = 'left'; + textLine[line].x = equalsCenter - 0.5 * equalsWidth[line] - leftOfEqualsWidth[line]; + + } else { + textLine[line].alignEquals = false; + } + } + } + */ + + // calc tight rect + if (lines[0].align == -1) { + var l = lines[0].x - padding; + var r = lines[0].x + lines[0].width + padding; + } else if (lines[0].align == 0) { + var l = lines[0].x - 0.5 * lines[0].width - padding; + var r = lines[0].x + 0.5 * lines[0].width + padding; + } else if (lines[0].align == 1) { + var l = lines[0].x - lines[0].width - padding; + var r = lines[0].x + padding; + } + var t = topPos - padding; + var b = topPos + totalLineHeight + padding; + for (var i = 1; i < lines.length; i++) { + if (lines[i].align == -1) { + l = Math.min(l, lines[i].x - padding); + r = Math.max(r, lines[i].x + lines[i].width + padding); + } else if (lines[i].align == 0) { + l = Math.min(l, lines[i].x - 0.5 * lines[i].width - padding); + r = Math.max(r, lines[i].x + 0.5 * lines[i].width + padding); + } else if (lines[i].align == 1) { + l = Math.min(l, lines[i].x - lines[i].width - padding); + r = Math.max(r, lines[i].x + padding); + } + } + if (r - l < minTightWidth) { + var alterBy = minTightWidth - (r - l); + l -= alterBy / 2; + r += alterBy / 2; + } + if (b - t < minTightHeight) { + var alterBy = minTightHeight - (b - t); + t -= alterBy / 2; + b += alterBy / 2; + } + + if (box.type == 'loose') { + roundedRect2(ctx,leftLoose,top,maxWidthLoose,maxHeight,box.radius,box.borderWidth,box.borderColor,box.color,box.dash); + } else if (box.type == 'tight') { + roundedRect2(ctx,l,t,r-l,b-t,box.radius,box.borderWidth,box.borderColor,box.color,box.dash); + } else if (box.type == 'flowArrow') { + var left = leftLoose; + var right = left + maxWidthLoose; + var bottom = top + maxHeight; + if (box.dir == 'left') { + var points = [[right,top],[left+box.arrowWidth/2,top],[left-box.arrowWidth/2,(top+bottom)/2],[left+box.arrowWidth/2,bottom],[right,bottom]]; + } else { + var points = [[left,top],[right-box.arrowWidth/2,top],[right+box.arrowWidth/2,(top+bottom)/2],[right-box.arrowWidth/2,bottom],[left,bottom]]; + } + drawPolygon({ctx:ctx,points:points,fillColor:box.color,lineColor:box.borderColor,lineWidth:box.borderWidth,dash:box.dash}); + } + var tightRect = [l,t,r-l,b-t]; + + var returnTextLoc = []; + var returnTextLoc2 = []; + var lineRects = []; + var totalTextWidth = 0; + var lineLocs = []; + + if (boolean(obj.showText,true) == true) { + for (var l = 0; l < lines.length; l++) { + var line = lines[l]; + //console.log(l,line); + + totalTextWidth += line.width; + var y = topPos + line.relY; + if (!un(line.words[0])) { + var style = line.words[0].tags; + } else { + var style = defaultTags; + } + var align2 = line.align == 1 ? 'right' : line.align == 0 ? 'center' : 'left'; + line.text = textArrayCombineAdjacentText(line.text); + + var measure = drawMathsText(ctx,line.text,style.fontSize,line.x,y,false,[],align2,'middle',style.color,'draw',style.backColor,style.bold,style.italic,style.font,style.selected,sf,backgroundColor,fracScale,algPadding); + + lineRects[l] = measure.tightRect; + var textLoc = measure.textLoc; + + for (var i = 0; i < textLoc.length; i++) { + arrayProcess(textLoc[i]); + } + function arrayProcess(arr) { + for (var j = 0; j < arr.length; j++) { + if (arr[j] instanceof Array) { + arrayProcess(arr[j]); + } else if (typeof arr[j] == 'object') { + arr[j].lineNum = l; + } + } + } + + if (line.hardBreak !== true && typeof line.text.last() == 'string' && line.text.last().slice(-1) == ' ' && l < lines.length-1) { + line.softBreakSpace = true; + textLoc[textLoc.length-1].pop(); + } + + var textLoc2Map = mapArray(textLoc,true); + lineLocs[l] = textLoc2Map.length; + + returnTextLoc2 = returnTextLoc2.concat(textLoc); + + /*//if (lines.length > 1) console.log('+',l,textLoc); + if (returnTextLoc.length > 0) { + //console.log('+',l,textLoc[0]); + if (textLoc[0].length == 1) { + returnTextLoc[returnTextLoc.length-1] = returnTextLoc[returnTextLoc.length-1].concat(textLoc[0]); + textLoc.shift(); + } + //console.log(textLoc); + returnTextLoc = returnTextLoc.concat(textLoc); + } else { + returnTextLoc = textLoc.slice(0); + }*/ + } + } + //if (lines.length > 1) console.log('-',returnTextLoc); + + for (var i = returnTextLoc2.length-1; i >= 1; i--) { + if (typeof returnTextLoc2[i][0] == 'object' && typeof returnTextLoc2[i-1][0] == 'object') { + returnTextLoc2[i-1] = returnTextLoc2[i-1].concat(returnTextLoc2[i]); + returnTextLoc2.splice(i,1); + } + } + //if (lines.length > 1) console.log('-',returnTextLoc2); + + var breakCount = 0; + var softBreaks = []; + var softBreakSpaces = []; + var hardBreaks = []; + for (var i = 0; i < lineLocs.length-1; i++) { + breakCount += lineLocs[i]; + if (lines[i].hardBreak == true) { + hardBreaks.push(breakCount); + } else if (lines[i].softBreakSpace == true) { + softBreakSpaces.push(breakCount); + } else { + softBreaks.push(breakCount); + } + } + + //console.log({textLoc:returnTextLoc,tightRect:tightRect,totalTextWidth:totalTextWidth,maxWordWidth:maxWordWidth,softBreaks:softBreaks,hardBreaks:hardBreaks}); + + return { + textLoc:returnTextLoc2, + tightRect:tightRect, + totalTextWidth:totalTextWidth, + //maxWordWidth:maxWordWidth, + softBreaks:softBreaks, + softBreakSpaces:softBreakSpaces, + hardBreaks:hardBreaks, + lineRects:lineRects + }; +} \ No newline at end of file diff --git a/tools/i2/construct.html b/tools/i2/construct.html new file mode 100644 index 0000000..ef3205c --- /dev/null +++ b/tools/i2/construct.html @@ -0,0 +1,57 @@ + + + + + + + Constructions Tool + + + + + + +
.
+
.
+
.
+
+
+
+ +
+
+
+ + + + + + \ No newline at end of file diff --git a/tools/i2/fonts/hobo-webfont.eot b/tools/i2/fonts/hobo-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..19121446691b0c71ace685d32bb109d61c76d410 GIT binary patch literal 29519 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/i2/fonts/hobo-webfont.ttf b/tools/i2/fonts/hobo-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..06510655d24f6f5fc5fe643d4c05ef7a47eee7d7 GIT binary patch literal 57040 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 literal 0 HcmV?d00001 diff --git a/tools/i2/fonts/hobo-webfont.woff b/tools/i2/fonts/hobo-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..fe4e4faedc7638fba8049a63965556d0a0ad32e1 GIT binary patch literal 32432 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 literal 0 HcmV?d00001 diff --git a/tools/i2/fonts/hobo-webfontd41d.eot b/tools/i2/fonts/hobo-webfontd41d.eot new file mode 100644 index 0000000000000000000000000000000000000000..19121446691b0c71ace685d32bb109d61c76d410 GIT binary patch literal 29519 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 + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : 2008 The Monotype Corporation All Rights Reserved +Foundry : Microsoft Corporation +Foundry URL : httpwwwmicrosoftcomtypography + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/i2/fonts/segeo-print-webfont.eot b/tools/i2/fonts/segeo-print-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..823b6036154cb069bce72502e0cac5559752b5d5 GIT binary patch literal 59430 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 GXafMvAQ5E% literal 0 HcmV?d00001 diff --git a/tools/i2/fonts/segeo-print-webfont.svg b/tools/i2/fonts/segeo-print-webfont.svg new file mode 100644 index 0000000000000000000000000000000000000000..2f3181cae6f00bed9dcd13b6fbda568bd5cb8ee0 GIT binary patch literal 36608 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

mGpnH^g7$mg{p2@ zNw>-+{1==qPQGUwn^#k*Yoc`j*0Q#Vi`EC~Mag`Y%nDMys9Sk){uPF7sVv6Hc6p(> zQO=eJeokr$-Is0@(f&Zpcb(Xm>91Vb7G!-*`cPmJCi|v#FWbS-JXtT=tc<(X<+1Jy z*qpELTl4SJ-AIq{Ik7W)I~fHIyI%F3b-&A#U8A&R-!-A+ZqDBDU2_g?jgeZjx>9C> zfz!;kl?SgZ+`iy(wbG^~GZd=TZpE41io0`ZYoz0m(mIv3+m?4Y1{O|V_xG5=@w2jh zIVW^u_eg8iue*14ne`TX?-C}3ulem0lI&#vw5*gB+Elabb*JL$4Re+3I`+&l$v-o# z;{UAw@874L&i&RiHRZ4Nni< zc_p&-#l!QFg8V)=jXif>e|%DGPUYXNaa=62Z~VF#jqyiE1#!Djkim{?MQdaJ|^4I#;(b6X@d8(@o z_sndYo1u2^(WL5mMh+Yo9$zT$Fa6lF=9=oPw(7~#gSX#$#V7r1YuQe(>vmk1=S^B1 zZ}PL()h)?e-!R|Pg>(Hf%WYr3|4sU@QtrC(uET7T&tVlg)yZw z$H=VLrSD-DFMIp8!@race5knoF7{KkQ2UZ+%{iQ%afa2kvf_v8?!98XxH7sWPb*NM zLU&>0(g_n{SA62!;}iaJs)!@21RK|{17|~fC+yTLv7R^OfujEO>{Y@gDwobq=Q(}; z-qZ6ulg&kQubr4BRQ6Eq+?<`sQ$jL-3~*scg;OiZwlz3zS0 zqx+dvZ`+wwySk33Ddu+mSblb0dC_5ov|fgLhx4!;1qxmu}{o`7D_cb6-*aRFLwm2(>v8Gjj{?osDC( zo%`s{R(ZxqT)p;)ewE8*6 zv2RnJL=WA_yjteu%QZfI641T-@WLSX zX{_NhkI!BwclyE=@1HLP-sW#V72)RfH=ldaXMKqck6mkDuWf5--!frqkoLxj?`$q} z?7D7Q9O?POZ;$`waQm`;FEfcL>*MmcWDNJ1O||^txHd*ETf0AQ`86I1_X+0~UcROG z{8yMta8i`0q|XZbj#s%WcSsjy8u|IGdZTf&!{6d$!{%$7-KJ?tt!G>|>+S`ci&m|y z&wRLlUU=KzylU_En;)J@*!V?T2VBeIV$qcQQm?TfLEZ3h;E|G2R|%KdPa-Rg+kD^a zA5FV&!yJ}wIq^bMob1f}k~)>+N*>L2cKyN^V>lIE?tZ;7ao#+i4aMuHxfuVNoZ+_f zm~p)7QQMlnFBc>fcb!)&Fv++5X1udk(X~B)+tSIJ62Bk!zM3%4+CfAs&f4g4h1M3^ zJAaS%H9IWo^g7eHEZcQg_gf)vlYO)AE`GM{#Okyi?IrJR+pfeJb|iK9H5)81`+DZY ziYWnqq9@K&?Bdrmyuc}$-AS%e#~bVZ7ytftSLx@I1FOXspJmft(tlpy z%NFJGM`a<`9m2(T%eJIdU6jc8S<=h1zUk~u^%nvMUoD=uHoZH{X+qWgx9V5sREw?u zbs}CV;myREQ!Seer5 z_a9I7ahcV0$6$Aq$=(>@3>}YBS=rK-OY4h5b-tv&x$@n9;^(>QoBWq7sVJ9i0#}~aCVy0JNLQUmrVV=C2oTCrRZMXwwWfUb+Xhp=5cb)$~dWH z_&GDFqmofhG&X`oj#JG3Xn9$fZT!l1weva~WLT|@Y`rq?_HvYmJDKI>rsRi3 zz7qWN+@a3-lUa!5cMlZ>#(C9`q3|TZiwskOgG0AQa~Q5(_4Ti> z=E4`1#n(@nY4`QA16V5jMoN&gm zZJk6FL-O=3>Wp^}oN8ZtcY4MpHveV$lHCi1H6v%Qk_iuSc`237!^WgjmhrglNch`# z-;&zJUTR@L1LI&M!)%=bE& z6&dqsZOEzFTm0fO7=Aq6eR{iY{>g?n-bF9OgC|~kd2Wx*oSoIbZ90R_8<^DET{F4H z@NVzX62sl>+LvcOKeOplt?J(o!qv)YYqgK>n{0S#{IQWlmCnA2G%Dc zWk>!NpWr;OA;~#McyB{SZp!(?ZrK8f*;`yDeLS|sWd*N3|J47h)77&&6Z)Hb8ABC) zw>^PBcr#yt6MNUUD6=@mk6aG?^ zDfsGU1w-m?r0jZ& zrzU+lJ4OyD8*)3fPcaAx}Q`|3i$ofLn`I3bXERV^Xxcv^9JjK~M=A6=>-O; zR{P4V>@S-yy$C$3Sv@J**lBCqlz5I>?f<%Lf7ZS8%RBJsPnxibep>xUp1SWk4^IhZ zSDa97N}u#u%WwGe_X$M3!9xFIzEz{X4d`K-< z(f9Kc?u6f>x`9tNE_PQxYi}}l%9bM*4VAa@cx89_B84bl9GJ3hiQ19G3_MgjlFH<*PHICZ>6(6&59is#MM42(%g$CR8BBIP^DS*jD)++` z#w_|`eMjVW1}8WBDDJKS(#P}zwv11w z^qI_Y-7BI|v};BGuU|=9MOn2Kr{_3kBp!9X_pD?4taUkaHho_u zX%wt3nB+TOn#1hJ@A5PM|JEdg9@#L<=*qGgMa_06D)#Fnubh_J&$mqdW@}%`=a}tw zp-cgCD;6G+Yd*$d#TT{DO0~WEzWj;UG;y292XxZP7{2u%_t}uy!R_py?+C1!wYhnb>SgaKd%jNAe*I+IGW9jJlb+l;c+KXk z&Y4^6Y3J*v)qPM3ZLqS7;*ZcWxz(0Ap)PgPq_yYTPE77vEiwZRfg!gYqh(@UVnL2+UPWK3G-K0{+WSAO5ZlD z{?yzyJu+dbK>zpK*S)2-vvbT|Ydk0JlF-G)r_Uc}yZ*<7W#L7$J=Iz6#n-1<95qtp zy|?4jqjoMOQNBeVtQ3u;Z`2u1ytJuZbBV%6$7ENNJIjBGT%TaZ@~?J!xNA$sLf`eQ zwNDn-OIWS%h>u`%*b{ns#ezEv4<5R9r)Sw78_Rj$CARId(^T8Jz~FVMvU2EG|37bU z>be=HuPD5_-9&8j+P5C@EB~m71SK$6G9+9$eeo_Qqiz1IjA`dzuaS!SQo?`#VO_GD zR`iUd?Xrgc23r$S)RuejOyq2I6qok@-o4}E)m0Z~&EQ(*-R+?imLqp~j>z5jDOyvK z#Lj<`xxf07D${SV?Cpp0oh{GA1owPNd(+93>U83I;gLF{g}LSq47zhJPLR9D#xVKy z>FOL)K=bD_O{VzMD0utEA)O*ZxEZ4cir;m2^XHiUE97LS>b4O zRFRnNujX%&4JrPca@6uEPr#(0OLqO!Mei**y0R~N;#13nOK(LTzO;BYZ+6byx$2AL z=65H<8n`bD&oy@K&Ysuu-E+mpuI0b)Z+71*+;26jc!A8Unr>0PvuBKLG#bi9*YL?@ zB%FBqE?exg+;8nY2T#x5|L=k8ORrF$VsqX-tNf0-hzA9=i3RoDGMQdEIcv|)X@@nW zDi?mtG`+F>(sK5w633twYwigCy)pOW!95b*zS3?xAD-HjEUy$yO`T-OR@#*Qc6s$omuRIG;mS*ARV3Ic{Ly~i#4xcbzA)n0^$VeQbfxy6 zza*mjWc5P}_pjN#O;;^KzN9RiVEAo`br;7$hhM++G?GmZ*E(Ma;(eUCDedNzvm&;0 zo3{Dg>?$kPm2mj9?d(kRE;R|(Z9iJheRkx%qP+C#{6LK@Cm$*Qna;G5zvjV~rCXBb zy`DiZ5^^h|ME zlvP>yf)>tQabxPqN1Og1SL)sT0d`C73ur-dqf zi1psoje82a)~s6ka_u_LB*zSW2GPqCmUucH_+s^z)2IF1qw0Bkl=rS*uD(j?PO=6& z=j<=lCoRO*Z0Y{c<7lX#y78G%|3!u+nvzY`wv5})S@h`T_nz+h_UB}J&&^xcGkYTM ztQ8h3&(~v~vwT0#YxgF`t&0V>Y?=9B=?mG&9tSH6gH^Kwrrn)*b@{ms5$_`w%y=VZ ztZ6?%VT*i?#Li7h7nL~qzfOLA{*%C^mtOgw(mcCUm%e!6hy7al)Yn>?kF zlkbrV$HQkU93G2#EokVQ^v6nZ(d86hj(>c4L5!MTW=xUu>Uw5=Rb8W3;e5n}_{aBu z^T!G8y?F4}lLY~ZBC+Ljg{EaDXB>XD%<5&0A-nFg^*s&CjtbmLj@RXJxBQcHkhT3p zUI>re(Udfyz_|-#_;j??b)X9sc zuRGeezn{M1hry=)Cy^6o1Z_+|<~xsTp=(;X#ogo$ZS3OjidBB=2)^^*t@@&W;nD(T z&iC7AWH8HHFdtXG^fumjnLx8s(y3E&f3JMs=6-)gsY^w!Fo@arfpw<9&Zn zHnUoT!OeD6p5b0?!wYKd6*dpE_LaQgu~zf<6FQKts>lC@~8<9<8expOx4 zE@0zj?)Y9XVX8=pxU={5fU8vt7K&vVeC^>m{c*pg(3K^ZOmBT>Vm@1)Eq~H<$mT21wR6ft*(#J33FxUk!o-rcPA36QirbJfM0U+?W7?>C0l!I8?(L)F}yt2#s$ zuifva&M;L}GNo<%cHrq5n=ZnXiMdcwT$N14wT z&N#Ot`gyX);d_fJ*4|=w^Gvy%y=c?A2~O-+Y@e-I{v|55M(XPfrZdX<``v?$5A-B| z5WD!v=(5AtOgq!6P&e+nTT3>(YqZ>uK4+^eq93%7N&T(GMwzvFQj7Qbwz_!NovJ>z zU~TQW4_`96Zi=ZYZ0FT??Z0D{JbPC_j!u}FL+Wfpe#HWV+rl@E6)&C$^EOOlbi0vg zB4H@8MOZQ@(#Cn&<#N|GuTI&8`Gm>tTCnWaC3(lC4igXVopRIF>ge*t(ra@T2A9qf zY*{$V_Jxd?TBb_4TfANU{JquxzSVWAW^Cb0k@%uL`&iEWtkp7(I|FNO-hId!?Gxv~ z)6k~#w8}W5Q>>JwVY;Pd_RhBL8aeT6`e7SA~O>MJyZNnGIY~Hy}FJSZ8 z+om_=ezZKiy)bz@qmaFTLB?*+7e|~<&R!;MsxNnQZ+`K9NtGSPr|=pvKX13aGQW}e zT*|9zBSnMbJUd@K>#bw_r&iQ6dvD-$XV#dA9TB##_PjcBeUtUsxT%q6dX{Ey`0CmlE zcjNJ@+CM)(*WS6l+U(|Z)s|DCS1mMG+dT2v?!M@8ICEbkcX`QQjf?lqFMFTe_4Uq~ zrw*F=s6DG?Zu9;FR^V@-S*-uS3o24BdyWCUGZ+z>&RpVIu zCYBd_UM_y;Rxw$-@Cq}_mF2gby~@5Dy3D+q<*ev8t#k49?NcOeEuJWzefKeve?!|h zt22-0xZJaEcvJsT=CWtv%D`^+`HKrDUGPo4C$e+NfA~&n?(w)E$Lo&G4Smf%@wk%>P2=4ar|G}Ap7p(p^!VySx0BS@XIe| zv0UnURD!3-Qu$?n1&i8)_HR#DKMzcaTEB12RCcuuw^FO7So=R`yQwkP<%Q5HJ2lnN zC*INDN*<===o>Rd{>p#n6qlvoUwO{*U_Fa*;nX8cF+CesCo9bKUZU1()#(4Co6%wA z&oix4CkD=&_QX?Zg|OGU+h?w|nSVaBkm0_sb71Ofc4vLdT?L_T6Ap=)i1M5$sEfSh z<+$X%T1L+g(UWIO)XFy*O;^|dDzzZf#?;s>^yGe#2TS+Pw&`AC^j~bM*)B2V&vz#t zSoG`uhXd1C)?S|4Hdi~ZhSl?`+Zi5%_lM>6)E-2gR+#@mdV9wG(wV`vr{?asaD>tD zJm>HH+GhzJ(G@~*oQ|C0RZ6LH^S|6(?)!C**kQ3Xh8uiinq8L#zc_ej@gj%K$!GOE zHC8cSQ;+Rg7cA5njcy%j0{xUW3+l&`0kNb$+iA{CA`N-k-qB}Z$y=pgk`+Utxmp7h%eqm*f zzRBHFe1D#>%Lj$YMz8(*I4n#y`^)2}?E0(p4t^G_dHF@)c>~|ty(>5}#Xcle*tFP9tubBfpBTj5d+?M%(6Y%&-5+O7 zxVM;ng}cQ;>G*i&n>$Q{T}^hyvU1jcUwJA+lc)PwhUF()*UGe>j$1Vy%6GR-&gGZ8 z5T5a7e$^ZP&`+b_bp&aB4rq%(J!8;^&%vajB^Z@YSBY5&Z^ zvu+{HA9D2HUr%>gUdzd`$*(4D@oE?69znH&quD2g_B;8C-;dGQ*TU@9t87{A75MgO zmu1lTiM~%Y-U}+JUdgv{zxQ|7o97!9UQ{^duQ@K_>9FtmF{_lIXH&keJr`)TOe!z$ zl83*p@`E!9*IKXro+R(16_$3*hUbK?POo9ST+`C^)#~0^>DP^qgqu5`YfO7`&~RUvX}XC^@qr~=md4YS9*MIBB=eO_)^vOm zy!oAzxKoq$j%?Tgmym3AI`qBD8x zUFUCyC0klEC;gsxqQu7WVbILm58i0`zG!LIe?K+7W|rOSnOa}h;NSEor#tPU!1b5kFX))+vIbe&>i^MI zUjOakEsJ+km%maF>07?;$5ZP%zc~@sB<&JSGDp8fKIxa#>wWumUGh(~%_O?FH5|4&RZvxx= z=JN@GSq|54KK1{8qcr)Ma?hn!9mORomxT66v4wVh)mUe^X64_syOxic9*Z|~UDK~U z?BaIp^33HCwt+ogjj#7^c6&JeO;qf|##w6pB^(zYS^f{*%@y{jD}e8}+b?OZ-JTmC z%FKP~`MHeu+TopEP#tvFUY;R&%?yUF}Yu+x?$)J7(Ahq%<#@ za!c;lY2Dj1-!oeG8>qjCwB^fQxG;IoLe(c%)n+eN?)W9>v0BcNr&cfgZ}*qZqBBBY zHXU$x{;=$f;l9Hk^OR@Yver>Z?cE%}ZJ+D7?MRULmU$KD5)SA|hwjjQ@#-R{(bvCM zuWxG?yY4A;Ug_auRk`e?$8OuyTbVaq|Mc(L0sYPQlR_?KDK4@7Wvb1{mT4O2t-R#c zWU=bm?X!%{%r0cUjAYEv-sz>WE#vdF>^#-{wPMT6zvmf8+rGD4{Hw)Ai+^q0LeH;{ z+rB)%E1jEMAiQd6pbGmQqyB>_EV)}A_Rjog-Sp<;&bH3;o&a7Xz%It}`WO}UIcUR4UZ>Ka?NG|<6%Xb^w z^4QK*N1nw$`}5~V(>O$YMsTewXa@&^={(%qZ#LC?)0)wTO!+ivA$67X<{3zLj9@RsyCz( zT;~ffO>Ud0yre2q?CIga-@40cTN8{rPwi~G@qDIjh&jif-zxp;XT{H|xp>&!cIb%U zy(Z&yC;XRkO8cGZ5B7LfE9=gZzhQnpf;HD{r8fUd?+?eXeC|H7>W0PR#Oiz@tFIRt zw(XAO$y@R=&*}2X+P3c=Wls|1KA~&gZcrTcFN9|9;$7{8#_^$smljO3{+EaNm ze&5mb*4w#kpH79BYi#W?DUjvKtd%^N*!+_Jw&NBThtx$5JV$38{iHByjmU%dmRE9& zg0Hy9xXxO0ZYOVEZkPqXz&6&qwcG2KX>T@ENz>`ou<*86W4%=AgyGp)Y{7>(Co~rQ zSNpr_>g5ahO4r5uGwymWdJwi#M`CWB)rp2SY2PZ=UFLlEYAYR&Hu&z2oxCS5?xp;G zAMNmSGe65cb-yZbrDDy)4Nv@@w4FMA-7@h<(8jqbyXQt-d+4aOgq^3U#Izt*^S0tV ze=m;}F&f^E$xj}uUrOexWt|;9qyJ7`?F~NJzE+F5UOrh{ZYX4i9FFzTF>kdi?y8-a z8MAKI(L2RcW(uDz$ar?=;?tXdHWzM@S!{LFY&L61*!9N;=VN@{Y=}lQ)=(d zTCn$=-lkn{DbCg2uJ_fNcBF1%`z`*cMu@9SLJ=|t=R`K2{JEN)FGmZT(8f`zH zl%FzZe_NDZ3Ri={^q98!Mn_9_8}INDkSg5xWy#9R=VT{xKV0G(8h$LPjn8|Ng>B2G zCzC_hi+y^#>dFq2X0JzT6V6TUw=(wFY@PEpp!36v&l-8mqM3oSj8)e-7K%yL(rt?aA{NjQOJ@xhC9Uixp z&${0;l~Z)9^eoR=8b<4+{r(xU3dQ_dmG2u~ll_ii_vTBt1AE@>@s!iz&z=^@H~q(2 z))h{2mhqFEGyNv>Xi7|8aiQzNrqyhVI^XuE9SSqfSR;J+(Ui0wwz4HxoP#cK#A)5o z`N}!N@~^~-Cw(JZC#@(^+n?B zQ$AkRO+xdmO~YQV+OjY+fJ3~><3jHB9cN;i5`}+hC?2@)%xp4K*^lvPuG8TunO@s? zX1=+(ai{UOzFjvz{4{+wyFDzda+Ou2Yn`v++xg3i7d9Stdg8F3gQxL*Li(~#&!qld zyctxt`C4wFB0odG(XBQMP2{SbUVeDG`0t#SOW!s5));dztXa#x=i>LxDcsv+CI8I7 zXnn0Ict*?lj=Ovh()TvXC2!`eXR_Gp?^M6<&LW#GhKzvE$jvF#so?(PW; zdy7lIf0^*iBhf{yJFfD@k?&vl zhBvyW{IlF6og?#gQTsjK5BKtBABnlYd#2A}r_k-bfq|WZT$MAQ=$+Xv_=j(e0Z5zFlk*hx9-nQJ&Q>i+vmS{omi;3{l@v9 z7j81wU-W-x=+M#LyWz*n9}8zVxNWbPWqj4=S7G_5vtK?4kZ$-#x2-;g?l$W{ZN(vYcWOxvI0L@T#`lQnO1U3pH~)%YxOUtNwrScHOW< zeQq*?5#zCU2HBsNOf#EpvfQZPz%NbRV!uhOd!J*eV&vYys#p6#u~@%7t~ZwzPq*D zbp3=YXFmseD;vz6eLSZ>`No?1N81ig)4O?ze}eNo!HjRZi-a$IHu~CS=IX6%e?jo3 z+1WpNNsbpkExC8PJ+-Ng`^_`4>P4FC_3hlI^PSlif8-(C$J<4gjLOGd#M#-!dJ@I1 z`%ON#&*gdY+)r^P)soSk#fv7NZ%JGKepyBo-)Sjv#j~yvMmM#zY9cZVc6-jP-DqO@ z=Emj6>1U40?shGXE!(oa%IBX*$d&b6oELtWGHO-UWg7{vxbT+qU-IYe_kGd}>b-V; zxl*`l7Q>@YzVZ8hmxz`;D6Eb+#oDc@zTnVBQ&sJw>C=}7Ec>EaoBdzG=T3-jot_^5 zqoudnxmxDiFBghs-anx#Vtv)h-~L+0?JTwLyv|QK7wTJ;2r+|D<2S!%{?H1V}kV@k`V zS~j&>tN1z064_@2cT`Wx;PBCrzkPM%X_KIZdQS@uzxA8Pb1lA^{bJhvm~T6R7upwk z1T1@aXU12B1@3h_Z=Lp^_x9Zh%NXYNUs8<8=br!jn0o%rw%ewDiOC=Smd_6}{U-h; z{({gZi&)Fe)7cYNr{_DH+UuNmO+#PS0{&!Y(shPL`{*U|lfA$|c)IUFI$)tk?fOlRYOMa`De)`!e~!{4(dZ&XDiB z=T~Pi>J`73cze;hWh+WrU-W2}Y~60)>Aw2KT#X&nNV$GikSc5p!YINk}**cW4Uh zGzDHIsYNf;&L#P|JB!X(ckNcJQsAjxz2^3tr(-?8xKu7XdMsSR*LdMe-D9Wb1Y2_o zIc;}aBlpv}gUaq62JF~^0 z)_KB_tEtoPF08BBvu~N-(?ek=!oJ`5k=z@9@B8^1n;x%f-n@9bnb=ApYl+COsZk2S zt@;b6<{CB%Yi&tUTzN9fFTbwm#fIz2%DYl0Tsq`ptQqp|!oiu(wRg21pZ_Go=S1pc z-;Fxey^|jI>0NDJdGO1>HS1UZca8e(<3HJ=h3kL9{H)MDhD&EZR0!XAJoMMTc%$8U z#w*h`Z142cvYC- z<4u)H#EGNNr^&y$vv^mSD$nLQKD$r0iwJQkMIKn0r^9mh<@Jy^0y^eZS<#9AWEL;u z(ye(N{kQVsbNlBZK@D}v$xIzfIMP!D`ES^=Uuybr@>}}~*QCX|SEgug?f2GN_NV-H z!NU4!`l~ig{ajtU+PAKIrI&X|l5xx*)e7YsGrRc`#-XDz6>aMo7NjqA_By`7-~bDygo^LfhOp!(V>A zHhHvo_v-R~#ibQrA`iJqKM%`S+}wVD!tA#bZF%xuJyTnt#$Mdg zp`-FZ?bu&F^XR1u&ps>oU7BWQTy!OBVO;6-S4R$4&h-8A^IOgRb78*AV&1+t6}-~+ z`_aAcM%S`S*Wb*wcw2sc#&J*al3WR6pW=JEZj+|!dQ5+OKFDTs)RI>pFV{`ly-!c< z_4Lr}{S)Ia%SS}M-hF6)cDv?{X{#guS^YP=_s}$8`OoBa%m3HEe-amc)}14AV@WSZ z)%N{+x9|ST_U_&KuVr(>?L8_Vv!*WD^5_4Z&zfyhcRdRF|1W&%xA*?w2pq+iUv?zwX~(@u&Rv?d$*R{_H*b<6hdI zQ`8v^=lvzkgL!qN z*zL#JrymwhRW#LhIr}8g@o`mmU4*Z#f9Zj%l@qwOJ&T`yH8p9O;@Qd#YSF8IC`i32 zFEL2#pZWXu+*7q5{~rD?ZU1MH^{W#9|A$Yp#MSPX{P%2H!p>Lk?+c&&_pI@W|GxW0 z^^bL@Ph5Ta<#y?I6W2u9o0s-?8$9WLZY!yKK9E_fYR$K;>#9`xjU^BMo0G8Tc*dbM zk4h&dcwD=3iTA0GRY?DeKNtQkH!#2T*2ZG%?phX0ISJ0LKXN^j*B+p(jB|NENLIvLI{H(ZSuKmC07#$_Vj4+@f2_?9h`D=57tx-@i6thQCC zg;UHPS8ZMG&1oBzXXGuPVS3cy-o(0<|2KHfS)kLKSAHmN*Uxpo#I!QQrY0#$^_I6j zdL<QQyZZlHtg(|xVe?OE|KtdPZ&G<@g_DxD97^Kz+Q`NK zGUSZZjo5sp>dS?vq^2*sx2BN)l!)Yu1)qC@uejW*p52_ivD!y)rQ(ZS0Z!R((>`$; zJ=xaiBX_nqrE|GgwZLA#pQrci(Vtfywx#sdjWdEDl5U0W5WaLI_v#|i?V*oKecJRc zDG1Cw*_l3TvTy3!b)B;>1@Wv}y!`G$&-D+VhOwEKdK{T1^*8X>)lJ7fsO?>(`aWE0 zUU*90kLSB0>wGemqI`Eh^S%-w=rz4!U77LcRo}n6(kOqX?X3A@A0Qfn@}H2?egjM`GRXeACI74|e^CQZiPcdJ&M;Na)~?#cbdXqJdu z#O&t|4o%q-E((?=<=cN*y5Enx7rVz|W_PJ&io4sr&n;h@zC`WSYr3`BCaU3-_8z6s zs3Nz+$3opFmHpDHjX&>iwW;HS$g=DrKK?StDNep+AJUJ6ie_+o<-gYp-xxJDqVw%{ z3!$CYKfMyESp09(g)qtNbKQ1d-`h`Z+T{LsV#fOVl)ZZK?caXAe*4eu?B)D89iAhq zhI=M1^WaSndgolE!mbj2uX@I|%1xJF@Y^xm(a&vSVoI6Jz3O3q%X3rtTTG!XSDl>vPJX}@^z7gWyaBQ~0#0?2lA4 z;9z5U_+gVt(u4^ehpej2OCt9Dvhn-$Q7%aDui%^Gl@rCzO6~j3_x1W0DbD%goV#77 zX@&I5uZsO^y5H?s`m>JSsc-(=+HJSA{O{I1$L>Er8ZWr=+@fjs3wLh0vwHfzpq(M{ z&L2NJZA^W3@Z-#MYoB{kFCKI^XuW;Rr|0;MUoI`TEGUd&pydg+3dM~crgJYR8GE6*qDulny< zH_!Xmq;TvNd_Pn7QHI9F8yVs(DlX=Gw_ap#|NgJFUSLwoHT!pA`_&AVsv6#sm(Zy* z&t96wl9(dh61}8eSCeaAVS!TCwMO@+s~5idnXvJI!I8BMs)8SH@BDG^&%}cM*yVOo zGPgIY@J1hbbThK_IaAv-vvnKQ?7H03o#b+U%`^~-mwucc^7ZwGdI$4di6=fsU;F!; ze0z3zlG{Q9-NT|MtwOzYd)Hros#??Y#UrxCqsVWcVD!T__EQfQ3Y?MSx>rztCTb^N z<3lCW*pt6EPAyEaT{cZmEYl{-li~i%zZ+r}Pk6^%bD}9G@P^C(pDUaqjw$9^vy|Qz zT5_{BPW#;?8 z36K7TT$7%(^ytpb_e>6#-aH*%yk2pgb9y$nS)t%7j>5X)BG)&sc1;$^IkJ>R_*j*K zYwm&fs!0!1i;teQn%nibPQBaU(W>(OH_R5^oV4-LZ(Hp-uf(Junl4pxPU`df?ok$Z zC-(!Z)>+RUu1!x*-@4E&H8W7@XyzW@xtkBK&EH|h^E|$1-QJh!7q_gkWcDc7aw+u` zm*T?XufMf+oIV}SB3xJ3yW_pmBQuvT8}|QdnsM|K(}JV=>ji(*KCzLl=e>JCzoA#> zzM1ov&l@M!?``{8F8D0;VD_hnn~S7ORr3ET?G1VSyW@3*^sHlDoA&V4ZTi4??Wgqm zT=tl2jo)wNS8T9wPI(<2GimFr>wyOHZv0j!n_QS?8L7Yce?F(lYms?E#HL%e#^$T5 zlr}~#n^3xj0}oPNn*Ue@80skhrU*#2oSa#Xsr^J)xMJT3ij!QYArs-at!&Rx5xiKl$4#`!&wE0*(yO`753y*WNyI&GnP)D~xP zGrqrHY!d>DB+Y*{C2zXGw8Z68v&F-o4l}LPqD2?`FIp$J;33BgmQHuRZ_2AP)m{~D z{HNoX=i;_XvGTjuR-;16ww0nGPZ_thAKCgfA>&Ql@_PnG&n4IM{(Rl@^|RI9%btsr z7OqI#)*8P-o`28EjJH{d!P6eBy)5fhyPwbK>a;+&v%9}^spl+_*AVIr)A%#()x|fy zo3l9`dv~gscq#dm+Lz?KQC8JWcc@bRJ||IseXp=nzxC;Fw&%J(7{tCX<9s6SS)n{# zf6w;Fd^ZR7w32Jx_Us3)=*--wCZ8L4UW2W*;^BuS_d2g`-)@j_Yxyj`eK|Ifq5md? zYApDnEu`fh+`2gYh4dFSO~vXJThCCR9`xi}&7(g)MG{tW8s?g1?GL*q7k z&*k`^arMm_1qWl#=XNC}w?0he4|~II@MzJArE4SF6r%KQR&y-h_{vbRKsWjMeWkd$iMv|9vgT5E?JC{9=XDgUel9hrJ$Uh)qW0J2H`Bj~M8-KT zbhY+*_`%ce!z+bLNnh>ueNA!N^mff8^Y%q1#e3%H*B1sXcU@WkeX17^6$|558X$mD+Y)k zUw!V_E2As>TVob@HFzCgF{j+qOZw9dB`x2dX}p1J)-{)_#%-7=pR|-!rc~eAMWc4l z+WO+tvOTX$YD9w={Ns-<-d3x1E-#BV>b7;^ygVCFZEt+ zJULU`vHp+Z;U6z8wVOnb*VyqJLzfk+-tevb(-U)4MBKvl-@nN+Tb4>ZpI*M1ceyw6AZ!df`dLGN`y_)L_Kew(nUuu#6{!golp6RZq1%|(Lt{tr{-`I8F z-BS@434d$8rlc1vY{xz8L*{VDS*&uIaaBxSI5Bo#8`taE?k`G&ZNu2(->RuzW$M-3 zVN`CIAa-yjmza8_;R^$_5PEa7$Vd4Kx)ny})9Ta#={F~(R33iu z3tQ9XRapP}{B^}C*@6%1Z%BJDlKi>X<%ry}cTawouiAQ$ecpd2&BP~6Ir~_wuWjT} zzy5-o(pnQ+fL-paa+8Y(d)(R zJU8F@DptQfI_!QbxUO`a(>kjO9@7u;mP^__H_m1W+BV%U*Z+LZqQ6g%&%6CpST4rJ z#cA!=>iCOQoA#l+n4e5JkMyhQcl3$xyFXf)MooqIL;rv~Gk zguKc*ANR)UZe9B;`c5EA-`gE?FaOaBIJDxgMWFOD0|}-0OIGEc4=#mAZ}s7Ru$TAs z#|ytqk51UL>`%+2#Gl);+T!aE?4GQ$Q26um$#bXW|8=PU%zRJxLc@8Btm*H=8NYq* zzqQ>y!9n5av!Xwf`wu>S{5&CYE9gD+hHZD4$^|;{UqeHLn{cPG;rM@KN`RhBODVavU^q8N{ zl)8O-Ms4&FHQ_oAz0Gf|*Erg88d*0<-`<~hFs-2CM98zWeLIX-DCm9Ny=FgWsF=OM z+hZHquY38w|5KQ2m{q*KewD85&ELy}4m_$jb8Q}Lp5$iJGLzd1<>o(p*_C!2@Hob{ zb3(GCh=*|E!zZ)%h#gPApmDY>|FFg}J2yAp_Mg%X+x4<%C9au&{q&aHfb;$fez1O* zJh*U{Mw;@@+0*>r?lary-x_7OYWwM3>Y*)PmBQmE%($|8$@F6>aantNOqQtU)T%}7 z|7zPK#<=Ffnqr@1zvmnF2DH}LMCJdq+-p@Ef0%uBXif&hGOd+qy2f2gIjl8vuN5Zi zMQ>!fB<;*Q(^|y;n&jgL!EDDf-KNc~pS;&0;h@FB9S%9{r8bs2Z*$fjtNWX(H>0Tj zWaz=lHy)Ex`^6va?A_hlGp*q8QXckwW|a%Yj~<=av~Z6EzpCz?&+8JyBhKupII!f% z^V#};3fuKxRiBsrSeln27Z+a@%&WB6P~qF<=3s6ECap;?~IO8 zoOV}vN=emJg+tEokF&g;pS#s0>C8sndrR*cOgtEzx|rok^PY@zD>S3CK06jl3S~*A zJnvrds>b5bf})HS+t)V#KIL)TdFI{nrqi}l6z*K?o3gE1KyuHmwe>%Lf11p;(?6$d z-^#e>xmA02nwxJwFzMV+_rrGgpIx6_Tf8~&OrPnVUAZ<(Z&pReIlnSn5+-o_(_^pP ze;YqV?2C9A7{B{5xnX-;#=`ryH>RCha83^n#o@aUCJtIcDRP~^ zcc5A->eVmjvIFebOMX=`?Bfy*jO%|?|8uKok!T2C(5$noPKWwC9{p`4Ii+z@&)(zR z2WBunOF7+>8Wb2MEcBAIZRWn@TNO7GSGO?Ej9Iem>b1uH9}cG$F>EV;P|`HVWM}Ht z;Of5QnU;?0PAOm0yUMS3FJ!IfoB2;JwiP=2>MuXJ$0pqU_vdVby(I>sRjXp^EK*yy z@883A-J3mp(u1@+-==5XnEt-X>esjU^@4vc3HCU>6mH*HZna};-6fs!%6DS#!mewy zt+Z-3;x1WT=g7@ubM4%um+DbrU2T6kp5$!3%D7WuYf0-5k1tFIG7xDR zWn7JZ{6Ftmz2Z`dxvkudS%BCOWro$ofyJ?GuJ2Qu*{r!EFC%NUC%37zh)LFt1XMVYpH6X zEpyoEQub%xH+Ro;s(j44lYWWAZ_CEgy)Ud*RR@M^C3c zlu+fJyv2y!ENG+fJ+p+;EAyP~`{ewzN>evQ)ai))4U5%0${4yLl&2<%ueZ+DVZo|t zZec4wec2MYWc_QO-wH`7=b0n9Vz{1sz2kCtipzU1_5OGMAKnFiyC%TH8ZUk9!|a_J z2~35r_pe!g;a2?mdRvwNPNoNr?z5Ikao$d2xE0zZDq?!Dv7`8qB_9XR6M%*Duq`1EsD6O^fq)yQ}$_ zPv_qeJyorFEVZYvN}M{tyyLcv?)z)kIr;qNw5wiMNpjey?EK~1+w4zOzH#p-hnv|i zvo$~A#mAw~vgzB8KOa{V%KzQ)v~9aU@Lju5XS+%4lS9AD|J#>bE!<`(`Y-U!CcYP; zw?3cudmFyE%&gC!KYiZy#kD_otG#8PHEELY+wcGX)Z~AAz9H>+|CD1X^-Z$BKbii$ zAL3{zGSV~Kd*iNAN$|aUAe^9*M93d;dj@&(w7R{IDbWF z4e*a_Xd;KF;{dr$*-|H28zfJA4hhgf|kJ85@ScErlBz%ksn`Tfo zO>~l-qd-E8z+UN)?GZk+-}E0VWs_Wc*-`mX-{&#?Pm->wd}LKo&l9IwxS!MW&LY8>rS~e1ZA#alacO)1 zblcP7YpWma&#Pd$EwxN;U5NhYdZyp$m1PEo_hyA{Dbm#leVwnr?AoHR9smFJui9F} zyK!2@YSaDCZ-zc%*uTH1#cs0lmD|VMw|RW4dHy?p*MfQG->vV<+kgCeS$*F>Ufs<{ ztMAu2@6_IT_vg3Scc#DEYkuGE?d^QOcXbayBA)er-|zia-hAi7$9(?njTcrk*ynkM z-PJ4Cwl;n9pkm{)>lYY3_Dz2$w&vH`uj;3x9y26Zaj)Wfr^T9ivExnzj|H!D$)4qh z*x$Auw4NML@KgHX{b;Sm2}1Ke{)zSo+ZY>Kl%(G<{c3vl+-v&^=d+&8srk^`y{szD ze75n8o`TI2CLUw@b@;GroMzG&RlP8KN9VTXMqX_JBuNx<4Pl;;{KkHI;-<9LxJU?dP!_G?!qFvu!TBv=)D`lt8+F7<9 zH&*GNo7vqyH)MLD5%)zUl^;4e|DyFjiN@Z(GiA!^#btkQHBY?$kC$KkWiZE_OQNYa zyw3k!kR+D$M9g;9)gPhTe?5A)>AM{F`m-)!=9)a4&wZWOQ?ci1M^CXW zxT6y~^{D8>s;ct~9>wGyE8F7AQTN~ZkVVMus-;BL`)_~8f!mwQXLG5%s=a1#r0&=9TdR8bt2B3U$0aXleWvnOA%9No(WBGd zn$maO+UqmfY_8Xfn{#_t&i|0Kps`7z^F!}_*@zdVI?ZEbb;G|d=)WfYn+ z=dJkJIYCq4>ihC>GwBoU9{!L8HeFHF1r zgzx=4rMV)1PJUi}!1(>wgooY{N7v=fTE1q-E`~2$LGnB2PA*hgYj7s1NJ>oT@WpL2 z)+Wj<=DpurC-U!Kux?5C>l3`W&)S}veDf#@_@n9*GOMAZL42K9_WGHM*2Y(U&wSYU zf7#-lyhb{1i&xybI@8GJtet+^3qG??`yMoE<(#THal{~3>gT(2ay|FI-JH#>(Ymw8 z=;vPR`qO7#JU?B-^er|2Z0-A&v{mN|zIHhpyZV1p`6|`YZhD{BA!IlE_ii1NRrQYb zBA>Ug*)TSp(LKxGcCatWNP{=WR{4Q!^8y2#Nx9-?WoA8VUvo$&D+qr%d_y5lR#n55 zPvLZNs%6dFdk=*#oDF76+xPyB8-J!pq|EB8K0M|Uj_VJ-4^4ey|8$etrvr^M)`lND z?UlZ-{j%ph?+0zIS9^`?!We)56)>x;x< zSUbC`ujeO!@VLsgai)zE^Sl7<7x8MRWCe*~m>XE%NgwZg>`a?PXRh zZ_=}kd+)xV&ZaKxD5=xHZ}*$M+`D}Jecc4c7omT9dv0&#l)e@E_RTpT@n&UiKK(oE zY9G#Y;VgRj)#%v0`g8ZcR^1PANt)&UwVF?7d!L}OnA_uJmCu_?g#Uec<}Nm$`%igZ z+@4I$ZDngCt8!1NotS4B^X+YBZ?C}hTpsopk|v&^Rxg+5o81a3V76~sz2?zT4r}gP z9+qbqt0ZHOmd*WL*nVRDnK>?R13qQS)h#ucpdg@_XmwA2g7D@)`vheJ)>T~Cx%iM? z!m8dKS&{E!``ov5XU{s!?W*^@8)6RTf z_wlyBQ^cjZ?wz2QCN_savBy-=&<13`3ztvcNTQh6w?$V-ni&C=MT_5bPQ^<{y zyiwTa{i;Vhu1M;%hj{pOy9qN(y)=%vP3)HPoVWFTqn>_naPxs{to|G)zvY+N%K7qc zmj23#bmc>n4Jfpbw>48vJw$(Y7n{1yL1+4hC_|L3U)8Z;UUrVH9 z`}qXNh3vd6@?z>{Z=ZkJY_GD} z97n|h{x{5?U0t&@&LB#6yUP56SF4>Rdafok`S>{-tM{ue*)&(`^LzH(0*6dHnP<0F zygaf&@ASu5OY<)7PdT5rcNV|btAAM`S5o&E-#vBPzsaY5-`Xj!x5ck7p0~*E;IsAWG7VRR zzixBnj+}loH(2wh#IgK|7uUTzb#L+kW-iC+2G&;nS+_S_J$=V#;p>IZUUH@GcOFj(_FA_fFQu@WwjfeFyy1 z4{tWwHKA$$vsceD!xE$?Rbu=i172 zJd@<+kJGt4ia(ki{<`FrPPA9g>lr5V#Ai`FU{@mx?S-7RbVc};Fvlx1A?=cUT* zU#j+4N+xeP(U{Jc`6>IejZl&8;>%x@qU71$dd^YpNKX^%RFn0Oi<{ae;rRD_W^!4i zYwWA5ed(WlgqL28KPGCR`_-h>j7^xOd1B+i+GzKC+(8p|r+7xZJe#ZGcJQ=hQKB&O z*6+#_n!fMREGSqWVz9a(Fgix!#w+g0Rka(k-fp+~W2VSF=bgqcf1Ws>|LH5s*C@%) zOo@{>z59B{zh(6uO4t9Nu)qIOzWm$OYwkX*-*5i@Zobb^b)rYy#F)5+(_QA|WPj5W zPhK|v0B0LdbCZtH{{O*~!&m-umf#d+JHr#(t5$Pw#@v?fmB;_^=*(dXUe9+lDN@{8 zEMsf6zxL%VNB7rGGgJ2kN7EMWIJS_le)3ax`_8UX z=6uF2wY`V?mi+&A(Bi`7Ep8jbN?#;gd-HwO{Mb#pHIaRj&aY$9FcsR!)7%qvn`Qpl z;M5Y={+g{Oe$2~fv)?`7{^i7>#!Vp#M^0@G`p}WN2_MiFS z_50$YyJd2PU2byO2=qy~k^F z1nz9fbN*)6k#Yd^C^~LvCuS@S{9JE(d);T|4 zsP5LbKX2{so?dQqPcTUP-|PLN`Zt+E-xP03TI$z7^O1xLPxQyn%K6I8zRdW>aJaGw#fUnZJz7%Z%g}UJon^2{avi;ym;m5 z={$S$t#$vjKhJsITEFM-{{OaLzg{=b|Nr;=|9{8#-~W5we)@lQ28RFtnHetrnI>`f H3?l;ox6cw+ literal 0 HcmV?d00001 diff --git a/tools/i2/fonts/segeo-print-webfont.ttf b/tools/i2/fonts/segeo-print-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c32e039413e2ac709530c0d4984951f42c43d798 GIT binary patch literal 59232 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

$hOh&>*tZ@iXAsrq-XJ2dSxiJl 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 literal 0 HcmV?d00001 diff --git a/tools/i2/fonts/smileymonster.eot b/tools/i2/fonts/smileymonster.eot new file mode 100644 index 0000000000000000000000000000000000000000..0cd367e43b746bab7950d937ba542872abc930f8 GIT binary patch literal 14882 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 OT9gO!BIE!63`_vWYfTsc literal 0 HcmV?d00001 diff --git a/tools/i2/fonts/smileymonster.svg b/tools/i2/fonts/smileymonster.svg new file mode 100644 index 0000000..2569e34 --- /dev/null +++ b/tools/i2/fonts/smileymonster.svg @@ -0,0 +1,279 @@ + + + + +Created by FontForge 20090914 at Fri Feb 24 06:29:58 2012 + By root +Typeface © (Fonts For Peas). 2006. All Rights Reserved http://www.geocities.com/fontsforpeas Free for personal use, contact for commercial use: fontsforpeas@yahoo.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/i2/fonts/smileymonster.ttf b/tools/i2/fonts/smileymonster.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3f1ec2b1fae6c571bd0e83fbf76ed3e92f716239 GIT binary patch literal 14624 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 b<>%!l7G-DVr5B}^losWIyvX?fKLZm0nE)m5 literal 0 HcmV?d00001 diff --git a/tools/i2/fonts/smileymonster.woff b/tools/i2/fonts/smileymonster.woff new file mode 100644 index 0000000000000000000000000000000000000000..81c75cf698c2169bbdbed132477f0ea7c2fe049a GIT binary patch literal 8448 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 OT9gO!BIE!63`_vWYfTsc literal 0 HcmV?d00001