From 83237b9a3826908f34f67606b2484376c49b0c94 Mon Sep 17 00:00:00 2001 From: Adriaan Wormgoor Date: Fri, 26 Jul 2013 22:39:28 +0200 Subject: [PATCH] bunch of javascript --- js/buttonbehaviors.js | 276 ++++ js/canvasDrawing_v01.js | 361 +++++ js/d3dServerInterfacing.js | 10 + js/doodlePrintCode.js | 66 + js/draw_logic.js | 2 + js/gcodeGenerating_v01.js | 214 +++ js/libs/bootstrap.js | 2280 +++++++++++++++++++++++++++++ js/libs/bootstrap.min.js | 6 + js/libs/excanvas.compiled.js | 35 + js/libs/excanvas.js | 924 ++++++++++++ js/libs/html5shiv-printshiv.js | 499 +++++++ js/libs/html5shiv.js | 301 ++++ js/{ => libs}/jquery-1.8.3.min.js | 0 js/previewRendering_v01.js | 154 ++ js/settings.js | 46 + 15 files changed, 5174 insertions(+) create mode 100644 js/buttonbehaviors.js create mode 100644 js/canvasDrawing_v01.js create mode 100644 js/d3dServerInterfacing.js create mode 100644 js/doodlePrintCode.js create mode 100644 js/gcodeGenerating_v01.js create mode 100644 js/libs/bootstrap.js create mode 100644 js/libs/bootstrap.min.js create mode 100644 js/libs/excanvas.compiled.js create mode 100644 js/libs/excanvas.js create mode 100644 js/libs/html5shiv-printshiv.js create mode 100644 js/libs/html5shiv.js rename js/{ => libs}/jquery-1.8.3.min.js (100%) create mode 100644 js/previewRendering_v01.js create mode 100644 js/settings.js diff --git a/js/buttonbehaviors.js b/js/buttonbehaviors.js new file mode 100644 index 0000000..685eb74 --- /dev/null +++ b/js/buttonbehaviors.js @@ -0,0 +1,276 @@ +var btnMoveUpInterval; +var btnMoveDownInterval; + +var btnTwistLeftInterval; +var btnTwistRightInterval; +var twistIncrement = Math.PI/1800; + +var btnOopsInterval; + +//var btnNew, btnPrevious, btnNext; +var btnOops, btnStop, btnClear; +var btnMoveUp, btnMoveDown, btnTwistLeft, btnTwistRight; +var btnInfo, btnSettings; +var btnDebug; // debug + +function initButtonBehavior() { + console.log("f:initButtonBehavior >> btnNew = " + btnNew); + + btnClear= $("#btnClear"); + btnOops = $("#btnOops"); + btnMoveUp = $("#btnMoveUp"); + btnMoveDown = $("#btnMoveDown"); + btnTwistLeft = $("#btnTwistLeft"); + btnTwistRight = $("#btnTwistRight"); + btnInfo = $("#btnInfo"); + btnSettings = $("#btnSettings"); +// btnPrint= $("#btnPrint"); + +// btnPrevious = $("#btnPrevious"); +// btnNext = $("#btnNext"); + + //debug + btnDebug = $(".debugBtn"); + + if (!btnNew.addEventListener) { + btnNew.attachEvent('onmousedown',clearDoodle); + btnNew.attachEvent('ontouchstart',clearDoodle); + btnPrint.attachEvent('onmousedown',print); + btnPrint.attachEvent('ontouchstart',print); + +// btnPrevious.attachEvent('onmousedown',prevDoodle); +// btnPrevious.attachEvent('ontouchstart',prevDoodle); +// btnNext.attachEvent('onmousedown',nextDoodle); +// btnNext.attachEvent('ontouchstart',nextDoodle); + } else { + btnNew.addEventListener('mousedown',clearDoodle,false); + btnNew.addEventListener('touchstart',clearDoodle,false); + btnPrint.addEventListener('mousedown',print,false); + btnPrint.addEventListener('touchstart',print,false); + +// btnPrevious.addEventListener('mousedown',prevDoodle,false); +// btnPrevious.addEventListener('touchstart',prevDoodle,false); +// btnNext.addEventListener('mousedown',nextDoodle,false); +// btnNext.addEventListener('touchstart',nextDoodle,false); + } + + btnClear.click(function(e) { + e.preventDefault(); + // console.log("clear"); + + clearDoodle(); + }); + +// btnPrevious.mouseup(function(e) { +// e.preventDefault(); +// console.log("btnPrevious"); +// }) +// btnPrevious.bind("touchend", function(e) { +// e.preventDefault(); +// console.log("btnPrevious"); +// }) +// btnNext.mouseup(function(e) { +// e.preventDefault(); +// console.log("btnNext"); +// }) +// btnNext.bind("touchend", function(e) { +// e.preventDefault(); +// console.log("btnPrevious"); +// }) + + btnOops.mousedown(function(e) { + e.preventDefault(); + // console.log("btnOops mouse down"); + btnOopsInterval = setInterval( function() { + oopsUndo(); + }, 1000/50); + }); + btnOops.mouseup(function(e) { + e.preventDefault(); + // console.log("btnOops mouse up"); + clearInterval(btnOopsInterval); + }); + + btnMoveUp.mousedown(function(e) { + e.preventDefault(); + // console.log("btnMoveUp mouse down"); + previewUp(); + clearInterval(btnMoveUpInterval); + btnMoveUpInterval = setInterval( function() { + previewUp(); + }, 1000/30); + }); + btnMoveUp.mouseup(function(e) { + e.preventDefault(); + console.log("btnMoveUp mouse up"); + clearInterval(btnMoveUpInterval); + }); + + btnMoveDown.mousedown(function(e) { + e.preventDefault(); + // console.log("btnMoveDown mouse down"); + previewDown(); + clearInterval(btnMoveDownInterval); + btnMoveDownInterval = setInterval( function() { + previewDown(); + }, 1000/30); + }); + btnMoveDown.mouseup(function(e) { + e.preventDefault(); + console.log("btnMoveDown mouse up"); + clearInterval(btnMoveDownInterval); + }); + + btnTwistLeft.mousedown(function(e) { + e.preventDefault(); + // console.log("btnTwistLeft mouse down"); + previewTwistLeft(); + clearInterval(btnTwistLeftInterval); + btnTwistLeftInterval = setInterval( function() { + previewTwistLeft(); + }, 1000/30); + }); + btnTwistLeft.mouseup(function(e) { + e.preventDefault(); + // console.log("btnTwistLeft mouse up"); + clearInterval(btnTwistLeftInterval); + }); + + btnTwistRight.mousedown(function(e) { + e.preventDefault(); + // console.log("btnTwistRight mouse down"); + previewTwistRight(); + clearInterval(btnTwistRightInterval); + btnTwistRightInterval = setInterval( function() { + previewTwistRight(); + }, 1000/30); + }); + btnTwistRight.mouseup(function(e) { + e.preventDefault(); + // console.log("btnTwistRight mouse up"); + clearInterval(btnTwistRightInterval); + }); + + btnSettings.mouseup(function(e) { + e.preventDefault(); + console.log("btnSettings mouse up"); + }); + + btnInfo.mouseup(function(e) { + e.preventDefault(); + console.log("btnInfo mouse up"); + }); + + + + // DEBUG + // $(".agentInfo").css("display", "none"); + btnDebug.click(function(e) { + console.log("debugClick"); + $(".agentInfo").toggleClass("agentInfoToggle"); + e.preventDefault(); + }) +} + + +function prevDoodle(e) { + console.log("f:prevDoodle()"); +} +function nextDoodle(e) { + console.log("f:nextDoodle()"); +} + +function print(e) { + console.log("f:print()"); + + $("#textdump").text(""); + if (_points.length > 2) { + //* + // generate_gcode(); + var gencode = generate_gcode(); + startPrint(); + + + +// console.log(""); +// console.log(""); +// console.log("-------------------------------------------------"); +// console.log("generated gcode:"); +// console.log(gencode); +// console.log("-------------------------------------------------"); +// console.log(""); +// console.log(""); +// console.log(""); + + $("#textdump").text(gencode); + // copyToClipboard(gencode); + //*/ + } else { + console.log("f:print >> not enough points!"); + } + + +// $.post("/doodle3d.of", { data:output }, function(data) { +// btnPrint.disabled = false; +// }); +} + + +function clearMainView() { + // console.log("f:clearMainView()"); + ctx.save(); + ctx.clearRect(0,0,canvas.width, canvas.height); + ctx.restore(); +} +function clearPreview() { + // console.log("f:clearPreview()"); + previewCtx.save(); + previewCtx.clearRect(0,0,canvas.width, canvas.height); + previewCtx.restore(); +} + +function oopsUndo() { + // console.log("f:oopsUndo()"); + _points.pop(); + redrawPreview(); + clearMainView(); + prevX = 0; + prevY = 0; + for (var i = 0; i < _points.length; i++) { + // console.log(" drawing points " + _points[i]); + if (_points[i][2] == true) { + draw(_points[i][0], _points[i][1], 0.5); + } else { + draw(_points[i][0], _points[i][1]); + } + } + +} +function previewUp() { + // console.log("f:previewUp()"); + if (numLayers < 100) { + numLayers++; + } + redrawPreview(); +} +function previewDown() { + // console.log("f:previewDown()"); + if (numLayers > 2) { + numLayers--; + } + redrawPreview(); +} +function previewTwistLeft() { + // console.log("f:previewTwistLeft()"); + // if (rStep < Math.PI) { + rStep -= twistIncrement; + // } + redrawPreview(); +} +function previewTwistRight() { + // console.log("f:previewTwistRight()"); + // if (rStep < Math.PI) { + rStep += twistIncrement; + // } + redrawPreview(); +} \ No newline at end of file diff --git a/js/canvasDrawing_v01.js b/js/canvasDrawing_v01.js new file mode 100644 index 0000000..6602064 --- /dev/null +++ b/js/canvasDrawing_v01.js @@ -0,0 +1,361 @@ +/* * * * * * * * * * + * + * VARS + * + * * * * * * * * * */ +var preview; +var previewCtx; + +var svgPathRegExp = /[LM]\d* \d*/ig; +var svgPathParamsRegExp = /([LM])(\d*) (\d*)/; + +var dragging = false; + +var canvas = $("#mycanvas")[0]; +var ctx = canvas.getContext('2d'); + +var canvasWidth = canvas.width; +var canvasHeight = canvas.height; + +var drawCanvas; +var drawCanvasTopLeftCoords = [0, 0]; + +var doodleBounds = [-1, -1, -1, -1]; // left, top, right, bottom +// var doodleScaleVals = [[0, 0], [1.0, 1.0]]; // [ [x, y], [scaleX, scaleY] ] +var doodleTransform = [0, 0, 1.0, 1.0]; // [ x, y, scaleX, scaleY ] + +var _points = []; + +var prevCountingTime = 0; +var movementCounter = 0; + +var drawVariableLineWeight = false; // set to true to have the momentum of the mouse/touch movement result in larger/smaller strokes +var lineweight = 2; + +/* * * * * * * * * * + * + * INIT + * + * * * * * * * * * */ +function initDoodleDrawing() { + console.log("f:initDoodleDrawing()"); + + if (!canvas.addEventListener) { + canvas.attachEvent('onmousedown',onCanvasMouseDown); + canvas.attachEvent('onmousemove',onCanvasMouseMove); + canvas.attachEvent('onmouseup',onCanvasMouseUp); + canvas.attachEvent('ontouchstart',onCanvasTouchDown); + canvas.attachEvent('ontouchmove',onCanvasTouchMove); + canvas.attachEvent('ontouchend',onCanvasTouchEnd); + document.body.attachEvent('ontouchmove',prevent); + } else { + canvas.addEventListener('mousedown',onCanvasMouseDown,false); + canvas.addEventListener('mousemove',onCanvasMouseMove,false); + canvas.addEventListener('mouseup',onCanvasMouseUp,false); + canvas.addEventListener('touchstart',onCanvasTouchDown,false); + canvas.addEventListener('touchmove',onCanvasTouchMove,false); + canvas.addEventListener('touchend',onCanvasTouchEnd,false); + document.body.addEventListener('touchmove',prevent,false); + } + + drawCanvas = $("#drawAreaContainer"); + + console.log("drawCanvasTopLeftCoords: " + drawCanvasTopLeftCoords); + drawCanvasTopLeftCoords[0] = drawCanvas.css("left").match(/[0-9]/g).join(""); + drawCanvasTopLeftCoords[1] = drawCanvas.css("top").match(/[0-9]/g).join(""); + + console.log("f:initDoodleDrawing() >> canvasWidth: " + canvasWidth); + console.log("f:initDoodleDrawing() >> canvasHeight: " + canvasHeight); + +} + +/* * * * * * * * * * + * + * CANVAS DRAWING FUNCTION + * + * * * * * * * * * */ +function draw(_x, _y, _width) { + // console.log("f:draw() >> _width: " + _width); + + if (prevX == 0 && prevY ==0) { + prevX = _x; + prevY = _y; + } + + ctx.beginPath(); + ctx.moveTo(prevX, prevY); + ctx.lineTo(_x, _y); + + if (_width != undefined) { + ctx.lineWidth = _width; + } else { + if (drawVariableLineWeight) { + var dist = Math.sqrt(Math.pow((prevX - _x), 2) + Math.pow((prevY - _y), 2)); + if (dist < 10) { + lineweight += .25; + } else if (dist < 20) { + lineweight += .5; + } else if (dist < 30) { + lineweight += .75; + } else if (dist < 50) { + lineweight += 1; + } else if (dist < 80) { + lineweight += 1.5; + } else if (dist < 120) { + lineweight += 2.25; + } else if (dist < 170) { + lineweight += 3.5; + } else { + lineweight += 2; + } + lineweight = Math.min(lineweight, 30); + lineweight *= 0.90; + lineweight = Math.max(lineweight, 1.0); + } else { + lineweight = 2; + } + + ctx.lineWidth = lineweight; + } + ctx.lineCap = 'round'; + ctx.stroke(); + + prevX = _x; + prevY = _y; +} + + +/* * * * * * * * * * + * + * SUPPORTING FUNCTIONS + * + * * * * * * * * * */ +function clearDoodle() { + console.log("f:clearDoodle"); + + _points = []; + + prevX = 0; + prevY = 0; + + doodleBounds = [-1, -1, -1, -1]; // left, top, right, bottom + doodleTransform = [0, 0, 1.0, 1.0]; // [ x, y, scaleX, scaleY ] + + clearMainView(); + clearPreview(); +} + + function adjustBounds(x, y) { + // console.log("f:adjustBounds("+x+","+y+")"); + + if (doodleBounds[0] == -1) { + // if doodleBounds[0] is -1 then it isn't initted yet, so x and y are both the min and max vals + + doodleBounds[0] = x; + doodleBounds[1] = y; + doodleBounds[2] = x; + doodleBounds[3] = y; + return; + } + + doodleBounds[0] = Math.min(doodleBounds[0], x); // left + doodleBounds[2] = Math.max(doodleBounds[2], x); // right + + doodleBounds[1] = Math.min(doodleBounds[1], y); // top + doodleBounds[3] = Math.max(doodleBounds[3], y); // bottom + + // draw the bounding rect (DEBUG) + /* + ctx.beginPath(); + ctx.rect(doodleBounds[0],doodleBounds[1], doodleBounds[2] - doodleBounds[0], doodleBounds[3] - doodleBounds[1]); + ctx.lineWidth = .2; + ctx.strokeStyle = "#333" + ctx.stroke(); + ctx.closePath(); + //*/ + + // console.log(" new bounds: " + doodleBounds); + +} + +// does what exactly? +function adjustPreviewTransformation() { + // console.log("f:adjustPreviewTransformation()"); + +// doodleTransform[0] = doodleBounds[0] - (doodleBounds[2] - doodleBounds[0]) / 2; +// doodleTransform[1] = doodleBounds[1] - (doodleBounds[3] - doodleBounds[1]) / 2; +// doodleTransform[0] = doodleBounds[0] - ((doodleBounds[2] - doodleBounds[0]) / 2); +// doodleTransform[1] = doodleBounds[1] - ((doodleBounds[3] - doodleBounds[1]) / 2); + doodleTransform[0] = doodleBounds[0]; + doodleTransform[1] = doodleBounds[1]; + + var sclX, sclY, finalScl; + if (_points.length < 2) { + console.log(_points); + sclX = 1.0; + sclY = 1.0; + finalScl = Math.min(sclX, sclY); + } else { + sclX = canvasWidth / (doodleBounds[2] - doodleBounds[0]); + sclY = canvasHeight / (doodleBounds[3] - doodleBounds[1]); + + // TODO this shouldn't be a matter if choosing the smallest but should probably involve maintaining aspect ratio?? + finalScl = Math.min(sclX, sclY); + } + + doodleTransform[2] = finalScl; + doodleTransform[3] = finalScl; +} + + +/* * * * * * * * * * + * + * MOUSE/TOUCH EVENTHANDLERS + * + * * * * * * * * * */ +function onCanvasMouseDown(e) { + // console.log("onmousedown"); + dragging = true; + + prevCountingTime = new Date().getTime(); + movementCounter = 0 + +// _points.push([e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop, true]); +// adjustBounds(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop); +// adjustPreviewTransformation(); +// draw(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop, 0.5); + _points.push([e.offsetX,e.offsetY, true]); + adjustBounds(e.offsetX,e.offsetY); + adjustPreviewTransformation(); + draw(e.offsetX,e.offsetY, 0.5); +} + +function onCanvasMouseMove(e) { + if (!dragging) return; + // console.log("onmousemove"); + +// _points.push([e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop, false]); +// adjustBounds(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop); +// adjustPreviewTransformation(); +// draw(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop); + + _points.push([e.offsetX,e.offsetY, false]); + adjustBounds(e.offsetX,e.offsetY); + adjustPreviewTransformation(); + draw(e.offsetX,e.offsetY); + + // update counter -> this was for getting a handle on how often the Canvas fires a move-event + /* + movementCounter++; + if (new Date().getTime() - prevCountingTime > 1000) { + // console.log("number of moves in 1sec: " + movementCounter) + prevCountingTime= new Date().getTime(); + $("#numtimes").text(movementCounter + " times"); + movementCounter = 0; + } + //*/ + + // DEBUG +// $("#textdump").text(""); +// $("#textdump").append("doodlebounds:" + doodleBounds + "\n"); +// $("#textdump").append("doodletransform:" + doodleTransform + "\n"); + + if (new Date().getTime() - prevRedrawTime > redrawInterval) { + redrawPreview(); + prevRedrawTime = new Date().getTime(); + } +} + +function onCanvasMouseUp(e) { + // console.log("onmouseup"); + dragging = false; + console.log("doodleBounds: " + doodleBounds); + console.log("doodleTransform: " + doodleTransform); + // ctx.stroke(); + + console.log("_points.length :" + _points.length); +// console.log(_points); + + // DEBUG +// $("#textdump").text(""); +// $("#textdump").append("doodlebounds:" + doodleBounds + "\n"); +// $("#textdump").append("doodletransform:" + doodleTransform + "\n"); + + redrawPreview(); +} + +function onCanvasTouchDown(e) { + e.preventDefault(); +// var x = e.touches[0].pageX - e.touches[0].target.offsetLeft; +// var y = e.touches[0].pageY - e.touches[0].target.offsetTop; + var x = e.touches[0].pageX - drawCanvasTopLeftCoords[0]; + var y = e.touches[0].pageY - drawCanvasTopLeftCoords[1]; + + _points.push([x, y, true]); + adjustBounds(x, y); + adjustPreviewTransformation(); + draw(x, y, .5); + + movementCounter = 0; + + prevRedrawTime = new Date().getTime(); +} + +function onCanvasTouchMove(e) { + e.preventDefault(); +// var x = e.touches[0].pageX - e.touches[0].target.offsetLeft; +// var y = e.touches[0].pageY - e.touches[0].target.offsetTop; + var x = e.touches[0].pageX - drawCanvasTopLeftCoords[0]; + var y = e.touches[0].pageY - drawCanvasTopLeftCoords[1]; + + _points.push([x, y, false]); + adjustBounds(x, y); + adjustPreviewTransformation(); + draw(x, y); + + + // update counter -> this was for getting a handle on how often the Canvas fires a move-event + /* + movementCounter++; + if (new Date().getTime() - prevCountingTime > 1000) { + // console.log("number of moves in 1sec: " + movementCounter) + prevCountingTime= new Date().getTime(); + $("#numtimes").text(movementCounter + " times"); + movementCounter = 0; + } + //*/ + + if (new Date().getTime() - prevRedrawTime > redrawInterval) { + redrawPreview(); + prevRedrawTime = new Date().getTime(); + } +} + +function onCanvasTouchEnd(e) { + // console.log("ontouchend"); + redrawPreview(); +} + +function prevent(e) { + e.preventDefault(); +} + + +/* +function print(e) { + + output = path.attributes.d.nodeValue; + console.log(output); + + output = output.split("M").join("\n"); + output = output.split(" L").join("_"); + output = output.split(" ").join(","); + output = output.split("_").join(" "); + + output = "\nBEGIN\n" + output + "\n\nEND\n"; + + $.post("/doodle3d.of", { data:output }, function(data) { + btnPrint.disabled = false; + }); +} +//*/ \ No newline at end of file diff --git a/js/d3dServerInterfacing.js b/js/d3dServerInterfacing.js new file mode 100644 index 0000000..5bed3b0 --- /dev/null +++ b/js/d3dServerInterfacing.js @@ -0,0 +1,10 @@ +function setTemperature(callback) { + + if (callback != undefined) callback(); + +} +function setTemperature(callback) { + + if (callback != undefined) callback(); + +} \ No newline at end of file diff --git a/js/doodlePrintCode.js b/js/doodlePrintCode.js new file mode 100644 index 0000000..8d0e8ba --- /dev/null +++ b/js/doodlePrintCode.js @@ -0,0 +1,66 @@ +var sendIndex; +var sendLength; + +//var kastjeURL = "http://192.168.10.1/cgi-bin/d3dapi/"; +var kastjeURL = "http://192.168.5.1/cgi-bin/d3dapi/"; + + +var mydata = ""; +function startPrint() { + console.log("f:startPrint()"); + +// sendIndex = 0; +// sendLength = 2000; // 2000 regels +// sendGCodeSlice(sendIndex, sendLength); + + $.post(kastjeURL + "test/" + "write/", { data: "test"}, function(data) { + console.log("returned data: " + JSON.stringify(data)); + data = JSON.parse(data); + console.log(" data.msg: " + data.msg); + console.log(" data.status: " + data.status); +// console.log(" status: " + data["status"]); +// btnPrint.disabled = false; + }); + + //http://192.168.10.1/cgi-bin/d3dapi/write +} + + +function sendGCodeSlice(startIndex, sendAmt) { + console.log("f:senGCodeSlice >> startIndex:" + startIndex + ", sendAmt:" + sendAmt); + + if (typeof startIndex == "number" && typeof sendAmt == "number") { + var lastOne = false; + if (data.length < (startIndex + sendAmt)) { + console.log("f:senGCodeSlice >> not enough data left for full slice, sending smaller (and last) one"); + sendAmt = data.length - startIndex; + lastOne = true; + } + var _tmp = data.slice(startIndex, startIndex+sendAmt); + // console.log("f:senGCodeSlice >> _tmp.length:" + _tmp.length); + + $.post("/doodle3d.of", { data:output }, function(data) { + btnPrint.disabled = false; + }); + sendBoy( { data: _tmp, lastOne: lastOne} , function(e) { + console.log("sendBoy callback: " + e); + // console.log(e); + // console.log("e.success: " + e.success); + if (e.success = true) { + if (lastOne) { + console.log("f:sendGCodeSlice >> DONE!"); + } else { + sendGCodeSlice(startIndex + sendAmt, sendAmt); + } + } + }) + } else { + console.log(" something wrong"); + } +} + +function sendBoy(sendObj, callback) { + console.log("f:sendBoy() (dummy kastje) >> data length: " + sendObj.data.length + ", lastOne: " + sendObj.lastOne); + console.log(""); + if (callback != undefined) callback({success:true}); +} \ No newline at end of file diff --git a/js/draw_logic.js b/js/draw_logic.js index 91767a2..ebabbe0 100644 --- a/js/draw_logic.js +++ b/js/draw_logic.js @@ -123,6 +123,8 @@ function print(e) { output = "\nBEGIN\n" + output + "\n\nEND\n"; + console.log("output :" + output ); + $.post("/doodle3d.of", { data:output }, function(data) { btnPrint.disabled = false; }); diff --git a/js/gcodeGenerating_v01.js b/js/gcodeGenerating_v01.js new file mode 100644 index 0000000..05d8cd0 --- /dev/null +++ b/js/gcodeGenerating_v01.js @@ -0,0 +1,214 @@ +var gcodeStart = []; +gcodeStart.push("G21 (mm)"); +gcodeStart.push("G91 (relative)"); +gcodeStart.push("G28 X0 Y0 Z0 (physical home)"); +gcodeStart.push("M104 S230 (temperature)"); +gcodeStart.push("G1 E10 F250 (flow)"); +gcodeStart.push("G92 X-100 Y-100 Z0 E10"); +gcodeStart.push("G1 Z3 F5000 (prevent diagonal line)"); +gcodeStart.push("G90 (absolute)"); +gcodeStart.push("M106 (fan on)"); + +var gcodeEnd= []; +gcodeEnd.push("G1 X-100 Y-100 F15000 (fast homing)"); +gcodeEnd.push("M107"); +gcodeEnd.push("M84 (disable axes)"); + +var gcode = []; +function generate_gcode(callback) { + console.log("f:generategcode()"); + + gcode = []; + + objectHeight = Math.ceil(numLayers / 5); // in settings objectHeight = 20, in previewRendering_v01.js numLayers is 100, hence the / 5 + objectHeight = numLayers; // in settings objectHeight = 20, in previewRendering_v01.js numLayers is 100, hence the / 5 + + // todo hier een array van PATHS maken wat de losse paths zijn + + // copy array without reference -> http://stackoverflow.com/questions/9885821/copying-of-an-array-of-objects-to-another-array-without-object-reference-in-java + var points = JSON.parse(JSON.stringify(_points)); + console.log("f:generategcode() >> points.length: " + points.length); + +// console.log("f:generategcode() >> paths: " + paths.toString()); +// console.log("paths.toString(): " + paths.toString()); +// return; + + + // add gcode begin commands + gcode = gcode.concat(gcodeStart); + + var layers = maxObjectHeight / layerHeight; //maxObjectHeight instead of objectHeight + var extruder = 0.0; + var prev = new Point(); prev.set(0, 0); + + // vervanger voor ofxGetCenterofMass + var centerOfDoodle = { + x: doodleBounds[0] + (doodleBounds[2]- doodleBounds[0])/2, + y: doodleBounds[1] + (doodleBounds[3] - doodleBounds[1])/2 +// x: doodleBounds[0], +// y: doodleBounds[1] + } + + console.log("f:generategcode() >> layers: " + layers); + for (var layer = 0; layer < layers; layer++) { + + var p = JSON.parse(JSON.stringify(points)); // [].concat(points); + + if (p.length < 2) return; + var even = (layer % 2 == 0); + var progress = layer / layers; + + // float layerScale = scaleFunction(float(layer)/layers); // scaleFactor van de layer -> lookup naar vfunc[] voor die scaleVals + var layerScale = 1.0; + + // if begin point this row and end point last row are close enough, isLoop is true + var isLoop = lineLength(points[0][0], points[0][1], points[points.length-1][0], points[points.length-1][1]) < 3; + + // set center of doodle as middle (ie subtract to that) + pointsTranslate(p, -centerOfDoodle.x, -centerOfDoodle.y); + pointsScale(p, screenToMillimeterScale,-screenToMillimeterScale); + pointsScale(p, layerScale, layerScale); + + // sort-of in de buurt van (360/2.5) + // // -> aight.. er zijn 750 lines vs 1000 in de d3d app. 135 = .75 * 180... dit kan je nog rechttrekken als je NET wat slimmer nadenkt :) + // update: NEE, het is niet .75 * 180 want 135 was niet de beste value. //TODO dus. + pointsRotate(p, rStep * progress * 139); + + if (layer == 0) { + gcode.push("M107"); //fan off + if (firstLayerSlow) gcode.push("M220 S40"); //slow speed + } else if (layer == 2) { ////////LET OP + gcode.push("M106"); //fan on + gcode.push("M220 S100"); //normal speed + } + + var curLayerCommand = 0; + var totalLayerCommands = p.length; + + var paths = []; + var pathCounter = -1; + // var points = []; + + for (var i = 0; i < p.length; i++) { + if (p[i][2] == true) { + pathCounter++; + paths.push([]); + paths[pathCounter].push([p[i][0], p[i][1]]); + } else { + paths[pathCounter].push([p[i][0], p[i][1]]); + } + } +// console.log("f:generategcode() >> paths.length: " + paths.length); + + // loop over the subpaths (the separately drawn lines) + for (var j = 0; j < paths.length; j++) { + // this line is probably for drawing efficiency, alternating going from 0->end and end->0 (i.e. to and fro) +// vector &commands = subpaths[even ? j : subpaths.size()-1-j].getCommands(); + var commands = paths[j]; + + // loop over the coordinates of the subpath + for (var i = 0; i < commands.length; i++) { + var last = commands.length - 1; + + // this line is probably for drawing efficiency, alternating going from 0->end and end->0 (i.e. to and fro) +// ofPoint to = commands[(even || isLoop || loopAlways) ? i : last-i].to; + var to = new Point(); to.set(commands[i][0], commands[i][1]); + var sublayer = (layer == 0) ? 0.0 : layer + (useSubLayers ? (curLayerCommand/totalLayerCommands) : 0); + var z = (sublayer + 1) * layerHeight + zOffset; + + var isTraveling = !isLoop && i==0; + var doRetract = prev.distance(to) > retractionminDistance; + + if (enableTraveling && isTraveling) { +// console.log("enableTraveling && isTraveling >> doRetract: " + doRetract + ", retractionspeed: " + retractionspeed); + if (doRetract) gcode.push("G1 E" + (extruder - retractionamount).toFixed(3) + " F" + (retractionspeed * 60).toFixed(3)); + gcode.push("G1 X" + to.x.toFixed(3) + " Y" + to.y.toFixed(3) + " Z" + (z + (doRetract ? hop : 0)).toFixed(3) + " F" + (travelSpeed * 60).toFixed(3)); + if (doRetract) gcode.push("G1 E" + extruder.toFixed(3) + " F" + (retractionspeed * 60).toFixed(3)); + } else { +// console.log(" else"); + extruder += prev.distance(to) * wallThickness * layerHeight / filamentThickness; + gcode.push("G1 X" + to.x.toFixed(3) + " Y" + to.y.toFixed(3) + " Z" + z.toFixed(3) + " F" + (speed * 60).toFixed(3) + " E" + extruder.toFixed(3)); + } + + curLayerCommand++; + prev = to; + + } + + } + + if ((layer/layers) > (objectHeight/maxObjectHeight)) { + console.log("f:generategcode() >> (layer/layers) > (objectHeight/maxObjectHeight) is true -> breaking"); + break; + } + + + } + + // add gcode end commands + gcode = gcode.concat(gcodeEnd); + + // debug +// var _gc = gc.join("\n"); +// console.log("f:generategcode() >> _gc = " + _gc); + + // Return the gcode array, joined to one string with '\n' (line break) as the join parameter + // This should result in a nice long string with line breaks + if (callback == undefined) { + return gcode.join("\n"); + } else { + // post + } +} + +pointsTranslate = function(p, x, y) { + for (var i = 0; i < p.length; i++) { + p[i][0] += x; + p[i][1] += y; + } +} + +pointsScale = function(p, sx, sy) { + for (var i = 0; i < p.length; i++) { + p[i][0] *= sx; + p[i][1] *= sy; + } +} + +// rotates around point 0,0 (origin). +// Not the prettiest kind of rotation solution but in our case we're assuming that the points have just been translated to origin +pointsRotate = function(p, ang) { + var _ang, dist; + for (var i = 0; i < p.length; i++) { + dist = Math.sqrt(p[i][0] * p[i][0] + p[i][1] * p[i][1]); + _ang = Math.atan2(p[i][1], p[i][0]); + p[i][0] = Math.cos(_ang + ang) * dist; + p[i][1] = Math.sin(_ang + ang) * dist; + } +} + +//+ Jonas Raoni Soares Silva +//@ http://jsfromhell.com/math/line-length [rev. #1] +lineLength = function(x, y, x0, y0){ + return Math.sqrt((x -= x0) * x + (y -= y0) * y); +}; + +var Point = function() {}; +Point.prototype = { + x: 0, + y: 0, + set: function(_x, _y) { + this.x = _x; + this.y = _y; + }, + distance: function(p) { + var d = -1; + if (p instanceof Point) { + d = Math.sqrt((p.x - this.x) * (p.x - this.x) + (p.y - this.y) * (p.y - this.y)); + } + return d; + }, + toString: function() { + console.log("x:" + this.x + ", y:" + this.y); + } +} \ No newline at end of file diff --git a/js/libs/bootstrap.js b/js/libs/bootstrap.js new file mode 100644 index 0000000..643e71c --- /dev/null +++ b/js/libs/bootstrap.js @@ -0,0 +1,2280 @@ +/* =================================================== + * bootstrap-transition.js v2.3.2 + * http://twitter.github.com/bootstrap/javascript.html#transitions + * =================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* CSS TRANSITION SUPPORT (http://www.modernizr.com/) + * ======================================================= */ + + $(function () { + + $.support.transition = (function () { + + var transitionEnd = (function () { + + var el = document.createElement('bootstrap') + , transEndEventNames = { + 'WebkitTransition' : 'webkitTransitionEnd' + , 'MozTransition' : 'transitionend' + , 'OTransition' : 'oTransitionEnd otransitionend' + , 'transition' : 'transitionend' + } + , name + + for (name in transEndEventNames){ + if (el.style[name] !== undefined) { + return transEndEventNames[name] + } + } + + }()) + + return transitionEnd && { + end: transitionEnd + } + + })() + + }) + +}(window.jQuery);/* ========================================================== + * bootstrap-alert.js v2.3.2 + * http://twitter.github.com/bootstrap/javascript.html#alerts + * ========================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* ALERT CLASS DEFINITION + * ====================== */ + + var dismiss = '[data-dismiss="alert"]' + , Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.prototype.close = function (e) { + var $this = $(this) + , selector = $this.attr('data-target') + , $parent + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 + } + + $parent = $(selector) + + e && e.preventDefault() + + $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) + + $parent.trigger(e = $.Event('close')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + $parent + .trigger('closed') + .remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent.on($.support.transition.end, removeElement) : + removeElement() + } + + + /* ALERT PLUGIN DEFINITION + * ======================= */ + + var old = $.fn.alert + + $.fn.alert = function (option) { + return this.each(function () { + var $this = $(this) + , data = $this.data('alert') + if (!data) $this.data('alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + $.fn.alert.Constructor = Alert + + + /* ALERT NO CONFLICT + * ================= */ + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + /* ALERT DATA-API + * ============== */ + + $(document).on('click.alert.data-api', dismiss, Alert.prototype.close) + +}(window.jQuery);/* ============================================================ + * bootstrap-button.js v2.3.2 + * http://twitter.github.com/bootstrap/javascript.html#buttons + * ============================================================ + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* BUTTON PUBLIC CLASS DEFINITION + * ============================== */ + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, $.fn.button.defaults, options) + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + , $el = this.$element + , data = $el.data() + , val = $el.is('input') ? 'val' : 'html' + + state = state + 'Text' + data.resetText || $el.data('resetText', $el[val]()) + + $el[val](data[state] || this.options[state]) + + // push to event loop to allow forms to submit + setTimeout(function () { + state == 'loadingText' ? + $el.addClass(d).attr(d, d) : + $el.removeClass(d).removeAttr(d) + }, 0) + } + + Button.prototype.toggle = function () { + var $parent = this.$element.closest('[data-toggle="buttons-radio"]') + + $parent && $parent + .find('.active') + .removeClass('active') + + this.$element.toggleClass('active') + } + + + /* BUTTON PLUGIN DEFINITION + * ======================== */ + + var old = $.fn.button + + $.fn.button = function (option) { + return this.each(function () { + var $this = $(this) + , data = $this.data('button') + , options = typeof option == 'object' && option + if (!data) $this.data('button', (data = new Button(this, options))) + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + $.fn.button.defaults = { + loadingText: 'loading...' + } + + $.fn.button.Constructor = Button + + + /* BUTTON NO CONFLICT + * ================== */ + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + /* BUTTON DATA-API + * =============== */ + + $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + $btn.button('toggle') + }) + +}(window.jQuery);/* ========================================================== + * bootstrap-carousel.js v2.3.2 + * http://twitter.github.com/bootstrap/javascript.html#carousel + * ========================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* CAROUSEL CLASS DEFINITION + * ========================= */ + + var Carousel = function (element, options) { + this.$element = $(element) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.options.pause == 'hover' && this.$element + .on('mouseenter', $.proxy(this.pause, this)) + .on('mouseleave', $.proxy(this.cycle, this)) + } + + Carousel.prototype = { + + cycle: function (e) { + if (!e) this.paused = false + if (this.interval) clearInterval(this.interval); + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + return this + } + + , getActiveIndex: function () { + this.$active = this.$element.find('.item.active') + this.$items = this.$active.parent().children() + return this.$items.index(this.$active) + } + + , to: function (pos) { + var activeIndex = this.getActiveIndex() + , that = this + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) { + return this.$element.one('slid', function () { + that.to(pos) + }) + } + + if (activeIndex == pos) { + return this.pause().cycle() + } + + return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) + } + + , pause: function (e) { + if (!e) this.paused = true + if (this.$element.find('.next, .prev').length && $.support.transition.end) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + clearInterval(this.interval) + this.interval = null + return this + } + + , next: function () { + if (this.sliding) return + return this.slide('next') + } + + , prev: function () { + if (this.sliding) return + return this.slide('prev') + } + + , slide: function (type, next) { + var $active = this.$element.find('.item.active') + , $next = next || $active[type]() + , isCycling = this.interval + , direction = type == 'next' ? 'left' : 'right' + , fallback = type == 'next' ? 'first' : 'last' + , that = this + , e + + this.sliding = true + + isCycling && this.pause() + + $next = $next.length ? $next : this.$element.find('.item')[fallback]() + + e = $.Event('slide', { + relatedTarget: $next[0] + , direction: direction + }) + + if ($next.hasClass('active')) return + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + this.$element.one('slid', function () { + var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]) + $nextIndicator && $nextIndicator.addClass('active') + }) + } + + if ($.support.transition && this.$element.hasClass('slide')) { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + this.$element.one($.support.transition.end, function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { that.$element.trigger('slid') }, 0) + }) + } else { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger('slid') + } + + isCycling && this.cycle() + + return this + } + + } + + + /* CAROUSEL PLUGIN DEFINITION + * ========================== */ + + var old = $.fn.carousel + + $.fn.carousel = function (option) { + return this.each(function () { + var $this = $(this) + , data = $this.data('carousel') + , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option) + , action = typeof option == 'string' ? option : options.slide + if (!data) $this.data('carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + $.fn.carousel.defaults = { + interval: 5000 + , pause: 'hover' + } + + $.fn.carousel.Constructor = Carousel + + + /* CAROUSEL NO CONFLICT + * ==================== */ + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + /* CAROUSEL DATA-API + * ================= */ + + $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { + var $this = $(this), href + , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 + , options = $.extend({}, $target.data(), $this.data()) + , slideIndex + + $target.carousel(options) + + if (slideIndex = $this.attr('data-slide-to')) { + $target.data('carousel').pause().to(slideIndex).cycle() + } + + e.preventDefault() + }) + +}(window.jQuery);/* ============================================================= + * bootstrap-collapse.js v2.3.2 + * http://twitter.github.com/bootstrap/javascript.html#collapse + * ============================================================= + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* COLLAPSE PUBLIC CLASS DEFINITION + * ================================ */ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, $.fn.collapse.defaults, options) + + if (this.options.parent) { + this.$parent = $(this.options.parent) + } + + this.options.toggle && this.toggle() + } + + Collapse.prototype = { + + constructor: Collapse + + , dimension: function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + , show: function () { + var dimension + , scroll + , actives + , hasData + + if (this.transitioning || this.$element.hasClass('in')) return + + dimension = this.dimension() + scroll = $.camelCase(['scroll', dimension].join('-')) + actives = this.$parent && this.$parent.find('> .accordion-group > .in') + + if (actives && actives.length) { + hasData = actives.data('collapse') + if (hasData && hasData.transitioning) return + actives.collapse('hide') + hasData || actives.data('collapse', null) + } + + this.$element[dimension](0) + this.transition('addClass', $.Event('show'), 'shown') + $.support.transition && this.$element[dimension](this.$element[0][scroll]) + } + + , hide: function () { + var dimension + if (this.transitioning || !this.$element.hasClass('in')) return + dimension = this.dimension() + this.reset(this.$element[dimension]()) + this.transition('removeClass', $.Event('hide'), 'hidden') + this.$element[dimension](0) + } + + , reset: function (size) { + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + [dimension](size || 'auto') + [0].offsetWidth + + this.$element[size !== null ? 'addClass' : 'removeClass']('collapse') + + return this + } + + , transition: function (method, startEvent, completeEvent) { + var that = this + , complete = function () { + if (startEvent.type == 'show') that.reset() + that.transitioning = 0 + that.$element.trigger(completeEvent) + } + + this.$element.trigger(startEvent) + + if (startEvent.isDefaultPrevented()) return + + this.transitioning = 1 + + this.$element[method]('in') + + $.support.transition && this.$element.hasClass('collapse') ? + this.$element.one($.support.transition.end, complete) : + complete() + } + + , toggle: function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + } + + + /* COLLAPSE PLUGIN DEFINITION + * ========================== */ + + var old = $.fn.collapse + + $.fn.collapse = function (option) { + return this.each(function () { + var $this = $(this) + , data = $this.data('collapse') + , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option) + if (!data) $this.data('collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.collapse.defaults = { + toggle: true + } + + $.fn.collapse.Constructor = Collapse + + + /* COLLAPSE NO CONFLICT + * ==================== */ + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + /* COLLAPSE DATA-API + * ================= */ + + $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) { + var $this = $(this), href + , target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 + , option = $(target).data('collapse') ? 'toggle' : $this.data() + $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed') + $(target).collapse(option) + }) + +}(window.jQuery);/* ============================================================ + * bootstrap-dropdown.js v2.3.2 + * http://twitter.github.com/bootstrap/javascript.html#dropdowns + * ============================================================ + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* DROPDOWN CLASS DEFINITION + * ========================= */ + + var toggle = '[data-toggle=dropdown]' + , Dropdown = function (element) { + var $el = $(element).on('click.dropdown.data-api', this.toggle) + $('html').on('click.dropdown.data-api', function () { + $el.parent().removeClass('open') + }) + } + + Dropdown.prototype = { + + constructor: Dropdown + + , toggle: function (e) { + var $this = $(this) + , $parent + , isActive + + if ($this.is('.disabled, :disabled')) return + + $parent = getParent($this) + + isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement) { + // if mobile we we use a backdrop because click events don't delegate + $('