How to make clickable line in JQuery Flot? - javascript

I am using flot to make line charts. One of the functionality I am trying to implement is highting the line (including points on the line and its corresponding legend), if the user clicks on the line, cancel the highlighting if the user clicks anywhere else on the chart.
Tried 'plotclick' event, but it requires clicking on points. I need the ability to get the series when clicking on the line as well.
Hopefully, there is a way to do that.

You have to manually search for the nearest point on the line and then calculate the distance with something like this:
$('#placeholder').on('plotclick', function(event, pos, item) {
$('#output').empty();
if (item) { // clicked on point
$('#output').text('series: ' + item.series.label + ' - datapoint: ' + item.dataIndex);
return;
}
else { // search for line
for (var i = 1; i < data.length; i++) {
if (data[i-1][0] <= pos.x && pos.x < data[i][0]) {
var lineX = (pos.x - data[i-1][0]) / (data[i][0] - data[i-1][0]);
var lineY = data[i-1][1] + lineX * (data[i][1] - data[i-1][1]);
if (Math.abs(pos.y - lineY) < maxDistance) {
$('#output').html('between datapoints ' + (i-1) + ' and ' + i + '<br />'
+ 'distance from line: ' + Math.abs(pos.y - lineY).toFixed(3));
}
return;
}
}
}
});
See this fiddle for a full example. If you have multiple data series you can search for the nearest point on each line and then calculate the nearest line.

Related

GAS to scan a sheet, look for values, and send e-mail wont do anything...thoughts?

Basically my script is supposed to:
Open a certain sheet
check a certain range of data for values below 60%
once it finds one, check the first row of that column to see if it says 'Sent'
If it doess, do nothing
If it doesn't, send jthe email message in the script with the value below 60% etc.
Then edit row one of that column with 'Sent'once it send messages for all of the values below 60% in that column. ( I haven't written this part yet.
It says it runs fine, but it doesn't send anything. I wrote most of this code from scratch and I'm kind of a beginner, so I'm wondering if I have errors that are keeping it from working. I am going to have this run on a timing trigger BTW.
If you would mind looking it over and giving me some feedback, I'd greatly appreciate it. I added the code below.
Happy Holidays!
Brandon
function sendEmail() {
var ss = SpreadsheetApp.openById('1CvK-ALbc-_GZwX4pqadBb67AVAou6euk55OE1axfbAk');
var sheet = ss.getSheets()[0]; // The first of the above spreadsheet
var range = sheet.getRange(3, 10, 40, 40); // Get 2D range (Starting Row (3) ,Starting Column (J), # Rows (40),# Colmuns (40))
var value = range.getValue(); // get values of all cells in that range
if (value < 0.6) { // if it it get values that are less than 60%
var editedsheet = value.getsheet(); // get sheet that value < 60% is in
var editedRow = value.getRow(); // get row that value < 60% is in
var column = value.getColumn(); // get column that value < 60% is in
var status = editedsheet.getRange(0, column).getValue() // check row 1 of that column and get value
if (status != 'Sent') { // if that value is sent, do nothing
}
else { // if the value isnt sent then...
var studentData = editedsheet.getRange(editedRow, 1, 1, 9).getValues(); // email message details
var message = 'Assessment score: ' + Math.round((value * 100) * 10) / 10 + ' %' +
'\nStudentId: ' + studentData[0][0] +
'\nName: ' + studentData[0][1] +
'\nHR: ' + studentData[0][2] +
'\nTeacher: ' + studentData[0][3] +
'\nGrade: ' + studentData[0][4] +
'\nRace: ' + studentData[0][5] +
'\nG: ' + studentData[0][6] +
'\nEd: ' + studentData[0][7] +
'\nAVG: ' + Math.round((studentData[0][8] * 100) * 10) / 10 + ' %';
var emailAddress = 'email address'; // email details
var subject = 'ALERT - Assessment score below 60% inputted.';
MailApp.sendEmail(emailAddress, subject, message);
}
}
}
Here is a link to an example spreadsheet like the one I'm using.
Example Spreadsheet
There are some issues with your code. E.g. use .getValues() instead of .getValue() if you want to get the values of the range. Also you will have to loop through the values that are returned.
I think your code should look something like this:
NOTE: untested code !
function assessmentAlert() {
SpreadsheetApp.getActive().getSheets()
.forEach( function (s) {
var val = s.getDataRange().getValues();
//check to see if correct cells where found ==> check the log when the script finishes
var count = 0;
//start looping through the values, first loop = rows, second loop = columns
for (var i = 0, ilen = val.length; i < ilen; i++) {
for (var j = 0, jlen = val[0].length; j < jlen; j++) {
//conditions: column > 8, row > 2, value should be numeric and < 0.6 and the headerrow should not have 'Sent'
if (j > 8 && i >2 && !isNaN(parseFloat(val[i][j])) && val[i][j] < 0.6 && val[0][j] != 'Sent') {
count += 1
//if all conditions are met, send the email
var message = 'Assessment score: ' + Math.round((val[i][j] * 100) * 10) / 10 + ' %' +
'\nStudentId: ' + val[i][0] +
'\nName: ' + val[i][1] +
'\nHR: ' + val[i][2] +
'\nTeacher: ' + val[i][3] +
'\nGrade: ' + val[i][4] +
'\nRace: ' + val[i][5] +
'\nG: ' + val[i][6] +
'\nEd: ' + val[i][7] +
'\nAVG: ' + Math.round((val[i][8] * 100) * 10) / 10 + ' %';
var emailAddress = 'email#email.com'; // email details
var subject = 'ALERT - Assessment score below 60% inputted.';
MailApp.sendEmail(emailAddress, subject, message);
s.getRange(1, j+1).setValue('Sent').setFontColor('Red');
}
}
}
Logger.log('number of values found:' + count);
});
}
NOTE: the above code is untested. So I'll suggest you comment out the line MailApp.sendEmail and run the code and check the logger if the number of values found is correct.
I hope this helps ?
Your check for 'sent' is looking at a getRange(0,column). While the array it returns starts at 0, you should specify row 1 as getRange(1,column)

Map Route Direction Zoom to Segment

Basically I am trying to zoom to certain route segment when getting direction on OneMap. Here is the JavaScript codes where I trying to plot a route and zoom to certain route segment:
function getDirections() {
var routeData = new Route;
var from = document.getElementById('txtFrom').value
var to = document.getElementById('txtTo').value
//Draw out the line from the cordinate to another cordinate
routeData.routeStops = from + ";" + to;
//What type of mode will it do
routeData.routeMode = "DRIVE";
//can draw out untill the following coordiante
routeData.barriers = '36908.388637,35897.420831';
{
if (document.getElementById('CbAvoid').checked) {
routeData.avoidERP = "1";
}
else
routeData.avoidERP = "0";
}
routeData.GetRoute(showRouteData)
}
function showRouteData(routeResults) {
if (routeResults.results == "No results") {
alert("No Route found, please try other location.")
return
}
$('#divComputedDirection').show();
directions = routeResults.results.directions[0];
directionFeatures = directions.features;
var routeSymbol = new esri.symbol.SimpleLineSymbol().setColor(new dojo.Color([0, 0, 255, 0.5])).setWidth(4);
var mergedGeometry = new esri.geometry.Polyline()
mergedGeometry.addPath(routeResults.results.routes.features[0].geometry.paths[0])
OneMap.map.graphics.add(new esri.Graphic(mergedGeometry, routeSymbol));
//Display the total time and distance of the route
var htmlStr = "";
htmlStr += "<img class='close-image' onclick='closeDirectionResultDIV();' alt='close' src='img/closeDirectionResult.png' />";
htmlStr += "<span style='font-weight:bold;'><br /> Total distance: " + Math.round(directions.summary.totalLength) + "km" + "<br /> Total time: " + Math.round(directions.summary.totalTime) + "mins <br/></span>";
document.getElementById("divComputedDirection").innerHTML = htmlStr;
//List the directions and create hyperlinks for each route segment
for (var i = 0; i < directions.features.length; i++) {
var feature = directions.features[i]
document.getElementById("divComputedDirection").innerHTML += '<br>' + parseInt(parseInt(i) + 1) + ". " + feature.attributes.text + " (" + formatDistance(feature.attributes.length, "miles") + ", " + formatTime(feature.attributes.time) + ") " + '';
}
}
//Zoom to the appropriate segment when the user clicks a hyperlink in the directions list
function zoomToSegment(index) {
var segment = directionFeatures[index];
map.setExtent(segment.geometry.getExtent(), true);
if (!segmentGraphic) {
segmentGraphic = map.graphics.add(new esri.Graphic(segment.geometry, segmentSymbol));
}
else {
segmentGraphic.setGeometry(segment.geometry);
}
}
It did plot the route and show all the directions. But when I click on certain direction and zoom to segement, it throws me an error message which is Uncaught TypeError: Cannot call method 'getExtent' of undefined.
I wonder why is it so. Thanks in advance.
The root cause of your error is that you're trying to get the extent of a .geometry property that doesn't exist - that part is relatively easy. The problem, I think, is that you're looking for the geometry of each segment of the journey, and the return from OneMap's RouteTask doesn't give you that directly.
The geometry from the entire route is in
routeResults.results.routes.features[0].geometry.paths[0]
and the individual segments are in one of ESRI's fun compressed formats in the value:
routeResults.results.directions[x].features[y].compressedGeometry
There's some documentation and C# code for this compressed format here:
http://resources.esri.com/help/9.3/arcgisengine/ArcObjects/esrinetworkanalyst/INACompactStreetDirection_CompressedGeometry.htm
It should be relatively easy to port that C# code to JS if you really need the geometry of individual segments.
OneMap have a full working example here which shows how to process the results from the RouteTask, but unfortunately they don't attempt to extract the compressedGeometry field.
Edit: More sample code from ESRI here, with examples in C#/Java/Python.

Detect swipe gesture with leap motion

I knowed how to detect gesture left and right from
this
I want to know how to detect gesture up , down and circle.
My English is poor. I dont think you can understand, but help me plz.
For swipe directions, you can compare the x and y coordinates of the direction property of the Gesture object. In the Leap Motion JavaScript API, vectors are represented by 3-element arrays. So:
gesture.direction[0] is the x coordinate (left to right)
gesture.direction[1] is the y coordinate ( up, down)
gesture.direction[2] is the z coordinate (front to back)
The example you cite only looks at the sign of the x-coordinate -- so all swipes are classified as either right or left. To also classify swipes as up or down, you will have to compare the magnitude of the x and y coordinates to determine if the swipe is more horizontal or more vertical and then compare the sign of the coordinate to determine if a horizontal swipe is left or right or a vertical swipe is up or down.
Circle gestures are reported as a different type of gesture, so you can look at the gesture.type property.
Here is some JavaScript that illustrates this (adapted from the Sample.html file included with the Leap Motion SDK):
// Store frame for motion functions
var previousFrame = null;
// Setup Leap loop with frame callback function
var controllerOptions = {enableGestures: true};
Leap.loop(controllerOptions, function(frame) {
// Display Gesture object data
var gestureOutput = document.getElementById("gestureData");
var gestureString = "";
if (frame.gestures.length > 0) {
for (var i = 0; i < frame.gestures.length; i++) {
var gesture = frame.gestures[i];
switch (gesture.type) {
case "circle":
gestureString += "<br>ID: " + gesture.id + "<br>type: " + gesture.type + ", "
+ "<br>center: " + vectorToString(gesture.center) + " mm, "
+ "<br>normal: " + vectorToString(gesture.normal, 2) + ", "
+ "<br>radius: " + gesture.radius.toFixed(1) + " mm, "
+ "<br>progress: " + gesture.progress.toFixed(2) + " rotations"
+ "<br>";
break;
case "swipe":
//Classify swipe as either horizontal or vertical
var isHorizontal = Math.abs(gesture.direction[0]) > Math.abs(gesture.direction[1]);
//Classify as right-left or up-down
if(isHorizontal){
if(gesture.direction[0] > 0){
swipeDirection = "right";
} else {
swipeDirection = "left";
}
} else { //vertical
if(gesture.direction[1] > 0){
swipeDirection = "up";
} else {
swipeDirection = "down";
}
}
gestureString += "<br>ID: " + gesture.id + "<br>type: " + gesture.type + ", "
+ "<br>direction " + swipeDirection
+ "<br>gesture.direction vector: " + vectorToString(gesture.direction, 2) + ", "
+ "<br>";
break;
}
}
}
gestureOutput.innerHTML = gestureString + gestureOutput.innerHTML;
})
function vectorToString(vector, digits) {
if (typeof digits === "undefined") {
digits = 1;
}
return "(" + vector[0].toFixed(digits) + ", "
+ vector[1].toFixed(digits) + ", "
+ vector[2].toFixed(digits) + ")";
}
To use this, put it somewhere it will be executed and include a element with the id gestureData in the HTML document body:
<div id="gestureData"></div>
A friend of mine made a library for exactly this purpose. It checks for swipes in 6 different directions and can tell which direction a circle gesture is going.
https://github.com/L1fescape/curtsy
His code should be easily readable too so if you want to see how he did things you can.

On the server, player movement is not rendered

This code renders the motion of the player, changing the picture. Locally it works fine on the server change picture is not visible. But if you uncomment the alert ("right1"); and alert ("right2"); will be seen as an image change. How do I make the server was also seen pictures change?
var timer;
function GoRight(toPosition, level, mines) {
clearInterval(timer);
var left = $("#man").position().left;
var top = $("#man").position().top;
$("#man").attr('style', 'position:absolute;display:block;left:' + left + 'px;top:' + top + 'px;')
$("#man").attr("class", "");
var tempi = 0;
timer = setInterval(
function () {
if (left >= toPosition) {
left = toPosition;
$("#man").attr('style', 'position:absolute;display:block;left:' + left + 'px;top:' + top + 'px;')
clearInterval(timer);
$("#man").attr('src', '/content/games/kamikaze2/right0.gif');
return;
}
tempi += 8;
left += 8;
$("#man").attr('style', 'position:absolute;display:block;left:' + left + 'px;top:' + top + 'px;')
if (tempi % 16 == 0) {
// alert("right1");
$("#man").attr('src', '/content/games/kamikaze2/right1.gif');
}
else {
// alert("right2");
$("#man").attr('src', '/content/games/kamikaze2/right2.gif');
}
}, 70);
}
It's like a surprise, but it helped me a line after the if else
$("#man").attr('src');
but it works

Can't find the issue: javascript/jQuery

I have the following function that gets called on the KeyDown event of a link.
Basically I'm trying to move down the table (the application calls it a ListBox). In the first loop what I'm trying to do is see if they use the mouse to click inside the table first and my hope was to find the row value and then manipulate the class (highlight) from there.
Unfortunately right now I'm not even getting that far. When the screen loads and I press the down button, the current row (0) has it's class changed as well as row (1). But on the next down button press the tr_lst says it is undefined. Which then throws the loop off and then I get all sorts of errors.
I tried to implement a jsfiddle, however, I couldn't get it working. But you can see some of the code I'm trying to implement.
function xKeyDown(event,ListBoxVal){
var tr_lst = $('#' + ListBoxVal).find('tr[class="LUGridRowHighlight"]');
var iCount = 0;
for (iCount = 0; iCount <= $('#' + ListBoxVal + ' tr').length; iCount++){
if($('#' + ListBoxVal + ' tr:eq('+iCount+')').attr('id') == tr_lst.attr('id')){
lstRow = iCount;
break;
}
}
if (event.keyCode == 40){
//arrow down
if(parseInt(lstRow) < $('#' + ListBoxVal + ' tr').length)
{
if(parseInt(lstRow) == 0){
document.getElementById(ListBoxVal).focus();
lstRow +=1;
document.getElementById($('#' + ListBoxVal + ' tr:eq('+parseInt(lstRow)+')').attr('id')).focus();
$('#' + ListBoxVal + ' tr:eq('+parseInt(lstRow)+')').addClass('LUGridRowHighlight');
$('#' + ListBoxVal + ' tr:eq('+parseInt(lstRow)+')').prev().removeClass('LUGridRowHighlight') .addClass('LUGridRow');
}else{
document.getElementById($('#' + ListBoxVal + ' tr:eq('+parseInt(lstRow)+')').attr('id')).focus();
$('#' + ListBoxVal + ' tr:eq('+parseInt(lstRow)+')').addClass('LUGridRowHighlight');
$('#' + ListBoxVal + ' tr:eq('+parseInt(lstRow)+')').prev().removeClass('LUGridRowHighlight') .addClass('LUGridRow');
lstRow +=1;
}
}
...
Update:
After looking into this further... It appears that when I click the down arrow more than once the following code is causing an error:
var tr_lst = $('#' + ListBoxVal).find('tr[class="LUGridRowHighlight"]');
When I try to print this out it is 'undefined'
I'm wondering since I am manipulating the class via jQuery do I need to add a .live somewhere to the find? As I believe when elements are manipulated dynamically the .live comes into play. Any suggestions?
Try this
function xKeyDown(event,ListBoxVal){
var $listBoxVal = $('#' + ListBoxVal);
var trs = $listBoxVal.find('tr');
var tr_lst = $listBoxVal.find('tr.LUGridRowHighlight');
var tr_lst_id = tr_lst.attr('id');
var iCount = 0;
for (iCount = 0; iCount <= trs.length; iCount++){
if($listBoxVal.find('tr:eq('+iCount+')').attr('id') == tr_lst_id){
lstRow = iCount;
break;
}
}
if (event.keyCode == 40){
//arrow down
if(parseInt(lstRow) < $listBoxVal.find('tr').length)
{
if(parseInt(lstRow) == 0){
$listBoxVal.focus();
lstRow +=1;
$("#"+$listBoxVal.find('tr:eq('+parseInt(lstRow)+')').attr('id')).focus();
$listBoxVal.find('tr:eq('+parseInt(lstRow)+')').addClass('LUGridRowHighlight');
$listBoxVal.find(' tr:eq('+parseInt(lstRow)+')').prev().removeClass('LUGridRowHighlight').addClass('LUGridRow');
}else{
$("#"+$listBoxVal.find('tr:eq('+parseInt(lstRow)+')').attr('id')).focus();
$listBoxVal.find('tr:eq('+parseInt(lstRow)+')').addClass('LUGridRowHighlight');
$listBoxVal.find('tr:eq('+parseInt(lstRow)+')').prev().removeClass('LUGridRowHighlight').addClass('LUGridRow');
lstRow +=1;
}
}
...

Categories