From 0e82fa1d9cbb3faf4a86aae987f35a202972eb01 Mon Sep 17 00:00:00 2001 From: Garrett Bartley Date: Thu, 18 Feb 2016 14:25:25 -0500 Subject: [PATCH 01/22] Updated serialport package requirement to 2.0.x --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4106bd4..db658ea 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description" : "web based gcode sender for grbl", "dependencies" : { "node-static" : "0.7.x" - , "serialport" : "1.4.x" + , "serialport" : "2.0.x" , "socket.io" : "1.0.x" } } From 7c3dd7328e5f4ad2fc4240cb6f9d0e65fc1f4ad9 Mon Sep 17 00:00:00 2001 From: Garrett Bartley Date: Thu, 18 Feb 2016 17:10:25 -0500 Subject: [PATCH 02/22] Updated config --- config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.js b/config.js index 05922db..9f46b04 100644 --- a/config.js +++ b/config.js @@ -3,11 +3,11 @@ var config = {}; config.webPort = 8000; -config.serialBaudRate = 9600; +config.serialBaudRate = 115200; -config.usettyAMA0 = 0; +config.usettyAMA0 = 1; // expects a webcam stream from mjpg_streamer -config.webcamPort = 8080; +//config.webcamPort = 8080; module.exports = config; From 478f5e2be1c5a0eb3c1264ba92f966482fccd898 Mon Sep 17 00:00:00 2001 From: Garrett Bartley Date: Thu, 18 Feb 2016 17:11:32 -0500 Subject: [PATCH 03/22] Changed jogging system to X/Y/Z up/down buttons. Added in/mm G20/G21 buttons. Changed nav button colors. Remove JSCut button. Added Unlock button. Added a queue status percent --- i/index.html | 71 +++++++++++++++++++++---- i/main.js | 144 +++++++++++---------------------------------------- server.js | 1 - 3 files changed, 92 insertions(+), 124 deletions(-) diff --git a/i/index.html b/i/index.html index 5f0aa64..f8228b4 100644 --- a/i/index.html +++ b/i/index.html @@ -107,6 +107,15 @@ padding: 10px; } +#wgbControls thead tr th { + font-size: 1.5em; + border-bottom: 1px solid gray; +} + +#wgbControls thead tr th:nth-child(2) { + border-bottom: none; + width: 10px; +} @@ -126,10 +135,10 @@ +
0%
+
diff --git a/i/main.js b/i/main.js index 1788500..eb3cef2 100644 --- a/i/main.js +++ b/i/main.js @@ -27,116 +27,6 @@ $(document).ready(function() { - // init vars for better controls - var isMouseDown = false; - var tsLast = Date.now(); - - // get dimensions for better controls - var betterWidth = $('#betterControls').width(); - var betterHeight = $('#betterControls').height(); - var bPointWidth = $('#betterControlsPoint').width(); - var bPointHeight = $('#betterControlsPoint').height(); - - // track the control mouse position externally - var betterX = 0; - var betterY = 0; - - // get the scale factor to reduce x and y to a resolution of -1 to 1 - var xSf = 2/betterWidth; - var ySf = 2/betterHeight; - - // center the control point - $('#betterControlsPoint').css('top', (betterHeight/2)-(bPointHeight/2) + 'px'); - $('#betterControlsPoint').css('left', (betterWidth/2)-(bPointWidth/2) + 'px'); - - // on mousedown, set isMouseDown to true - $('#betterControls').mousedown(function(event) { - event.preventDefault(); - isMouseDown = true; - }); - document.getElementById('betterControls').addEventListener('touchstart', function(event) { - event.preventDefault(); - isMouseDown = true; - }, false); - - // on mouseup reset center point - $('#betterControls').mouseup(function(event) { - event.preventDefault(); - isMouseDown = false; - $('#betterControlsPoint').css('top', (betterHeight/2)-(bPointHeight/2) + 'px'); - $('#betterControlsPoint').css('left', (betterWidth/2)-(bPointWidth/2) + 'px'); - }); - document.getElementById('betterControls').addEventListener('touchend', function(event) { - event.preventDefault(); - isMouseDown = false; - $('#betterControlsPoint').css('top', (betterHeight/2)-(bPointHeight/2) + 'px'); - $('#betterControlsPoint').css('left', (betterWidth/2)-(bPointWidth/2) + 'px'); - }); - - // loop for bettercontrol - setInterval(function() { - if (isMouseDown) { - - var gcX = betterX-(betterWidth/2); - var gcY = betterY-(betterHeight/2); - - // add the scale factors - gcX = xSf*gcX; - gcY = ySf*gcY; - - // invert y axis because JS and CNC are opposite there - if (gcY < 0) { - gcY = Math.abs(gcY); - } else if (gcY > 0) { - gcY = -gcY; - } - - // first get speed, calculated from the mean of abs(x) and abs(y) - //var fSpeed = (Math.abs(gcX)+Math.abs(gcY))/2; - - // first get speed, calculated from the highest abs of x and y - if (Math.abs(gcX) > Math.abs(gcY)) { - var fSpeed = Math.abs(gcX)*$('#jogSpeed').val(); - } else { - var fSpeed = Math.abs(gcY)*$('#jogSpeed').val(); - } - - fSpeed = Math.round(fSpeed*100)/100; - - // set final position movements based on #jogSize - gcX = Math.round(gcX*$('#jogSize').val()*1000)/1000; - gcY = Math.round(gcY*$('#jogSize').val()*1000)/1000; - - // gcode to send - socket.emit('gcodeLine', { line: 'G91\nG0 F'+fSpeed+' X'+gcX+' Y'+gcY+'\nG90\n'}); - } - }, 200); - - // on mousemove send gcode - $('#betterControls').mousemove(function(event) { - if (isMouseDown) { - betterX = event.pageX-this.offsetLeft; - betterY = event.pageY-this.offsetTop; - - // move point - $('#betterControlsPoint').css('top',betterY-(bPointHeight/2) + 'px'); - $('#betterControlsPoint').css('left',betterX-(bPointWidth/2) + 'px'); - - } - }); - document.getElementById('betterControls').addEventListener('touchmove', function(event) { - event.preventDefault(); - if (isMouseDown) { - betterX = event.pageX-this.offsetLeft; - betterY = event.pageY-this.offsetTop; - - // move point - $('#betterControlsPoint').css('top',betterY-(bPointHeight/2) + 'px'); - $('#betterControlsPoint').css('left',betterX-(bPointWidth/2) + 'px'); - - } - }); - $( window ).resize(function() { // when header resizes, move ui down $('.table-layout').css('margin-top',$('.navbar-collapse').height()-34); @@ -174,16 +64,30 @@ $(document).ready(function() { //console.log('ports event',data); $('#choosePort').html(''); for (var i=0; i'+data[i].comName+':'+data[i].pnpId+''); + var selected = ''; + + if(data[i].pnpId!=undefined && data[i].pnpId.toUpperCase().indexOf("ARDUINO_UNO")>=0) + selected = 'selected="selected"'; + + $('#choosePort').append(''); } if (data.length == 1) { $('#choosePort').val('0'); $('#choosePort').change(); } + + $('#choosePort').change(); }); socket.on('qStatus', function (data) { $('#qStatus').html(data.currentLength+'/'+data.currentMax); + $('.qStatus').html(data.currentLength+'/'+data.currentMax); + + var pct = Math.round((data.currentMax-data.currentLength)/data.currentMax*100); + if(isNaN(pct)) + pct = '100'; + + $('.qStatusPct').html(pct+'%'); }); socket.on('machineStatus', function (data) { @@ -262,11 +166,25 @@ $(document).ready(function() { socket.emit('gcodeLine', { line: 'G92 X0 Y0 Z0' }); }); - $('#sendCommand').on('click', function() { + $('#setInches').on('click', function() { + socket.emit('gcodeLine', { line: 'G20' }); + }); + + $('#setMillimeters').on('click', function() { + socket.emit('gcodeLine', { line: 'G21' }); + }); + + $('#sendHome').on('click', function() { + socket.emit('gcodeLine', { line: '$H' }); + }); + $('#sendUnlock').on('click', function() { + socket.emit('gcodeLine', { line: '$X' }); + }); + + $('#sendCommand').on('click', function() { socket.emit('gcodeLine', { line: $('#command').val() }); $('#command').val(''); - }); // shift enter for send command diff --git a/server.js b/server.js index c7aca3f..638c0c9 100644 --- a/server.js +++ b/server.js @@ -271,7 +271,6 @@ io.sockets.on('connection', function (socket) { // lines from web ui socket.on('gcodeLine', function (data) { - if (typeof currentSocketPort[socket.id] != 'undefined') { // valid serial port selected, safe to send From 249e651d22ddd3642776f4b3706bce9ecda57e8b Mon Sep 17 00:00:00 2001 From: Garrett Bartley Date: Sun, 28 Feb 2016 20:10:30 -0500 Subject: [PATCH 04/22] Loads of updates: Auto-reload config.js, change between in and mm, probe controls, jog controls, progress bar, temperature/sensors, better tracking of in vs mm --- config.js | 39 +++ i/index.html | 658 +++++++++++++++++++++++++++++++++++---------------- i/main.js | 314 +++++++++++++++++++----- package.json | 13 +- server.js | 209 +++++++++++----- 5 files changed, 906 insertions(+), 327 deletions(-) diff --git a/config.js b/config.js index 9f46b04..61547e4 100644 --- a/config.js +++ b/config.js @@ -1,12 +1,51 @@ var config = {}; +// Port to listen on for web site config.webPort = 8000; +// Serial baud rate to Arduino config.serialBaudRate = 115200; +// Use /dev/ttyAMA0 config.usettyAMA0 = 1; +// Enable simple jog controls +config.enableSimpleControls = 1; + +// Enable probe controls +config.enableProbeControls = 1; + +// Enable jsCut +config.enableJsCut = 0; + +// Default step increment for jogging +config.jogControlDefaultIncr = 0.1; + +// Default feed rate for jogging +config.jogControlDefaultFeed = 1000; + +// X offset for probe +config.probeControlXOffset = -0.5; + +// Y offset for probe +config.probeControlYOffset = -1.1; + +// Z offset for probe +config.probeControlZOffset = 3.1; + +// Auto-read temperature +config.enablePiTemperature = 1; +config.piTemperatureFahrenheit = 1; +config.piTemperatureFile = [ + '/sys/class/thermal/thermal_zone0/temp', + '/sys/bus/w1/devices/28-000002aa87dd/w1_slave' +]; + +// TO DO: +// - Add probe diameters +// - Allow configuration of gcode for probing buttons + // expects a webcam stream from mjpg_streamer //config.webcamPort = 8080; diff --git a/i/index.html b/i/index.html index f8228b4..dc31394 100644 --- a/i/index.html +++ b/i/index.html @@ -1,3 +1,9 @@ + + - - - - - - + + + + + + + - +#simpleControls, +#probeControls { + min-width: 230px; + width: 100%; + height: 230px; + border: 1px solid orange; + padding: 0; + background-color: #fff; +} - +#probeControls { + height: 20px; + max-height: 230px; +} - +#probeControls td { + padding: 2px; +} -
0%
+#probeControls input[type="number"] { + width: 4em; +} - -
-
-
- - - -
Select Port
- - - -
- - +#mX, #mY, #mZ, +#wX, #wY, #wZ { + text-align: left; + padding-left: 15px; + margin: 4px; + font-size: 30px; + width: 100%; + border: 1px solid #aaa; + font-weight: bold; +} -
-
-
X: 0.000
-
Y: 0.000
-
Z: 0.000
-
-
+#probeHomeZero, +#probeX, +#probeY, +#probeZ, +#probeAll { + width: 100%; +} -
- - - -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
X/YZ
- Incr: - Feed: -
+#sendReset, +#sendUnlock, +#sendGrblHelp, +#sendGrblSettings, +#jsCutButton { + margin: 10px; +} +#choosePort { + width: 80%; + float: left; +} - +#sendZero, +#setInches, +#setMillimeters { + margin-top: 5px; +} -
+#queueProgress { + min-width: 2em; + color: #000; +} -
+#GRBLWebHeader { + margin: 0; + display: inline; +} -

-© XYZBots 2015 -

- -
- -
+#betterControls { + width: 230px; + height: 230px; + border: 1px solid orange; + padding; none; + background-color: #fff; +} -
-

-
- +#betterControlsPoint { + position: relative; + top: 100px; + left: 100px; + background-color: red; + width: 20px; + height: 20px; + padding: none; +} -
- -
+#XYZURL { + color: #aaa; + font-size: 0.8em; + margin-top: 20px; +} -
-Drag a .gcode or .nc file to the command box or click Upload GCODE to upload it. -
+#mTemp { + float: left; +} + + -
- - - - - -
- -
-Upload GCODE - -
+ -
+ + +
+ + +
+ +
+ +
+ +
+ + + +
+ + +
+
+
Select Port
+
+ + + + + +
+ + +
+
+
X: 0.000
+
Y: 0.000
+
Z: 0.000
+
+
+
+ + +
+
+ + + +
+
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+ + +
+ + +
+ +
+
+
+
+
+ + +
+
+
+
0%
+
+
+
+ + +
+
+ +
+
+
+ + + +
+ +
+
+
+

+			
+
+ + +
+
+
+ +
+
+ + +
+
+ Drag a .gcode or .nc file to the command box or click Upload GCODE to upload it. +
+
+ + +
+
+ + + + + +
+
+ + +
+
+ Upload GCODE + +
+
+
+
diff --git a/i/main.js b/i/main.js index eb3cef2..ae74336 100644 --- a/i/main.js +++ b/i/main.js @@ -1,3 +1,8 @@ +/* +TODO: Auto-reconnect web sockets +TODO: "Click" the "Jog Control" tab when the config reloads (just in case) +*/ + /* GRBLWeb - a web based CNC controller for GRBL @@ -25,8 +30,11 @@ */ -$(document).ready(function() { +var lastUnitsOfMeasurement = ''; +var unitsBeforeProbe = ''; +var config = {}; +$(document).ready(function() { $( window ).resize(function() { // when header resizes, move ui down $('.table-layout').css('margin-top',$('.navbar-collapse').height()-34); @@ -46,22 +54,57 @@ $(document).ready(function() { // config from server socket.on('config', function (data) { + console.log('config', data); + config = data; + if (data.showWebCam == true) { // show the webcam and link - var webroot = window.location.protocol+'//'+window.location.hostname; - //console.log(webroot); $('#wcImg').attr('src', webroot+':'+data.webcamPort+'/?action=stream'); - $('#wcLink').attr('href', webroot+':'+data.webcamPort+'/javascript_simple.html'); - $('#webcam').css('display','inline-block'); } + + // Hide the jsCut button if jsCut is "disabled" + if(data.enableJsCut==0) { + $('#jsCutButton').hide(); + } + + // Show controls tabs to "enable" probe controls + if(data.enableProbeControls==1) { + $('#controlTabs').show(); + } + + // Hide "better controls" and show simple controls + if(data.enableSimpleControls==1) { + $('#betterControlsWrapper').hide(); + $('#simpleControls').show(); + $('#probeControls').hide(); + } + + // Fill in jog default step increment + if(data.jogControlDefaultIncr!=undefined) + $('#jogSize').val(data.jogControlDefaultIncr).attr('placeholder', data.jogControlDefaultIncr); + + // Fill in jog default feed rate + if(data.jogControlDefaultFeed!=undefined) + $('#jogSpeed').val(data.jogControlDefaultFeed).attr('placeholder', data.jogControlDefaultFeed); + + // Fill in default X offset for probe + if(data.probeControlXOffset!=undefined) + $('#probeOffsetX').val(data.probeControlXOffset).attr('placeholder', data.probeControlXOffset); + + // Fill in default Y offset for probe + if(data.probeControlYOffset!=undefined) + $('#probeOffsetY').val(data.probeControlYOffset).attr('placeholder', data.probeControlYOffset); + + // Fill in default Z offset for probe + if(data.probeControlZOffset!=undefined) + $('#probeOffsetZ').val(data.probeControlZOffset).attr('placeholder', data.probeControlZOffset); }); socket.on('ports', function (data) { - //console.log('ports event',data); $('#choosePort').html(''); for (var i=0; i0) + $('#queueProgress').addClass('active'); + else + $('#queueProgress').removeClass('active'); }); socket.on('machineStatus', function (data) { + if(data.status.toUpperCase()=='ALARM') { + data.status = ''+data.status+''; + } + + // $('#console').append('

'+JSON.stringify(data)+'

'); + // data.status = ''+data+''; + $('#mStatus').html(data.status); + + // Convert machine coordinates from mm to inches + if(data.unitsOfMeasurement=='in') { + for(var i in data) { + if(i.indexOf("pos")>=0) { + for(var j in data[i]) { + data[i][j] = (Math.round(parseFloat(data[i][j]/25.4) * 10000)/10000);; + } + } + } + } + $('#mX').html('X: '+data.mpos[0]); $('#mY').html('Y: '+data.mpos[1]); $('#mZ').html('Z: '+data.mpos[2]); $('#wX').html('X: '+data.wpos[0]); $('#wY').html('Y: '+data.wpos[1]); $('#wZ').html('Z: '+data.wpos[2]); - //console.log(data); + + // Only attempt to change things if the unit of measurement has changed + if(data.unitsOfMeasurement!=lastUnitsOfMeasurement) { + lastUnitsOfMeasurement = data.unitsOfMeasurement; + + $('.unitsOfMeasurementText').text(lastUnitsOfMeasurement); + + if(data.unitsOfMeasurement.toUpperCase()=='IN') { + $('#setInches').addClass('btn-primary'); + $('#setMillimeters').removeClass('btn-primary'); + } else if(data.unitsOfMeasurement.toUpperCase()=='MM') { + $('#setInches').removeClass('btn-primary'); + $('#setMillimeters').addClass('btn-primary'); + } + } }); + socket.on('serialRead', function (data) { if ($('#console p').length > 300) { // remove oldest if already at 300 lines @@ -110,6 +194,28 @@ $(document).ready(function() { $('#console').scrollTop($("#console")[0].scrollHeight - $("#console").height()); }); + + socket.on('sensors', function(data) { + if(config.enablePiTemperature==1) { + var tempHtml = ''; + + for(var i in data) { + if(config.piTemperatureFahrenheit==1) { + tempHtml += Math.round(parseFloat(data[i]) * 9/5 + 32)+'°F, '; + } else { + tempHtml += (Math.round(parseFloat(data[i]) * 100)/100) +'°C, '; + } + } + + // Cut off the last ", " + tempHtml = tempHtml.trim(); + tempHtml = tempHtml.substr(0, tempHtml.length-7); + + $('#mTemp').html(tempHtml); + } + }) + + $('#choosePort').on('change', function() { // select port socket.emit('usePort', $('#choosePort').val()); @@ -162,6 +268,102 @@ $(document).ready(function() { $('#mPosition').hide(); }); + $('#probeTabLink').on('click', function() { + $('#probeTab').addClass('active'); + $('#controlTab').removeClass('active'); + $('#probeControls').show(); + $('#simpleControls').hide(); + }); + + $('#controlTabLink').on('click', function() { + $('#controlTab').addClass('active'); + $('#probeTab').removeClass('active'); + $('#simpleControls').show(); + $('#probeControls').hide(); + }); + + $('#probeHomeZero').on('click', function() { + socket.emit('gcodeLine', { line: '$H' }); + socket.emit('gcodeLine', { line: 'G92 X0 Y0 Z0' }); + }); + + $('#probeX').on('click', function() { + // var offsetX = parseFloat($('#probeOffsetX').val())+parseFloat($('#probeBitDiameter').val()/2); + var offsetX = parseFloat($('#probeOffsetX').val())+parseFloat($('#probeBitDiameter').val()/2); + + // Remember the units of measurement before we start the probe + unitsBeforeProbe = lastUnitsOfMeasurement; + + socket.emit('gcodeLine', { line: 'G21' }); // Set to mm + socket.emit('gcodeLine', { line: 'G38.2 X-50 F20' }); // Probe X + socket.emit('gcodeLine', { line: 'G92 X'+(offsetX*-1) }); // Set X offset + socket.emit('gcodeLine', { line: 'G1 X'+(offsetX+2)+' F1000' }); // Move probe 2mm away from offset + + // Restore the units of measurement after the probe + if(unitsBeforeProbe=='in') + socket.emit('gcodeLine', { line: 'G20' }); + }); + + $('#probeY').on('click', function() { + // var offsetY = parseFloat($('#probeOffsetY').val())+parseFloat($('#probeBitDiameter').val()/2); + var offsetY = parseFloat($('#probeOffsetY').val())+parseFloat($('#probeBitDiameter').val()/2); + + // Remember the units of measurement before we start the probe + unitsBeforeProbe = lastUnitsOfMeasurement; + + socket.emit('gcodeLine', { line: 'G21' }); // Set to mm + socket.emit('gcodeLine', { line: 'G38.2 Y-50 F20' }); // Probe Y + socket.emit('gcodeLine', { line: 'G92 Y'+(offsetY*-1) }); // Set Y offset + socket.emit('gcodeLine', { line: 'G1 Y'+(offsetY+2)+' F1000' }); // Move probe 2mm away from offset + + // Restore the units of measurement after the probe + if(unitsBeforeProbe=='in') + socket.emit('gcodeLine', { line: 'G20' }); + }); + + $('#probeZ').on('click', function() { + var offsetZ = parseFloat($('#probeOffsetZ').val()); + + // Remember the units of measurement before we start the probe + unitsBeforeProbe = lastUnitsOfMeasurement; + + socket.emit('gcodeLine', { line: 'G21' }); // Set to mm + socket.emit('gcodeLine', { line: 'G38.2 Z-75 F20'}); // Probe Z + socket.emit('gcodeLine', { line: 'G92 Z'+offsetZ }); // Set Z offset + socket.emit('gcodeLine', { line: 'G1 Z'+(offsetZ+5)+' F1000' }); // Move probe 5mm away from offset + + // Restore the units of measurement after the probe + if(unitsBeforeProbe=='in') + socket.emit('gcodeLine', { line: 'G20' }); + }); + + $('#probeAll').on('click', function() { + // var offsetX = parseFloat($('#probeOffsetX').val())+parseFloat($('#probeBitDiameter').val()/2)+10; + // var offsetY = parseFloat($('#probeOffsetY').val())+parseFloat($('#probeBitDiameter').val()/2)+10; + // var offsetZ = parseFloat($('#probeOffsetZ').val())+10; + var offsetX = parseFloat($('#probeOffsetX').val())+parseFloat($('#probeBitDiameter').val())+10; + var offsetY = parseFloat($('#probeOffsetY').val())+parseFloat($('#probeBitDiameter').val())+10; + var offsetZ = parseFloat($('#probeOffsetZ').val())+10; + + $('#probeZ').click(); // Probe Z + $('#probeX').click(); // Probe X + $('#probeY').click(); // Probe Y + + // Remember the units of measurement before we start the probe + unitsBeforeProbe = lastUnitsOfMeasurement; + + socket.emit('gcodeLine', { line: 'G21' }); + socket.emit('gcodeLine', { line: 'G1 X'+offsetX+' Y'+offsetY+' Z'+offsetZ+' F1000' }); // Move probe away from offset + + // Restore the units of measurement after the probe + if(unitsBeforeProbe=='in') + socket.emit('gcodeLine', { line: 'G20' }); + }); + + $('#gotoZeroZeroZero').on('click', function() { + socket.emit('gcodeLine', { line: 'G1 X0 Y0 Z0 F500' }); + }); + $('#sendZero').on('click', function() { socket.emit('gcodeLine', { line: 'G92 X0 Y0 Z0' }); }); @@ -187,6 +389,10 @@ $(document).ready(function() { $('#command').val(''); }); + $('#refreshPorts').on('click', function() { + socket.emit('refreshPorts', {}); + }); + // shift enter for send command $('#command').keydown(function (e) { if (e.shiftKey) { @@ -220,56 +426,55 @@ $(document).ready(function() { }); // WASD and up/down keys - $(document).keydown(function (e) { - var keyCode = e.keyCode || e.which; - - if ($('#command').is(':focus')) { - // don't handle keycodes inside command window - return; - } - - switch (keyCode) { - case 65: - // a key X- - e.preventDefault(); - $('#xM').click(); - break; - case 68: - // d key X+ - e.preventDefault(); - $('#xP').click(); - break; - case 87: - // w key Y+ - e.preventDefault(); - $('#yP').click(); - break; - case 83: - // s key Y- - e.preventDefault(); - $('#yM').click(); - break; - case 38: - // up arrow Z+ - e.preventDefault(); - $('#zP').click(); - break; - case 40: - // down arrow Z- - e.preventDefault(); - $('#zM').click(); - break; - } - }); + // $(document).keydown(function (e) { + // var keyCode = e.keyCode || e.which; + // + // if ($('#command').is(':focus')) { + // // don't handle keycodes inside command window + // return; + // } + // + // switch (keyCode) { + // case 65: + // // a key X- + // e.preventDefault(); + // $('#xM').click(); + // break; + // case 68: + // // d key X+ + // e.preventDefault(); + // $('#xP').click(); + // break; + // case 87: + // // w key Y+ + // e.preventDefault(); + // $('#yP').click(); + // break; + // case 83: + // // s key Y- + // e.preventDefault(); + // $('#yM').click(); + // break; + // case 38: + // // up arrow Z+ + // e.preventDefault(); + // $('#zP').click(); + // break; + // case 40: + // // down arrow Z- + // e.preventDefault(); + // $('#zM').click(); + // break; + // } + // }); // handle gcode uploads if (window.FileReader) { - var reader = new FileReader (); // drag and drop function dragEvent (ev) { - ev.stopPropagation (); + ev.stopPropagation (); ev.preventDefault (); if (ev.type == 'drop') { reader.onloadend = function (ev) { @@ -277,7 +482,7 @@ $(document).ready(function() { openGCodeFromText(); }; reader.readAsText (ev.dataTransfer.files[0]); - } + } } document.getElementById('command').addEventListener ('dragenter', dragEvent, false); @@ -297,5 +502,4 @@ $(document).ready(function() { } else { alert('your browser is too old to upload files, get the latest Chromium or Firefox'); } - }); diff --git a/package.json b/package.json index db658ea..eb5b6f7 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,10 @@ { - "name" : "grblweb", - "description" : "web based gcode sender for grbl", - "dependencies" : - { "node-static" : "0.7.x" - , "serialport" : "2.0.x" - , "socket.io" : "1.0.x" + "name": "grblweb", + "description": "web based gcode sender for grbl", + "dependencies": { + "node-static": "0.7.x", + "require-reload": "^0.2.2", + "serialport": "2.0.x", + "socket.io": "1.0.x" } } diff --git a/server.js b/server.js index 638c0c9..f160622 100644 --- a/server.js +++ b/server.js @@ -1,3 +1,7 @@ +/* + TODO: Allow saving/loading of probing profiles +*/ + /* GRBLWeb - a web based CNC controller for GRBL @@ -25,6 +29,8 @@ */ +var reload = require('require-reload')(require); +var config = reload('./config.js'); var config = require('./config'); var serialport = require("serialport"); var SerialPort = serialport.SerialPort; // localize object constructor @@ -40,6 +46,19 @@ var http = require('http'); // test for webcam config.showWebCam = false; + +// Monitor config.js for changes +fs.watch('./config.js', function(e, f) { + console.log('config.js changed, reloading'); + console.log('config: '+JSON.stringify(config)); + + config = reload('./config.js'); + + for(var i in io.sockets.connected) + io.sockets.connected[i].emit('config', config); +}); + + http.get('http://127.0.0.1:8080', function(res) { // valid response, enable webcam console.log('enabling webcam'); @@ -57,10 +76,8 @@ http.get('http://127.0.0.1:8080', function(res) { app.listen(config.webPort); var fileServer = new static.Server('./i'); -function handler (req, res) { - - //console.log(req.url); +function handler (req, res) { if (req.url.indexOf('/api/uploadGcode') == 0 && req.method == 'POST') { // this is a gcode upload, probably from jscut console.log('new data from jscut'); @@ -85,62 +102,69 @@ function handler (req, res) { } } + function ConvChar( str ) { c = {'<':'<', '>':'>', '&':'&', '"':'"', "'":''', '#':'#' }; return str.replace( /[<&>'"#]/g, function(s) { return c[s]; } ); } + var sp = []; var allPorts = []; +var piTemp = []; + +function doSerialPortList() { + serialport.list(function (err, ports) { + // if on rPi - http://www.hobbytronics.co.uk/raspberry-pi-serial-port + if (fs.existsSync('/dev/ttyAMA0') && config.usettyAMA0 == 1) { + (ports = ports || []).push({comName:'/dev/ttyAMA0',manufacturer: undefined,pnpId: 'raspberryPi__GPIO'}); + console.log('adding /dev/ttyAMA0 because it is enabled in config.js, you may need to enable it in the os - http://www.hobbytronics.co.uk/raspberry-pi-serial-port'); + } + + allPorts = ports; + + for (var i=0; iRESP: '+data+''}); @@ -191,7 +231,6 @@ function serialData(data, port) { sp[port].lastSerialWrite.shift(); } else if (data.indexOf('error') == 0) { - // error is red emitToPortSockets(port, 'serialRead', {'line':'RESP: '+data+''}); @@ -208,6 +247,14 @@ function serialData(data, port) { } else { // other is grey emitToPortSockets(port, 'serialRead', {'line':'RESP: '+data+''}); + + // This is where we're likely to see the units of measurement response + // Inches + if(data.indexOf(' G20 ')>=0) + unitsOfMeasurement = 'in'; + + if(data.indexOf(' G21 ')>=0) + unitsOfMeasurement = 'mm'; } if (sp[port].q.length == 0) { @@ -222,14 +269,16 @@ function serialData(data, port) { } + var currentSocketPort = {}; -function sendFirstQ(port) { +function sendFirstQ(port) { if (sp[port].q.length < 1) { // nothing to send return; } + var t = sp[port].q.shift(); // remove any comments after the command @@ -242,7 +291,8 @@ function sendFirstQ(port) { sendFirstQ(port); return; } - //console.log('sending '+t+' ### '+sp[port].q.length+' current q length'); + + // console.log('sending '+t+' ### '+sp[port].q.length+' current q length'); // loop through all registered port clients for (var i=0; i=0 || data.line.toUpperCase().indexOf("G21")>=0 || data.line.toUpperCase().indexOf("$X")>=0) + data.line += "\n$G"; // valid serial port selected, safe to send // split newlines @@ -288,7 +349,6 @@ io.sockets.on('connection', function (socket) { } else { socket.emit('serverError', 'you must select a serial port'); } - }); socket.on('clearQ', function(data) { @@ -311,7 +371,6 @@ io.sockets.on('connection', function (socket) { }); socket.on('disconnect', function() { - if (typeof currentSocketPort[socket.id] != 'undefined') { for (var c=0; c=0 && data.indexOf('YES')>=0) { + piTemp[i] = parseFloat(data.substr(data.indexOf('t=')+2).trim())/1000; + + // Maybe the internal temp + } else + piTemp[i] = parseFloat(data)/1000; + } + + emitToAllPortSockets('sensors', piTemp); + + setTimeout(getSensors, 2000); +} From 9be90ce8422c85953c32cf388c569d997996c4c7 Mon Sep 17 00:00:00 2001 From: Garrett Bartley Date: Sun, 28 Feb 2016 20:16:00 -0500 Subject: [PATCH 05/22] "Click" the "Jog Control" tab when the config reloads (just in case) Fixes #2 --- i/main.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/i/main.js b/i/main.js index ae74336..d6d72cb 100644 --- a/i/main.js +++ b/i/main.js @@ -102,6 +102,9 @@ $(document).ready(function() { // Fill in default Z offset for probe if(data.probeControlZOffset!=undefined) $('#probeOffsetZ').val(data.probeControlZOffset).attr('placeholder', data.probeControlZOffset); + + // "Click" the "Jog Control" tab + $('#controlTabLink').click().parent().click(); }); socket.on('ports', function (data) { From 17daf43b38b2de3585c616b3786dd23ed98cddc4 Mon Sep 17 00:00:00 2001 From: Garrett Bartley Date: Sun, 28 Feb 2016 22:59:07 -0500 Subject: [PATCH 06/22] Updated config.js. Should probably commit these as some "default" values and add to .gitignore --- config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.js b/config.js index 61547e4..f73da2f 100644 --- a/config.js +++ b/config.js @@ -29,7 +29,7 @@ config.jogControlDefaultFeed = 1000; config.probeControlXOffset = -0.5; // Y offset for probe -config.probeControlYOffset = -1.1; +config.probeControlYOffset = -1.5; // Z offset for probe config.probeControlZOffset = 3.1; From 2111f3b176a97df10db3b07143f6717fa9feb5ce Mon Sep 17 00:00:00 2001 From: Garrett Bartley Date: Sun, 28 Feb 2016 22:59:57 -0500 Subject: [PATCH 07/22] Added css for (maybe) useful icon "badge". Also set proper bit diameters. --- i/index.html | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/i/index.html b/i/index.html index dc31394..73ebffd 100644 --- a/i/index.html +++ b/i/index.html @@ -62,6 +62,10 @@ @@ -444,11 +462,11 @@ @@ -465,7 +483,7 @@ - + From 8ecd294bbf852a3ad2cb0cef1400f31eb510b33a Mon Sep 17 00:00:00 2001 From: Garrett Bartley Date: Sun, 28 Feb 2016 23:00:25 -0500 Subject: [PATCH 08/22] Removed unnecessary inline css --- i/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i/index.html b/i/index.html index 73ebffd..6b8ee14 100644 --- a/i/index.html +++ b/i/index.html @@ -483,7 +483,7 @@ - + From b1293a085f4143dadb270a80469e8eeed9086a8f Mon Sep 17 00:00:00 2001 From: Garrett Bartley Date: Sun, 28 Feb 2016 23:41:57 -0500 Subject: [PATCH 09/22] Added lots of tooltip help. Fixes #5 --- i/index.html | 90 +- i/jquery.powertip/LICENSE.txt | 20 + .../css/jquery.powertip-blue.css | 96 ++ .../css/jquery.powertip-blue.min.css | 1 + .../css/jquery.powertip-dark.css | 96 ++ .../css/jquery.powertip-dark.min.css | 1 + .../css/jquery.powertip-green.css | 96 ++ .../css/jquery.powertip-green.min.css | 1 + .../css/jquery.powertip-light.css | 96 ++ .../css/jquery.powertip-light.min.css | 1 + .../css/jquery.powertip-orange.css | 96 ++ .../css/jquery.powertip-orange.min.css | 1 + .../css/jquery.powertip-purple.css | 96 ++ .../css/jquery.powertip-purple.min.css | 1 + i/jquery.powertip/css/jquery.powertip-red.css | 96 ++ .../css/jquery.powertip-red.min.css | 1 + .../css/jquery.powertip-yellow.css | 96 ++ .../css/jquery.powertip-yellow.min.css | 1 + i/jquery.powertip/css/jquery.powertip.css | 93 ++ i/jquery.powertip/css/jquery.powertip.min.css | 1 + i/jquery.powertip/examples/examples.html | 150 +++ i/jquery.powertip/examples/examples_svg.html | 131 ++ i/jquery.powertip/jquery.powertip.js | 1166 +++++++++++++++++ i/jquery.powertip/jquery.powertip.min.js | 8 + i/main.js | 10 + 25 files changed, 2401 insertions(+), 44 deletions(-) create mode 100644 i/jquery.powertip/LICENSE.txt create mode 100644 i/jquery.powertip/css/jquery.powertip-blue.css create mode 100644 i/jquery.powertip/css/jquery.powertip-blue.min.css create mode 100644 i/jquery.powertip/css/jquery.powertip-dark.css create mode 100644 i/jquery.powertip/css/jquery.powertip-dark.min.css create mode 100644 i/jquery.powertip/css/jquery.powertip-green.css create mode 100644 i/jquery.powertip/css/jquery.powertip-green.min.css create mode 100644 i/jquery.powertip/css/jquery.powertip-light.css create mode 100644 i/jquery.powertip/css/jquery.powertip-light.min.css create mode 100644 i/jquery.powertip/css/jquery.powertip-orange.css create mode 100644 i/jquery.powertip/css/jquery.powertip-orange.min.css create mode 100644 i/jquery.powertip/css/jquery.powertip-purple.css create mode 100644 i/jquery.powertip/css/jquery.powertip-purple.min.css create mode 100644 i/jquery.powertip/css/jquery.powertip-red.css create mode 100644 i/jquery.powertip/css/jquery.powertip-red.min.css create mode 100644 i/jquery.powertip/css/jquery.powertip-yellow.css create mode 100644 i/jquery.powertip/css/jquery.powertip-yellow.min.css create mode 100644 i/jquery.powertip/css/jquery.powertip.css create mode 100644 i/jquery.powertip/css/jquery.powertip.min.css create mode 100644 i/jquery.powertip/examples/examples.html create mode 100644 i/jquery.powertip/examples/examples_svg.html create mode 100644 i/jquery.powertip/jquery.powertip.js create mode 100644 i/jquery.powertip/jquery.powertip.min.js diff --git a/i/index.html b/i/index.html index 6b8ee14..57f08a5 100644 --- a/i/index.html +++ b/i/index.html @@ -46,6 +46,7 @@ + @@ -58,6 +59,7 @@ + + + + + + + + + + + + + +

PowerTip Examples

+ + +
+

Placement examples

+
+ + + + +
+ +
+ + + + + +
+
+ + + +
+

Mouse follow example

+
+ The PowerTip for this box will follow the mouse. +
+
+ + + +
+

Mouse on to tooltip example

+
+ The PowerTip for this box will appear on the right and you will be able to interact with its content. +
+
+ + + +
+

API examples

+ + + +
+ Delegated manual mouseover, with lazy-loaded tooltip: + + +
+
+ + + + diff --git a/i/jquery.powertip/examples/examples_svg.html b/i/jquery.powertip/examples/examples_svg.html new file mode 100644 index 0000000..fed86d3 --- /dev/null +++ b/i/jquery.powertip/examples/examples_svg.html @@ -0,0 +1,131 @@ + + + + + PowerTip SVG Examples + + + + + + + + + + + + + +

PowerTip SVG Examples

+ + +
+

Simple Placement Examples

+
+ + + + + + + + + + + + + + ​ +
+
+ + + +
+

Complex Shape Placement Examples

+
+ + + + + + + + + + + + + + + Text + ​ +
+
+ + + +
+

Rotation Placement Examples

+
+ + + + + + + + + + + ​ +
+
+ + + + diff --git a/i/jquery.powertip/jquery.powertip.js b/i/jquery.powertip/jquery.powertip.js new file mode 100644 index 0000000..07e87fe --- /dev/null +++ b/i/jquery.powertip/jquery.powertip.js @@ -0,0 +1,1166 @@ +/*! + PowerTip - v1.2.0 - 2013-04-03 + http://stevenbenner.github.com/jquery-powertip/ + Copyright (c) 2013 Steven Benner (http://stevenbenner.com/). + Released under MIT license. + https://raw.github.com/stevenbenner/jquery-powertip/master/LICENSE.txt +*/ +(function(factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else { + // Browser globals + factory(jQuery); + } +}(function($) { + + // useful private variables + var $document = $(document), + $window = $(window), + $body = $('body'); + + // constants + var DATA_DISPLAYCONTROLLER = 'displayController', + DATA_HASACTIVEHOVER = 'hasActiveHover', + DATA_FORCEDOPEN = 'forcedOpen', + DATA_HASMOUSEMOVE = 'hasMouseMove', + DATA_MOUSEONTOTIP = 'mouseOnToPopup', + DATA_ORIGINALTITLE = 'originalTitle', + DATA_POWERTIP = 'powertip', + DATA_POWERTIPJQ = 'powertipjq', + DATA_POWERTIPTARGET = 'powertiptarget', + RAD2DEG = 180 / Math.PI; + + /** + * Session data + * Private properties global to all powerTip instances + */ + var session = { + isTipOpen: false, + isFixedTipOpen: false, + isClosing: false, + tipOpenImminent: false, + activeHover: null, + currentX: 0, + currentY: 0, + previousX: 0, + previousY: 0, + desyncTimeout: null, + mouseTrackingActive: false, + delayInProgress: false, + windowWidth: 0, + windowHeight: 0, + scrollTop: 0, + scrollLeft: 0 + }; + + /** + * Collision enumeration + * @enum {number} + */ + var Collision = { + none: 0, + top: 1, + bottom: 2, + left: 4, + right: 8 + }; + + /** + * Display hover tooltips on the matched elements. + * @param {(Object|string)} opts The options object to use for the plugin, or + * the name of a method to invoke on the first matched element. + * @param {*=} [arg] Argument for an invoked method (optional). + * @return {jQuery} jQuery object for the matched selectors. + */ + $.fn.powerTip = function(opts, arg) { + // don't do any work if there were no matched elements + if (!this.length) { + return this; + } + + // handle api method calls on the plugin, e.g. powerTip('hide') + if ($.type(opts) === 'string' && $.powerTip[opts]) { + return $.powerTip[opts].call(this, this, arg); + } + + // extend options and instantiate TooltipController + var options = $.extend({}, $.fn.powerTip.defaults, opts), + tipController = new TooltipController(options); + + // hook mouse and viewport dimension tracking + initTracking(); + + // setup the elements + this.each(function elementSetup() { + var $this = $(this), + dataPowertip = $this.data(DATA_POWERTIP), + dataElem = $this.data(DATA_POWERTIPJQ), + dataTarget = $this.data(DATA_POWERTIPTARGET), + title; + + // handle repeated powerTip calls on the same element by destroying the + // original instance hooked to it and replacing it with this call + if ($this.data(DATA_DISPLAYCONTROLLER)) { + $.powerTip.destroy($this); + } + + // attempt to use title attribute text if there is no data-powertip, + // data-powertipjq or data-powertiptarget. If we do use the title + // attribute, delete the attribute so the browser will not show it + title = $this.attr('title'); + if (!dataPowertip && !dataTarget && !dataElem && title) { + $this.data(DATA_POWERTIP, title); + $this.data(DATA_ORIGINALTITLE, title); + $this.removeAttr('title'); + } + + // create hover controllers for each element + $this.data( + DATA_DISPLAYCONTROLLER, + new DisplayController($this, options, tipController) + ); + }); + + // attach events to matched elements if the manual options is not enabled + if (!options.manual) { + this.on({ + // mouse events + 'mouseenter.powertip': function elementMouseEnter(event) { + $.powerTip.show(this, event); + }, + 'mouseleave.powertip': function elementMouseLeave() { + $.powerTip.hide(this); + }, + // keyboard events + 'focus.powertip': function elementFocus() { + $.powerTip.show(this); + }, + 'blur.powertip': function elementBlur() { + $.powerTip.hide(this, true); + }, + 'keydown.powertip': function elementKeyDown(event) { + // close tooltip when the escape key is pressed + if (event.keyCode === 27) { + $.powerTip.hide(this, true); + } + } + }); + } + + return this; + }; + + /** + * Default options for the powerTip plugin. + */ + $.fn.powerTip.defaults = { + fadeInTime: 200, + fadeOutTime: 100, + followMouse: false, + popupId: 'powerTip', + intentSensitivity: 7, + intentPollInterval: 100, + closeDelay: 100, + placement: 'n', + smartPlacement: false, + offset: 10, + mouseOnToPopup: false, + manual: false + }; + + /** + * Default smart placement priority lists. + * The first item in the array is the highest priority, the last is the lowest. + * The last item is also the default, which will be used if all previous options + * do not fit. + */ + $.fn.powerTip.smartPlacementLists = { + n: ['n', 'ne', 'nw', 's'], + e: ['e', 'ne', 'se', 'w', 'nw', 'sw', 'n', 's', 'e'], + s: ['s', 'se', 'sw', 'n'], + w: ['w', 'nw', 'sw', 'e', 'ne', 'se', 'n', 's', 'w'], + nw: ['nw', 'w', 'sw', 'n', 's', 'se', 'nw'], + ne: ['ne', 'e', 'se', 'n', 's', 'sw', 'ne'], + sw: ['sw', 'w', 'nw', 's', 'n', 'ne', 'sw'], + se: ['se', 'e', 'ne', 's', 'n', 'nw', 'se'], + 'nw-alt': ['nw-alt', 'n', 'ne-alt', 'sw-alt', 's', 'se-alt', 'w', 'e'], + 'ne-alt': ['ne-alt', 'n', 'nw-alt', 'se-alt', 's', 'sw-alt', 'e', 'w'], + 'sw-alt': ['sw-alt', 's', 'se-alt', 'nw-alt', 'n', 'ne-alt', 'w', 'e'], + 'se-alt': ['se-alt', 's', 'sw-alt', 'ne-alt', 'n', 'nw-alt', 'e', 'w'] + }; + + /** + * Public API + */ + $.powerTip = { + /** + * Attempts to show the tooltip for the specified element. + * @param {jQuery|Element} element The element to open the tooltip for. + * @param {jQuery.Event=} event jQuery event for hover intent and mouse + * tracking (optional). + */ + show: function apiShowTip(element, event) { + if (event) { + trackMouse(event); + session.previousX = event.pageX; + session.previousY = event.pageY; + $(element).data(DATA_DISPLAYCONTROLLER).show(); + } else { + $(element).first().data(DATA_DISPLAYCONTROLLER).show(true, true); + } + return element; + }, + + /** + * Repositions the tooltip on the element. + * @param {jQuery|Element} element The element the tooltip is shown for. + */ + reposition: function apiResetPosition(element) { + $(element).first().data(DATA_DISPLAYCONTROLLER).resetPosition(); + return element; + }, + + /** + * Attempts to close any open tooltips. + * @param {(jQuery|Element)=} element The element with the tooltip that + * should be closed (optional). + * @param {boolean=} immediate Disable close delay (optional). + */ + hide: function apiCloseTip(element, immediate) { + if (element) { + $(element).first().data(DATA_DISPLAYCONTROLLER).hide(immediate); + } else { + if (session.activeHover) { + session.activeHover.data(DATA_DISPLAYCONTROLLER).hide(true); + } + } + return element; + }, + + /** + * Destroy and roll back any powerTip() instance on the specified element. + * @param {jQuery|Element} element The element with the powerTip instance. + */ + destroy: function apiDestroy(element) { + $(element).off('.powertip').each(function destroy() { + var $this = $(this), + dataAttributes = [ + DATA_ORIGINALTITLE, + DATA_DISPLAYCONTROLLER, + DATA_HASACTIVEHOVER, + DATA_FORCEDOPEN + ]; + + if ($this.data(DATA_ORIGINALTITLE)) { + $this.attr('title', $this.data(DATA_ORIGINALTITLE)); + dataAttributes.push(DATA_POWERTIP); + } + + $this.removeData(dataAttributes); + }); + return element; + } + }; + + // API aliasing + $.powerTip.showTip = $.powerTip.show; + $.powerTip.closeTip = $.powerTip.hide; + + /** + * Creates a new CSSCoordinates object. + * @private + * @constructor + */ + function CSSCoordinates() { + var me = this; + + // initialize object properties + me.top = 'auto'; + me.left = 'auto'; + me.right = 'auto'; + me.bottom = 'auto'; + + /** + * Set a property to a value. + * @private + * @param {string} property The name of the property. + * @param {number} value The value of the property. + */ + me.set = function(property, value) { + if ($.isNumeric(value)) { + me[property] = Math.round(value); + } + }; + } + + /** + * Creates a new tooltip display controller. + * @private + * @constructor + * @param {jQuery} element The element that this controller will handle. + * @param {Object} options Options object containing settings. + * @param {TooltipController} tipController The TooltipController object for + * this instance. + */ + function DisplayController(element, options, tipController) { + var hoverTimer = null; + + /** + * Begins the process of showing a tooltip. + * @private + * @param {boolean=} immediate Skip intent testing (optional). + * @param {boolean=} forceOpen Ignore cursor position and force tooltip to + * open (optional). + */ + function openTooltip(immediate, forceOpen) { + cancelTimer(); + if (!element.data(DATA_HASACTIVEHOVER)) { + if (!immediate) { + session.tipOpenImminent = true; + hoverTimer = setTimeout( + function intentDelay() { + hoverTimer = null; + checkForIntent(); + }, + options.intentPollInterval + ); + } else { + if (forceOpen) { + element.data(DATA_FORCEDOPEN, true); + } + tipController.showTip(element); + } + } + } + + /** + * Begins the process of closing a tooltip. + * @private + * @param {boolean=} disableDelay Disable close delay (optional). + */ + function closeTooltip(disableDelay) { + cancelTimer(); + session.tipOpenImminent = false; + if (element.data(DATA_HASACTIVEHOVER)) { + element.data(DATA_FORCEDOPEN, false); + if (!disableDelay) { + session.delayInProgress = true; + hoverTimer = setTimeout( + function closeDelay() { + hoverTimer = null; + tipController.hideTip(element); + session.delayInProgress = false; + }, + options.closeDelay + ); + } else { + tipController.hideTip(element); + } + } + } + + /** + * Checks mouse position to make sure that the user intended to hover on the + * specified element before showing the tooltip. + * @private + */ + function checkForIntent() { + // calculate mouse position difference + var xDifference = Math.abs(session.previousX - session.currentX), + yDifference = Math.abs(session.previousY - session.currentY), + totalDifference = xDifference + yDifference; + + // check if difference has passed the sensitivity threshold + if (totalDifference < options.intentSensitivity) { + tipController.showTip(element); + } else { + // try again + session.previousX = session.currentX; + session.previousY = session.currentY; + openTooltip(); + } + } + + /** + * Cancels active hover timer. + * @private + */ + function cancelTimer() { + hoverTimer = clearTimeout(hoverTimer); + session.delayInProgress = false; + } + + /** + * Repositions the tooltip on this element. + * @private + */ + function repositionTooltip() { + tipController.resetPosition(element); + } + + // expose the methods + this.show = openTooltip; + this.hide = closeTooltip; + this.cancel = cancelTimer; + this.resetPosition = repositionTooltip; + } + + /** + * Creates a new Placement Calculator. + * @private + * @constructor + */ + function PlacementCalculator() { + /** + * Compute the CSS position to display a tooltip at the specified placement + * relative to the specified element. + * @private + * @param {jQuery} element The element that the tooltip should target. + * @param {string} placement The placement for the tooltip. + * @param {number} tipWidth Width of the tooltip element in pixels. + * @param {number} tipHeight Height of the tooltip element in pixels. + * @param {number} offset Distance to offset tooltips in pixels. + * @return {CSSCoordinates} A CSSCoordinates object with the position. + */ + function computePlacementCoords(element, placement, tipWidth, tipHeight, offset) { + var placementBase = placement.split('-')[0], // ignore 'alt' for corners + coords = new CSSCoordinates(), + position; + + if (isSvgElement(element)) { + position = getSvgPlacement(element, placementBase); + } else { + position = getHtmlPlacement(element, placementBase); + } + + // calculate the appropriate x and y position in the document + switch (placement) { + case 'n': + coords.set('left', position.left - (tipWidth / 2)); + coords.set('bottom', session.windowHeight - position.top + offset); + break; + case 'e': + coords.set('left', position.left + offset); + coords.set('top', position.top - (tipHeight / 2)); + break; + case 's': + coords.set('left', position.left - (tipWidth / 2)); + coords.set('top', position.top + offset); + break; + case 'w': + coords.set('top', position.top - (tipHeight / 2)); + coords.set('right', session.windowWidth - position.left + offset); + break; + case 'nw': + coords.set('bottom', session.windowHeight - position.top + offset); + coords.set('right', session.windowWidth - position.left - 20); + break; + case 'nw-alt': + coords.set('left', position.left); + coords.set('bottom', session.windowHeight - position.top + offset); + break; + case 'ne': + coords.set('left', position.left - 20); + coords.set('bottom', session.windowHeight - position.top + offset); + break; + case 'ne-alt': + coords.set('bottom', session.windowHeight - position.top + offset); + coords.set('right', session.windowWidth - position.left); + break; + case 'sw': + coords.set('top', position.top + offset); + coords.set('right', session.windowWidth - position.left - 20); + break; + case 'sw-alt': + coords.set('left', position.left); + coords.set('top', position.top + offset); + break; + case 'se': + coords.set('left', position.left - 20); + coords.set('top', position.top + offset); + break; + case 'se-alt': + coords.set('top', position.top + offset); + coords.set('right', session.windowWidth - position.left); + break; + } + + return coords; + } + + /** + * Finds the tooltip attachment point in the document for a HTML DOM element + * for the specified placement. + * @private + * @param {jQuery} element The element that the tooltip should target. + * @param {string} placement The placement for the tooltip. + * @return {Object} An object with the top,left position values. + */ + function getHtmlPlacement(element, placement) { + var objectOffset = element.offset(), + objectWidth = element.outerWidth(), + objectHeight = element.outerHeight(), + left, + top; + + // calculate the appropriate x and y position in the document + switch (placement) { + case 'n': + left = objectOffset.left + objectWidth / 2; + top = objectOffset.top; + break; + case 'e': + left = objectOffset.left + objectWidth; + top = objectOffset.top + objectHeight / 2; + break; + case 's': + left = objectOffset.left + objectWidth / 2; + top = objectOffset.top + objectHeight; + break; + case 'w': + left = objectOffset.left; + top = objectOffset.top + objectHeight / 2; + break; + case 'nw': + left = objectOffset.left; + top = objectOffset.top; + break; + case 'ne': + left = objectOffset.left + objectWidth; + top = objectOffset.top; + break; + case 'sw': + left = objectOffset.left; + top = objectOffset.top + objectHeight; + break; + case 'se': + left = objectOffset.left + objectWidth; + top = objectOffset.top + objectHeight; + break; + } + + return { + top: top, + left: left + }; + } + + /** + * Finds the tooltip attachment point in the document for a SVG element for + * the specified placement. + * @private + * @param {jQuery} element The element that the tooltip should target. + * @param {string} placement The placement for the tooltip. + * @return {Object} An object with the top,left position values. + */ + function getSvgPlacement(element, placement) { + var svgElement = element.closest('svg')[0], + domElement = element[0], + point = svgElement.createSVGPoint(), + boundingBox = domElement.getBBox(), + matrix = domElement.getScreenCTM(), + halfWidth = boundingBox.width / 2, + halfHeight = boundingBox.height / 2, + placements = [], + placementKeys = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'], + coords, + rotation, + steps, + x; + + function pushPlacement() { + placements.push(point.matrixTransform(matrix)); + } + + // get bounding box corners and midpoints + point.x = boundingBox.x; + point.y = boundingBox.y; + pushPlacement(); + point.x += halfWidth; + pushPlacement(); + point.x += halfWidth; + pushPlacement(); + point.y += halfHeight; + pushPlacement(); + point.y += halfHeight; + pushPlacement(); + point.x -= halfWidth; + pushPlacement(); + point.x -= halfWidth; + pushPlacement(); + point.y -= halfHeight; + pushPlacement(); + + // determine rotation + if (placements[0].y !== placements[1].y || placements[0].x !== placements[7].x) { + rotation = Math.atan2(matrix.b, matrix.a) * RAD2DEG; + steps = Math.ceil(((rotation % 360) - 22.5) / 45); + if (steps < 1) { + steps += 8; + } + while (steps--) { + placementKeys.push(placementKeys.shift()); + } + } + + // find placement + for (x = 0; x < placements.length; x++) { + if (placementKeys[x] === placement) { + coords = placements[x]; + break; + } + } + + return { + top: coords.y + session.scrollTop, + left: coords.x + session.scrollLeft + }; + } + + // expose methods + this.compute = computePlacementCoords; + } + + /** + * Creates a new tooltip controller. + * @private + * @constructor + * @param {Object} options Options object containing settings. + */ + function TooltipController(options) { + var placementCalculator = new PlacementCalculator(), + tipElement = $('#' + options.popupId); + + // build and append tooltip div if it does not already exist + if (tipElement.length === 0) { + tipElement = $('
', { id: options.popupId }); + // grab body element if it was not populated when the script loaded + // note: this hack exists solely for jsfiddle support + if ($body.length === 0) { + $body = $('body'); + } + $body.append(tipElement); + } + + // hook mousemove for cursor follow tooltips + if (options.followMouse) { + // only one positionTipOnCursor hook per tooltip element, please + if (!tipElement.data(DATA_HASMOUSEMOVE)) { + $document.on('mousemove', positionTipOnCursor); + $window.on('scroll', positionTipOnCursor); + tipElement.data(DATA_HASMOUSEMOVE, true); + } + } + + // if we want to be able to mouse onto the tooltip then we need to attach + // hover events to the tooltip that will cancel a close request on hover and + // start a new close request on mouseleave + if (options.mouseOnToPopup) { + tipElement.on({ + mouseenter: function tipMouseEnter() { + // we only let the mouse stay on the tooltip if it is set to let + // users interact with it + if (tipElement.data(DATA_MOUSEONTOTIP)) { + // check activeHover in case the mouse cursor entered the + // tooltip during the fadeOut and close cycle + if (session.activeHover) { + session.activeHover.data(DATA_DISPLAYCONTROLLER).cancel(); + } + } + }, + mouseleave: function tipMouseLeave() { + // check activeHover in case the mouse cursor entered the + // tooltip during the fadeOut and close cycle + if (session.activeHover) { + session.activeHover.data(DATA_DISPLAYCONTROLLER).hide(); + } + } + }); + } + + /** + * Gives the specified element the active-hover state and queues up the + * showTip function. + * @private + * @param {jQuery} element The element that the tooltip should target. + */ + function beginShowTip(element) { + element.data(DATA_HASACTIVEHOVER, true); + // show tooltip, asap + tipElement.queue(function queueTipInit(next) { + showTip(element); + next(); + }); + } + + /** + * Shows the tooltip, as soon as possible. + * @private + * @param {jQuery} element The element that the tooltip should target. + */ + function showTip(element) { + var tipContent; + + // it is possible, especially with keyboard navigation, to move on to + // another element with a tooltip during the queue to get to this point + // in the code. if that happens then we need to not proceed or we may + // have the fadeout callback for the last tooltip execute immediately + // after this code runs, causing bugs. + if (!element.data(DATA_HASACTIVEHOVER)) { + return; + } + + // if the tooltip is open and we got asked to open another one then the + // old one is still in its fadeOut cycle, so wait and try again + if (session.isTipOpen) { + if (!session.isClosing) { + hideTip(session.activeHover); + } + tipElement.delay(100).queue(function queueTipAgain(next) { + showTip(element); + next(); + }); + return; + } + + // trigger powerTipPreRender event + element.trigger('powerTipPreRender'); + + // set tooltip content + tipContent = getTooltipContent(element); + if (tipContent) { + tipElement.empty().append(tipContent); + } else { + // we have no content to display, give up + return; + } + + // trigger powerTipRender event + element.trigger('powerTipRender'); + + session.activeHover = element; + session.isTipOpen = true; + + tipElement.data(DATA_MOUSEONTOTIP, options.mouseOnToPopup); + + // set tooltip position + if (!options.followMouse) { + positionTipOnElement(element); + session.isFixedTipOpen = true; + } else { + positionTipOnCursor(); + } + + // fadein + tipElement.fadeIn(options.fadeInTime, function fadeInCallback() { + // start desync polling + if (!session.desyncTimeout) { + session.desyncTimeout = setInterval(closeDesyncedTip, 500); + } + + // trigger powerTipOpen event + element.trigger('powerTipOpen'); + }); + } + + /** + * Hides the tooltip. + * @private + * @param {jQuery} element The element that the tooltip should target. + */ + function hideTip(element) { + // reset session + session.isClosing = true; + session.activeHover = null; + session.isTipOpen = false; + + // stop desync polling + session.desyncTimeout = clearInterval(session.desyncTimeout); + + // reset element state + element.data(DATA_HASACTIVEHOVER, false); + element.data(DATA_FORCEDOPEN, false); + + // fade out + tipElement.fadeOut(options.fadeOutTime, function fadeOutCallback() { + var coords = new CSSCoordinates(); + + // reset session and tooltip element + session.isClosing = false; + session.isFixedTipOpen = false; + tipElement.removeClass(); + + // support mouse-follow and fixed position tips at the same time by + // moving the tooltip to the last cursor location after it is hidden + coords.set('top', session.currentY + options.offset); + coords.set('left', session.currentX + options.offset); + tipElement.css(coords); + + // trigger powerTipClose event + element.trigger('powerTipClose'); + }); + } + + /** + * Moves the tooltip to the users mouse cursor. + * @private + */ + function positionTipOnCursor() { + // to support having fixed tooltips on the same page as cursor tooltips, + // where both instances are referencing the same tooltip element, we + // need to keep track of the mouse position constantly, but we should + // only set the tip location if a fixed tip is not currently open, a tip + // open is imminent or active, and the tooltip element in question does + // have a mouse-follow using it. + if (!session.isFixedTipOpen && (session.isTipOpen || (session.tipOpenImminent && tipElement.data(DATA_HASMOUSEMOVE)))) { + // grab measurements + var tipWidth = tipElement.outerWidth(), + tipHeight = tipElement.outerHeight(), + coords = new CSSCoordinates(), + collisions, + collisionCount; + + // grab collisions + coords.set('top', session.currentY + options.offset); + coords.set('left', session.currentX + options.offset); + collisions = getViewportCollisions( + coords, + tipWidth, + tipHeight + ); + + // handle tooltip view port collisions + if (collisions !== Collision.none) { + collisionCount = countFlags(collisions); + if (collisionCount === 1) { + // if there is only one collision (bottom or right) then + // simply constrain the tooltip to the view port + if (collisions === Collision.right) { + coords.set('left', session.windowWidth - tipWidth); + } else if (collisions === Collision.bottom) { + coords.set('top', session.scrollTop + session.windowHeight - tipHeight); + } + } else { + // if the tooltip has more than one collision then it is + // trapped in the corner and should be flipped to get it out + // of the users way + coords.set('left', session.currentX - tipWidth - options.offset); + coords.set('top', session.currentY - tipHeight - options.offset); + } + } + + // position the tooltip + tipElement.css(coords); + } + } + + /** + * Sets the tooltip to the correct position relative to the specified target + * element. Based on options settings. + * @private + * @param {jQuery} element The element that the tooltip should target. + */ + function positionTipOnElement(element) { + var priorityList, + finalPlacement; + + if (options.smartPlacement) { + priorityList = $.fn.powerTip.smartPlacementLists[options.placement]; + + // iterate over the priority list and use the first placement option + // that does not collide with the view port. if they all collide + // then the last placement in the list will be used. + $.each(priorityList, function(idx, pos) { + // place tooltip and find collisions + var collisions = getViewportCollisions( + placeTooltip(element, pos), + tipElement.outerWidth(), + tipElement.outerHeight() + ); + + // update the final placement variable + finalPlacement = pos; + + // break if there were no collisions + if (collisions === Collision.none) { + return false; + } + }); + } else { + // if we're not going to use the smart placement feature then just + // compute the coordinates and do it + placeTooltip(element, options.placement); + finalPlacement = options.placement; + } + + // add placement as class for CSS arrows + tipElement.addClass(finalPlacement); + } + + /** + * Sets the tooltip position to the appropriate values to show the tip at + * the specified placement. This function will iterate and test the tooltip + * to support elastic tooltips. + * @private + * @param {jQuery} element The element that the tooltip should target. + * @param {string} placement The placement for the tooltip. + * @return {CSSCoordinates} A CSSCoordinates object with the top, left, and + * right position values. + */ + function placeTooltip(element, placement) { + var iterationCount = 0, + tipWidth, + tipHeight, + coords = new CSSCoordinates(); + + // set the tip to 0,0 to get the full expanded width + coords.set('top', 0); + coords.set('left', 0); + tipElement.css(coords); + + // to support elastic tooltips we need to check for a change in the + // rendered dimensions after the tooltip has been positioned + do { + // grab the current tip dimensions + tipWidth = tipElement.outerWidth(); + tipHeight = tipElement.outerHeight(); + + // get placement coordinates + coords = placementCalculator.compute( + element, + placement, + tipWidth, + tipHeight, + options.offset + ); + + // place the tooltip + tipElement.css(coords); + } while ( + // sanity check: limit to 5 iterations, and... + ++iterationCount <= 5 && + // try again if the dimensions changed after placement + (tipWidth !== tipElement.outerWidth() || tipHeight !== tipElement.outerHeight()) + ); + + return coords; + } + + /** + * Checks for a tooltip desync and closes the tooltip if one occurs. + * @private + */ + function closeDesyncedTip() { + var isDesynced = false; + // It is possible for the mouse cursor to leave an element without + // firing the mouseleave or blur event. This most commonly happens when + // the element is disabled under mouse cursor. If this happens it will + // result in a desynced tooltip because the tooltip was never asked to + // close. So we should periodically check for a desync situation and + // close the tip if such a situation arises. + if (session.isTipOpen && !session.isClosing && !session.delayInProgress) { + // user moused onto another tip or active hover is disabled + if (session.activeHover.data(DATA_HASACTIVEHOVER) === false || session.activeHover.is(':disabled')) { + isDesynced = true; + } else { + // hanging tip - have to test if mouse position is not over the + // active hover and not over a tooltip set to let the user + // interact with it. + // for keyboard navigation: this only counts if the element does + // not have focus. + // for tooltips opened via the api: we need to check if it has + // the forcedOpen flag. + if (!isMouseOver(session.activeHover) && !session.activeHover.is(':focus') && !session.activeHover.data(DATA_FORCEDOPEN)) { + if (tipElement.data(DATA_MOUSEONTOTIP)) { + if (!isMouseOver(tipElement)) { + isDesynced = true; + } + } else { + isDesynced = true; + } + } + } + + if (isDesynced) { + // close the desynced tip + hideTip(session.activeHover); + } + } + } + + // expose methods + this.showTip = beginShowTip; + this.hideTip = hideTip; + this.resetPosition = positionTipOnElement; + } + + /** + * Determine whether a jQuery object is an SVG element + * @private + * @param {jQuery} element The element to check + * @return {boolean} Whether this is an SVG element + */ + function isSvgElement(element) { + return window.SVGElement && element[0] instanceof SVGElement; + } + + /** + * Initializes the viewport dimension cache and hooks up the mouse position + * tracking and viewport dimension tracking events. + * Prevents attaching the events more than once. + * @private + */ + function initTracking() { + if (!session.mouseTrackingActive) { + session.mouseTrackingActive = true; + + // grab the current viewport dimensions on load + $(function getViewportDimensions() { + session.scrollLeft = $window.scrollLeft(); + session.scrollTop = $window.scrollTop(); + session.windowWidth = $window.width(); + session.windowHeight = $window.height(); + }); + + // hook mouse move tracking + $document.on('mousemove', trackMouse); + + // hook viewport dimensions tracking + $window.on({ + resize: function trackResize() { + session.windowWidth = $window.width(); + session.windowHeight = $window.height(); + }, + scroll: function trackScroll() { + var x = $window.scrollLeft(), + y = $window.scrollTop(); + if (x !== session.scrollLeft) { + session.currentX += x - session.scrollLeft; + session.scrollLeft = x; + } + if (y !== session.scrollTop) { + session.currentY += y - session.scrollTop; + session.scrollTop = y; + } + } + }); + } + } + + /** + * Saves the current mouse coordinates to the session object. + * @private + * @param {jQuery.Event} event The mousemove event for the document. + */ + function trackMouse(event) { + session.currentX = event.pageX; + session.currentY = event.pageY; + } + + /** + * Tests if the mouse is currently over the specified element. + * @private + * @param {jQuery} element The element to check for hover. + * @return {boolean} + */ + function isMouseOver(element) { + // use getBoundingClientRect() because jQuery's width() and height() + // methods do not work with SVG elements + // compute width/height because those properties do not exist on the object + // returned by getBoundingClientRect() in older versions of IE + var elementPosition = element.offset(), + elementBox = element[0].getBoundingClientRect(), + elementWidth = elementBox.right - elementBox.left, + elementHeight = elementBox.bottom - elementBox.top; + + return session.currentX >= elementPosition.left && + session.currentX <= elementPosition.left + elementWidth && + session.currentY >= elementPosition.top && + session.currentY <= elementPosition.top + elementHeight; + } + + /** + * Fetches the tooltip content from the specified element's data attributes. + * @private + * @param {jQuery} element The element to get the tooltip content for. + * @return {(string|jQuery|undefined)} The text/HTML string, jQuery object, or + * undefined if there was no tooltip content for the element. + */ + function getTooltipContent(element) { + var tipText = element.data(DATA_POWERTIP), + tipObject = element.data(DATA_POWERTIPJQ), + tipTarget = element.data(DATA_POWERTIPTARGET), + targetElement, + content; + + if (tipText) { + if ($.isFunction(tipText)) { + tipText = tipText.call(element[0]); + } + content = tipText; + } else if (tipObject) { + if ($.isFunction(tipObject)) { + tipObject = tipObject.call(element[0]); + } + if (tipObject.length > 0) { + content = tipObject.clone(true, true); + } + } else if (tipTarget) { + targetElement = $('#' + tipTarget); + if (targetElement.length > 0) { + content = targetElement.html(); + } + } + + return content; + } + + /** + * Finds any viewport collisions that an element (the tooltip) would have if it + * were absolutely positioned at the specified coordinates. + * @private + * @param {CSSCoordinates} coords Coordinates for the element. + * @param {number} elementWidth Width of the element in pixels. + * @param {number} elementHeight Height of the element in pixels. + * @return {number} Value with the collision flags. + */ + function getViewportCollisions(coords, elementWidth, elementHeight) { + var viewportTop = session.scrollTop, + viewportLeft = session.scrollLeft, + viewportBottom = viewportTop + session.windowHeight, + viewportRight = viewportLeft + session.windowWidth, + collisions = Collision.none; + + if (coords.top < viewportTop || Math.abs(coords.bottom - session.windowHeight) - elementHeight < viewportTop) { + collisions |= Collision.top; + } + if (coords.top + elementHeight > viewportBottom || Math.abs(coords.bottom - session.windowHeight) > viewportBottom) { + collisions |= Collision.bottom; + } + if (coords.left < viewportLeft || coords.right + elementWidth > viewportRight) { + collisions |= Collision.left; + } + if (coords.left + elementWidth > viewportRight || coords.right < viewportLeft) { + collisions |= Collision.right; + } + + return collisions; + } + + /** + * Counts the number of bits set on a flags value. + * @param {number} value The flags value. + * @return {number} The number of bits that have been set. + */ + function countFlags(value) { + var count = 0; + while (value) { + value &= value - 1; + count++; + } + return count; + } + +})); diff --git a/i/jquery.powertip/jquery.powertip.min.js b/i/jquery.powertip/jquery.powertip.min.js new file mode 100644 index 0000000..40874ae --- /dev/null +++ b/i/jquery.powertip/jquery.powertip.min.js @@ -0,0 +1,8 @@ +/*! + PowerTip - v1.2.0 - 2013-04-03 + http://stevenbenner.github.com/jquery-powertip/ + Copyright (c) 2013 Steven Benner (http://stevenbenner.com/). + Released under MIT license. + https://raw.github.com/stevenbenner/jquery-powertip/master/LICENSE.txt +*/ +(function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function t(){var t=this;t.top="auto",t.left="auto",t.right="auto",t.bottom="auto",t.set=function(o,n){e.isNumeric(n)&&(t[o]=Math.round(n))}}function o(e,t,o){function n(n,i){r(),e.data(v)||(n?(i&&e.data(m,!0),o.showTip(e)):(P.tipOpenImminent=!0,l=setTimeout(function(){l=null,s()},t.intentPollInterval)))}function i(n){r(),P.tipOpenImminent=!1,e.data(v)&&(e.data(m,!1),n?o.hideTip(e):(P.delayInProgress=!0,l=setTimeout(function(){l=null,o.hideTip(e),P.delayInProgress=!1},t.closeDelay)))}function s(){var i=Math.abs(P.previousX-P.currentX),s=Math.abs(P.previousY-P.currentY),r=i+s;t.intentSensitivity>r?o.showTip(e):(P.previousX=P.currentX,P.previousY=P.currentY,n())}function r(){l=clearTimeout(l),P.delayInProgress=!1}function a(){o.resetPosition(e)}var l=null;this.show=n,this.hide=i,this.cancel=r,this.resetPosition=a}function n(){function e(e,i,r,a,l){var p,c=i.split("-")[0],u=new t;switch(p=s(e)?n(e,c):o(e,c),i){case"n":u.set("left",p.left-r/2),u.set("bottom",P.windowHeight-p.top+l);break;case"e":u.set("left",p.left+l),u.set("top",p.top-a/2);break;case"s":u.set("left",p.left-r/2),u.set("top",p.top+l);break;case"w":u.set("top",p.top-a/2),u.set("right",P.windowWidth-p.left+l);break;case"nw":u.set("bottom",P.windowHeight-p.top+l),u.set("right",P.windowWidth-p.left-20);break;case"nw-alt":u.set("left",p.left),u.set("bottom",P.windowHeight-p.top+l);break;case"ne":u.set("left",p.left-20),u.set("bottom",P.windowHeight-p.top+l);break;case"ne-alt":u.set("bottom",P.windowHeight-p.top+l),u.set("right",P.windowWidth-p.left);break;case"sw":u.set("top",p.top+l),u.set("right",P.windowWidth-p.left-20);break;case"sw-alt":u.set("left",p.left),u.set("top",p.top+l);break;case"se":u.set("left",p.left-20),u.set("top",p.top+l);break;case"se-alt":u.set("top",p.top+l),u.set("right",P.windowWidth-p.left)}return u}function o(e,t){var o,n,i=e.offset(),s=e.outerWidth(),r=e.outerHeight();switch(t){case"n":o=i.left+s/2,n=i.top;break;case"e":o=i.left+s,n=i.top+r/2;break;case"s":o=i.left+s/2,n=i.top+r;break;case"w":o=i.left,n=i.top+r/2;break;case"nw":o=i.left,n=i.top;break;case"ne":o=i.left+s,n=i.top;break;case"sw":o=i.left,n=i.top+r;break;case"se":o=i.left+s,n=i.top+r}return{top:n,left:o}}function n(e,t){function o(){d.push(p.matrixTransform(u))}var n,i,s,r,a=e.closest("svg")[0],l=e[0],p=a.createSVGPoint(),c=l.getBBox(),u=l.getScreenCTM(),f=c.width/2,w=c.height/2,d=[],h=["nw","n","ne","e","se","s","sw","w"];if(p.x=c.x,p.y=c.y,o(),p.x+=f,o(),p.x+=f,o(),p.y+=w,o(),p.y+=w,o(),p.x-=f,o(),p.x-=f,o(),p.y-=w,o(),d[0].y!==d[1].y||d[0].x!==d[7].x)for(i=Math.atan2(u.b,u.a)*O,s=Math.ceil((i%360-22.5)/45),1>s&&(s+=8);s--;)h.push(h.shift());for(r=0;d.length>r;r++)if(h[r]===t){n=d[r];break}return{top:n.y+P.scrollTop,left:n.x+P.scrollLeft}}this.compute=e}function i(o){function i(e){e.data(v,!0),O.queue(function(t){s(e),t()})}function s(e){var t;if(e.data(v)){if(P.isTipOpen)return P.isClosing||r(P.activeHover),O.delay(100).queue(function(t){s(e),t()}),void 0;e.trigger("powerTipPreRender"),t=p(e),t&&(O.empty().append(t),e.trigger("powerTipRender"),P.activeHover=e,P.isTipOpen=!0,O.data(g,o.mouseOnToPopup),o.followMouse?a():(b(e),P.isFixedTipOpen=!0),O.fadeIn(o.fadeInTime,function(){P.desyncTimeout||(P.desyncTimeout=setInterval(H,500)),e.trigger("powerTipOpen")}))}}function r(e){P.isClosing=!0,P.activeHover=null,P.isTipOpen=!1,P.desyncTimeout=clearInterval(P.desyncTimeout),e.data(v,!1),e.data(m,!1),O.fadeOut(o.fadeOutTime,function(){var n=new t;P.isClosing=!1,P.isFixedTipOpen=!1,O.removeClass(),n.set("top",P.currentY+o.offset),n.set("left",P.currentX+o.offset),O.css(n),e.trigger("powerTipClose")})}function a(){if(!P.isFixedTipOpen&&(P.isTipOpen||P.tipOpenImminent&&O.data(T))){var e,n,i=O.outerWidth(),s=O.outerHeight(),r=new t;r.set("top",P.currentY+o.offset),r.set("left",P.currentX+o.offset),e=c(r,i,s),e!==I.none&&(n=u(e),1===n?e===I.right?r.set("left",P.windowWidth-i):e===I.bottom&&r.set("top",P.scrollTop+P.windowHeight-s):(r.set("left",P.currentX-i-o.offset),r.set("top",P.currentY-s-o.offset))),O.css(r)}}function b(t){var n,i;o.smartPlacement?(n=e.fn.powerTip.smartPlacementLists[o.placement],e.each(n,function(e,o){var n=c(y(t,o),O.outerWidth(),O.outerHeight());return i=o,n===I.none?!1:void 0})):(y(t,o.placement),i=o.placement),O.addClass(i)}function y(e,n){var i,s,r=0,a=new t;a.set("top",0),a.set("left",0),O.css(a);do i=O.outerWidth(),s=O.outerHeight(),a=k.compute(e,n,i,s,o.offset),O.css(a);while(5>=++r&&(i!==O.outerWidth()||s!==O.outerHeight()));return a}function H(){var e=!1;!P.isTipOpen||P.isClosing||P.delayInProgress||(P.activeHover.data(v)===!1||P.activeHover.is(":disabled")?e=!0:l(P.activeHover)||P.activeHover.is(":focus")||P.activeHover.data(m)||(O.data(g)?l(O)||(e=!0):e=!0),e&&r(P.activeHover))}var k=new n,O=e("#"+o.popupId);0===O.length&&(O=e("
",{id:o.popupId}),0===d.length&&(d=e("body")),d.append(O)),o.followMouse&&(O.data(T)||(f.on("mousemove",a),w.on("scroll",a),O.data(T,!0))),o.mouseOnToPopup&&O.on({mouseenter:function(){O.data(g)&&P.activeHover&&P.activeHover.data(h).cancel()},mouseleave:function(){P.activeHover&&P.activeHover.data(h).hide()}}),this.showTip=i,this.hideTip=r,this.resetPosition=b}function s(e){return window.SVGElement&&e[0]instanceof SVGElement}function r(){P.mouseTrackingActive||(P.mouseTrackingActive=!0,e(function(){P.scrollLeft=w.scrollLeft(),P.scrollTop=w.scrollTop(),P.windowWidth=w.width(),P.windowHeight=w.height()}),f.on("mousemove",a),w.on({resize:function(){P.windowWidth=w.width(),P.windowHeight=w.height()},scroll:function(){var e=w.scrollLeft(),t=w.scrollTop();e!==P.scrollLeft&&(P.currentX+=e-P.scrollLeft,P.scrollLeft=e),t!==P.scrollTop&&(P.currentY+=t-P.scrollTop,P.scrollTop=t)}}))}function a(e){P.currentX=e.pageX,P.currentY=e.pageY}function l(e){var t=e.offset(),o=e[0].getBoundingClientRect(),n=o.right-o.left,i=o.bottom-o.top;return P.currentX>=t.left&&P.currentX<=t.left+n&&P.currentY>=t.top&&P.currentY<=t.top+i}function p(t){var o,n,i=t.data(y),s=t.data(H),r=t.data(k);return i?(e.isFunction(i)&&(i=i.call(t[0])),n=i):s?(e.isFunction(s)&&(s=s.call(t[0])),s.length>0&&(n=s.clone(!0,!0))):r&&(o=e("#"+r),o.length>0&&(n=o.html())),n}function c(e,t,o){var n=P.scrollTop,i=P.scrollLeft,s=n+P.windowHeight,r=i+P.windowWidth,a=I.none;return(n>e.top||n>Math.abs(e.bottom-P.windowHeight)-o)&&(a|=I.top),(e.top+o>s||Math.abs(e.bottom-P.windowHeight)>s)&&(a|=I.bottom),(i>e.left||e.right+t>r)&&(a|=I.left),(e.left+t>r||i>e.right)&&(a|=I.right),a}function u(e){for(var t=0;e;)e&=e-1,t++;return t}var f=e(document),w=e(window),d=e("body"),h="displayController",v="hasActiveHover",m="forcedOpen",T="hasMouseMove",g="mouseOnToPopup",b="originalTitle",y="powertip",H="powertipjq",k="powertiptarget",O=180/Math.PI,P={isTipOpen:!1,isFixedTipOpen:!1,isClosing:!1,tipOpenImminent:!1,activeHover:null,currentX:0,currentY:0,previousX:0,previousY:0,desyncTimeout:null,mouseTrackingActive:!1,delayInProgress:!1,windowWidth:0,windowHeight:0,scrollTop:0,scrollLeft:0},I={none:0,top:1,bottom:2,left:4,right:8};e.fn.powerTip=function(t,n){if(!this.length)return this;if("string"===e.type(t)&&e.powerTip[t])return e.powerTip[t].call(this,this,n);var s=e.extend({},e.fn.powerTip.defaults,t),a=new i(s);return r(),this.each(function(){var t,n=e(this),i=n.data(y),r=n.data(H),l=n.data(k);n.data(h)&&e.powerTip.destroy(n),t=n.attr("title"),i||l||r||!t||(n.data(y,t),n.data(b,t),n.removeAttr("title")),n.data(h,new o(n,s,a))}),s.manual||this.on({"mouseenter.powertip":function(t){e.powerTip.show(this,t)},"mouseleave.powertip":function(){e.powerTip.hide(this)},"focus.powertip":function(){e.powerTip.show(this)},"blur.powertip":function(){e.powerTip.hide(this,!0)},"keydown.powertip":function(t){27===t.keyCode&&e.powerTip.hide(this,!0)}}),this},e.fn.powerTip.defaults={fadeInTime:200,fadeOutTime:100,followMouse:!1,popupId:"powerTip",intentSensitivity:7,intentPollInterval:100,closeDelay:100,placement:"n",smartPlacement:!1,offset:10,mouseOnToPopup:!1,manual:!1},e.fn.powerTip.smartPlacementLists={n:["n","ne","nw","s"],e:["e","ne","se","w","nw","sw","n","s","e"],s:["s","se","sw","n"],w:["w","nw","sw","e","ne","se","n","s","w"],nw:["nw","w","sw","n","s","se","nw"],ne:["ne","e","se","n","s","sw","ne"],sw:["sw","w","nw","s","n","ne","sw"],se:["se","e","ne","s","n","nw","se"],"nw-alt":["nw-alt","n","ne-alt","sw-alt","s","se-alt","w","e"],"ne-alt":["ne-alt","n","nw-alt","se-alt","s","sw-alt","e","w"],"sw-alt":["sw-alt","s","se-alt","nw-alt","n","ne-alt","w","e"],"se-alt":["se-alt","s","sw-alt","ne-alt","n","nw-alt","e","w"]},e.powerTip={show:function(t,o){return o?(a(o),P.previousX=o.pageX,P.previousY=o.pageY,e(t).data(h).show()):e(t).first().data(h).show(!0,!0),t},reposition:function(t){return e(t).first().data(h).resetPosition(),t},hide:function(t,o){return t?e(t).first().data(h).hide(o):P.activeHover&&P.activeHover.data(h).hide(!0),t},destroy:function(t){return e(t).off(".powertip").each(function(){var t=e(this),o=[b,h,v,m];t.data(b)&&(t.attr("title",t.data(b)),o.push(y)),t.removeData(o)}),t}},e.powerTip.showTip=e.powerTip.show,e.powerTip.closeTip=e.powerTip.hide}); \ No newline at end of file diff --git a/i/main.js b/i/main.js index d6d72cb..f238379 100644 --- a/i/main.js +++ b/i/main.js @@ -40,6 +40,16 @@ $(document).ready(function() { $('.table-layout').css('margin-top',$('.navbar-collapse').height()-34); }); + // Set up powertip and optional location + $('[data-powertip]').each(function() { + var opts = {}; + + if($(this).data('powertippos')!=undefined) + opts.placement = $(this).data('powertippos'); + + $(this).powerTip(opts); + }); + var socket = io.connect(''); socket.on('serverError', function (data) { From 5fe42de5f98c522b78adc78ec11780d7a79be1ab Mon Sep 17 00:00:00 2001 From: Garrett Bartley Date: Sun, 28 Feb 2016 23:43:17 -0500 Subject: [PATCH 10/22] Added warning --- config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/config.js b/config.js index f73da2f..9c0dd4c 100644 --- a/config.js +++ b/config.js @@ -1,3 +1,4 @@ +// !!! All values that are distance measures should be entered in millimeters !!! var config = {}; From 03b263890d306198686571ff86b0ac313f29ab3b Mon Sep 17 00:00:00 2001 From: Garrett Bartley Date: Sun, 28 Feb 2016 23:44:51 -0500 Subject: [PATCH 11/22] Removed commented code. Fixed bit radius offsets for #probeAll --- i/main.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/i/main.js b/i/main.js index f238379..23e7a26 100644 --- a/i/main.js +++ b/i/main.js @@ -301,7 +301,6 @@ $(document).ready(function() { }); $('#probeX').on('click', function() { - // var offsetX = parseFloat($('#probeOffsetX').val())+parseFloat($('#probeBitDiameter').val()/2); var offsetX = parseFloat($('#probeOffsetX').val())+parseFloat($('#probeBitDiameter').val()/2); // Remember the units of measurement before we start the probe @@ -318,7 +317,6 @@ $(document).ready(function() { }); $('#probeY').on('click', function() { - // var offsetY = parseFloat($('#probeOffsetY').val())+parseFloat($('#probeBitDiameter').val()/2); var offsetY = parseFloat($('#probeOffsetY').val())+parseFloat($('#probeBitDiameter').val()/2); // Remember the units of measurement before we start the probe @@ -351,11 +349,8 @@ $(document).ready(function() { }); $('#probeAll').on('click', function() { - // var offsetX = parseFloat($('#probeOffsetX').val())+parseFloat($('#probeBitDiameter').val()/2)+10; - // var offsetY = parseFloat($('#probeOffsetY').val())+parseFloat($('#probeBitDiameter').val()/2)+10; - // var offsetZ = parseFloat($('#probeOffsetZ').val())+10; - var offsetX = parseFloat($('#probeOffsetX').val())+parseFloat($('#probeBitDiameter').val())+10; - var offsetY = parseFloat($('#probeOffsetY').val())+parseFloat($('#probeBitDiameter').val())+10; + var offsetX = parseFloat($('#probeOffsetX').val())+parseFloat($('#probeBitDiameter').val()/2)+10; + var offsetY = parseFloat($('#probeOffsetY').val())+parseFloat($('#probeBitDiameter').val()/2)+10; var offsetZ = parseFloat($('#probeOffsetZ').val())+10; $('#probeZ').click(); // Probe Z From 2358402be048ed4a15da5e1a18b3013a39c9b805 Mon Sep 17 00:00:00 2001 From: Garrett Bartley Date: Sun, 28 Feb 2016 23:59:16 -0500 Subject: [PATCH 12/22] Convert feed rate when toggling between units of measurement. Fixes #4 --- i/main.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/i/main.js b/i/main.js index 23e7a26..c470ed1 100644 --- a/i/main.js +++ b/i/main.js @@ -183,17 +183,30 @@ $(document).ready(function() { // Only attempt to change things if the unit of measurement has changed if(data.unitsOfMeasurement!=lastUnitsOfMeasurement) { - lastUnitsOfMeasurement = data.unitsOfMeasurement; + $('.unitsOfMeasurementText').text(data.unitsOfMeasurement); - $('.unitsOfMeasurementText').text(lastUnitsOfMeasurement); + // Values for jog options + var step_incr = parseFloat($('#jogSize').val()); + var step_feed = parseFloat($('#jogSpeed').val()); if(data.unitsOfMeasurement.toUpperCase()=='IN') { $('#setInches').addClass('btn-primary'); $('#setMillimeters').removeClass('btn-primary'); + + $('#jogSize').val(step_incr/25.4); + $('#jogSpeed').val(step_feed/25.4); } else if(data.unitsOfMeasurement.toUpperCase()=='MM') { $('#setInches').removeClass('btn-primary'); $('#setMillimeters').addClass('btn-primary'); + + // Don't do any conversion if this is the first page load + if(lastUnitsOfMeasurement!='') { + $('#jogSize').val(step_incr*25.4); + $('#jogSpeed').val(step_feed*25.4); + } } + + lastUnitsOfMeasurement = data.unitsOfMeasurement; } }); From 42f962ab35f2d507f906487ad151da6b8c524a5b Mon Sep 17 00:00:00 2001 From: Garrett Bartley Date: Mon, 29 Feb 2016 00:08:35 -0500 Subject: [PATCH 13/22] Added a safety distance for movement on Z axis (your spindle and wasteboard will thank you). Fixes #3 --- config.js | 3 +++ i/main.js | 44 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/config.js b/config.js index 9c0dd4c..5c73787 100644 --- a/config.js +++ b/config.js @@ -35,6 +35,9 @@ config.probeControlYOffset = -1.5; // Z offset for probe config.probeControlZOffset = 3.1; +// Maximum you can move Z with any 1 command +config.maxMoveZ = 50; + // Auto-read temperature config.enablePiTemperature = 1; config.piTemperatureFahrenheit = 1; diff --git a/i/main.js b/i/main.js index c470ed1..66f8e19 100644 --- a/i/main.js +++ b/i/main.js @@ -56,12 +56,14 @@ $(document).ready(function() { alert(data); }); + socket.on('gcodeFromJscut', function (data) { $('#command').val(data.val); openGCodeFromText(); alert('new data from jscut'); }); + // config from server socket.on('config', function (data) { console.log('config', data); @@ -117,6 +119,7 @@ $(document).ready(function() { $('#controlTabLink').click().parent().click(); }); + socket.on('ports', function (data) { $('#choosePort').html(''); for (var i=0; i'; @@ -430,20 +435,55 @@ $(document).ready(function() { $('#xM').on('click', function() { socket.emit('gcodeLine', { line: 'G91\nG1 F'+$('#jogSpeed').val()+' X-'+$('#jogSize').val()+'\nG90'}); }); + $('#xP').on('click', function() { socket.emit('gcodeLine', { line: 'G91\nG1 F'+$('#jogSpeed').val()+' X'+$('#jogSize').val()+'\nG90'}); }); + $('#yP').on('click', function() { socket.emit('gcodeLine', { line: 'G91\nG1 F'+$('#jogSpeed').val()+' Y'+$('#jogSize').val()+'\nG90'}); }); + $('#yM').on('click', function() { socket.emit('gcodeLine', { line: 'G91\nG1 F'+$('#jogSpeed').val()+' Y-'+$('#jogSize').val()+'\nG90'}); }); + $('#zP').on('click', function() { - socket.emit('gcodeLine', { line: 'G91\nG1 F'+$('#jogSpeed').val()+' Z'+$('#jogSize').val()+'\nG90'}); + var distance = parseFloat($('#jogSize').val()); + + if(lastUnitsOfMeasurement=='in') + distance = distance*25.4 + + if(distance>=config.maxMoveZ) + distance = config.maxMoveZ; + + if(lastUnitsOfMeasurement=='in') + distance = distance/25.4; + + // Throw a warning in the console + $('#console').append('

!!!!: Only moved '+distance+lastUnitsOfMeasurement+' due to maxMoveZ safety limit

'); + $('#console').scrollTop($("#console")[0].scrollHeight - $("#console").height()); + + socket.emit('gcodeLine', { line: 'G91\nG1 F'+$('#jogSpeed').val()+' Z'+distance+'\nG90'}); }); + $('#zM').on('click', function() { - socket.emit('gcodeLine', { line: 'G91\nG1 F'+$('#jogSpeed').val()+' Z-'+$('#jogSize').val()+'\nG90'}); + var distance = parseFloat($('#jogSize').val()); + + if(lastUnitsOfMeasurement=='in') + distance = distance*25.4 + + if(distance>=config.maxMoveZ) + distance = config.maxMoveZ; + + if(lastUnitsOfMeasurement=='in') + distance = distance/25.4; + + // Throw a warning in the console + $('#console').append('

!!!!: Only moved '+distance+lastUnitsOfMeasurement+' due to maxMoveZ safety limit

'); + $('#console').scrollTop($("#console")[0].scrollHeight - $("#console").height()); + + socket.emit('gcodeLine', { line: 'G91\nG1 F'+$('#jogSpeed').val()+' Z-'+distance+'\nG90'}); }); // WASD and up/down keys From 2348c885ffe9bb9d2e8dc3e04a2ad98cfb246d34 Mon Sep 17 00:00:00 2001 From: Bryant Chandler Date: Thu, 29 Dec 2016 17:22:56 -0600 Subject: [PATCH 14/22] preliminary fixes for new serial port library --- config.js | 4 ++-- package.json | 2 +- server.js | 9 ++++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/config.js b/config.js index 5c73787..3497f3f 100644 --- a/config.js +++ b/config.js @@ -18,7 +18,7 @@ config.enableSimpleControls = 1; config.enableProbeControls = 1; // Enable jsCut -config.enableJsCut = 0; +config.enableJsCut = 1; // Default step increment for jogging config.jogControlDefaultIncr = 0.1; @@ -39,7 +39,7 @@ config.probeControlZOffset = 3.1; config.maxMoveZ = 50; // Auto-read temperature -config.enablePiTemperature = 1; +config.enablePiTemperature = 0; config.piTemperatureFahrenheit = 1; config.piTemperatureFile = [ '/sys/class/thermal/thermal_zone0/temp', diff --git a/package.json b/package.json index eb5b6f7..7475895 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "dependencies": { "node-static": "0.7.x", "require-reload": "^0.2.2", - "serialport": "2.0.x", + "serialport": "4.0.x", "socket.io": "1.0.x" } } diff --git a/server.js b/server.js index f160622..ed4e7a8 100644 --- a/server.js +++ b/server.js @@ -30,7 +30,7 @@ */ var reload = require('require-reload')(require); -var config = reload('./config.js'); +//var config = reload('./config.js'); var config = require('./config'); var serialport = require("serialport"); var SerialPort = serialport.SerialPort; // localize object constructor @@ -48,6 +48,7 @@ config.showWebCam = false; // Monitor config.js for changes +/* fs.watch('./config.js', function(e, f) { console.log('config.js changed, reloading'); console.log('config: '+JSON.stringify(config)); @@ -57,6 +58,7 @@ fs.watch('./config.js', function(e, f) { for(var i in io.sockets.connected) io.sockets.connected[i].emit('config', config); }); +*/ http.get('http://127.0.0.1:8080', function(res) { @@ -97,7 +99,7 @@ function handler (req, res) { }); } else { fileServer.serve(req, res, function (err, result) { - if (err) console.log('fileServer error: ',err); + if (err) console.log('fileServer error: ', err, req.url); }); } } @@ -150,7 +152,8 @@ function doSerialPortList() { // loop for status ? setInterval(function() { - sp[i].handle.write('?'); + sp[i].handle.write('?', function(err) { + }); }, 1000); }); From e3334b4e08aac1e5f125501389ac3142223ab12a Mon Sep 17 00:00:00 2001 From: Bryant Chandler Date: Thu, 29 Dec 2016 17:24:26 -0600 Subject: [PATCH 15/22] changed whitespace for consistency. --- server.js | 200 +++++++++++++++++++++++++++--------------------------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/server.js b/server.js index ed4e7a8..56a3fe0 100644 --- a/server.js +++ b/server.js @@ -4,28 +4,28 @@ /* - GRBLWeb - a web based CNC controller for GRBL - Copyright (C) 2015 Andrew Hodel + GRBLWeb - a web based CNC controller for GRBL + Copyright (C) 2015 Andrew Hodel - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ @@ -51,7 +51,7 @@ config.showWebCam = false; /* fs.watch('./config.js', function(e, f) { console.log('config.js changed, reloading'); - console.log('config: '+JSON.stringify(config)); + console.log('config: '+JSON.stringify(config)); config = reload('./config.js'); @@ -107,7 +107,7 @@ function handler (req, res) { function ConvChar( str ) { c = {'<':'<', '>':'>', '&':'&', '"':'"', "'":''', - '#':'#' }; + '#':'#' }; return str.replace( /[<&>'"#]/g, function(s) { return c[s]; } ); } @@ -117,56 +117,56 @@ var allPorts = []; var piTemp = []; function doSerialPortList() { - serialport.list(function (err, ports) { - // if on rPi - http://www.hobbytronics.co.uk/raspberry-pi-serial-port - if (fs.existsSync('/dev/ttyAMA0') && config.usettyAMA0 == 1) { - (ports = ports || []).push({comName:'/dev/ttyAMA0',manufacturer: undefined,pnpId: 'raspberryPi__GPIO'}); - console.log('adding /dev/ttyAMA0 because it is enabled in config.js, you may need to enable it in the os - http://www.hobbytronics.co.uk/raspberry-pi-serial-port'); - } - - allPorts = ports; - - for (var i=0; iRESP: '+data+''}); - // This is where we're likely to see the units of measurement response - // Inches - if(data.indexOf(' G20 ')>=0) - unitsOfMeasurement = 'in'; + // This is where we're likely to see the units of measurement response + // Inches + if(data.indexOf(' G20 ')>=0) + unitsOfMeasurement = 'in'; - if(data.indexOf(' G21 ')>=0) - unitsOfMeasurement = 'mm'; + if(data.indexOf(' G21 ')>=0) + unitsOfMeasurement = 'mm'; } if (sp[port].q.length == 0) { @@ -311,13 +311,13 @@ io.sockets.on('connection', function (socket) { socket.emit('ports', allPorts); socket.emit('config', config); - socket.on('refreshPorts', function(data) { - doSerialPortList(); + socket.on('refreshPorts', function(data) { + doSerialPortList(); - setTimeout(function() { - socket.emit('ports', allPorts); - }, 1000); - }); + setTimeout(function() { + socket.emit('ports', allPorts); + }, 1000); + }); // do soft reset, this has it's own clear and direct function call socket.on('doReset', function (data) { @@ -332,10 +332,10 @@ io.sockets.on('connection', function (socket) { // lines from web ui socket.on('gcodeLine', function (data) { - if (typeof currentSocketPort[socket.id] != 'undefined') { - // Append a $G if G20, G21, or $X is detected - if(data.line.toUpperCase().indexOf("G20")>=0 || data.line.toUpperCase().indexOf("G21")>=0 || data.line.toUpperCase().indexOf("$X")>=0) - data.line += "\n$G"; + if (typeof currentSocketPort[socket.id] != 'undefined') { + // Append a $G if G20, G21, or $X is detected + if(data.line.toUpperCase().indexOf("G20")>=0 || data.line.toUpperCase().indexOf("G21")>=0 || data.line.toUpperCase().indexOf("$X")>=0) + data.line += "\n$G"; // valid serial port selected, safe to send // split newlines @@ -401,7 +401,7 @@ io.sockets.on('connection', function (socket) { currentSocketPort[socket.id] = data; sp[data].sockets.push(socket); - // add to queue + // add to queue sp[currentSocketPort[socket.id]].q = sp[currentSocketPort[socket.id]].q.concat('$G'); // add to qCurrentMax sp[currentSocketPort[socket.id]].qCurrentMax += 1; @@ -419,19 +419,19 @@ io.sockets.on('connection', function (socket) { function getSensors() { - for(var i in config.piTemperatureFile) { - var data = fs.readFileSync(config.piTemperatureFile[i]).toString(); + for(var i in config.piTemperatureFile) { + var data = fs.readFileSync(config.piTemperatureFile[i]).toString(); - // Most likely a DS18B20 - if(data.indexOf('crc')>=0 && data.indexOf('YES')>=0) { - piTemp[i] = parseFloat(data.substr(data.indexOf('t=')+2).trim())/1000; + // Most likely a DS18B20 + if(data.indexOf('crc')>=0 && data.indexOf('YES')>=0) { + piTemp[i] = parseFloat(data.substr(data.indexOf('t=')+2).trim())/1000; - // Maybe the internal temp - } else - piTemp[i] = parseFloat(data)/1000; - } + // Maybe the internal temp + } else + piTemp[i] = parseFloat(data)/1000; + } - emitToAllPortSockets('sensors', piTemp); + emitToAllPortSockets('sensors', piTemp); - setTimeout(getSensors, 2000); + setTimeout(getSensors, 2000); } From d55ecd509667857c80d6680212e1d770e05dd732 Mon Sep 17 00:00:00 2001 From: Bryant Chandler Date: Thu, 29 Dec 2016 20:29:39 -0600 Subject: [PATCH 16/22] Various bug fixes, and reconfigured the UI to a little more intuitive layout. --- i/gcode-viewer/gcode-parser.js | 59 +++++++++--------- i/index.html | 105 +++++++++++++++++++-------------- i/main.js | 18 +++++- server.js | 3 +- 4 files changed, 111 insertions(+), 74 deletions(-) diff --git a/i/gcode-viewer/gcode-parser.js b/i/gcode-viewer/gcode-parser.js index d7dfad7..fbb3ee8 100644 --- a/i/gcode-viewer/gcode-parser.js +++ b/i/gcode-viewer/gcode-parser.js @@ -1,35 +1,40 @@ function GCodeParser(handlers) { - this.handlers = handlers || {}; + this.handlers = handlers || {}; } GCodeParser.prototype.parseLine = function(text, info) { - text = text.replace(/;.*$/, '').trim(); // Remove comments - if (text) { - var tokens = text.split(' '); - if (tokens) { - var cmd = tokens[0]; - var args = { - 'cmd': cmd - }; - tokens.splice(1).forEach(function(token) { - var key = token[0].toLowerCase(); - var value = parseFloat(token.substring(1)); - args[key] = value; - }); - var handler = this.handlers[tokens[0]] || this.handlers['default']; - if (handler) { - return handler(args, info); - } - } - } + text = text.replace(/;.*$/, '').trim(); // Remove comments + if (text) { + var tokens = text.split(' '); + if (tokens) { + var cmd = tokens[0]; + var args = { + 'cmd': cmd + }; + tokens.splice(1).forEach(function(token) { + //console.log(token); + if (token[0]) { + var key = token[0].toLowerCase(); + var value = parseFloat(token.substring(1)); + args[key] = value; + } else { + console.log("null token 1 : " + token); + } + }); + var handler = this.handlers[tokens[0]] || this.handlers['default']; + if (handler) { + return handler(args, info); + } + } + } }; GCodeParser.prototype.parse = function(gcode) { - var lines = gcode.split('\n'); - for (var i = 0; i < lines.length; i++) { - if (this.parseLine(lines[i], i) === false) { - console.log('hmm'); - break; - } - } + var lines = gcode.split('\n'); + for (var i = 0; i < lines.length; i++) { + if (this.parseLine(lines[i], i) === false) { + console.log('hmm'); + break; + } + } }; diff --git a/i/index.html b/i/index.html index 57f08a5..ef20e9e 100644 --- a/i/index.html +++ b/i/index.html @@ -32,7 +32,7 @@ --> - + @@ -102,7 +102,7 @@ #renderArea { background-color: #ffffff; - height: 300px; + height: 600px; border: 1px solid #333; } @@ -174,7 +174,6 @@ width: 4em; } - #mX, #mY, #mZ, #wX, #wY, #wZ { text-align: left; @@ -218,11 +217,13 @@ text-align: right; } +/* #sendZero, #setInches, #setMillimeters { margin-top: 5px; } +*/ #queueProgress { min-width: 2em; @@ -338,27 +339,42 @@
- + +
+
+
+
0%
+
+
+
+
@@ -566,24 +601,6 @@
- -
-
- - - - - -
-
- - -
-
- Upload GCODE - -
-
diff --git a/i/main.js b/i/main.js index 66f8e19..55d7d49 100644 --- a/i/main.js +++ b/i/main.js @@ -53,7 +53,7 @@ $(document).ready(function() { var socket = io.connect(''); socket.on('serverError', function (data) { - alert(data); + console.log(data); }); @@ -392,6 +392,22 @@ $(document).ready(function() { $('#sendZero').on('click', function() { socket.emit('gcodeLine', { line: 'G92 X0 Y0 Z0' }); + socket.emit('gcodeLine', { line: 'G28.1' }); + }); + + $('#sendZeroX').on('click', function() { + socket.emit('gcodeLine', { line: 'G92 X0' }); + socket.emit('gcodeLine', { line: 'G28.1 X0' }); + }); + + $('#sendZeroY').on('click', function() { + socket.emit('gcodeLine', { line: 'G92 Y0' }); + socket.emit('gcodeLine', { line: 'G28.1 Y0' }); + }); + + $('#sendZeroZ').on('click', function() { + socket.emit('gcodeLine', { line: 'G92 Z0' }); + socket.emit('gcodeLine', { line: 'G28.1 Z0' }); }); $('#setInches').on('click', function() { diff --git a/server.js b/server.js index 56a3fe0..80f58f1 100644 --- a/server.js +++ b/server.js @@ -33,7 +33,6 @@ var reload = require('require-reload')(require); //var config = reload('./config.js'); var config = require('./config'); var serialport = require("serialport"); -var SerialPort = serialport.SerialPort; // localize object constructor var app = require('http').createServer(handler) , io = require('socket.io').listen(app) , fs = require('fs'); @@ -136,7 +135,7 @@ function doSerialPortList() { sp[i].lastSerialWrite = []; sp[i].lastSerialReadLine = ''; // 1 means clear to send, 0 means waiting for response - sp[i].handle = new SerialPort(ports[i].comName, { + sp[i].handle = new serialport(ports[i].comName, { parser: serialport.parsers.readline("\n"), baudrate: config.serialBaudRate }); From 8f56db456251a1d65d14233c982b75a7ec74d81e Mon Sep 17 00:00:00 2001 From: Bryant Chandler Date: Fri, 30 Dec 2016 09:51:26 -0600 Subject: [PATCH 17/22] added config setting to hide unit backdrop. It can be confusing. --- config.js | 14 ++++++++------ i/main.js | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/config.js b/config.js index 3497f3f..cb4f729 100644 --- a/config.js +++ b/config.js @@ -9,16 +9,16 @@ config.webPort = 8000; config.serialBaudRate = 115200; // Use /dev/ttyAMA0 -config.usettyAMA0 = 1; +config.usettyAMA0 = true; // Enable simple jog controls -config.enableSimpleControls = 1; +config.enableSimpleControls = true; // Enable probe controls -config.enableProbeControls = 1; +config.enableProbeControls = true; // Enable jsCut -config.enableJsCut = 1; +config.enableJsCut = true; // Default step increment for jogging config.jogControlDefaultIncr = 0.1; @@ -39,13 +39,15 @@ config.probeControlZOffset = 3.1; config.maxMoveZ = 50; // Auto-read temperature -config.enablePiTemperature = 0; -config.piTemperatureFahrenheit = 1; +config.enablePiTemperature = false; +config.piTemperatureFahrenheit = true; config.piTemperatureFile = [ '/sys/class/thermal/thermal_zone0/temp', '/sys/bus/w1/devices/28-000002aa87dd/w1_slave' ]; +config.showUnitsBackdrop = false; + // TO DO: // - Add probe diameters // - Allow configuration of gcode for probing buttons diff --git a/i/main.js b/i/main.js index 55d7d49..1d5fc4a 100644 --- a/i/main.js +++ b/i/main.js @@ -188,7 +188,8 @@ $(document).ready(function() { // Only attempt to change things if the unit of measurement has changed if(data.unitsOfMeasurement!=lastUnitsOfMeasurement) { - $('.unitsOfMeasurementText').text(data.unitsOfMeasurement); + if (config.showUnitsBackdrop) + $('.unitsOfMeasurementText').text(data.unitsOfMeasurement); // Values for jog options var step_incr = parseFloat($('#jogSize').val()); From 1277ccc007238f232d18411c9972228e5736720e Mon Sep 17 00:00:00 2001 From: chronoglass Date: Tue, 16 May 2017 20:30:22 +0000 Subject: [PATCH 18/22] bug fixes --- config.js | 10 +- i/gcode-viewer/gcode-parser.js | 59 +- i/index.html | 20 +- .../css/jquery.powertip-blue.css | 8 +- .../css/jquery.powertip-blue.min.css | 2 +- .../css/jquery.powertip-dark.css | 8 +- .../css/jquery.powertip-dark.min.css | 2 +- .../css/jquery.powertip-green.css | 8 +- .../css/jquery.powertip-green.min.css | 2 +- .../css/jquery.powertip-light.css | 8 +- .../css/jquery.powertip-light.min.css | 2 +- .../css/jquery.powertip-orange.css | 8 +- .../css/jquery.powertip-orange.min.css | 2 +- .../css/jquery.powertip-purple.css | 8 +- .../css/jquery.powertip-purple.min.css | 2 +- i/jquery.powertip/css/jquery.powertip-red.css | 8 +- .../css/jquery.powertip-red.min.css | 2 +- .../css/jquery.powertip-yellow.css | 8 +- .../css/jquery.powertip-yellow.min.css | 2 +- i/jquery.powertip/css/jquery.powertip.css | 8 +- i/jquery.powertip/css/jquery.powertip.min.css | 2 +- i/jquery.powertip/jquery.powertip.js | 587 ++++++++++++------ i/jquery.powertip/jquery.powertip.min.js | 8 +- package.json | 13 +- server.js | 16 +- 25 files changed, 526 insertions(+), 277 deletions(-) diff --git a/config.js b/config.js index cb4f729..6954357 100644 --- a/config.js +++ b/config.js @@ -3,19 +3,19 @@ var config = {}; // Port to listen on for web site -config.webPort = 8000; +config.webPort = 80; // Serial baud rate to Arduino config.serialBaudRate = 115200; // Use /dev/ttyAMA0 -config.usettyAMA0 = true; +config.usettyAMA0 = 0; // Enable simple jog controls -config.enableSimpleControls = true; +config.enableSimpleControls = 1; // Enable probe controls -config.enableProbeControls = true; +config.enableProbeControls = 1; // Enable jsCut config.enableJsCut = true; @@ -53,6 +53,6 @@ config.showUnitsBackdrop = false; // - Allow configuration of gcode for probing buttons // expects a webcam stream from mjpg_streamer -//config.webcamPort = 8080; +config.webcamPort = 8080; module.exports = config; diff --git a/i/gcode-viewer/gcode-parser.js b/i/gcode-viewer/gcode-parser.js index fbb3ee8..d7dfad7 100644 --- a/i/gcode-viewer/gcode-parser.js +++ b/i/gcode-viewer/gcode-parser.js @@ -1,40 +1,35 @@ function GCodeParser(handlers) { - this.handlers = handlers || {}; + this.handlers = handlers || {}; } GCodeParser.prototype.parseLine = function(text, info) { - text = text.replace(/;.*$/, '').trim(); // Remove comments - if (text) { - var tokens = text.split(' '); - if (tokens) { - var cmd = tokens[0]; - var args = { - 'cmd': cmd - }; - tokens.splice(1).forEach(function(token) { - //console.log(token); - if (token[0]) { - var key = token[0].toLowerCase(); - var value = parseFloat(token.substring(1)); - args[key] = value; - } else { - console.log("null token 1 : " + token); - } - }); - var handler = this.handlers[tokens[0]] || this.handlers['default']; - if (handler) { - return handler(args, info); - } - } - } + text = text.replace(/;.*$/, '').trim(); // Remove comments + if (text) { + var tokens = text.split(' '); + if (tokens) { + var cmd = tokens[0]; + var args = { + 'cmd': cmd + }; + tokens.splice(1).forEach(function(token) { + var key = token[0].toLowerCase(); + var value = parseFloat(token.substring(1)); + args[key] = value; + }); + var handler = this.handlers[tokens[0]] || this.handlers['default']; + if (handler) { + return handler(args, info); + } + } + } }; GCodeParser.prototype.parse = function(gcode) { - var lines = gcode.split('\n'); - for (var i = 0; i < lines.length; i++) { - if (this.parseLine(lines[i], i) === false) { - console.log('hmm'); - break; - } - } + var lines = gcode.split('\n'); + for (var i = 0; i < lines.length; i++) { + if (this.parseLine(lines[i], i) === false) { + console.log('hmm'); + break; + } + } }; diff --git a/i/index.html b/i/index.html index ef20e9e..6040200 100644 --- a/i/index.html +++ b/i/index.html @@ -521,6 +521,14 @@

+ +
+
+
+ Webcam +
+
+
© XYZBots 2015 @@ -534,17 +542,17 @@
-
+ - - +
--> +
diff --git a/i/jquery.powertip/css/jquery.powertip-blue.css b/i/jquery.powertip/css/jquery.powertip-blue.css index d68008a..2a39d33 100644 --- a/i/jquery.powertip/css/jquery.powertip-blue.css +++ b/i/jquery.powertip/css/jquery.powertip-blue.css @@ -1,4 +1,10 @@ -/* PowerTip Plugin */ +/** + * PowerTip + * https://stevenbenner.github.io/jquery-powertip/ + * + * Stylesheet for the blue theme. + */ + #powerTip { cursor: default; background-color: #d2e6fa; diff --git a/i/jquery.powertip/css/jquery.powertip-blue.min.css b/i/jquery.powertip/css/jquery.powertip-blue.min.css index 02338dc..717fa00 100644 --- a/i/jquery.powertip/css/jquery.powertip-blue.min.css +++ b/i/jquery.powertip/css/jquery.powertip-blue.min.css @@ -1 +1 @@ -#powerTip{cursor:default;background-color:#d2e6fa;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #a5d2fa inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #a5d2fa inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #a5d2fa inset;border:1px solid #4b91d2;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #4b91d2;border-top:10px solid rgba(75,145,210,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #4b91d2;border-right:10px solid rgba(75,145,210,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #4b91d2;border-bottom:10px solid rgba(75,145,210,.8);top:-10px}#powerTip.w:before{border-left:10px solid #4b91d2;border-left:10px solid rgba(75,145,210,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #4b91d2;border-top:10px solid rgba(75,145,210,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #4b91d2;border-bottom:10px solid rgba(75,145,210,.8);top:-10px}#powerTip.nw-alt:before,#powerTip.ne-alt:before,#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:10px solid #4b91d2;border-top:10px solid rgba(75,145,210,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before{left:auto;right:10px}#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:0;border-bottom:10px solid #4b91d2;border-bottom:10px solid rgba(75,145,210,.8);bottom:auto;top:-10px}#powerTip.se-alt:before{left:auto;right:10px} \ No newline at end of file +#powerTip{cursor:default;background-color:#d2e6fa;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #a5d2fa inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #a5d2fa inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #a5d2fa inset;border:1px solid #4b91d2;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #4b91d2;border-top:10px solid rgba(75,145,210,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #4b91d2;border-right:10px solid rgba(75,145,210,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #4b91d2;border-bottom:10px solid rgba(75,145,210,.8);top:-10px}#powerTip.w:before{border-left:10px solid #4b91d2;border-left:10px solid rgba(75,145,210,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #4b91d2;border-top:10px solid rgba(75,145,210,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #4b91d2;border-bottom:10px solid rgba(75,145,210,.8);top:-10px}#powerTip.ne-alt:before,#powerTip.nw-alt:before,#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:10px solid #4b91d2;border-top:10px solid rgba(75,145,210,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before,#powerTip.se-alt:before{left:auto;right:10px}#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:none;border-bottom:10px solid #4b91d2;border-bottom:10px solid rgba(75,145,210,.8);bottom:auto;top:-10px} \ No newline at end of file diff --git a/i/jquery.powertip/css/jquery.powertip-dark.css b/i/jquery.powertip/css/jquery.powertip-dark.css index bd39793..c0c4c9f 100644 --- a/i/jquery.powertip/css/jquery.powertip-dark.css +++ b/i/jquery.powertip/css/jquery.powertip-dark.css @@ -1,4 +1,10 @@ -/* PowerTip Plugin */ +/** + * PowerTip + * https://stevenbenner.github.io/jquery-powertip/ + * + * Stylesheet for the dark monochrome theme. + */ + #powerTip { cursor: default; background-color: #424242; diff --git a/i/jquery.powertip/css/jquery.powertip-dark.min.css b/i/jquery.powertip/css/jquery.powertip-dark.min.css index 7342786..9d72a72 100644 --- a/i/jquery.powertip/css/jquery.powertip-dark.min.css +++ b/i/jquery.powertip/css/jquery.powertip-dark.min.css @@ -1 +1 @@ -#powerTip{cursor:default;background-color:#424242;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.2) inset,0 -2px 2px #323232 inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.2) inset,0 -2px 2px #323232 inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.2) inset,0 -2px 2px #323232 inset;border:1px solid #000;border-radius:6px;color:#fff;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #000;border-top:10px solid rgba(0,0,0,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #000;border-right:10px solid rgba(0,0,0,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #000;border-bottom:10px solid rgba(0,0,0,.8);top:-10px}#powerTip.w:before{border-left:10px solid #000;border-left:10px solid rgba(0,0,0,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #000;border-top:10px solid rgba(0,0,0,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #000;border-bottom:10px solid rgba(0,0,0,.8);top:-10px}#powerTip.nw-alt:before,#powerTip.ne-alt:before,#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:10px solid #000;border-top:10px solid rgba(0,0,0,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before{left:auto;right:10px}#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:0;border-bottom:10px solid #000;border-bottom:10px solid rgba(0,0,0,.8);bottom:auto;top:-10px}#powerTip.se-alt:before{left:auto;right:10px} \ No newline at end of file +#powerTip{cursor:default;background-color:#424242;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.2) inset,0 -2px 2px #323232 inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.2) inset,0 -2px 2px #323232 inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.2) inset,0 -2px 2px #323232 inset;border:1px solid #000;border-radius:6px;color:#fff;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #000;border-top:10px solid rgba(0,0,0,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #000;border-right:10px solid rgba(0,0,0,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #000;border-bottom:10px solid rgba(0,0,0,.8);top:-10px}#powerTip.w:before{border-left:10px solid #000;border-left:10px solid rgba(0,0,0,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #000;border-top:10px solid rgba(0,0,0,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #000;border-bottom:10px solid rgba(0,0,0,.8);top:-10px}#powerTip.ne-alt:before,#powerTip.nw-alt:before,#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:10px solid #000;border-top:10px solid rgba(0,0,0,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before,#powerTip.se-alt:before{left:auto;right:10px}#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:none;border-bottom:10px solid #000;border-bottom:10px solid rgba(0,0,0,.8);bottom:auto;top:-10px} \ No newline at end of file diff --git a/i/jquery.powertip/css/jquery.powertip-green.css b/i/jquery.powertip/css/jquery.powertip-green.css index 687e65f..cdc84ff 100644 --- a/i/jquery.powertip/css/jquery.powertip-green.css +++ b/i/jquery.powertip/css/jquery.powertip-green.css @@ -1,4 +1,10 @@ -/* PowerTip Plugin */ +/** + * PowerTip + * https://stevenbenner.github.io/jquery-powertip/ + * + * Stylesheet for the green theme. + */ + #powerTip { cursor: default; background-color: #f0ffb9; diff --git a/i/jquery.powertip/css/jquery.powertip-green.min.css b/i/jquery.powertip/css/jquery.powertip-green.min.css index 47b374f..ad7a644 100644 --- a/i/jquery.powertip/css/jquery.powertip-green.min.css +++ b/i/jquery.powertip/css/jquery.powertip-green.min.css @@ -1 +1 @@ -#powerTip{cursor:default;background-color:#f0ffb9;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.8) inset,0 -2px 2px #dcf582 inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.8) inset,0 -2px 2px #dcf582 inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.8) inset,0 -2px 2px #dcf582 inset;border:1px solid #9bc800;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #9bc800;border-top:10px solid rgba(155,200,0,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #9bc800;border-right:10px solid rgba(155,200,0,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #9bc800;border-bottom:10px solid rgba(155,200,0,.8);top:-10px}#powerTip.w:before{border-left:10px solid #9bc800;border-left:10px solid rgba(155,200,0,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #9bc800;border-top:10px solid rgba(155,200,0,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #9bc800;border-bottom:10px solid rgba(155,200,0,.8);top:-10px}#powerTip.nw-alt:before,#powerTip.ne-alt:before,#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:10px solid #9bc800;border-top:10px solid rgba(155,200,0,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before{left:auto;right:10px}#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:0;border-bottom:10px solid #9bc800;border-bottom:10px solid rgba(155,200,0,.8);bottom:auto;top:-10px}#powerTip.se-alt:before{left:auto;right:10px} \ No newline at end of file +#powerTip{cursor:default;background-color:#f0ffb9;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.8) inset,0 -2px 2px #dcf582 inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.8) inset,0 -2px 2px #dcf582 inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.8) inset,0 -2px 2px #dcf582 inset;border:1px solid #9bc800;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #9bc800;border-top:10px solid rgba(155,200,0,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #9bc800;border-right:10px solid rgba(155,200,0,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #9bc800;border-bottom:10px solid rgba(155,200,0,.8);top:-10px}#powerTip.w:before{border-left:10px solid #9bc800;border-left:10px solid rgba(155,200,0,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #9bc800;border-top:10px solid rgba(155,200,0,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #9bc800;border-bottom:10px solid rgba(155,200,0,.8);top:-10px}#powerTip.ne-alt:before,#powerTip.nw-alt:before,#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:10px solid #9bc800;border-top:10px solid rgba(155,200,0,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before,#powerTip.se-alt:before{left:auto;right:10px}#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:none;border-bottom:10px solid #9bc800;border-bottom:10px solid rgba(155,200,0,.8);bottom:auto;top:-10px} \ No newline at end of file diff --git a/i/jquery.powertip/css/jquery.powertip-light.css b/i/jquery.powertip/css/jquery.powertip-light.css index d4aac23..244fd09 100644 --- a/i/jquery.powertip/css/jquery.powertip-light.css +++ b/i/jquery.powertip/css/jquery.powertip-light.css @@ -1,4 +1,10 @@ -/* PowerTip Plugin */ +/** + * PowerTip + * https://stevenbenner.github.io/jquery-powertip/ + * + * Stylesheet for the light monochrome theme. + */ + #powerTip { cursor: default; background-color: #f2f2f2; diff --git a/i/jquery.powertip/css/jquery.powertip-light.min.css b/i/jquery.powertip/css/jquery.powertip-light.min.css index e1a3e4e..a990638 100644 --- a/i/jquery.powertip/css/jquery.powertip-light.min.css +++ b/i/jquery.powertip/css/jquery.powertip-light.min.css @@ -1 +1 @@ -#powerTip{cursor:default;background-color:#f2f2f2;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #dcdcdc inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #dcdcdc inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #dcdcdc inset;border:1px solid #acacac;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #acacac;border-top:10px solid rgba(172,172,172,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #acacac;border-right:10px solid rgba(172,172,172,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #acacac;border-bottom:10px solid rgba(172,172,172,.8);top:-10px}#powerTip.w:before{border-left:10px solid #acacac;border-left:10px solid rgba(172,172,172,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #acacac;border-top:10px solid rgba(172,172,172,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #acacac;border-bottom:10px solid rgba(172,172,172,.8);top:-10px}#powerTip.nw-alt:before,#powerTip.ne-alt:before,#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:10px solid #acacac;border-top:10px solid rgba(172,172,172,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before{left:auto;right:10px}#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:0;border-bottom:10px solid #acacac;border-bottom:10px solid rgba(172,172,172,.8);bottom:auto;top:-10px}#powerTip.se-alt:before{left:auto;right:10px} \ No newline at end of file +#powerTip{cursor:default;background-color:#f2f2f2;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #dcdcdc inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #dcdcdc inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #dcdcdc inset;border:1px solid #acacac;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #acacac;border-top:10px solid rgba(172,172,172,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #acacac;border-right:10px solid rgba(172,172,172,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #acacac;border-bottom:10px solid rgba(172,172,172,.8);top:-10px}#powerTip.w:before{border-left:10px solid #acacac;border-left:10px solid rgba(172,172,172,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #acacac;border-top:10px solid rgba(172,172,172,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #acacac;border-bottom:10px solid rgba(172,172,172,.8);top:-10px}#powerTip.ne-alt:before,#powerTip.nw-alt:before,#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:10px solid #acacac;border-top:10px solid rgba(172,172,172,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before,#powerTip.se-alt:before{left:auto;right:10px}#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:none;border-bottom:10px solid #acacac;border-bottom:10px solid rgba(172,172,172,.8);bottom:auto;top:-10px} \ No newline at end of file diff --git a/i/jquery.powertip/css/jquery.powertip-orange.css b/i/jquery.powertip/css/jquery.powertip-orange.css index 81880df..441a45c 100644 --- a/i/jquery.powertip/css/jquery.powertip-orange.css +++ b/i/jquery.powertip/css/jquery.powertip-orange.css @@ -1,4 +1,10 @@ -/* PowerTip Plugin */ +/** + * PowerTip + * https://stevenbenner.github.io/jquery-powertip/ + * + * Stylesheet for the orange theme. + */ + #powerTip { cursor: default; background-color: #ffcdaf; diff --git a/i/jquery.powertip/css/jquery.powertip-orange.min.css b/i/jquery.powertip/css/jquery.powertip-orange.min.css index d3c44f7..294f63b 100644 --- a/i/jquery.powertip/css/jquery.powertip-orange.min.css +++ b/i/jquery.powertip/css/jquery.powertip-orange.min.css @@ -1 +1 @@ -#powerTip{cursor:default;background-color:#ffcdaf;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #fab482 inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #fab482 inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #fab482 inset;border:1px solid #f5a550;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #f5a550;border-top:10px solid rgba(245,165,80,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #f5a550;border-right:10px solid rgba(245,165,80,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #f5a550;border-bottom:10px solid rgba(245,165,80,.8);top:-10px}#powerTip.w:before{border-left:10px solid #f5a550;border-left:10px solid rgba(245,165,80,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #f5a550;border-top:10px solid rgba(245,165,80,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #f5a550;border-bottom:10px solid rgba(245,165,80,.8);top:-10px}#powerTip.nw-alt:before,#powerTip.ne-alt:before,#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:10px solid #f5a550;border-top:10px solid rgba(245,165,80,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before{left:auto;right:10px}#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:0;border-bottom:10px solid #f5a550;border-bottom:10px solid rgba(245,165,80,.8);bottom:auto;top:-10px}#powerTip.se-alt:before{left:auto;right:10px} \ No newline at end of file +#powerTip{cursor:default;background-color:#ffcdaf;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #fab482 inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #fab482 inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #fab482 inset;border:1px solid #f5a550;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #f5a550;border-top:10px solid rgba(245,165,80,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #f5a550;border-right:10px solid rgba(245,165,80,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #f5a550;border-bottom:10px solid rgba(245,165,80,.8);top:-10px}#powerTip.w:before{border-left:10px solid #f5a550;border-left:10px solid rgba(245,165,80,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #f5a550;border-top:10px solid rgba(245,165,80,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #f5a550;border-bottom:10px solid rgba(245,165,80,.8);top:-10px}#powerTip.ne-alt:before,#powerTip.nw-alt:before,#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:10px solid #f5a550;border-top:10px solid rgba(245,165,80,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before,#powerTip.se-alt:before{left:auto;right:10px}#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:none;border-bottom:10px solid #f5a550;border-bottom:10px solid rgba(245,165,80,.8);bottom:auto;top:-10px} \ No newline at end of file diff --git a/i/jquery.powertip/css/jquery.powertip-purple.css b/i/jquery.powertip/css/jquery.powertip-purple.css index 0dc8ceb..8198907 100644 --- a/i/jquery.powertip/css/jquery.powertip-purple.css +++ b/i/jquery.powertip/css/jquery.powertip-purple.css @@ -1,4 +1,10 @@ -/* PowerTip Plugin */ +/** + * PowerTip + * https://stevenbenner.github.io/jquery-powertip/ + * + * Stylesheet for the purple theme. + */ + #powerTip { cursor: default; background-color: #ebd2fa; diff --git a/i/jquery.powertip/css/jquery.powertip-purple.min.css b/i/jquery.powertip/css/jquery.powertip-purple.min.css index db43cc7..7b11db9 100644 --- a/i/jquery.powertip/css/jquery.powertip-purple.min.css +++ b/i/jquery.powertip/css/jquery.powertip-purple.min.css @@ -1 +1 @@ -#powerTip{cursor:default;background-color:#ebd2fa;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #d796ff inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #d796ff inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #d796ff inset;border:1px solid #914bd2;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #914bd2;border-top:10px solid rgba(145,75,210,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #914bd2;border-right:10px solid rgba(145,75,210,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #914bd2;border-bottom:10px solid rgba(145,75,210,.8);top:-10px}#powerTip.w:before{border-left:10px solid #914bd2;border-left:10px solid rgba(145,75,210,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #914bd2;border-top:10px solid rgba(145,75,210,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #914bd2;border-bottom:10px solid rgba(145,75,210,.8);top:-10px}#powerTip.nw-alt:before,#powerTip.ne-alt:before,#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:10px solid #914bd2;border-top:10px solid rgba(145,75,210,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before{left:auto;right:10px}#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:0;border-bottom:10px solid #914bd2;border-bottom:10px solid rgba(145,75,210,.8);bottom:auto;top:-10px}#powerTip.se-alt:before{left:auto;right:10px} \ No newline at end of file +#powerTip{cursor:default;background-color:#ebd2fa;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #d796ff inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #d796ff inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #d796ff inset;border:1px solid #914bd2;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #914bd2;border-top:10px solid rgba(145,75,210,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #914bd2;border-right:10px solid rgba(145,75,210,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #914bd2;border-bottom:10px solid rgba(145,75,210,.8);top:-10px}#powerTip.w:before{border-left:10px solid #914bd2;border-left:10px solid rgba(145,75,210,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #914bd2;border-top:10px solid rgba(145,75,210,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #914bd2;border-bottom:10px solid rgba(145,75,210,.8);top:-10px}#powerTip.ne-alt:before,#powerTip.nw-alt:before,#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:10px solid #914bd2;border-top:10px solid rgba(145,75,210,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before,#powerTip.se-alt:before{left:auto;right:10px}#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:none;border-bottom:10px solid #914bd2;border-bottom:10px solid rgba(145,75,210,.8);bottom:auto;top:-10px} \ No newline at end of file diff --git a/i/jquery.powertip/css/jquery.powertip-red.css b/i/jquery.powertip/css/jquery.powertip-red.css index b71eab4..1c8c056 100644 --- a/i/jquery.powertip/css/jquery.powertip-red.css +++ b/i/jquery.powertip/css/jquery.powertip-red.css @@ -1,4 +1,10 @@ -/* PowerTip Plugin */ +/** + * PowerTip + * https://stevenbenner.github.io/jquery-powertip/ + * + * Stylesheet for the red theme. + */ + #powerTip { cursor: default; background-color: #ffc8c3; diff --git a/i/jquery.powertip/css/jquery.powertip-red.min.css b/i/jquery.powertip/css/jquery.powertip-red.min.css index dae3a74..c50b8de 100644 --- a/i/jquery.powertip/css/jquery.powertip-red.min.css +++ b/i/jquery.powertip/css/jquery.powertip-red.min.css @@ -1 +1 @@ -#powerTip{cursor:default;background-color:#ffc8c3;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #f5aa9b inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #f5aa9b inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #f5aa9b inset;border:1px solid #eb5037;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #eb5037;border-top:10px solid rgba(235,80,55,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #eb5037;border-right:10px solid rgba(235,80,55,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #eb5037;border-bottom:10px solid rgba(235,80,55,.8);top:-10px}#powerTip.w:before{border-left:10px solid #eb5037;border-left:10px solid rgba(235,80,55,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #eb5037;border-top:10px solid rgba(235,80,55,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #eb5037;border-bottom:10px solid rgba(235,80,55,.8);top:-10px}#powerTip.nw-alt:before,#powerTip.ne-alt:before,#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:10px solid #eb5037;border-top:10px solid rgba(235,80,55,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before{left:auto;right:10px}#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:0;border-bottom:10px solid #eb5037;border-bottom:10px solid rgba(235,80,55,.8);bottom:auto;top:-10px}#powerTip.se-alt:before{left:auto;right:10px} \ No newline at end of file +#powerTip{cursor:default;background-color:#ffc8c3;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #f5aa9b inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #f5aa9b inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.5) inset,0 -2px 2px #f5aa9b inset;border:1px solid #eb5037;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #eb5037;border-top:10px solid rgba(235,80,55,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #eb5037;border-right:10px solid rgba(235,80,55,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #eb5037;border-bottom:10px solid rgba(235,80,55,.8);top:-10px}#powerTip.w:before{border-left:10px solid #eb5037;border-left:10px solid rgba(235,80,55,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #eb5037;border-top:10px solid rgba(235,80,55,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #eb5037;border-bottom:10px solid rgba(235,80,55,.8);top:-10px}#powerTip.ne-alt:before,#powerTip.nw-alt:before,#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:10px solid #eb5037;border-top:10px solid rgba(235,80,55,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before,#powerTip.se-alt:before{left:auto;right:10px}#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:none;border-bottom:10px solid #eb5037;border-bottom:10px solid rgba(235,80,55,.8);bottom:auto;top:-10px} \ No newline at end of file diff --git a/i/jquery.powertip/css/jquery.powertip-yellow.css b/i/jquery.powertip/css/jquery.powertip-yellow.css index 94c8069..dbf0a6a 100644 --- a/i/jquery.powertip/css/jquery.powertip-yellow.css +++ b/i/jquery.powertip/css/jquery.powertip-yellow.css @@ -1,4 +1,10 @@ -/* PowerTip Plugin */ +/** + * PowerTip + * https://stevenbenner.github.io/jquery-powertip/ + * + * Stylesheet for the yellow theme. + */ + #powerTip { cursor: default; background-color: #ffffb4; diff --git a/i/jquery.powertip/css/jquery.powertip-yellow.min.css b/i/jquery.powertip/css/jquery.powertip-yellow.min.css index a8b8d6b..2ecae25 100644 --- a/i/jquery.powertip/css/jquery.powertip-yellow.min.css +++ b/i/jquery.powertip/css/jquery.powertip-yellow.min.css @@ -1 +1 @@ -#powerTip{cursor:default;background-color:#ffffb4;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.8) inset,0 -2px 2px #fafa6e inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.8) inset,0 -2px 2px #fafa6e inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.8) inset,0 -2px 2px #fafa6e inset;border:1px solid #fafa50;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #fafa50;border-top:10px solid rgba(250,250,80,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #fafa50;border-right:10px solid rgba(250,250,80,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #fafa50;border-bottom:10px solid rgba(250,250,80,.8);top:-10px}#powerTip.w:before{border-left:10px solid #fafa50;border-left:10px solid rgba(250,250,80,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #fafa50;border-top:10px solid rgba(250,250,80,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #fafa50;border-bottom:10px solid rgba(250,250,80,.8);top:-10px}#powerTip.nw-alt:before,#powerTip.ne-alt:before,#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:10px solid #fafa50;border-top:10px solid rgba(250,250,80,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before{left:auto;right:10px}#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:0;border-bottom:10px solid #fafa50;border-bottom:10px solid rgba(250,250,80,.8);bottom:auto;top:-10px}#powerTip.se-alt:before{left:auto;right:10px} \ No newline at end of file +#powerTip{cursor:default;background-color:#ffffb4;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.8) inset,0 -2px 2px #fafa6e inset;-moz-box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.8) inset,0 -2px 2px #fafa6e inset;box-shadow:0 1px 1px rgba(0,0,0,.15),0 2px 1px rgba(255,255,255,.8) inset,0 -2px 2px #fafa6e inset;border:1px solid #fafa50;border-radius:6px;color:#000;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #fafa50;border-top:10px solid rgba(250,250,80,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #fafa50;border-right:10px solid rgba(250,250,80,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #fafa50;border-bottom:10px solid rgba(250,250,80,.8);top:-10px}#powerTip.w:before{border-left:10px solid #fafa50;border-left:10px solid rgba(250,250,80,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #fafa50;border-top:10px solid rgba(250,250,80,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #fafa50;border-bottom:10px solid rgba(250,250,80,.8);top:-10px}#powerTip.ne-alt:before,#powerTip.nw-alt:before,#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:10px solid #fafa50;border-top:10px solid rgba(250,250,80,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before,#powerTip.se-alt:before{left:auto;right:10px}#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:none;border-bottom:10px solid #fafa50;border-bottom:10px solid rgba(250,250,80,.8);bottom:auto;top:-10px} \ No newline at end of file diff --git a/i/jquery.powertip/css/jquery.powertip.css b/i/jquery.powertip/css/jquery.powertip.css index ec1e325..5811bfb 100644 --- a/i/jquery.powertip/css/jquery.powertip.css +++ b/i/jquery.powertip/css/jquery.powertip.css @@ -1,4 +1,10 @@ -/* PowerTip Plugin */ +/** + * PowerTip + * https://stevenbenner.github.io/jquery-powertip/ + * + * Stylesheet for the monochrome (default) theme. + */ + #powerTip { cursor: default; background-color: #333; diff --git a/i/jquery.powertip/css/jquery.powertip.min.css b/i/jquery.powertip/css/jquery.powertip.min.css index 3be2a39..0747f02 100644 --- a/i/jquery.powertip/css/jquery.powertip.min.css +++ b/i/jquery.powertip/css/jquery.powertip.min.css @@ -1 +1 @@ -#powerTip{cursor:default;background-color:#333;background-color:rgba(0,0,0,.8);border-radius:6px;color:#fff;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #333;border-top:10px solid rgba(0,0,0,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #333;border-right:10px solid rgba(0,0,0,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #333;border-bottom:10px solid rgba(0,0,0,.8);top:-10px}#powerTip.w:before{border-left:10px solid #333;border-left:10px solid rgba(0,0,0,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #333;border-top:10px solid rgba(0,0,0,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #333;border-bottom:10px solid rgba(0,0,0,.8);top:-10px}#powerTip.nw-alt:before,#powerTip.ne-alt:before,#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:10px solid #333;border-top:10px solid rgba(0,0,0,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before{left:auto;right:10px}#powerTip.sw-alt:before,#powerTip.se-alt:before{border-top:0;border-bottom:10px solid #333;border-bottom:10px solid rgba(0,0,0,.8);bottom:auto;top:-10px}#powerTip.se-alt:before{left:auto;right:10px} \ No newline at end of file +#powerTip{cursor:default;background-color:#333;background-color:rgba(0,0,0,.8);border-radius:6px;color:#fff;display:none;padding:10px;position:absolute;white-space:nowrap;z-index:2147483647}#powerTip:before{content:"";position:absolute}#powerTip.n:before,#powerTip.s:before{border-right:5px solid transparent;border-left:5px solid transparent;left:50%;margin-left:-5px}#powerTip.e:before,#powerTip.w:before{border-bottom:5px solid transparent;border-top:5px solid transparent;margin-top:-5px;top:50%}#powerTip.n:before{border-top:10px solid #333;border-top:10px solid rgba(0,0,0,.8);bottom:-10px}#powerTip.e:before{border-right:10px solid #333;border-right:10px solid rgba(0,0,0,.8);left:-10px}#powerTip.s:before{border-bottom:10px solid #333;border-bottom:10px solid rgba(0,0,0,.8);top:-10px}#powerTip.w:before{border-left:10px solid #333;border-left:10px solid rgba(0,0,0,.8);right:-10px}#powerTip.ne:before,#powerTip.se:before{border-right:10px solid transparent;border-left:0;left:10px}#powerTip.nw:before,#powerTip.sw:before{border-left:10px solid transparent;border-right:0;right:10px}#powerTip.ne:before,#powerTip.nw:before{border-top:10px solid #333;border-top:10px solid rgba(0,0,0,.8);bottom:-10px}#powerTip.se:before,#powerTip.sw:before{border-bottom:10px solid #333;border-bottom:10px solid rgba(0,0,0,.8);top:-10px}#powerTip.ne-alt:before,#powerTip.nw-alt:before,#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:10px solid #333;border-top:10px solid rgba(0,0,0,.8);bottom:-10px;border-left:5px solid transparent;border-right:5px solid transparent;left:10px}#powerTip.ne-alt:before,#powerTip.se-alt:before{left:auto;right:10px}#powerTip.se-alt:before,#powerTip.sw-alt:before{border-top:none;border-bottom:10px solid #333;border-bottom:10px solid rgba(0,0,0,.8);bottom:auto;top:-10px} \ No newline at end of file diff --git a/i/jquery.powertip/jquery.powertip.js b/i/jquery.powertip/jquery.powertip.js index 07e87fe..b91552d 100644 --- a/i/jquery.powertip/jquery.powertip.js +++ b/i/jquery.powertip/jquery.powertip.js @@ -1,19 +1,23 @@ /*! - PowerTip - v1.2.0 - 2013-04-03 - http://stevenbenner.github.com/jquery-powertip/ - Copyright (c) 2013 Steven Benner (http://stevenbenner.com/). + PowerTip v1.3.0 (2017-01-15) + https://stevenbenner.github.io/jquery-powertip/ + Copyright (c) 2017 Steven Benner (http://stevenbenner.com/). Released under MIT license. https://raw.github.com/stevenbenner/jquery-powertip/master/LICENSE.txt */ -(function(factory) { +(function(root, factory) { + // support loading the plugin via common patterns if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(['jquery'], factory); + // load the plugin as an amd module + define([ 'jquery' ], factory); + } else if (typeof module === 'object' && module.exports) { + // load the plugin as a commonjs module + module.exports = factory(require('jquery')); } else { - // Browser globals - factory(jQuery); + // load the plugin as a global + factory(root.jQuery); } -}(function($) { +}(this, function($) { // useful private variables var $document = $(document), @@ -30,6 +34,7 @@ DATA_POWERTIP = 'powertip', DATA_POWERTIPJQ = 'powertipjq', DATA_POWERTIPTARGET = 'powertiptarget', + EVENT_NAMESPACE = '.powertip', RAD2DEG = 180 / Math.PI; /** @@ -37,6 +42,8 @@ * Private properties global to all powerTip instances */ var session = { + elements: null, + tooltips: null, isTipOpen: false, isFixedTipOpen: false, isClosing: false, @@ -47,6 +54,7 @@ previousX: 0, previousY: 0, desyncTimeout: null, + closeDelayTimeout: null, mouseTrackingActive: false, delayInProgress: false, windowWidth: 0, @@ -69,31 +77,35 @@ /** * Display hover tooltips on the matched elements. - * @param {(Object|string)} opts The options object to use for the plugin, or + * @param {(Object|string)=} opts The options object to use for the plugin, or * the name of a method to invoke on the first matched element. * @param {*=} [arg] Argument for an invoked method (optional). * @return {jQuery} jQuery object for the matched selectors. */ $.fn.powerTip = function(opts, arg) { + var targetElements = this, + options, + tipController; + // don't do any work if there were no matched elements - if (!this.length) { - return this; + if (!targetElements.length) { + return targetElements; } // handle api method calls on the plugin, e.g. powerTip('hide') if ($.type(opts) === 'string' && $.powerTip[opts]) { - return $.powerTip[opts].call(this, this, arg); + return $.powerTip[opts].call(targetElements, targetElements, arg); } // extend options and instantiate TooltipController - var options = $.extend({}, $.fn.powerTip.defaults, opts), - tipController = new TooltipController(options); + options = $.extend({}, $.fn.powerTip.defaults, opts); + tipController = new TooltipController(options); // hook mouse and viewport dimension tracking initTracking(); // setup the elements - this.each(function elementSetup() { + targetElements.each(function elementSetup() { var $this = $(this), dataPowertip = $this.data(DATA_POWERTIP), dataElem = $this.data(DATA_POWERTIPJQ), @@ -123,33 +135,45 @@ ); }); - // attach events to matched elements if the manual options is not enabled + // attach events to matched elements if the manual option is not enabled if (!options.manual) { - this.on({ - // mouse events - 'mouseenter.powertip': function elementMouseEnter(event) { - $.powerTip.show(this, event); - }, - 'mouseleave.powertip': function elementMouseLeave() { - $.powerTip.hide(this); - }, - // keyboard events - 'focus.powertip': function elementFocus() { - $.powerTip.show(this); - }, - 'blur.powertip': function elementBlur() { + // attach open events + $.each(options.openEvents, function(idx, evt) { + if ($.inArray(evt, options.closeEvents) > -1) { + // event is in both openEvents and closeEvents, so toggle it + targetElements.on(evt + EVENT_NAMESPACE, function elementToggle(event) { + $.powerTip.toggle(this, event); + }); + } else { + targetElements.on(evt + EVENT_NAMESPACE, function elementOpen(event) { + $.powerTip.show(this, event); + }); + } + }); + + // attach close events + $.each(options.closeEvents, function(idx, evt) { + if ($.inArray(evt, options.openEvents) < 0) { + targetElements.on(evt + EVENT_NAMESPACE, function elementClose(event) { + // set immediate to true for any event without mouse info + $.powerTip.hide(this, !isMouseEvent(event)); + }); + } + }); + + // attach escape key close event + targetElements.on('keydown' + EVENT_NAMESPACE, function elementKeyDown(event) { + // always close tooltip when the escape key is pressed + if (event.keyCode === 27) { $.powerTip.hide(this, true); - }, - 'keydown.powertip': function elementKeyDown(event) { - // close tooltip when the escape key is pressed - if (event.keyCode === 27) { - $.powerTip.hide(this, true); - } } }); } - return this; + // remember elements that the plugin is attached to + session.elements = session.elements ? session.elements.add(targetElements) : targetElements; + + return targetElements; }; /** @@ -160,6 +184,7 @@ fadeOutTime: 100, followMouse: false, popupId: 'powerTip', + popupClass: null, intentSensitivity: 7, intentPollInterval: 100, closeDelay: 100, @@ -167,7 +192,9 @@ smartPlacement: false, offset: 10, mouseOnToPopup: false, - manual: false + manual: false, + openEvents: [ 'mouseenter', 'focus' ], + closeEvents: [ 'mouseleave', 'blur' ] }; /** @@ -177,18 +204,18 @@ * do not fit. */ $.fn.powerTip.smartPlacementLists = { - n: ['n', 'ne', 'nw', 's'], - e: ['e', 'ne', 'se', 'w', 'nw', 'sw', 'n', 's', 'e'], - s: ['s', 'se', 'sw', 'n'], - w: ['w', 'nw', 'sw', 'e', 'ne', 'se', 'n', 's', 'w'], - nw: ['nw', 'w', 'sw', 'n', 's', 'se', 'nw'], - ne: ['ne', 'e', 'se', 'n', 's', 'sw', 'ne'], - sw: ['sw', 'w', 'nw', 's', 'n', 'ne', 'sw'], - se: ['se', 'e', 'ne', 's', 'n', 'nw', 'se'], - 'nw-alt': ['nw-alt', 'n', 'ne-alt', 'sw-alt', 's', 'se-alt', 'w', 'e'], - 'ne-alt': ['ne-alt', 'n', 'nw-alt', 'se-alt', 's', 'sw-alt', 'e', 'w'], - 'sw-alt': ['sw-alt', 's', 'se-alt', 'nw-alt', 'n', 'ne-alt', 'w', 'e'], - 'se-alt': ['se-alt', 's', 'sw-alt', 'ne-alt', 'n', 'nw-alt', 'e', 'w'] + n: [ 'n', 'ne', 'nw', 's' ], + e: [ 'e', 'ne', 'se', 'w', 'nw', 'sw', 'n', 's', 'e' ], + s: [ 's', 'se', 'sw', 'n' ], + w: [ 'w', 'nw', 'sw', 'e', 'ne', 'se', 'n', 's', 'w' ], + nw: [ 'nw', 'w', 'sw', 'n', 's', 'se', 'nw' ], + ne: [ 'ne', 'e', 'se', 'n', 's', 'sw', 'ne' ], + sw: [ 'sw', 'w', 'nw', 's', 'n', 'ne', 'sw' ], + se: [ 'se', 'e', 'ne', 's', 'n', 'nw', 'se' ], + 'nw-alt': [ 'nw-alt', 'n', 'ne-alt', 'sw-alt', 's', 'se-alt', 'w', 'e' ], + 'ne-alt': [ 'ne-alt', 'n', 'nw-alt', 'se-alt', 's', 'sw-alt', 'e', 'w' ], + 'sw-alt': [ 'sw-alt', 's', 'se-alt', 'nw-alt', 'n', 'ne-alt', 'w', 'e' ], + 'se-alt': [ 'se-alt', 's', 'sw-alt', 'ne-alt', 'n', 'nw-alt', 'e', 'w' ] }; /** @@ -200,9 +227,12 @@ * @param {jQuery|Element} element The element to open the tooltip for. * @param {jQuery.Event=} event jQuery event for hover intent and mouse * tracking (optional). + * @return {jQuery|Element} The original jQuery object or DOM Element. */ show: function apiShowTip(element, event) { - if (event) { + // if we were given a mouse event then run the hover intent testing, + // otherwise, simply show the tooltip asap + if (isMouseEvent(event)) { trackMouse(event); session.previousX = event.pageX; session.previousY = event.pageY; @@ -216,6 +246,7 @@ /** * Repositions the tooltip on the element. * @param {jQuery|Element} element The element the tooltip is shown for. + * @return {jQuery|Element} The original jQuery object or DOM Element. */ reposition: function apiResetPosition(element) { $(element).first().data(DATA_DISPLAYCONTROLLER).resetPosition(); @@ -227,24 +258,69 @@ * @param {(jQuery|Element)=} element The element with the tooltip that * should be closed (optional). * @param {boolean=} immediate Disable close delay (optional). + * @return {jQuery|Element|undefined} The original jQuery object or DOM + * Element, if one was specified. */ hide: function apiCloseTip(element, immediate) { + var displayController; + + // set immediate to true when no element is specified + immediate = element ? immediate : true; + + // find the relevant display controller if (element) { - $(element).first().data(DATA_DISPLAYCONTROLLER).hide(immediate); + displayController = $(element).first().data(DATA_DISPLAYCONTROLLER); + } else if (session.activeHover) { + displayController = session.activeHover.data(DATA_DISPLAYCONTROLLER); + } + + // if found, hide the tip + if (displayController) { + displayController.hide(immediate); + } + + return element; + }, + + /** + * Toggles the tooltip for the specified element. This will open a closed + * tooltip, or close an open tooltip. + * @param {jQuery|Element} element The element with the tooltip that + * should be toggled. + * @param {jQuery.Event=} event jQuery event for hover intent and mouse + * tracking (optional). + * @return {jQuery|Element} The original jQuery object or DOM Element. + */ + toggle: function apiToggle(element, event) { + if (session.activeHover && session.activeHover.is(element)) { + // tooltip for element is active, so close it + $.powerTip.hide(element, !isMouseEvent(event)); } else { - if (session.activeHover) { - session.activeHover.data(DATA_DISPLAYCONTROLLER).hide(true); - } + // tooltip for element is not active, so open it + $.powerTip.show(element, event); } return element; }, /** - * Destroy and roll back any powerTip() instance on the specified element. - * @param {jQuery|Element} element The element with the powerTip instance. + * Destroy and roll back any powerTip() instance on the specified elements. + * If no elements are specified then all elements that the plugin is + * currently attached to will be rolled back. + * @param {(jQuery|Element)=} element The element with the powerTip instance. + * @return {jQuery|Element|undefined} The original jQuery object or DOM + * Element, if one was specified. */ destroy: function apiDestroy(element) { - $(element).off('.powertip').each(function destroy() { + var $element = element ? $(element) : session.elements; + + // if the plugin is not hooked to any elements then there is no point + // trying to destroy anything, or dealing with the possible errors + if (!session.elements || session.elements.length === 0) { + return element; + } + + // unhook events and destroy plugin changes to each element + $element.off(EVENT_NAMESPACE).each(function destroy() { var $this = $(this), dataAttributes = [ DATA_ORIGINALTITLE, @@ -253,13 +329,29 @@ DATA_FORCEDOPEN ]; + // revert title attribute if ($this.data(DATA_ORIGINALTITLE)) { $this.attr('title', $this.data(DATA_ORIGINALTITLE)); dataAttributes.push(DATA_POWERTIP); } + // remove data attributes $this.removeData(dataAttributes); }); + + // remove destroyed element from active elements collection + session.elements = session.elements.not($element); + + // if there are no active elements left then we will unhook all of the + // events that we've bound code to and remove the tooltip elements + if (session.elements.length === 0) { + $window.off(EVENT_NAMESPACE); + $document.off(EVENT_NAMESPACE); + session.mouseTrackingActive = false; + session.tooltips.remove(); + session.tooltips = null; + } + return element; } }; @@ -305,7 +397,8 @@ * this instance. */ function DisplayController(element, options, tipController) { - var hoverTimer = null; + var hoverTimer = null, + myCloseDelay = null; /** * Begins the process of showing a tooltip. @@ -330,8 +423,12 @@ if (forceOpen) { element.data(DATA_FORCEDOPEN, true); } + closeAnyDelayed(); tipController.showTip(element); } + } else { + // cursor left and returned to this element, cancel close + cancelClose(); } } @@ -341,20 +438,29 @@ * @param {boolean=} disableDelay Disable close delay (optional). */ function closeTooltip(disableDelay) { + // if this instance already has a close delay in progress then halt it + if (myCloseDelay) { + myCloseDelay = session.closeDelayTimeout = clearTimeout(myCloseDelay); + session.delayInProgress = false; + } cancelTimer(); session.tipOpenImminent = false; if (element.data(DATA_HASACTIVEHOVER)) { element.data(DATA_FORCEDOPEN, false); if (!disableDelay) { session.delayInProgress = true; - hoverTimer = setTimeout( + session.closeDelayTimeout = setTimeout( function closeDelay() { - hoverTimer = null; + session.closeDelayTimeout = null; tipController.hideTip(element); session.delayInProgress = false; + myCloseDelay = null; }, options.closeDelay ); + // save internal reference close delay id so we can check if the + // active close delay belongs to this instance + myCloseDelay = session.closeDelayTimeout; } else { tipController.hideTip(element); } @@ -374,6 +480,8 @@ // check if difference has passed the sensitivity threshold if (totalDifference < options.intentSensitivity) { + cancelClose(); + closeAnyDelayed(); tipController.showTip(element); } else { // try again @@ -386,12 +494,39 @@ /** * Cancels active hover timer. * @private + * @param {boolean=} stopClose Cancel any active close delay timer. */ - function cancelTimer() { + function cancelTimer(stopClose) { hoverTimer = clearTimeout(hoverTimer); + // cancel the current close delay if the active close delay is for this + // element or the stopClose argument is true + if (session.closeDelayTimeout && myCloseDelay === session.closeDelayTimeout || stopClose) { + cancelClose(); + } + } + + /** + * Cancels any active close delay timer. + * @private + */ + function cancelClose() { + session.closeDelayTimeout = clearTimeout(session.closeDelayTimeout); session.delayInProgress = false; } + /** + * Asks any tooltips waiting on their close delay to close now. + * @private + */ + function closeAnyDelayed() { + // if another element is waiting for its close delay then we should ask + // it to close immediately so we can proceed without unexpected timeout + // code being run during this tooltip's lifecycle + if (session.delayInProgress && session.activeHover && !session.activeHover.is(element)) { + session.activeHover.data(DATA_DISPLAYCONTROLLER).hide(true); + } + } + /** * Repositions the tooltip on this element. * @private @@ -437,54 +572,54 @@ // calculate the appropriate x and y position in the document switch (placement) { - case 'n': - coords.set('left', position.left - (tipWidth / 2)); - coords.set('bottom', session.windowHeight - position.top + offset); - break; - case 'e': - coords.set('left', position.left + offset); - coords.set('top', position.top - (tipHeight / 2)); - break; - case 's': - coords.set('left', position.left - (tipWidth / 2)); - coords.set('top', position.top + offset); - break; - case 'w': - coords.set('top', position.top - (tipHeight / 2)); - coords.set('right', session.windowWidth - position.left + offset); - break; - case 'nw': - coords.set('bottom', session.windowHeight - position.top + offset); - coords.set('right', session.windowWidth - position.left - 20); - break; - case 'nw-alt': - coords.set('left', position.left); - coords.set('bottom', session.windowHeight - position.top + offset); - break; - case 'ne': - coords.set('left', position.left - 20); - coords.set('bottom', session.windowHeight - position.top + offset); - break; - case 'ne-alt': - coords.set('bottom', session.windowHeight - position.top + offset); - coords.set('right', session.windowWidth - position.left); - break; - case 'sw': - coords.set('top', position.top + offset); - coords.set('right', session.windowWidth - position.left - 20); - break; - case 'sw-alt': - coords.set('left', position.left); - coords.set('top', position.top + offset); - break; - case 'se': - coords.set('left', position.left - 20); - coords.set('top', position.top + offset); - break; - case 'se-alt': - coords.set('top', position.top + offset); - coords.set('right', session.windowWidth - position.left); - break; + case 'n': + coords.set('left', position.left - (tipWidth / 2)); + coords.set('bottom', session.windowHeight - position.top + offset); + break; + case 'e': + coords.set('left', position.left + offset); + coords.set('top', position.top - (tipHeight / 2)); + break; + case 's': + coords.set('left', position.left - (tipWidth / 2)); + coords.set('top', position.top + offset); + break; + case 'w': + coords.set('top', position.top - (tipHeight / 2)); + coords.set('right', session.windowWidth - position.left + offset); + break; + case 'nw': + coords.set('bottom', session.windowHeight - position.top + offset); + coords.set('right', session.windowWidth - position.left - 20); + break; + case 'nw-alt': + coords.set('left', position.left); + coords.set('bottom', session.windowHeight - position.top + offset); + break; + case 'ne': + coords.set('left', position.left - 20); + coords.set('bottom', session.windowHeight - position.top + offset); + break; + case 'ne-alt': + coords.set('bottom', session.windowHeight - position.top + offset); + coords.set('right', session.windowWidth - position.left); + break; + case 'sw': + coords.set('top', position.top + offset); + coords.set('right', session.windowWidth - position.left - 20); + break; + case 'sw-alt': + coords.set('left', position.left); + coords.set('top', position.top + offset); + break; + case 'se': + coords.set('left', position.left - 20); + coords.set('top', position.top + offset); + break; + case 'se-alt': + coords.set('top', position.top + offset); + coords.set('right', session.windowWidth - position.left); + break; } return coords; @@ -507,38 +642,38 @@ // calculate the appropriate x and y position in the document switch (placement) { - case 'n': - left = objectOffset.left + objectWidth / 2; - top = objectOffset.top; - break; - case 'e': - left = objectOffset.left + objectWidth; - top = objectOffset.top + objectHeight / 2; - break; - case 's': - left = objectOffset.left + objectWidth / 2; - top = objectOffset.top + objectHeight; - break; - case 'w': - left = objectOffset.left; - top = objectOffset.top + objectHeight / 2; - break; - case 'nw': - left = objectOffset.left; - top = objectOffset.top; - break; - case 'ne': - left = objectOffset.left + objectWidth; - top = objectOffset.top; - break; - case 'sw': - left = objectOffset.left; - top = objectOffset.top + objectHeight; - break; - case 'se': - left = objectOffset.left + objectWidth; - top = objectOffset.top + objectHeight; - break; + case 'n': + left = objectOffset.left + objectWidth / 2; + top = objectOffset.top; + break; + case 'e': + left = objectOffset.left + objectWidth; + top = objectOffset.top + objectHeight / 2; + break; + case 's': + left = objectOffset.left + objectWidth / 2; + top = objectOffset.top + objectHeight; + break; + case 'w': + left = objectOffset.left; + top = objectOffset.top + objectHeight / 2; + break; + case 'nw': + left = objectOffset.left; + top = objectOffset.top; + break; + case 'ne': + left = objectOffset.left + objectWidth; + top = objectOffset.top; + break; + case 'sw': + left = objectOffset.left; + top = objectOffset.top + objectHeight; + break; + case 'se': + left = objectOffset.left + objectWidth; + top = objectOffset.top + objectHeight; + break; } return { @@ -564,7 +699,7 @@ halfWidth = boundingBox.width / 2, halfHeight = boundingBox.height / 2, placements = [], - placementKeys = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'], + placementKeys = [ 'nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w' ], coords, rotation, steps, @@ -642,44 +777,20 @@ $body = $('body'); } $body.append(tipElement); + // remember the tooltip elements that the plugin has created + session.tooltips = session.tooltips ? session.tooltips.add(tipElement) : tipElement; } // hook mousemove for cursor follow tooltips if (options.followMouse) { // only one positionTipOnCursor hook per tooltip element, please if (!tipElement.data(DATA_HASMOUSEMOVE)) { - $document.on('mousemove', positionTipOnCursor); - $window.on('scroll', positionTipOnCursor); + $document.on('mousemove' + EVENT_NAMESPACE, positionTipOnCursor); + $window.on('scroll' + EVENT_NAMESPACE, positionTipOnCursor); tipElement.data(DATA_HASMOUSEMOVE, true); } } - // if we want to be able to mouse onto the tooltip then we need to attach - // hover events to the tooltip that will cancel a close request on hover and - // start a new close request on mouseleave - if (options.mouseOnToPopup) { - tipElement.on({ - mouseenter: function tipMouseEnter() { - // we only let the mouse stay on the tooltip if it is set to let - // users interact with it - if (tipElement.data(DATA_MOUSEONTOTIP)) { - // check activeHover in case the mouse cursor entered the - // tooltip during the fadeOut and close cycle - if (session.activeHover) { - session.activeHover.data(DATA_DISPLAYCONTROLLER).cancel(); - } - } - }, - mouseleave: function tipMouseLeave() { - // check activeHover in case the mouse cursor entered the - // tooltip during the fadeOut and close cycle - if (session.activeHover) { - session.activeHover.data(DATA_DISPLAYCONTROLLER).hide(); - } - } - }); - } - /** * Gives the specified element the active-hover state and queues up the * showTip function. @@ -753,6 +864,48 @@ positionTipOnCursor(); } + // add custom class to tooltip element + tipElement.addClass(options.popupClass); + + // close tooltip when clicking anywhere on the page, with the exception + // of the tooltip's trigger element and any elements that are within a + // tooltip that has 'mouseOnToPopup' option enabled + if (!element.data(DATA_FORCEDOPEN)) { + $document.on('click' + EVENT_NAMESPACE, function documentClick(event) { + var target = event.target; + if (target !== element[0]) { + if (options.mouseOnToPopup) { + if (target !== tipElement[0] && !$.contains(tipElement[0], target)) { + $.powerTip.hide(); + } + } else { + $.powerTip.hide(); + } + } + }); + } + + // if we want to be able to mouse on to the tooltip then we need to + // attach hover events to the tooltip that will cancel a close request + // on mouseenter and start a new close request on mouseleave + // only hook these listeners if we're not in manual mode + if (options.mouseOnToPopup && !options.manual) { + tipElement.on('mouseenter' + EVENT_NAMESPACE, function tipMouseEnter() { + // check activeHover in case the mouse cursor entered the + // tooltip during the fadeOut and close cycle + if (session.activeHover) { + session.activeHover.data(DATA_DISPLAYCONTROLLER).cancel(); + } + }); + tipElement.on('mouseleave' + EVENT_NAMESPACE, function tipMouseLeave() { + // check activeHover in case the mouse cursor left the tooltip + // during the fadeOut and close cycle + if (session.activeHover) { + session.activeHover.data(DATA_DISPLAYCONTROLLER).hide(); + } + }); + } + // fadein tipElement.fadeIn(options.fadeInTime, function fadeInCallback() { // start desync polling @@ -773,7 +926,6 @@ function hideTip(element) { // reset session session.isClosing = true; - session.activeHover = null; session.isTipOpen = false; // stop desync polling @@ -783,11 +935,18 @@ element.data(DATA_HASACTIVEHOVER, false); element.data(DATA_FORCEDOPEN, false); + // remove document click handler + $document.off('click' + EVENT_NAMESPACE); + + // unbind the mouseOnToPopup events if they were set + tipElement.off(EVENT_NAMESPACE); + // fade out tipElement.fadeOut(options.fadeOutTime, function fadeOutCallback() { var coords = new CSSCoordinates(); // reset session and tooltip element + session.activeHover = null; session.isClosing = false; session.isFixedTipOpen = false; tipElement.removeClass(); @@ -896,6 +1055,7 @@ } // add placement as class for CSS arrows + tipElement.removeClass('w nw sw e ne se n s w se-alt sw-alt ne-alt nw-alt'); tipElement.addClass(finalPlacement); } @@ -960,7 +1120,7 @@ // result in a desynced tooltip because the tooltip was never asked to // close. So we should periodically check for a desync situation and // close the tip if such a situation arises. - if (session.isTipOpen && !session.isClosing && !session.delayInProgress) { + if (session.isTipOpen && !session.isClosing && !session.delayInProgress && ($.inArray('mouseleave', options.closeEvents) > -1 || $.inArray('mouseout', options.closeEvents) > -1 || $.inArray('blur', options.closeEvents) > -1 || $.inArray('focusout', options.closeEvents) > -1)) { // user moused onto another tip or active hover is disabled if (session.activeHover.data(DATA_HASACTIVEHOVER) === false || session.activeHover.is(':disabled')) { isDesynced = true; @@ -1003,7 +1163,17 @@ * @return {boolean} Whether this is an SVG element */ function isSvgElement(element) { - return window.SVGElement && element[0] instanceof SVGElement; + return Boolean(window.SVGElement && element[0] instanceof SVGElement); + } + + /** + * Determines if the specified jQuery.Event object has mouse data. + * @private + * @param {jQuery.Event=} event The jQuery.Event object to test. + * @return {boolean} True if there is mouse data, otherwise false. + */ + function isMouseEvent(event) { + return Boolean(event && typeof event.pageX === 'number'); } /** @@ -1017,35 +1187,52 @@ session.mouseTrackingActive = true; // grab the current viewport dimensions on load - $(function getViewportDimensions() { - session.scrollLeft = $window.scrollLeft(); - session.scrollTop = $window.scrollTop(); - session.windowWidth = $window.width(); - session.windowHeight = $window.height(); - }); + getViewportDimensions(); + $(getViewportDimensions); // hook mouse move tracking - $document.on('mousemove', trackMouse); + $document.on('mousemove' + EVENT_NAMESPACE, trackMouse); // hook viewport dimensions tracking - $window.on({ - resize: function trackResize() { - session.windowWidth = $window.width(); - session.windowHeight = $window.height(); - }, - scroll: function trackScroll() { - var x = $window.scrollLeft(), - y = $window.scrollTop(); - if (x !== session.scrollLeft) { - session.currentX += x - session.scrollLeft; - session.scrollLeft = x; - } - if (y !== session.scrollTop) { - session.currentY += y - session.scrollTop; - session.scrollTop = y; - } - } - }); + $window.on('resize' + EVENT_NAMESPACE, trackResize); + $window.on('scroll' + EVENT_NAMESPACE, trackScroll); + } + } + + /** + * Updates the viewport dimensions cache. + * @private + */ + function getViewportDimensions() { + session.scrollLeft = $window.scrollLeft(); + session.scrollTop = $window.scrollTop(); + session.windowWidth = $window.width(); + session.windowHeight = $window.height(); + } + + /** + * Updates the window size info in the viewport dimensions cache. + * @private + */ + function trackResize() { + session.windowWidth = $window.width(); + session.windowHeight = $window.height(); + } + + /** + * Updates the scroll offset info in the viewport dimensions cache. + * @private + */ + function trackScroll() { + var x = $window.scrollLeft(), + y = $window.scrollTop(); + if (x !== session.scrollLeft) { + session.currentX += x - session.scrollLeft; + session.scrollLeft = x; + } + if (y !== session.scrollTop) { + session.currentY += y - session.scrollTop; + session.scrollTop = y; } } @@ -1163,4 +1350,6 @@ return count; } +// return api for commonjs and amd environments + return $.powerTip; })); diff --git a/i/jquery.powertip/jquery.powertip.min.js b/i/jquery.powertip/jquery.powertip.min.js index 40874ae..9f20856 100644 --- a/i/jquery.powertip/jquery.powertip.min.js +++ b/i/jquery.powertip/jquery.powertip.min.js @@ -1,8 +1,8 @@ /*! - PowerTip - v1.2.0 - 2013-04-03 - http://stevenbenner.github.com/jquery-powertip/ - Copyright (c) 2013 Steven Benner (http://stevenbenner.com/). + PowerTip v1.3.0 (2017-01-15) + https://stevenbenner.github.io/jquery-powertip/ + Copyright (c) 2017 Steven Benner (http://stevenbenner.com/). Released under MIT license. https://raw.github.com/stevenbenner/jquery-powertip/master/LICENSE.txt */ -(function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function t(){var t=this;t.top="auto",t.left="auto",t.right="auto",t.bottom="auto",t.set=function(o,n){e.isNumeric(n)&&(t[o]=Math.round(n))}}function o(e,t,o){function n(n,i){r(),e.data(v)||(n?(i&&e.data(m,!0),o.showTip(e)):(P.tipOpenImminent=!0,l=setTimeout(function(){l=null,s()},t.intentPollInterval)))}function i(n){r(),P.tipOpenImminent=!1,e.data(v)&&(e.data(m,!1),n?o.hideTip(e):(P.delayInProgress=!0,l=setTimeout(function(){l=null,o.hideTip(e),P.delayInProgress=!1},t.closeDelay)))}function s(){var i=Math.abs(P.previousX-P.currentX),s=Math.abs(P.previousY-P.currentY),r=i+s;t.intentSensitivity>r?o.showTip(e):(P.previousX=P.currentX,P.previousY=P.currentY,n())}function r(){l=clearTimeout(l),P.delayInProgress=!1}function a(){o.resetPosition(e)}var l=null;this.show=n,this.hide=i,this.cancel=r,this.resetPosition=a}function n(){function e(e,i,r,a,l){var p,c=i.split("-")[0],u=new t;switch(p=s(e)?n(e,c):o(e,c),i){case"n":u.set("left",p.left-r/2),u.set("bottom",P.windowHeight-p.top+l);break;case"e":u.set("left",p.left+l),u.set("top",p.top-a/2);break;case"s":u.set("left",p.left-r/2),u.set("top",p.top+l);break;case"w":u.set("top",p.top-a/2),u.set("right",P.windowWidth-p.left+l);break;case"nw":u.set("bottom",P.windowHeight-p.top+l),u.set("right",P.windowWidth-p.left-20);break;case"nw-alt":u.set("left",p.left),u.set("bottom",P.windowHeight-p.top+l);break;case"ne":u.set("left",p.left-20),u.set("bottom",P.windowHeight-p.top+l);break;case"ne-alt":u.set("bottom",P.windowHeight-p.top+l),u.set("right",P.windowWidth-p.left);break;case"sw":u.set("top",p.top+l),u.set("right",P.windowWidth-p.left-20);break;case"sw-alt":u.set("left",p.left),u.set("top",p.top+l);break;case"se":u.set("left",p.left-20),u.set("top",p.top+l);break;case"se-alt":u.set("top",p.top+l),u.set("right",P.windowWidth-p.left)}return u}function o(e,t){var o,n,i=e.offset(),s=e.outerWidth(),r=e.outerHeight();switch(t){case"n":o=i.left+s/2,n=i.top;break;case"e":o=i.left+s,n=i.top+r/2;break;case"s":o=i.left+s/2,n=i.top+r;break;case"w":o=i.left,n=i.top+r/2;break;case"nw":o=i.left,n=i.top;break;case"ne":o=i.left+s,n=i.top;break;case"sw":o=i.left,n=i.top+r;break;case"se":o=i.left+s,n=i.top+r}return{top:n,left:o}}function n(e,t){function o(){d.push(p.matrixTransform(u))}var n,i,s,r,a=e.closest("svg")[0],l=e[0],p=a.createSVGPoint(),c=l.getBBox(),u=l.getScreenCTM(),f=c.width/2,w=c.height/2,d=[],h=["nw","n","ne","e","se","s","sw","w"];if(p.x=c.x,p.y=c.y,o(),p.x+=f,o(),p.x+=f,o(),p.y+=w,o(),p.y+=w,o(),p.x-=f,o(),p.x-=f,o(),p.y-=w,o(),d[0].y!==d[1].y||d[0].x!==d[7].x)for(i=Math.atan2(u.b,u.a)*O,s=Math.ceil((i%360-22.5)/45),1>s&&(s+=8);s--;)h.push(h.shift());for(r=0;d.length>r;r++)if(h[r]===t){n=d[r];break}return{top:n.y+P.scrollTop,left:n.x+P.scrollLeft}}this.compute=e}function i(o){function i(e){e.data(v,!0),O.queue(function(t){s(e),t()})}function s(e){var t;if(e.data(v)){if(P.isTipOpen)return P.isClosing||r(P.activeHover),O.delay(100).queue(function(t){s(e),t()}),void 0;e.trigger("powerTipPreRender"),t=p(e),t&&(O.empty().append(t),e.trigger("powerTipRender"),P.activeHover=e,P.isTipOpen=!0,O.data(g,o.mouseOnToPopup),o.followMouse?a():(b(e),P.isFixedTipOpen=!0),O.fadeIn(o.fadeInTime,function(){P.desyncTimeout||(P.desyncTimeout=setInterval(H,500)),e.trigger("powerTipOpen")}))}}function r(e){P.isClosing=!0,P.activeHover=null,P.isTipOpen=!1,P.desyncTimeout=clearInterval(P.desyncTimeout),e.data(v,!1),e.data(m,!1),O.fadeOut(o.fadeOutTime,function(){var n=new t;P.isClosing=!1,P.isFixedTipOpen=!1,O.removeClass(),n.set("top",P.currentY+o.offset),n.set("left",P.currentX+o.offset),O.css(n),e.trigger("powerTipClose")})}function a(){if(!P.isFixedTipOpen&&(P.isTipOpen||P.tipOpenImminent&&O.data(T))){var e,n,i=O.outerWidth(),s=O.outerHeight(),r=new t;r.set("top",P.currentY+o.offset),r.set("left",P.currentX+o.offset),e=c(r,i,s),e!==I.none&&(n=u(e),1===n?e===I.right?r.set("left",P.windowWidth-i):e===I.bottom&&r.set("top",P.scrollTop+P.windowHeight-s):(r.set("left",P.currentX-i-o.offset),r.set("top",P.currentY-s-o.offset))),O.css(r)}}function b(t){var n,i;o.smartPlacement?(n=e.fn.powerTip.smartPlacementLists[o.placement],e.each(n,function(e,o){var n=c(y(t,o),O.outerWidth(),O.outerHeight());return i=o,n===I.none?!1:void 0})):(y(t,o.placement),i=o.placement),O.addClass(i)}function y(e,n){var i,s,r=0,a=new t;a.set("top",0),a.set("left",0),O.css(a);do i=O.outerWidth(),s=O.outerHeight(),a=k.compute(e,n,i,s,o.offset),O.css(a);while(5>=++r&&(i!==O.outerWidth()||s!==O.outerHeight()));return a}function H(){var e=!1;!P.isTipOpen||P.isClosing||P.delayInProgress||(P.activeHover.data(v)===!1||P.activeHover.is(":disabled")?e=!0:l(P.activeHover)||P.activeHover.is(":focus")||P.activeHover.data(m)||(O.data(g)?l(O)||(e=!0):e=!0),e&&r(P.activeHover))}var k=new n,O=e("#"+o.popupId);0===O.length&&(O=e("
",{id:o.popupId}),0===d.length&&(d=e("body")),d.append(O)),o.followMouse&&(O.data(T)||(f.on("mousemove",a),w.on("scroll",a),O.data(T,!0))),o.mouseOnToPopup&&O.on({mouseenter:function(){O.data(g)&&P.activeHover&&P.activeHover.data(h).cancel()},mouseleave:function(){P.activeHover&&P.activeHover.data(h).hide()}}),this.showTip=i,this.hideTip=r,this.resetPosition=b}function s(e){return window.SVGElement&&e[0]instanceof SVGElement}function r(){P.mouseTrackingActive||(P.mouseTrackingActive=!0,e(function(){P.scrollLeft=w.scrollLeft(),P.scrollTop=w.scrollTop(),P.windowWidth=w.width(),P.windowHeight=w.height()}),f.on("mousemove",a),w.on({resize:function(){P.windowWidth=w.width(),P.windowHeight=w.height()},scroll:function(){var e=w.scrollLeft(),t=w.scrollTop();e!==P.scrollLeft&&(P.currentX+=e-P.scrollLeft,P.scrollLeft=e),t!==P.scrollTop&&(P.currentY+=t-P.scrollTop,P.scrollTop=t)}}))}function a(e){P.currentX=e.pageX,P.currentY=e.pageY}function l(e){var t=e.offset(),o=e[0].getBoundingClientRect(),n=o.right-o.left,i=o.bottom-o.top;return P.currentX>=t.left&&P.currentX<=t.left+n&&P.currentY>=t.top&&P.currentY<=t.top+i}function p(t){var o,n,i=t.data(y),s=t.data(H),r=t.data(k);return i?(e.isFunction(i)&&(i=i.call(t[0])),n=i):s?(e.isFunction(s)&&(s=s.call(t[0])),s.length>0&&(n=s.clone(!0,!0))):r&&(o=e("#"+r),o.length>0&&(n=o.html())),n}function c(e,t,o){var n=P.scrollTop,i=P.scrollLeft,s=n+P.windowHeight,r=i+P.windowWidth,a=I.none;return(n>e.top||n>Math.abs(e.bottom-P.windowHeight)-o)&&(a|=I.top),(e.top+o>s||Math.abs(e.bottom-P.windowHeight)>s)&&(a|=I.bottom),(i>e.left||e.right+t>r)&&(a|=I.left),(e.left+t>r||i>e.right)&&(a|=I.right),a}function u(e){for(var t=0;e;)e&=e-1,t++;return t}var f=e(document),w=e(window),d=e("body"),h="displayController",v="hasActiveHover",m="forcedOpen",T="hasMouseMove",g="mouseOnToPopup",b="originalTitle",y="powertip",H="powertipjq",k="powertiptarget",O=180/Math.PI,P={isTipOpen:!1,isFixedTipOpen:!1,isClosing:!1,tipOpenImminent:!1,activeHover:null,currentX:0,currentY:0,previousX:0,previousY:0,desyncTimeout:null,mouseTrackingActive:!1,delayInProgress:!1,windowWidth:0,windowHeight:0,scrollTop:0,scrollLeft:0},I={none:0,top:1,bottom:2,left:4,right:8};e.fn.powerTip=function(t,n){if(!this.length)return this;if("string"===e.type(t)&&e.powerTip[t])return e.powerTip[t].call(this,this,n);var s=e.extend({},e.fn.powerTip.defaults,t),a=new i(s);return r(),this.each(function(){var t,n=e(this),i=n.data(y),r=n.data(H),l=n.data(k);n.data(h)&&e.powerTip.destroy(n),t=n.attr("title"),i||l||r||!t||(n.data(y,t),n.data(b,t),n.removeAttr("title")),n.data(h,new o(n,s,a))}),s.manual||this.on({"mouseenter.powertip":function(t){e.powerTip.show(this,t)},"mouseleave.powertip":function(){e.powerTip.hide(this)},"focus.powertip":function(){e.powerTip.show(this)},"blur.powertip":function(){e.powerTip.hide(this,!0)},"keydown.powertip":function(t){27===t.keyCode&&e.powerTip.hide(this,!0)}}),this},e.fn.powerTip.defaults={fadeInTime:200,fadeOutTime:100,followMouse:!1,popupId:"powerTip",intentSensitivity:7,intentPollInterval:100,closeDelay:100,placement:"n",smartPlacement:!1,offset:10,mouseOnToPopup:!1,manual:!1},e.fn.powerTip.smartPlacementLists={n:["n","ne","nw","s"],e:["e","ne","se","w","nw","sw","n","s","e"],s:["s","se","sw","n"],w:["w","nw","sw","e","ne","se","n","s","w"],nw:["nw","w","sw","n","s","se","nw"],ne:["ne","e","se","n","s","sw","ne"],sw:["sw","w","nw","s","n","ne","sw"],se:["se","e","ne","s","n","nw","se"],"nw-alt":["nw-alt","n","ne-alt","sw-alt","s","se-alt","w","e"],"ne-alt":["ne-alt","n","nw-alt","se-alt","s","sw-alt","e","w"],"sw-alt":["sw-alt","s","se-alt","nw-alt","n","ne-alt","w","e"],"se-alt":["se-alt","s","sw-alt","ne-alt","n","nw-alt","e","w"]},e.powerTip={show:function(t,o){return o?(a(o),P.previousX=o.pageX,P.previousY=o.pageY,e(t).data(h).show()):e(t).first().data(h).show(!0,!0),t},reposition:function(t){return e(t).first().data(h).resetPosition(),t},hide:function(t,o){return t?e(t).first().data(h).hide(o):P.activeHover&&P.activeHover.data(h).hide(!0),t},destroy:function(t){return e(t).off(".powertip").each(function(){var t=e(this),o=[b,h,v,m];t.data(b)&&(t.attr("title",t.data(b)),o.push(y)),t.removeData(o)}),t}},e.powerTip.showTip=e.powerTip.show,e.powerTip.closeTip=e.powerTip.hide}); \ No newline at end of file +!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof module&&module.exports?module.exports=b(require("jquery")):b(a.jQuery)}(this,function(a){function b(){var b=this;b.top="auto",b.left="auto",b.right="auto",b.bottom="auto",b.set=function(c,d){a.isNumeric(d)&&(b[c]=Math.round(d))}}function c(a,b,c){function d(d,e){g(),a.data(u)?h():d?(e&&a.data(v,!0),i(),c.showTip(a)):(E.tipOpenImminent=!0,k=setTimeout(function(){k=null,f()},b.intentPollInterval))}function e(d){l&&(l=E.closeDelayTimeout=clearTimeout(l),E.delayInProgress=!1),g(),E.tipOpenImminent=!1,a.data(u)&&(a.data(v,!1),d?c.hideTip(a):(E.delayInProgress=!0,E.closeDelayTimeout=setTimeout(function(){E.closeDelayTimeout=null,c.hideTip(a),E.delayInProgress=!1,l=null},b.closeDelay),l=E.closeDelayTimeout))}function f(){var e=Math.abs(E.previousX-E.currentX),f=Math.abs(E.previousY-E.currentY),g=e+f;g-1||a.inArray("mouseout",c.closeEvents)>-1||a.inArray("blur",c.closeEvents)>-1||a.inArray("focusout",c.closeEvents)>-1)&&(E.activeHover.data(u)===!1||E.activeHover.is(":disabled")?b=!0:m(E.activeHover)||E.activeHover.is(":focus")||E.activeHover.data(v)||(y.data(x)?m(y)||(b=!0):b=!0),b&&g(E.activeHover))}var l=new d,y=a("#"+c.popupId);0===y.length&&(y=a("
",{id:c.popupId}),0===s.length&&(s=a("body")),s.append(y),E.tooltips=E.tooltips?E.tooltips.add(y):y),c.followMouse&&(y.data(w)||(q.on("mousemove"+C,h),r.on("scroll"+C,h),y.data(w,!0))),this.showTip=e,this.hideTip=g,this.resetPosition=i}function f(a){return Boolean(window.SVGElement&&a[0]instanceof SVGElement)}function g(a){return Boolean(a&&"number"==typeof a.pageX)}function h(){E.mouseTrackingActive||(E.mouseTrackingActive=!0,i(),a(i),q.on("mousemove"+C,l),r.on("resize"+C,j),r.on("scroll"+C,k))}function i(){E.scrollLeft=r.scrollLeft(),E.scrollTop=r.scrollTop(),E.windowWidth=r.width(),E.windowHeight=r.height()}function j(){E.windowWidth=r.width(),E.windowHeight=r.height()}function k(){var a=r.scrollLeft(),b=r.scrollTop();a!==E.scrollLeft&&(E.currentX+=a-E.scrollLeft,E.scrollLeft=a),b!==E.scrollTop&&(E.currentY+=b-E.scrollTop,E.scrollTop=b)}function l(a){E.currentX=a.pageX,E.currentY=a.pageY}function m(a){var b=a.offset(),c=a[0].getBoundingClientRect(),d=c.right-c.left,e=c.bottom-c.top;return E.currentX>=b.left&&E.currentX<=b.left+d&&E.currentY>=b.top&&E.currentY<=b.top+e}function n(b){var c,d,e=b.data(z),f=b.data(A),g=b.data(B);return e?(a.isFunction(e)&&(e=e.call(b[0])),d=e):f?(a.isFunction(f)&&(f=f.call(b[0])),f.length>0&&(d=f.clone(!0,!0))):g&&(c=a("#"+g),c.length>0&&(d=c.html())),d}function o(a,b,c){var d=E.scrollTop,e=E.scrollLeft,f=d+E.windowHeight,g=e+E.windowWidth,h=F.none;return(a.topf||Math.abs(a.bottom-E.windowHeight)>f)&&(h|=F.bottom),(a.leftg)&&(h|=F.left),(a.left+b>g||a.right-1?j.on(c+C,function(b){a.powerTip.toggle(this,b)}):j.on(c+C,function(b){a.powerTip.show(this,b)})}),a.each(f.closeEvents,function(b,c){a.inArray(c,f.openEvents)<0&&j.on(c+C,function(b){a.powerTip.hide(this,!g(b))})}),j.on("keydown"+C,function(b){27===b.keyCode&&a.powerTip.hide(this,!0)})),E.elements=E.elements?E.elements.add(j):j,j):j},a.fn.powerTip.defaults={fadeInTime:200,fadeOutTime:100,followMouse:!1,popupId:"powerTip",popupClass:null,intentSensitivity:7,intentPollInterval:100,closeDelay:100,placement:"n",smartPlacement:!1,offset:10,mouseOnToPopup:!1,manual:!1,openEvents:["mouseenter","focus"],closeEvents:["mouseleave","blur"]},a.fn.powerTip.smartPlacementLists={n:["n","ne","nw","s"],e:["e","ne","se","w","nw","sw","n","s","e"],s:["s","se","sw","n"],w:["w","nw","sw","e","ne","se","n","s","w"],nw:["nw","w","sw","n","s","se","nw"],ne:["ne","e","se","n","s","sw","ne"],sw:["sw","w","nw","s","n","ne","sw"],se:["se","e","ne","s","n","nw","se"],"nw-alt":["nw-alt","n","ne-alt","sw-alt","s","se-alt","w","e"],"ne-alt":["ne-alt","n","nw-alt","se-alt","s","sw-alt","e","w"],"sw-alt":["sw-alt","s","se-alt","nw-alt","n","ne-alt","w","e"],"se-alt":["se-alt","s","sw-alt","ne-alt","n","nw-alt","e","w"]},a.powerTip={show:function(b,c){return g(c)?(l(c),E.previousX=c.pageX,E.previousY=c.pageY,a(b).data(t).show()):a(b).first().data(t).show(!0,!0),b},reposition:function(b){return a(b).first().data(t).resetPosition(),b},hide:function(b,c){var d;return c=!b||c,b?d=a(b).first().data(t):E.activeHover&&(d=E.activeHover.data(t)),d&&d.hide(c),b},toggle:function(b,c){return E.activeHover&&E.activeHover.is(b)?a.powerTip.hide(b,!g(c)):a.powerTip.show(b,c),b},destroy:function(b){var c=b?a(b):E.elements;return E.elements&&0!==E.elements.length?(c.off(C).each(function(){var b=a(this),c=[y,t,u,v];b.data(y)&&(b.attr("title",b.data(y)),c.push(z)),b.removeData(c)}),E.elements=E.elements.not(c),0===E.elements.length&&(r.off(C),q.off(C),E.mouseTrackingActive=!1,E.tooltips.remove(),E.tooltips=null),b):b}},a.powerTip.showTip=a.powerTip.show,a.powerTip.closeTip=a.powerTip.hide,a.powerTip}); \ No newline at end of file diff --git a/package.json b/package.json index 7475895..4106bd4 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,9 @@ { - "name": "grblweb", - "description": "web based gcode sender for grbl", - "dependencies": { - "node-static": "0.7.x", - "require-reload": "^0.2.2", - "serialport": "4.0.x", - "socket.io": "1.0.x" + "name" : "grblweb", + "description" : "web based gcode sender for grbl", + "dependencies" : + { "node-static" : "0.7.x" + , "serialport" : "1.4.x" + , "socket.io" : "1.0.x" } } diff --git a/server.js b/server.js index 80f58f1..1021bb4 100644 --- a/server.js +++ b/server.js @@ -29,10 +29,11 @@ */ -var reload = require('require-reload')(require); +//var reload = require('require-reload')(require); //var config = reload('./config.js'); var config = require('./config'); var serialport = require("serialport"); +var SerialPort = serialport.SerialPort; var app = require('http').createServer(handler) , io = require('socket.io').listen(app) , fs = require('fs'); @@ -60,16 +61,16 @@ fs.watch('./config.js', function(e, f) { */ -http.get('http://127.0.0.1:8080', function(res) { +http.get('http://127.0.0.1:8080/?action=snapshot', function(res) { // valid response, enable webcam console.log('enabling webcam'); config.showWebCam = true; }).on('socket', function(socket) { // 2 second timeout on this socket socket.setTimeout(2000); - socket.on('timeout', function() { - this.abort(); - }); + //socket.on('timeout', function() { + // this.abort(); + //}); }).on('error', function(e) { console.log('Got error: '+e.message+' not enabling webcam') }); @@ -135,7 +136,10 @@ function doSerialPortList() { sp[i].lastSerialWrite = []; sp[i].lastSerialReadLine = ''; // 1 means clear to send, 0 means waiting for response - sp[i].handle = new serialport(ports[i].comName, { + //console.log(ports[i].comName); + //console.log(serialport.parsers.readline("\n")); + //console.log(config.serialBaudRate); + sp[i].handle = new SerialPort(ports[i].comName, { parser: serialport.parsers.readline("\n"), baudrate: config.serialBaudRate }); From 600616a252aa2e56ac32961377d5d9024132a4a1 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 16 May 2017 13:33:32 -0700 Subject: [PATCH 19/22] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 76953a0..227e1b4 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,9 @@ node server.js npm install -g forever forever start server.js ``` +## Camera +you'll presently need to install mjpg-streamer, working on something more integrated, but no promises +https://github.com/jacksonliam/mjpg-streamer ## Access From 459e8a6daf642fb13815ded9cb088f72bff1675a Mon Sep 17 00:00:00 2001 From: David Date: Tue, 16 May 2017 13:39:05 -0700 Subject: [PATCH 20/22] Update README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 227e1b4..3a460a2 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,11 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. If you would like to include this code in a project which is not licensed under the AGPL V3, please contact the author at andrewhodel@gmail.com +## additional credits +This repo is a mixture of the above repo, and work done by bryanchanrobot +https://github.com/wgbartley/grblweb +https://github.com/brychanrobot/grblweb + ## Raspberry Pi prebuilt Image There is a prebuilt Raspberry Pi Image that is fully configured. More information and a link to the .img can be found at http://xyzbots.com @@ -75,3 +80,9 @@ Read http://www.hobbytronics.co.uk/raspberry-pi-serial-port Set config.usettyAMA0 to 1 in config.js This is already enabled on the prebuilt GRBLWeb Raspbian image. + +## Fixes +script is looking for mjpg port again +camera viewport readded to main screen +minjs stuff is included again (have to check to see if that's actually "ok" or if I need to point to downloads there as well) +USB serial port is available for selection From 69ee6240de27ae27468488177f2d64529d426319 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 16 May 2017 13:45:03 -0700 Subject: [PATCH 21/22] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3a460a2..2b0f1ed 100644 --- a/README.md +++ b/README.md @@ -86,3 +86,7 @@ script is looking for mjpg port again camera viewport readded to main screen minjs stuff is included again (have to check to see if that's actually "ok" or if I need to point to downloads there as well) USB serial port is available for selection + +## dev setup +raspi V1 - raspi camera - linksprite 3 axis CNC +http://linksprite.com/wiki/index.php5?title=DIY_CNC_3_Axis_Engraver_Machine_PCB_Milling_Wood_Carving_Router_Kit_Arduino_Grbl From 5c2b80c635888afc797301f1340e093390b6d49b Mon Sep 17 00:00:00 2001 From: David Date: Sun, 4 Jun 2017 23:03:15 -0700 Subject: [PATCH 22/22] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 2b0f1ed..e816400 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,12 @@ https://github.com/grbl/grbl/wiki/Configuring-Grbl-v0.8 http://onehossshay.wordpress.com/2011/08/21/grbl-how-it-works-and-other-thoughts/ ## Installation +node --version +v0.10.32 +npm --version +1.4.28 + +I ran into errors with a base install of nodejs on ubuntu ``` git clone https://github.com/andrewhodel/grblweb.git