diff --git a/imgs/back.png b/imgs/back.png new file mode 100644 index 0000000..309608f Binary files /dev/null and b/imgs/back.png differ diff --git a/imgs/btd4.jpg b/imgs/btd4.jpg new file mode 100644 index 0000000..739196c Binary files /dev/null and b/imgs/btd4.jpg differ diff --git a/imgs/calculator.png b/imgs/calculator.png new file mode 100644 index 0000000..71a64ae Binary files /dev/null and b/imgs/calculator.png differ diff --git a/imgs/download.png b/imgs/download.png deleted file mode 100644 index f17d5e7..0000000 Binary files a/imgs/download.png and /dev/null differ diff --git a/imgs/flash.png b/imgs/flash.png new file mode 100644 index 0000000..78033d9 Binary files /dev/null and b/imgs/flash.png differ diff --git a/imgs/retrobowl.png b/imgs/retrobowl.png new file mode 100644 index 0000000..3f6b6c7 Binary files /dev/null and b/imgs/retrobowl.png differ 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 0000000..1912144 Binary files /dev/null and b/tools/i2/fonts/hobo-webfont.eot differ diff --git a/tools/i2/fonts/hobo-webfont.svg b/tools/i2/fonts/hobo-webfont.svg new file mode 100644 index 0000000..11c9be6 --- /dev/null +++ b/tools/i2/fonts/hobo-webfont.svg @@ -0,0 +1,706 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 0000000..0651065 Binary files /dev/null and b/tools/i2/fonts/hobo-webfont.ttf differ diff --git a/tools/i2/fonts/hobo-webfont.woff b/tools/i2/fonts/hobo-webfont.woff new file mode 100644 index 0000000..fe4e4fa Binary files /dev/null and b/tools/i2/fonts/hobo-webfont.woff differ diff --git a/tools/i2/fonts/hobo-webfontd41d.eot b/tools/i2/fonts/hobo-webfontd41d.eot new file mode 100644 index 0000000..1912144 Binary files /dev/null and b/tools/i2/fonts/hobo-webfontd41d.eot differ diff --git a/tools/i2/fonts/segeo-print-webfont-2.svg b/tools/i2/fonts/segeo-print-webfont-2.svg new file mode 100644 index 0000000..b303e54 --- /dev/null +++ b/tools/i2/fonts/segeo-print-webfont-2.svg @@ -0,0 +1,249 @@ + + + + +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 0000000..823b603 Binary files /dev/null and b/tools/i2/fonts/segeo-print-webfont.eot differ diff --git a/tools/i2/fonts/segeo-print-webfont.svg b/tools/i2/fonts/segeo-print-webfont.svg new file mode 100644 index 0000000..2f3181c Binary files /dev/null and b/tools/i2/fonts/segeo-print-webfont.svg differ diff --git a/tools/i2/fonts/segeo-print-webfont.ttf b/tools/i2/fonts/segeo-print-webfont.ttf new file mode 100644 index 0000000..c32e039 Binary files /dev/null and b/tools/i2/fonts/segeo-print-webfont.ttf differ diff --git a/tools/i2/fonts/segeo-print-webfont.woff b/tools/i2/fonts/segeo-print-webfont.woff new file mode 100644 index 0000000..c1ebf42 Binary files /dev/null and b/tools/i2/fonts/segeo-print-webfont.woff differ diff --git a/tools/i2/fonts/smileymonster.eot b/tools/i2/fonts/smileymonster.eot new file mode 100644 index 0000000..0cd367e Binary files /dev/null and b/tools/i2/fonts/smileymonster.eot differ 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 0000000..3f1ec2b Binary files /dev/null and b/tools/i2/fonts/smileymonster.ttf differ diff --git a/tools/i2/fonts/smileymonster.woff b/tools/i2/fonts/smileymonster.woff new file mode 100644 index 0000000..81c75cf Binary files /dev/null and b/tools/i2/fonts/smileymonster.woff differ diff --git a/tools/i2/fonts/smileymonsterd41d.eot b/tools/i2/fonts/smileymonsterd41d.eot new file mode 100644 index 0000000..0cd367e Binary files /dev/null and b/tools/i2/fonts/smileymonsterd41d.eot differ