{"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":"<div id=\"@@AUTOID@@\"></div>","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":""}