{"name":"Abacus","key":"abacus","version":"1.0.0","instructions":"A simple abacus for Moodle. Based on Thorsten Thormaehlen's abacus.","showatto":"1","showplayers":"0","requirecss":"","requirejs":"","shim":"","defaults":"","amd":"1","body":"
","bodyend":"","script":"// Copyright (C) Thorsten Thormaehlen, Marburg, 2013, All rights reserved\n// Contact: www.thormae.de\n\n// This software is written for educational (non-commercial) purpose. \n// There is no warranty or other guarantee of fitness for this software, \n// it is provided solely \"as is\". \n\nfunction UIElement(x, y, width, height, type, ref, subref, slotType) {\n this.x = x;\n this.y = y;\n this.x2 = x + width;\n this.y2 = y + height;\n this.type = type; // 0 = node, 1 = slot, 2 connection\n this.ref = ref;\n}\n\nfunction Bead() {\n this.position = [0.0, 0.0];\n this.value = 0;\n this.active = false;\n this.uniqueID = -1;\n}\n\nfunction AbacusCtrl(type) {\n this.type = type; // 0 Japanese, 1 Chinese\n \n this.beadLines = 8\n this.beadPerLine = (this.type == 0) ? 5 : 7;\n this.beadSep = (this.type == 0) ? 3 : 4;\n this.beadHeight = 40;\n this.beadSpacing = 80;\n this.beadWidth = 60;\n this.nodes = new Array();\n \n this.init = function() {\n this.nodes.length = 0;\n var id = 0;\n for(var i=0; i < this.beadLines; i++) {\n for(var j=0; j < this.beadPerLine; j++) {\n var bead = new Bead();\n bead.position[0] = 580 - i * this.beadSpacing;\n bead.position[1] = 60 + this.beadPerLine * this.beadHeight - j * this.beadHeight;\n bead.value = 1;\n if(j > this.beadSep) {\n bead.position[1] = 60 + this.beadPerLine * this.beadHeight - (j * this.beadHeight + 2 * this.beadHeight);\n bead.value = 5;\n }\n bead.uniqueID = id;\n this.nodes.push(bead);\n id++;\n }\n }\n };\n \n this.getBeadsCount = function() {\n return this.nodes.length;\n };\n \n this.getBeadPositionX = function(nodeId) {\n return this.nodes[nodeId].position[0];\n };\n\n this.getBeadPositionY = function(nodeId) {\n return this.nodes[nodeId].position[1];\n };\n \n this.activated = function(nodeId) {\n var line = Math.floor(nodeId / this.beadPerLine);\n var beadInLine = nodeId - line * this.beadPerLine;\n //console.log(nodeId +\" \" + line + \" \" + beadInLine);\n \n var active = this.nodes[nodeId].active;\n this.nodes[nodeId].active = !active;\n \n var dir = 1;\n if(beadInLine > this.beadSep) dir = -1;\n \n var offset = dir * (-1) * this.beadHeight ;\n if (active) offset = dir * this.beadHeight;\n this.nodes[nodeId].position[1] += offset;\n \n if (beadInLine <= this.beadSep) {\n for (var j = 0; j < this.beadPerLine; j++) {\n var n = line * this.beadPerLine + j;\n if (j <= this.beadSep && j !== beadInLine) {\n if ((!active && j > beadInLine) || (active && j < beadInLine)) {\n if (this.nodes[n].active === active) {\n this.nodes[n].position[1] += offset;\n this.nodes[n].active = !this.nodes[n].active;\n }\n }\n\n }\n }\n }else{\n for (var j = 0; j < this.beadPerLine; j++) {\n var n = line * this.beadPerLine + j;\n if (j > this.beadSep && j !== beadInLine) {\n if ((!active && j < beadInLine) || (active && j > beadInLine)) {\n if (this.nodes[n].active === active) {\n this.nodes[n].position[1] += offset;\n this.nodes[n].active = !this.nodes[n].active;\n }\n }\n }\n }\n }\n };\n}\n\nfunction Abacus(parentDivId, type) {\n var abacusCtrl = new AbacusCtrl(type);\n var canvas;\n var divId = parentDivId;\n var beadColor = \"rgba(133, 178, 255, 1.0)\";\n var hooveredBeadColor = \"rgba(170, 215, 255, 1.0)\";\n var hooveredElement = -1;\n var hooveredBead = -1;\n var uiElements = new Array();\n var that = this;\n \n this.init = function() {\n \n abacusCtrl.init();\n \n canvas = document.createElement('canvas');\n if(!canvas) console.log(\"Abacus error: can not create a canvas element\");\n canvas.id = parentDivId + \"_Abacus\";\n canvas.width = 40 + abacusCtrl.beadLines * abacusCtrl.beadSpacing;\n canvas.height= 60 + (abacusCtrl.beadPerLine+2) * abacusCtrl.beadHeight;\n document.body.appendChild(canvas);\n var parent = document.getElementById(divId);\n if(!parent) console.log(\"Abacus error: can not find an element with the given name: \" + divId);\n parent.appendChild(canvas);\n \n canvas.onmousedown = function(event) {\n canvasMouseDown(event);\n };\n canvas.onmousemove = function(event) {\n canvasMouseMove(event);\n };\n canvas.onmouseup = function(event) {\n canvasMouseUp(event);\n };\n canvas.onmouseup = function(event) {\n canvasMouseUp(event);\n };\n \n this.update();\n };\n\n function drawBead(nodeId, ctx) {\n\n\n var nodePosX = abacusCtrl.getBeadPositionX(nodeId);\n var nodePosY = abacusCtrl.getBeadPositionY(nodeId);\n \n var dn = new UIElement(nodePosX, nodePosY+2, abacusCtrl.beadWidth, abacusCtrl.beadHeight-4, 0, nodeId, 0, 0);\n\n ctx.fillStyle = \"rgba(60, 60, 60, 0.3)\";\n drawRoundRectFilled(ctx, dn.x+4, dn.y+4, dn.x2-dn.x, dn.y2-dn.y, 15);\n ctx.fillStyle = beadColor;\n \n if(nodeId === hooveredBead) {\n ctx.fillStyle=hooveredBeadColor;\n } \n drawRoundRectFilled(ctx, dn.x, dn.y, dn.x2-dn.x, dn.y2-dn.y, 15);\n ctx.fillStyle = \"rgba(255, 255, 255, 1.0)\";\n \n uiElements.push(dn);\n if (false) {\n ctx.fillStyle = \"rgba(0, 0, 0, 1.0)\";\n ctx.textAlign = 'left';\n ctx.font = '10pt sans-serif';\n ctx.fillText(\"ID: \" + nodeId, dn.x + 4, dn.y2 - 13);\n ctx.lineWidth = 1;\n }\n }\n\n function drawBeads(ctx) {\n var count = abacusCtrl.getBeadsCount();\n for (var i = 0; i < count; i++) {\n drawBead(i, ctx);\n }\n }\n\n this.update = function() {\n \n canvas.width = canvas.width;\n \n uiElements.length = 0;\n var ctx = canvas.getContext('2d');\n ctx.strokeStyle = '#000000';\n \n \n // draw grid\n if (false) {\n ctx.strokeStyle = '#808080';\n var stepsX = 20.0 - 0.0;\n var stepsY = 20.0 - 0.0;\n\n var lx = 0 % stepsX;\n var ly = 0 % stepsY;\n var Lx = 0 % (stepsX * 5.0);\n if (Lx < 0.0)\n Lx += (stepsX * 5.0);\n var Ly = 0 % (stepsY * 5.0);\n if (Ly < 0.0)\n Ly += (stepsY * 5.0);\n\n while (lx < canvas.width) {\n if (Math.abs(Lx - lx) < 0.001) {\n ctx.strokeStyle = '#404040';\n Lx += (stepsX * 5.0);\n } else {\n ctx.strokeStyle = '#808080';\n }\n ctx.beginPath();\n ctx.moveTo(lx, 0);\n ctx.lineTo(lx, canvas.height);\n ctx.stroke();\n lx += stepsX;\n }\n\n while (ly < canvas.height) {\n if (Math.abs(Ly - ly) < 0.001) {\n ctx.strokeStyle = '#404040';\n Ly += (stepsY * 5.0);\n } else {\n ctx.strokeStyle = '#808080';\n }\n ctx.beginPath();\n ctx.moveTo(0, ly);\n ctx.lineTo(canvas.width, ly);\n ctx.stroke();\n ly += stepsY;\n }\n }\n // draw frame\n ctx.strokeStyle = '#000000';\n ctx.lineWidth = 5;\n for(var i=0; i < abacusCtrl.beadLines; i++) {\n var x = -30 + abacusCtrl.beadLines * abacusCtrl.beadSpacing - i * abacusCtrl.beadSpacing;\n var y = 20 + (abacusCtrl.beadPerLine+2) * abacusCtrl.beadHeight\n ctx.beginPath();\n ctx.moveTo(x, 20);\n ctx.lineTo(x, y);\n ctx.stroke();\n }\n for(var j=0; j < 3; j++) {\n var y = 20;\n if(j === 1) y = 20 + (abacusCtrl.beadPerLine - abacusCtrl.beadSep) * abacusCtrl.beadHeight;\n if(j === 2) y = 20 + (abacusCtrl.beadPerLine+2) * abacusCtrl.beadHeight;\n ctx.beginPath();\n ctx.moveTo(20, y);\n ctx.lineTo(640, y);\n ctx.stroke();\n }\n ctx.lineWidth = 1;\n \n // draws all nodes\n drawBeads(ctx);\n \n // draw value\n ctx.fillStyle = \"rgba(0, 0, 0, 1.0)\";\n ctx.textAlign = 'center';\n ctx.font = '20pt sans-serif';\n var textY = 50 + (abacusCtrl.beadPerLine+2) * abacusCtrl.beadHeight;\n for(var i=0; i < abacusCtrl.beadLines; i++) {\n var textX = -30 + abacusCtrl.beadLines * abacusCtrl.beadSpacing - i * abacusCtrl.beadSpacing;\n var valueSum = 0;\n for(var j=0; j < abacusCtrl.beadPerLine; j++) {\n var n = i * abacusCtrl.beadPerLine + j;\n if(abacusCtrl.nodes[n].active) {\n valueSum += abacusCtrl.nodes[n].value;\n }\n }\n \n var valueSting;\n if(abacusCtrl.type === 0) {\n valueSting = valueSum.toString(10);\n }else{\n valueSting = valueSum.toString(16);\n }\n \n ctx.fillText(valueSting, textX, textY);\n }\n };\n \n function mouseOverElement(pos) {\n var selectedElement = -1;\n for (var n in uiElements) {\n if (uiElements[n].type !== 2) {\n // not of type \"connection\"\n if (uiElements[n].x - 1 < pos.x && \n uiElements[n].x2 + 1 > pos.x && \n uiElements[n].y - 1 < pos.y && \n uiElements[n].y2 + 1 > pos.y)\n {\n selectedElement = n;\n }\n } \n }\n return selectedElement;\n }\n \n function canvasMouseDown(event) {\n var pos = getMouse(event);\n \n // handle selection\n if (!event.altKey && event.which === 1) {\n var selectedElement = mouseOverElement(pos);\n if (selectedElement !== -1) {\n // handle node selection\n if (uiElements[selectedElement].type === 0) {\n var newSelectedBead = uiElements[selectedElement].ref;\n abacusCtrl.activated(newSelectedBead);\n }\n }\n that.update();\n } \n event.preventDefault();\n }\n\n function canvasMouseUp(event) {\n }\n\n function canvasMouseMove(event) {\n var pos = getMouse(event);\n\n hooveredBead = -1;\n var oldHooveredElement = hooveredElement;\n hooveredElement = mouseOverElement(pos);\n\n if (hooveredElement !== -1) {\n hooveredBead = uiElements[hooveredElement].ref;\n }\n if (oldHooveredElement !== hooveredElement) that.update();\n oldPos = pos;\n event.preventDefault();\n }\n\n function getMouse(e) {\n var element = canvas;\n var offsetX = 0, offsetY = 0, mx, my;\n\n // compute the total offset\n if (element.offsetParent !== undefined) {\n do {\n offsetX += element.offsetLeft;\n offsetY += element.offsetTop;\n } while ((element = element.offsetParent));\n }\n\n mx = e.pageX - offsetX;\n my = e.pageY - offsetY;\n\n return {x: mx, y: my};\n }\n\n function drawRoundRectFilled(ctx, x, y, width, height, radius) {\n var lineWidthBackup = ctx.lineWidth;\n var strokeStyleBackup = ctx.strokeStyle;\n ctx.strokeStyle = ctx.fillStyle;\n ctx.lineJoin = \"round\";\n ctx.lineWidth = radius;\n ctx.strokeRect(x+(radius/2),y+(radius/2), width-radius, height-radius);\n ctx.fillRect(x+(radius/2),y+(radius/2), width-radius, height-radius);\n ctx.lineWidth = lineWidthBackup;\n ctx.strokeStyle = strokeStyleBackup;\n }\n}\n//END OF ABACUS CODE\n\n\n//USING THE ABACUS\nAbacus(@@AUTOID@@);\ninit();","style":"","dataset":"","datasetvars":"","alternate":"","alternateend":""}