location accuracy javascript navigator.geolocation - javascript

I have noticed that when using google.maps location you can track the location of your car or walking very closely. Yet when I try this of code in javascript:
navigator.geolocation.getCurrentPosition(
success,
error, {
maximumAge: 600000,
timeout: 10000,
enableHighAccuracy: true
});
It returns success with an accuracy of 800m --> 1200m (meters) I must be totally wrong on how to accurately track location in javascript. I will be using window.setInterval( to update the position continuously! Thanks everyone.

Use watchPosition() instead of repeatedly calling getCurrentPosition().
You can also use position.coords.accuracy to decide if the reading is accurate enough for you to use. The value is in meters and can be thought of as a radius of a circle and you're in there somewhere.
If you want to spend the time/effort you can also provide a sophisticated filter and sanity checker such as: -
'use strict';
/* identification division.
* program-id. TravelManagerPolyfill.
* author. Richard Maher.
* version. 1.0
*/
// Simple polyfill paper-tiger
ServiceWorkerRegistration.prototype.travelManager
= new BackgroundGeolocation();
/*
* This module simulates the work that needs to be done by the UA or
* SW daemon in order to facilitate background geolocation on the web.
*
* Each client even in the same scope will have their own TravelManager
* object/registration. With individual "options" configuration.
*
* TODO: Ask someone why registrations just can't be done in Ultimate
* Web App manifests?
*
* NB: Please treat this as a black-box and concentrate on what it does
* and not how it does it. Most of the magic will be in the UA. Some of
* the proposed throttle functionality may be duplicating existing UA
* functionality and therefore be redundant.
*/
function BackgroundGeolocation()
{
const EARTH_RADIUS = 6371000;
const SHUSH = 60000;
const NUMBER = "number";
const STRING = "string";
const BOOLEAN = "boolean";
const DATE = "date";
const MIN_DATE = -864*Math.pow(10,13);
const MAX_DATE = 864*Math.pow(10,13);
const MSECS = 1000;
const TIMEOUT_IS_USELESS
= Number.POSITIVE_INFINITY;
const toRad = function(num){return num*Math.PI/180};
const DEFAULT_OPTIONS =
{
"maxSilence" : 900000,
"minSilence" : 4000,
"maxSnail" : 15000,
"minProgress": 15,
"maxAge" : 0,
"accurate" : true,
"dropDodgy" : false
}
var options = DEFAULT_OPTIONS;
var lastOptions = options;
var seqNum = 0;
var lastPos, trackerId, loiterTimer, deltaMetres, lastDrop,
replayTimer, timeDetent, spaceDetent, loiterDetent,
maxLocAge, accurate, maxSilence, watchCnt, acceptCnt,
lastUpdate, kept, broken, currActive, regId, subscription,
dropDodgy, mostConfident, leastConfident, recalibrateTimer
;
var OptionElem = function(name,valType,minValue,maxValue,shift)
{
this.name = name;
this.type = valType;
this.minValue = minValue;
this.maxValue = maxValue;
this.shift = shift;
return this;
}
var optionRules =
[
new OptionElem("maxSilence", NUMBER, 0,Number.POSITIVE_INFINITY,MSECS),
new OptionElem("minSilence", NUMBER, 0,SHUSH,MSECS),
new OptionElem("maxSnail", NUMBER, 0,Number.POSITIVE_INFINITY,MSECS),
new OptionElem("maxAge", NUMBER, 0,Number.POSITIVE_INFINITY,MSECS),
new OptionElem("minProgress",NUMBER, 0,Number.POSITIVE_INFINITY,1),
new OptionElem("accurate", BOOLEAN,0,1,0),
new OptionElem("dropDodgy", BOOLEAN,0,1,0),
];
var subscribe =
function(userOptions)
{
if(!navigator.geolocation)
return Promise.reject(new Error("Unsupported browser - No Geolocation support"));
if (regId) return Promise.resolve(subscription);
if (userOptions != undefined) {
parseOptions(userOptions);
lastOptions = options;
}
regId = ++seqNum;
subscription =
{
getId: function(){return regId},
setOptions: setOptions,
unsubscribe: unsubscribe
}
return new Promise((resolve, reject) =>
{
kept = resolve;
broken = reject;
getCurrent(startPosition, startError);
});
}
var getCurrent =
function(currentSuccess, currentFailure)
{
navigator.geolocation.getCurrentPosition(currentSuccess, currentFailure ,{
maximumAge: Number.POSITIVE_INFINITY,
timeout: TIMEOUT_IS_USELESS
});
}
var startPosition =
function(position)
{
kept(subscription);
kept = null;
broken = null;
fireTravelEvent({
"cmd":"start",
"position": pos4Net(position)
},function(){
lastPos = position;
watchCnt = 1;
acceptCnt = 0;
mostConfident = position.coords.accuracy.toFixed();
leastConfident = mostConfident;
startWatch();
loiterTimer = setTimeout(loiterLimit, loiterDetent);
})
}
var startError =
function(positionError){
regId = null;
broken(positionError);
broken = null;
kept = null;
}
var startWatch =
function()
{
trackerId = navigator.geolocation.watchPosition(filterLocation, locError, {
enableHighAccuracy: accurate,
maximumAge: maxLocAge,
timeout: TIMEOUT_IS_USELESS
});
recalibrateTimer = setTimeout(recalibrate, maxSilence);
}
var stopWatch =
function()
{
navigator.geolocation.clearWatch(trackerId);
clearTimeout(recalibrateTimer);
trackerId = null;
recalibrateTimer = null;
}
var fireTravelEvent =
function(msg,callback)
{
try {
currActive.postMessage(msg);
console.log("Msg Sent to SW");
if (callback) callback();
} catch (e) {
if (e.name == "InvalidStateError" || e.name == "TypeError") {
navigator.serviceWorker.ready
.then(reg => {
currActive = reg.active;
fireTravelEvent(msg, callback)})
} else {
throw e;
}
}
}
var vetOption = function(userOption,rule)
{
var result;
switch(rule.type){
case NUMBER:
case DATE:
result = Number(userOption*rule.shift);
if (Number.isNaN(result) || result < rule.minValue || result > rule.maxValue) {
result = null;
}
break;
case STRING:
result = String(userOption);
if (typeof result != STRING){
result = null;
}
break;
case BOOLEAN:
result = Boolean(userOption);
if (typeof result != BOOLEAN){
result = null;
}
break;
default:
console.log("Invalid data type '"+rule.type+"'")
}
if (result == null) {
console.log("Invalid parameter '"+rule.name+"'")
}
return result;
}
var setOptions =
function(userOptions)
{
parseOptions(userOptions);
for (var x in options) {
if (options[x] != lastOptions[x]){
stopWatch();
startWatch();
break;
}
}
lastOptions = options;
}
var parseOptions =
function(userOptions)
{
var rawOptions = userOptions || {};
for (var i=0; i<optionRules.length; i++){
var currOption = optionRules[i].name;
if ((currOption in rawOptions)){
var currentTarget = vetOption(rawOptions[currOption], optionRules[i]);
if (currentTarget){
options[currOption] = currentTarget;
} else {
console.log("Invalid option "+optionRules[i].name+" value = " + rawOptions[currOption])
}
}
}
for (var opt in rawOptions){
if (!optionRules.some(function(rule){return rule.name==this},opt)){
console.log("Unknown option '"+opt+"'")
}
}
timeDetent = options.minSilence;
maxSilence = options.maxSilence;
spaceDetent = options.minProgress;
loiterDetent = options.maxSnail;
maxLocAge = options.maxAge;
accurate = options.accurate;
dropDodgy = options.dropDodgy;
if (timeDetent > maxSilence){
timeDetent = maxSilence;
console.log("Minimum Silence overridden by Maximum Silence");
}
if (loiterDetent > maxSilence){
loiterDetent = maxSilence;
console.log("Maximum Snail overridden by Maximum Silence");
}
if (loiterDetent < timeDetent) {
loiterDetent = timeDetent;
console.log("Maximum Snail overridden by Minimum Silence");
}
return;
}
var locError =
function(error)
{
fireTravelEvent({"cmd":"error","error": {
"code": error.code,
"message": error.message
}});
}
var recalibrate =
function()
{
console.log("recalibrating");
stopWatch();
startWatch();
mostConfident = leastConfident;
}
var filterLocation =
function(position)
{
watchCnt++;
if (position.timestamp <= lastPos.timestamp) return;
var currTime = Date.now();
var updateDelta = currTime - lastUpdate;
var dropping = (updateDelta < timeDetent);
deltaMetres = calculateDistance(
position.coords.latitude,
position.coords.longitude,
lastPos.coords.latitude,
lastPos.coords.longitude)
if (deltaMetres.toFixed() < spaceDetent) return;
if (dropping) {
lastDrop = position;
if (!replayTimer)
replayTimer = setTimeout(moveReplay, (timeDetent - updateDelta));
return;
}
var giveOrTake = position.coords.accuracy.toFixed();
if (giveOrTake > leastConfident) leastConfident = giveOrTake;
if (giveOrTake < mostConfident ) mostConfident = giveOrTake;
if (dropDodgy &&
giveOrTake > spaceDetent &&
giveOrTake > deltaMetres &&
giveOrTake > (2*mostConfident)) {
return; // Not legit. Dicky phone tower or access point?
}
acceptCnt++;
clearTimeout(recalibrateTimer);
recalibrateTimer = setTimeout(recalibrate, maxSilence);
clearTimeout(loiterTimer);
loiterTimer = setTimeout(loiterLimit, loiterDetent);
fireTravelEvent({
"cmd":"travel",
"position":pos4Net(position)
})
lastPos = position;
lastUpdate = currTime;
}
var loiterLimit =
function()
{
loiterTimer = null;
fireTravelEvent({"cmd":"loiter","position":pos4Net(lastPos)})
}
var moveReplay =
function()
{
replayTimer = null;
if ((lastDrop.timestamp > lastPos.timestamp)) {
filterLocation(lastDrop);
}
}
var calculateDistance =
function(lat1, lon1, lat2, lon2){
var dLat = toRad(lat2 - lat1);
var dLon = toRad(lon2 - lon1);
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRad(lat1)) *
Math.cos(toRad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
var distance = EARTH_RADIUS * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return distance;
}
var unsubscribe =
function(cancel)
{
if (!regId) return Promise.resolve(false);
stopWatch();
if (loiterTimer) {
clearTimeout(loiterTimer);
loiterTimer = null;
}
if (replayTimer) {
clearTimeout(replayTimer);
replayTimer = null;
}
regId = null;
lastUpdate = 0;
if (cancel) {
return Promise.resolve(true);
} else {
return new Promise((resolve, reject) =>
{
kept = resolve;
broken = reject;
getCurrent(endPosition, endError);
});
}
}
var endPosition =
function(position)
{
fireTravelEvent({
"cmd":"end",
"position":pos4Net(position)
})
kept(true);
kept = null;
broken = null;
}
var endError =
function(error)
{
fireTravelEvent({"cmd":"error","error": {
"code": error.code,
"message": error.message
}});
broken(false);
broken = null;
kept = null;
}
var pos4Net =
function(pos)
{
return {
coords: {
latitude: pos.coords.latitude,
longitude: pos.coords.longitude,
accuracy: pos.coords.accuracy.toFixed()
},
timestamp: pos.timestamp
}
}
return {subscribe: subscribe};
}

Related

Error while adding a custom html to Google Tag Manager

I'm not really into JS, could you tell me what's wrong with this code? I tried to add this but an error occurs "Error at line 74, character 3: Parse error. ')' expected". I don't really know what to repair.
<script id="gtm-scroll-tracking" type="text/javascript">
; (function (document, window, config) {
// Browser dependencies, script fails silently
if (!document.querySelector || !document.body.getBoundingClientRect) {
return false;
}
// Get our dataLayer ready, in case we're not in GTM or we've got a special name
var dataLayerName = config.dataLayerName || 'dataLayer';
var dataLayer = window[dataLayerName] || (window[dataLayerName] = []);
var cache = {};
// Initialize our distances, for later
config.distances = config.distances || {};
checkDepth();
addEvent(window, 'scroll', throttle(checkDepth, 500));
function getMarks(_docHeight, _offset) {
var marks = {};
var percents = [];
var pixels = []
if (config.distances.percentages) {
if (config.distances.percentages.each) {
percents = percents.concat(config.distances.percentages.each);
}
if (config.distances.percentages.every) {
var _every = every_(config.distances.percentages.every, 100);
percents = percents.concat(_every);
}
}
if (config.distances.pixels) {
if (config.distances.pixels.each) {
pixels = pixels.concat(config.distances.pixels.each);
}
if (config.distances.pixels.every) {
var _every = every_(config.distances.pixels.every, _docHeight);
pixels = pixels.concat(_every);
}
}
marks = addMarks_(marks, percents, '%', _docHeight, _offset);
marks = addMarks_(marks, pixels, 'px', _docHeight, _offset);
return marks;
}
function addMarks_(marks, points, symbol, _docHeight, _offset) {
var i;
for (i = 0; i < points.length; i++) {
var _point = parseInt(points[i], 10);
var height = symbol !== '%' ? _point + _offset : _docHeight *
(_point / 100) + _offset;
var mark = _point + symbol;
if (height <= _docHeight + _offset) { marks[mark] = height; }
}
return marks;
}
function every_(n, total) {
var n = parseInt(n, 10);
var _num = total / n;
var arr = [];
for (i = 1; i < _num + 1; i++) { arr.push(i * n); }
return arr;
}
function checkDepth() {
var _bottom = parseBorder_(config.bottom);
var _top = parseBorder_(config.top);
var height = docHeight(_bottom, _top);
var marks = getMarks(height, (_top || 0));
var _curr = currentPosition();
for (key in marks) {
if (_curr > marks[key] && !cache[key]) {
cache[key] = true;
fireAnalyticsEvent(key);
}
}
}
function fireAnalyticsEvent(distance) {
dataLayer.push({
'event': 'scrollTracking',
'attributes': { 'distance': distance }
});
}
}
function parseBorder_(border) {
if (typeof border === 'Number' || parseInt(border, 10)) {
return parseInt(border, 10);
}
try {
// If we have an element or a query selector, poll getBoundingClientRect
var el = border.nodeType && border.nodeType === 1 ? border :
document.querySelector(border);
var docTop = document.body.getBoundingClientRect().top;
var _elTop = Math.floor(el.getBoundingClientRect().top - docTop);
return _elTop;
} catch (e) { return void (0); }
}
// Adapted from
https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY
function currentPosition() {
var supportPageOffset = window.pageXOffset !== undefined;
var isCSS1Compat = ((document.compatMode || "") === "CSS1Compat");
var currScrollTop = supportPageOffset ?
window.pageYOffset :
isCSS1Compat ?
document.documentElement.scrollTop :
document.body.scrollTop;
return parseInt(currScrollTop, 10) + parseInt(viewportHeight(), 10);
}
function viewportHeight() {
var elem = (document.compatMode === "CSS1Compat") ?
document.documentElement :
document.body;
return elem.clientHeight;
}
function docHeight(_bottom, _top) {
var body = document.body;
var html = document.documentElement;
var height = Math.max(body.scrollHeight, body.offsetHeight,
html.clientHeight, html.scrollHeight, html.offsetHeight);
if (_top) { height = height - _top; }
if (_bottom) { height = _bottom - _top; }
return height - 5;
}
/*
* Throttle function borrowed from:
* Underscore.js 1.5.2
* http://underscorejs.org
*
(c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative
Reporters & Editors
* Underscore may be freely distributed under the MIT license.
*/
function throttle(func, wait) {
var context, args, result;
var timeout = null;
var previous = 0;
var later = function () {
previous = new Date;
timeout = null;
result = func.apply(context, args);
};
return function () {
var now = new Date;
if (!previous) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
return result;
};
}
// Cross-browser compliant event listener
function addEvent(el, evt, fn) {
if (el.addEventListener) { return el.addEventListener(evt, fn); }
if (el.attachEvent) {
return el.attachEvent('on' + evt, function (evt) {
// Call the event to ensure uniform 'this' handling, pass it event
fn.call(el, evt);
});
}
if (typeof el['on' + evt] === 'undefined' || el['on' + evt] === null) {
return el['on' + evt] = function (evt) {
// Call the event to ensure uniform 'this' handling, pass it event
fn.call(el, evt); \
}
}
}
})(document, window,
{
// False if you just use the default dataLayer variable, otherwise enter it here
'dataLayerName': false,
'distances': {
// Configure percentages of page you'd like to see if users scroll past
'percentages': {
'each': [10, 90],
'every': 25
},
// Configure for pixel measurements of page you'd like to see if users scroll past
'pixels': {
'each': [],
'every': null
}
},
// Accepts a number, DOM element, or query selector to determine the top of the scrolling area
'top': null,
// Accepts a number, DOM element, or query selector to determine the bottom of the scrolling area
'bottom': null,
});
</script>
While trying to preview (debug), the tool tells me:
Error at line 74, character 3: Parse error. ')' expected

Call external javascript functions from java code : error No such function

through this discussion, I knew how to call an external function of javascript via Java : Call external javascript functions from java code
I tried the same thing with my script and it did not work
metar.js :
(function() {
// http://www.met.tamu.edu/class/metar/metar-pg10-sky.html
// https://ww8.fltplan.com/AreaForecast/abbreviations.htm
// http://en.wikipedia.org/wiki/METAR
// http://www.unc.edu/~haines/metar.html
var CLOUDS = {
NCD: "no clouds",
SKC: "sky clear",
CLR: "no clouds under 12,000 ft",
NSC: "no significant",
FEW: "few",
SCT: "scattered",
BKN: "broken",
OVC: "overcast",
VV: "vertical visibility"
};
var WEATHER = {
// Intensity
"-": "light intensity",
"+": "heavy intensity",
VC: "in the vicinity",
// Descriptor
MI: "shallow",
PR: "partial",
BC: "patches",
DR: "low drifting",
BL: "blowing",
SH: "showers",
TS: "thunderstorm",
FZ: "freezing",
// Precipitation
RA: "rain",
DZ: "drizzle",
SN: "snow",
SG: "snow grains",
IC: "ice crystals",
PL: "ice pellets",
GR: "hail",
GS: "small hail",
UP: "unknown precipitation",
// Obscuration
FG: "fog",
VA: "volcanic ash",
BR: "mist",
HZ: "haze",
DU: "widespread dust",
FU: "smoke",
SA: "sand",
PY: "spray",
// Other
SQ: "squall",
PO: "dust or sand whirls",
DS: "duststorm",
SS: "sandstorm",
FC: "funnel cloud"
};
function parseAbbreviation(s, map) {
var abbreviation, meaning, length = 3;
if (!s) return;
while (length && !meaning) {
abbreviation = s.slice(0, length);
meaning = map[abbreviation];
length--;
}
if (meaning) {
return {
abbreviation: abbreviation,
meaning: meaning
};
}
}
function asInt(s) {
return parseInt(s, 10);
}
function METAR(metarString) {
this.fields = metarString.split(" ").map(function(f) {
return f.trim();
}).filter(function(f) {
return !!f;
});
this.i = -1;
this.current = null;
this.result = {};
}
METAR.prototype.next = function() {
this.i++;
return this.current = this.fields[this.i];
};
METAR.prototype.peek = function() {
return this.fields[this.i+1];
};
METAR.prototype.parseStation = function() {
this.next();
this.result.station = this.current;
};
METAR.prototype.parseDate = function() {
this.next();
var d = new Date();
d.setUTCDate(asInt(this.current.slice(0,2)));
d.setUTCHours(asInt(this.current.slice(2,4)));
d.setUTCMinutes(asInt(this.current.slice(4,6)));
this.result.time = d;
};
METAR.prototype.parseAuto = function() {
this.result.auto = this.peek() === "AUTO";
if (this.result.auto) this.next();
};
METAR.prototype.parseCorrection = function() {
var token = this.peek();
this.result.correction = false;
if (token.lastIndexOf('CC', 0) == 0) {
this.result.correction = token.substr(2,1);
this.next();
}
};
var variableWind = /^([0-9]{3})V([0-9]{3})$/;
METAR.prototype.parseWind = function() {
this.next();
this.result.wind = {
speed: null,
gust: null,
direction: null,
variation: null
};
var direction = this.current.slice(0,3);
if (direction === "VRB") {
this.result.wind.direction = "VRB";
this.result.wind.variation = true;
}
else {
this.result.wind.direction = asInt(direction);
}
var gust = this.current.slice(5,8);
if (gust[0] === "G") {
this.result.wind.gust = asInt(gust.slice(1));
}
this.result.wind.speed = asInt(this.current.slice(3,5));
var unitMatch;
if (unitMatch = this.current.match(/KT|MPS|KPH$/)) {
this.result.wind.unit = unitMatch[0];
}
else {
throw new Error("Bad wind unit: " + this.current);
}
var varMatch;
if (varMatch = this.peek().match(variableWind)) {
this.next();
this.result.wind.variation = {
min: asInt(varMatch[1]),
max: asInt(varMatch[2])
};
}
};
METAR.prototype.parseCavok = function() {
this.result.cavok = this.peek() === "CAVOK";
if (this.result.cavok) this.next();
};
METAR.prototype.parseVisibility = function() {
this.result.visibility = null;
if (this.result.cavok) return;
this.next();
if (this.current === "////") return;
this.result.visibility = asInt(this.current.slice(0,4));
// TODO: Direction too. I've not seen it in finnish METARs...
};
METAR.prototype.parseRunwayVisibility = function() {
if (this.result.cavok) return;
if (this.peek().match(/^R[0-9]+/)) {
this.next();
// TODO: Parse it!
}
};
function parseWeatherAbbrv(s, res) {
var weather = parseAbbreviation(s, WEATHER);
if (weather) {
res = res || [];
res.push(weather);
return parseWeatherAbbrv(s.slice(weather.abbreviation.length), res);
}
return res;
}
METAR.prototype.parseWeather = function() {
if (this.result.weather === undefined) this.result.weather = null;
if (this.result.cavok) return;
var weather = parseWeatherAbbrv(this.peek());
if (!weather) return;
if (!this.result.weather) this.result.weather = [];
this.result.weather = this.result.weather.concat(weather);
this.next();
this.parseWeather();
};
METAR.prototype.parseClouds = function() {
if (!this.result.clouds) this.result.clouds = null;
if (this.result.cavok) return;
var cloud = parseAbbreviation(this.peek(), CLOUDS);
if (!cloud) return;
this.next();
cloud.altitude = asInt(this.current.slice(cloud.abbreviation.length))*100 || null;
cloud.cumulonimbus = /CB$/.test(this.current);
this.result.clouds = (this.result.clouds || []);
this.result.clouds.push(cloud);
this.parseClouds();
};
METAR.prototype.parseTempDewpoint = function() {
this.next();
var replaced = this.current.replace(/M/g, "-");
var a = replaced.split("/");
if( 2 !== a.length ) return; // expecting XX/XX
this.result.temperature = asInt( a[0] );
this.result.dewpoint = asInt( a[1] );
};
METAR.prototype.parseAltimeter = function() {
var temp;
this.next();
if (this.current === undefined || this.current === null) return;
// inches of mercury if AXXXX
if (this.current.length === 5 && "A" === this.current[0]) {
temp = this.current.substr(1, 2);
temp += ".";
temp += this.current.substr(3, 5);
this.result.altimeter_in_hg = parseFloat(temp, 10);
}
else if (this.current.length && "Q" === this.current[0]) {
temp = this.current.substr(1);
this.result.altimeter_hpa = parseInt(temp, 10);
}
};
METAR.prototype.parse = function() {
this.parseStation();
this.parseDate();
this.parseAuto();
this.parseCorrection();
this.parseWind();
this.parseCavok();
this.parseVisibility();
this.parseRunwayVisibility();
this.parseWeather();
this.parseClouds();
this.parseTempDewpoint();
this.parseAltimeter();
};
function parseMETAR(metarString) {
var m = new METAR(metarString);
m.parse();
return m.result;
}
if (typeof module !== "undefined") {
module.exports = parseMETAR;
}
else if (typeof window !== "undefined") {
window.parseMETAR = parseMETAR;
}
}());
Code java :
package testjavascript;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.io.FileReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
public class testjavascript
{
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
engine.eval(new FileReader("C:\\metar.js"));
Invocable inv = (Invocable) engine;
System.out.println(inv.invokeFunction("parseMETAR","EFJY 171950Z AUTO 27006KT 220V310 9999 FEW012 SCT015 BKN060 13/12 Q1006"));
}
}
error :
Exception in thread "main" java.lang.NoSuchMethodException: No such function parseMETAR
at jdk.nashorn.api.scripting.ScriptObjectMirror.callMember(ScriptObjectMirror.java:197)
at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:381)
at jdk.nashorn.api.scripting.NashornScriptEngine.invokeFunction(NashornScriptEngine.java:187)
at testjavascript.testjavascript.main(testjavascript.java:18)
Can someone help me please

how to break a timer while it's not being used anymore

I have problem with removing a timer while it not being used anymore in javascript ?
let say we have a variable tm for the timer and set it with window.setInterval, while it still running we replace it by a new setInterval, how possible to get rid of the first setInterval ?
example code
var tm = setInterval(function(){
console.log('tm1')
}, 10);
tm = setInterval(function(){
console.log('tm2')
}, 10)
this is the real problem
motion:function(ele,cssStart,cssEnd,duration,tangent, easing,loop, complete){
if(ele==null||typeof cssEnd !== 'string') throw new TypeError();
if(!$hd.isElement(ele) || !$hd(ele).isExists()) throw new TypeError();
var validRules = /(left|top|width|color|height|background-color|margin-left|margin-right|margin-top|margin-botom|border-color|padding-left|padding-right|padding-top|border-bottom-width|border-top-width|border-left-width|border-right-width|border-bottom-color|border-top-color|border-left-color|border-right-color|padding-bottom|border-width|opacity|font-size)\s?(?=:).[^;]*(?=;?)/gim;
// filtering css input
cssEnd = cssEnd.replace(/\s{2,}|\n|\t/gim,'').replace(/\s?(:)\s?/gi,'$1').match(validRules);
cssStart = !cssStart?[]:cssStart.replace(/\s{2,}|\n|\t/gim,'').replace(/\s(:)\s/gi,'$1').match(validRules);
if(!cssEnd) throw new Error('invalid css rules, please refer to the documentation about the valid css rules for animation');
// creating properties
var _cssEnd = [],
_paused = false,
_cssStart = [],
_tm = null,
_step,
_complete = typeof complete ==='function'?complete:null,
_loop = 0,
_easing = typeof easing ==='string'?easing.match(/^easein|easeout|easeinout$/)?easing:'easein':'easein',
_tangent = typeof tangent ==='string'?tangent in hd.classes.motionTangents ? tangent:'linear':'linear';
this.ele = $hd(ele),
this.duration = isNaN(duration)?1:parseFloat(duration),
this.loop = isNaN(loop)?0:parseInt(loop),
this.isPlaying = false;
this.complete = false;
// verifying the css rules of the end point
var verify = function(cssStart, cssEnd){
for (var i = 0; i < cssEnd.length; i++) {
var colorPattern = /#[a-z0-9]+|rgba?\s?\(([0-9]{1,3}\s?,\s?)+((([0-9]+)?(\.)?([0-9]+))+)?\)/gi,
name = cssEnd[i].replace(/^([a-z0-9-]+)\s?(?=:).*/gi,'$1'),
value = cssEnd[i].replace(/^[a-z0-9-]+\s?:(.*)/gi,'$1'),
startIndex = $hd(cssStart).inspectData(name+':',false),
startValue = !startIndex.length?this.ele.getcss(name):cssStart[startIndex].replace(/^[a-z0-9-]+:(.*)/gi,'$1');
if(value=='') continue;
// parsing values
if(name.match(/color/i)){
//if color
// validate the color
if(!value.match(colorPattern)) continue;
if(value.match(/#[a-z0-9]+/ig)){
// if hex then convert to rgb
var rgb = $hd(value).hex2rgb(),
value = !rgb?null:rgb;
if(!value) continue;
}
// verifying cssStart's value
startValue = !startValue.match(colorPattern)?this.ele.getcss(name):startValue;
if(!startValue.match(colorPattern)) continue;
if(startValue.match(/#[a-z0-9]+/ig)){
// if hex then convert to rgb
var rgb = $hd(startValue).hex2rgb(),
startValue = rgb==null?null:rgb;
}
// if browser doesn't support rgba then convert the value to rgb
value = !$hd.supports.rgba && value.match(/rgba/i)?value.replace(/(.*)a\s?(\((\d{1,3},)(\d{1,3},)(\d{1,3})).*/i,'$1$2)'):value;
startValue = !$hd.supports.rgba && startValue.match(/rgba/i)?startValue.replace(/(.*)a\s?(\((\d{1,3},)(\d{1,3},)(\d{1,3})).*/i,'$1$2)'):startValue;
// compare and convert the value of both to object
var colora = value.match(/[0-9]{1,3}/g),
colorb = startValue.match(/[0-9]{1,3}/g);
if(colora.length > colorb.length){
colorb.push(this.ele.getcss('opacity'))
}else if(colorb.length>colora.length){
colora.push(colorb[colorb.length-1])
}
_cssEnd.push({
type:'color',
name:name,
value:{
r:parseInt(colora[0]),
g:parseInt(colora[1]),
b:parseInt(colora[2]),
a:colora[3]?parseFloat(colora[3]):null
}
});
_cssStart.push({
type:'color',
name:name,
value:{
r:parseInt(colorb[0]),
g:parseInt(colorb[1]),
b:parseInt(colorb[2]),
a:colorb[3]?parseFloat(colorb[3]):null
}
});
}else{
value = parseFloat(value),
startValue = parseFloat((isNaN(parseFloat(startValue))?parseFloat(this.ele.getcss(name)):startValue));
if(isNaN(value)) continue;
if(isNaN(startValue)) startValue = 0;
_cssEnd.push({
type:'unit',
name:name,
value:parseFloat(value)
});
_cssStart.push({
type:'unit',
name:name,
value:parseFloat(startValue)
});
}
}
};
verify.apply(this,[cssStart,cssEnd]);
// clearing the arguments
cssStart = complete = cssEnd = duration = tangent = loop = ele = verify = null;
if($hd(_cssEnd).isEmpty()) throw new Error('MotionClass::invalid css rules');// raise error if cssEnd is empty
var _pauseTime = 0;
this.play = function(){
if(this.isPlaying) return;
if(!this.ele.isExists() || !this.ele.visible()) {
this.stop();
return;
}
this.isPlaying = true;
_paused = false;
if( $hd(_cssEnd).inspectData('left',true,true).length||
$hd(_cssEnd).inspectData('top',true, true).length)
this.ele.css('position:absolute');
var st = new Date() - _pauseTime;
_tm = window.setInterval(function(){
if(!this.ele.isExists() || !this.ele.visible()) {
this.stop();
return;
}
var pg, delta,
timePassed = new Date() - st;
pg = timePassed / (this.duration*1000);
if (pg > 1) pg = 1;
if(_easing === 'easeout'){
delta = 1 - hd.classes.motionTangents[_tangent](1 - pg);
}else if(_easing==='easeinout'){
if (pg <= 0.5) {
// the first half of animation will easing in
delta= hd.classes.motionTangents[_tangent](2 * pg) / 2
} else {
// the rest of animation will easing out
delta= (2 - hd.classes.motionTangents[_tangent](2 * (1 - pg))) / 2
}
}else{
delta = hd.classes.motionTangents[_tangent](pg);
}
// the movement
_step.call(this,delta);
if(_paused){
window.clearInterval(_tm);
_tm=null;
_pauseTime = timePassed;
this.isPlaying = false;
return;
}
if (pg == 1) {
_pauseTime =0;
if(_loop>=this.loop){
this.complete = true;
this.stop();
if(_complete)_complete.call(null,this);
}else{
_loop++;
st = new Date(),
timePassed = new Date() - st,
pg = 0;
}
}
}.bind(this),15);
};
_step = function(delta){
var styles = '';
for(var i=0;i<_cssEnd.length;i++){
var name = _cssEnd[i].name,
svalue =_cssStart[i].value,
value = _cssEnd[i].value;
if(_cssEnd[i].type == 'color'){
styles += name+':'+(value.a !=null?'rgba(':'rgb(')
+ Math.max(Math.min(parseInt((delta * (value.r-svalue.r)) + svalue.r, 10), 255), 0) + ','
+ Math.max(Math.min(parseInt((delta * (value.g-svalue.g)) + svalue.g, 10), 255), 0) + ','
+ Math.max(Math.min(parseInt((delta * (value.b-svalue.b)) + svalue.b, 10), 255), 0)
+ (value.a ==null?'':',' + $hd(parseFloat((value.a-svalue.a)*delta+svalue.a)).round(1)) +') !important;';
}else{
styles += name+':'+ $hd(parseFloat((value-svalue)*delta+svalue)).round(2) + (name.match(/opacity/i)?'':'px')+ '!important;';
}
this.ele.css(styles);
}
};
this.stop = function(){
this.isPlaying = false;
window.clearInterval(_tm);
_tm=null;
_loop = 0;
};
this.pause = function(){
_paused = true;
};
}
this how the problems came up;
var color = 'rgba('+($(1).randomize(0,255)+',')+
($(1).randomize(0,255)+',')+
($(1).randomize(0,255)+',')+
'1)';
var bcolor = 'rgb('+($(1).randomize(0,255)+',')+
($(1).randomize(0,255)+',')+
($(1).randomize(0,255)+',')+
')';
var motion = new hd.classes.motion(box,'',('height:'+($(1).randomize(10,50))+
'px;border-color:'+bcolor+
';background-color:'+color+
';left :'+((e.x || e.clientX)-(box.rectangle().width/2))+
';top : '+((e.y || e.clientY)-(box.rectangle().height/2))+
';border-top-width:'+($(1).randomize(0,50))),
2,'circle','easeinout',1, function(e){
status.html('Idle');
});
motion.play();
while variable motion has been referenced with an motion class (animation), how possible to stop the first motion's timer if it being replaced with new motion class(animation)
Really easy! clearInterval(tm). If you call them different names, you will be able to differentiate between them.

Unreachable code in varien/configurable.js Magento

I was taking a look at the Magento source file to try to understand why I can't move varien/configurable.js whithout throwing an error with another extension, so Google Closure Compiler to shrink it, but it returns an error at line 267:
JSC_UNREACHABLE_CODE: unreachable code at line 267 character 8
if($('product-price-'+this.config.productId)){ ^
In particular these is the slice of code:
getAttributeOptions: function(attributeId){
if(this.config.attributes[attributeId]){
return this.config.attributes[attributeId].options;
}
},
Could someone explain me why it throw out that warning?
This is the whole code:
/**
* Magento
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
* that is bundled with this package in the file LICENSE_AFL.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/afl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license#magentocommerce.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Magento to newer
* versions in the future. If you wish to customize Magento for your
* needs please refer to http://www.magentocommerce.com for more information.
*
* #category Varien
* #package js
* #copyright Copyright (c) 2014 Magento Inc. (http://www.magentocommerce.com)
* #license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
*/
if (typeof Product == 'undefined') {
var Product = {};
}
/**************************** CONFIGURABLE PRODUCT **************************/
Product.Config = Class.create();
Product.Config.prototype = {
initialize: function(config){
this.config = config;
this.taxConfig = this.config.taxConfig;
if (config.containerId) {
this.settings = $$('#' + config.containerId + ' ' + '.super-attribute-select');
} else {
this.settings = $$('.super-attribute-select');
}
this.state = new Hash();
this.priceTemplate = new Template(this.config.template);
this.prices = config.prices;
// Set default values from config
if (config.defaultValues) {
this.values = config.defaultValues;
}
// Overwrite defaults by url
var separatorIndex = window.location.href.indexOf('#');
if (separatorIndex != -1) {
var paramsStr = window.location.href.substr(separatorIndex+1);
var urlValues = paramsStr.toQueryParams();
if (!this.values) {
this.values = {};
}
for (var i in urlValues) {
this.values[i] = urlValues[i];
}
}
// Overwrite defaults by inputs values if needed
if (config.inputsInitialized) {
this.values = {};
this.settings.each(function(element) {
if (element.value) {
var attributeId = element.id.replace(/[a-z]*/, '');
this.values[attributeId] = element.value;
}
}.bind(this));
}
// Put events to check select reloads
this.settings.each(function(element){
Event.observe(element, 'change', this.configure.bind(this))
}.bind(this));
// fill state
this.settings.each(function(element){
var attributeId = element.id.replace(/[a-z]*/, '');
if(attributeId && this.config.attributes[attributeId]) {
element.config = this.config.attributes[attributeId];
element.attributeId = attributeId;
this.state[attributeId] = false;
}
}.bind(this))
// Init settings dropdown
var childSettings = [];
for(var i=this.settings.length-1;i>=0;i--){
var prevSetting = this.settings[i-1] ? this.settings[i-1] : false;
var nextSetting = this.settings[i+1] ? this.settings[i+1] : false;
if (i == 0){
this.fillSelect(this.settings[i])
} else {
this.settings[i].disabled = true;
}
$(this.settings[i]).childSettings = childSettings.clone();
$(this.settings[i]).prevSetting = prevSetting;
$(this.settings[i]).nextSetting = nextSetting;
childSettings.push(this.settings[i]);
}
// Set values to inputs
this.configureForValues();
document.observe("dom:loaded", this.configureForValues.bind(this));
},
configureForValues: function () {
if (this.values) {
this.settings.each(function(element){
var attributeId = element.attributeId;
element.value = (typeof(this.values[attributeId]) == 'undefined')? '' : this.values[attributeId];
this.configureElement(element);
}.bind(this));
}
},
configure: function(event){
var element = Event.element(event);
this.configureElement(element);
},
configureElement : function(element) {
this.reloadOptionLabels(element);
if(element.value){
this.state[element.config.id] = element.value;
if(element.nextSetting){
element.nextSetting.disabled = false;
this.fillSelect(element.nextSetting);
this.resetChildren(element.nextSetting);
}
}
else {
this.resetChildren(element);
}
this.reloadPrice();
},
reloadOptionLabels: function(element){
var selectedPrice;
if(element.options[element.selectedIndex].config && !this.config.stablePrices){
selectedPrice = parseFloat(element.options[element.selectedIndex].config.price)
}
else{
selectedPrice = 0;
}
for(var i=0;i<element.options.length;i++){
if(element.options[i].config){
element.options[i].text = this.getOptionLabel(element.options[i].config, element.options[i].config.price-selectedPrice);
}
}
},
resetChildren : function(element){
if(element.childSettings) {
for(var i=0;i<element.childSettings.length;i++){
element.childSettings[i].selectedIndex = 0;
element.childSettings[i].disabled = true;
if(element.config){
this.state[element.config.id] = false;
}
}
}
},
fillSelect: function(element){
var attributeId = element.id.replace(/[a-z]*/, '');
var options = this.getAttributeOptions(attributeId);
this.clearSelect(element);
element.options[0] = new Option('', '');
element.options[0].innerHTML = this.config.chooseText;
var prevConfig = false;
if(element.prevSetting){
prevConfig = element.prevSetting.options[element.prevSetting.selectedIndex];
}
if(options) {
var index = 1;
for(var i=0;i<options.length;i++){
var allowedProducts = [];
if(prevConfig) {
for(var j=0;j<options[i].products.length;j++){
if(prevConfig.config.allowedProducts
&& prevConfig.config.allowedProducts.indexOf(options[i].products[j])>-1){
allowedProducts.push(options[i].products[j]);
}
}
} else {
allowedProducts = options[i].products.clone();
}
if(allowedProducts.size()>0){
options[i].allowedProducts = allowedProducts;
element.options[index] = new Option(this.getOptionLabel(options[i], options[i].price), options[i].id);
if (typeof options[i].price != 'undefined') {
element.options[index].setAttribute('price', options[i].price);
}
element.options[index].config = options[i];
index++;
}
}
}
},
getOptionLabel: function(option, price){
var price = parseFloat(price);
if (this.taxConfig.includeTax) {
var tax = price / (100 + this.taxConfig.defaultTax) * this.taxConfig.defaultTax;
var excl = price - tax;
var incl = excl*(1+(this.taxConfig.currentTax/100));
} else {
var tax = price * (this.taxConfig.currentTax / 100);
var excl = price;
var incl = excl + tax;
}
if (this.taxConfig.showIncludeTax || this.taxConfig.showBothPrices) {
price = incl;
} else {
price = excl;
}
var str = option.label;
if(price){
if (this.taxConfig.showBothPrices) {
str+= ' ' + this.formatPrice(excl, true) + ' (' + this.formatPrice(price, true) + ' ' + this.taxConfig.inclTaxTitle + ')';
} else {
str+= ' ' + this.formatPrice(price, true);
}
}
return str;
},
formatPrice: function(price, showSign){
var str = '';
price = parseFloat(price);
if(showSign){
if(price<0){
str+= '-';
price = -price;
}
else{
str+= '+';
}
}
var roundedPrice = (Math.round(price*100)/100).toString();
if (this.prices && this.prices[roundedPrice]) {
str+= this.prices[roundedPrice];
}
else {
str+= this.priceTemplate.evaluate({price:price.toFixed(2)});
}
return str;
},
clearSelect: function(element){
for(var i=element.options.length-1;i>=0;i--){
element.remove(i);
}
},
getAttributeOptions: function(attributeId){
if(this.config.attributes[attributeId]){
return this.config.attributes[attributeId].options;
}
},
reloadPrice: function(){
if (this.config.disablePriceReload) {
return;
}
var price = 0;
var oldPrice = 0;
for(var i=this.settings.length-1;i>=0;i--){
var selected = this.settings[i].options[this.settings[i].selectedIndex];
if(selected.config){
price += parseFloat(selected.config.price);
oldPrice += parseFloat(selected.config.oldPrice);
}
}
optionsPrice.changePrice('config', {'price': price, 'oldPrice': oldPrice});
optionsPrice.reload();
return price;
if($('product-price-'+this.config.productId)){
$('product-price-'+this.config.productId).innerHTML = price;
}
this.reloadOldPrice();
},
reloadOldPrice: function(){
if (this.config.disablePriceReload) {
return;
}
if ($('old-price-'+this.config.productId)) {
var price = parseFloat(this.config.oldPrice);
for(var i=this.settings.length-1;i>=0;i--){
var selected = this.settings[i].options[this.settings[i].selectedIndex];
if(selected.config){
price+= parseFloat(selected.config.price);
}
}
if (price < 0)
price = 0;
price = this.formatPrice(price);
if($('old-price-'+this.config.productId)){
$('old-price-'+this.config.productId).innerHTML = price;
}
}
}
}
Please check the below code in reloadOldPrice function:
return price;
if($('product-price-'+this.config.productId)){
$('product-price-'+this.config.productId).innerHTML = price;
}
this.reloadOldPrice();
when you are using return price; the return statement doesn't allow next code to run. So its unreachable, hence garbage.
Either use some condition if you want the function to stop there in specific situations or put that return statement at the end of function or simply remove return price; if its not required.

modifying webkitdragdrop.js use classes instead of ID

I've found a script that seems perfect for my needs, but it uses IDs, rather than classes to create ipad-friendly drag & drop elements.
I really need it to use classes, as the draggable elements could potentially be in the thousands.
[edit] I'm not that great at javascript and am having difficulties in understanding how I could alter the script to use classes instead of IDs.
I have also contacted the script author, but have had no reply from him.
I'm offering this bounty as I've not had any response to my original query.
Please could someone change the script below so that it uses classes? [/edit]
Below is the script in its entirety, and here is the script page (API was not helpful to me in being able to use classes vs id).
// webkitdragdrop.js v1.0, Mon May 15 2010
//
// Copyright (c) 2010 Tommaso Buvoli (http://www.tommasobuvoli.com)
// No Extra Libraries are required, simply download this file, add it to your pages!
//
// To See this library in action, grab an ipad and head over to http://www.gotproject.com
// webkitdragdrop is freely distributable under the terms of an MIT-style license.
//Description
// Because this library was designed to run without requiring any other libraries, several basic helper functions were implemented
// 6 helper functons in this webkit_tools class have been taked directly from Prototype 1.6.1 (http://prototypejs.org/) (c) 2005-2009 Sam Stephenson
var webkit_tools =
{
//$ function - simply a more robust getElementById
$:function(e)
{
if(typeof(e) == 'string')
{
return document.getElementById(e);
}
return e;
},
//extend function - copies the values of b into a (Shallow copy)
extend:function(a,b)
{
for (var key in b)
{
a[key] = b[key];
}
return a;
},
//empty function - used as defaut for events
empty:function()
{
},
//remove null values from an array
compact:function(a)
{
var b = []
var l = a.length;
for(var i = 0; i < l; i ++)
{
if(a[i] !== null)
{
b.push(a[i]);
}
}
return b;
},
//DESCRIPTION
// This function was taken from the internet (http://robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/) and returns
// the computed style of an element independantly from the browser
//INPUT
// oELM (DOM ELEMENT) element whose style should be extracted
// strCssRule element
getCalculatedStyle:function(oElm, strCssRule)
{
var strValue = "";
if(document.defaultView && document.defaultView.getComputedStyle){
strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
}
else if(oElm.currentStyle){
strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return strValue;
},
//bindAsEventListener function - used to bind events
bindAsEventListener:function(f,object)
{
var __method = f;
return function(event) {
__method.call(object, event || window.event);
};
},
//cumulative offset - courtesy of Prototype (http://www.prototypejs.org)
cumulativeOffset:function(element)
{
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
if (element.offsetParent == document.body)
if (element.style.position == 'absolute') break;
element = element.offsetParent;
} while (element);
return {left : valueL, top : valueT};
},
//getDimensions - courtesy of Prototype (http://www.prototypejs.org)
getDimensions: function(element)
{
var display = element.style.display;
if (display != 'none' && display != null) // Safari bug
return {width: element.offsetWidth, height: element.offsetHeight};
var els = element.style;
var originalVisibility = els.visibility;
var originalPosition = els.position;
var originalDisplay = els.display;
els.visibility = 'hidden';
if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari
els.position = 'absolute';
els.display = 'block';
var originalWidth = element.clientWidth;
var originalHeight = element.clientHeight;
els.display = originalDisplay;
els.position = originalPosition;
els.visibility = originalVisibility;
return {width: originalWidth, height: originalHeight};
},
//hasClassName - courtesy of Prototype (http://www.prototypejs.org)
hasClassName: function(element, className)
{
var elementClassName = element.className;
return (elementClassName.length > 0 && (elementClassName == className ||
new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
},
//addClassName - courtesy of Prototype (http://www.prototypejs.org)
addClassName: function(element, className)
{
if (!this.hasClassName(element, className))
element.className += (element.className ? ' ' : '') + className;
return element;
},
//removeClassName - courtesy of Prototype (http://www.prototypejs.org)
removeClassName: function(element, className)
{
element.className = this.strip(element.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' '));
return element;
},
//strip - courtesy of Prototype (http://www.prototypejs.org)
strip:function(s)
{
return s.replace(/^\s+/, '').replace(/\s+$/, '');
}
}
//Description
// Droppable fire events when a draggable is dropped on them
var webkit_droppables = function()
{
this.initialize = function()
{
this.droppables = [];
this.droppableRegions = [];
}
this.add = function(root, instance_props)
{
root = webkit_tools.$(root);
var default_props = {accept : [], hoverClass : null, onDrop : webkit_tools.empty, onOver : webkit_tools.empty, onOut : webkit_tools.empty};
default_props = webkit_tools.extend(default_props, instance_props || {});
this.droppables.push({r : root, p : default_props});
}
this.remove = function(root)
{
root = webkit_tools.$(root);
var d = this.droppables;
var i = d.length;
while(i--)
{
if(d[i].r == root)
{
d[i] = null;
this.droppables = webkit_tools.compact(d);
return true;
}
}
return false;
}
//calculate position and size of all droppables
this.prepare = function()
{
var d = this.droppables;
var i = d.length;
var dR = [];
var r = null;
while(i--)
{
r = d[i].r;
if(r.style.display != 'none')
{
dR.push({i : i, size : webkit_tools.getDimensions(r), offset : webkit_tools.cumulativeOffset(r)})
}
}
this.droppableRegions = dR;
}
this.finalize = function(x,y,r,e)
{
var indices = this.isOver(x,y);
var index = this.maxZIndex(indices);
var over = this.process(index,r);
if(over)
{
this.drop(index, r,e);
}
this.process(-1,r);
return over;
}
this.check = function(x,y,r)
{
var indices = this.isOver(x,y);
var index = this.maxZIndex(indices);
return this.process(index,r);
}
this.isOver = function(x, y)
{
var dR = this.droppableRegions;
var i = dR.length;
var active = [];
var r = 0;
var maxX = 0;
var minX = 0;
var maxY = 0;
var minY = 0;
while(i--)
{
r = dR[i];
minY = r.offset.top;
maxY = minY + r.size.height;
if((y > minY) && (y < maxY))
{
minX = r.offset.left;
maxX = minX + r.size.width;
if((x > minX) && (x < maxX))
{
active.push(r.i);
}
}
}
return active;
}
this.maxZIndex = function(indices)
{
var d = this.droppables;
var l = indices.length;
var index = -1;
var maxZ = -100000000;
var curZ = 0;
while(l--)
{
curZ = parseInt(d[indices[l]].r.style.zIndex || 0);
if(curZ > maxZ)
{
maxZ = curZ;
index = indices[l];
}
}
return index;
}
this.process = function(index, draggableRoot)
{
//only perform update if a change has occured
if(this.lastIndex != index)
{
//remove previous
if(this.lastIndex != null)
{
var d = this.droppables[this.lastIndex]
var p = d.p;
var r = d.r;
if(p.hoverClass)
{
webkit_tools.removeClassName(r,p.hoverClass);
}
p.onOut();
this.lastIndex = null;
this.lastOutput = false;
}
//add new
if(index != -1)
{
var d = this.droppables[index]
var p = d.p;
var r = d.r;
if(this.hasClassNames(draggableRoot, p.accept))
{
if(p.hoverClass)
{
webkit_tools.addClassName(r,p.hoverClass);
}
p.onOver();
this.lastIndex = index;
this.lastOutput = true;
}
}
}
return this.lastOutput;
}
this.drop = function(index, r, e)
{
if(index != -1)
{
this.droppables[index].p.onDrop(r,e);
}
}
this.hasClassNames = function(r, names)
{
var l = names.length;
if(l == 0){return true}
while(l--)
{
if(webkit_tools.hasClassName(r,names[l]))
{
return true;
}
}
return false;
}
this.initialize();
}
webkit_drop = new webkit_droppables();
//Description
//webkit draggable - allows users to drag elements with their hands
var webkit_draggable = function(r, ip)
{
this.initialize = function(root, instance_props)
{
this.root = webkit_tools.$(root);
var default_props = {scroll : false, revert : false, handle : this.root, zIndex : 1000, onStart : webkit_tools.empty, onEnd : webkit_tools.empty};
this.p = webkit_tools.extend(default_props, instance_props || {});
default_props.handle = webkit_tools.$(default_props.handle);
this.prepare();
this.bindEvents();
}
this.prepare = function()
{
var rs = this.root.style;
//set position
if(webkit_tools.getCalculatedStyle(this.root,'position') != 'absolute')
{
rs.position = 'relative';
}
//set top, right, bottom, left
rs.top = rs.top || '0px';
rs.left = rs.left || '0px';
rs.right = "";
rs.bottom = "";
//set zindex;
rs.zIndex = rs.zIndex || '0';
}
this.bindEvents = function()
{
var handle = this.p.handle;
this.ts = webkit_tools.bindAsEventListener(this.touchStart, this);
this.tm = webkit_tools.bindAsEventListener(this.touchMove, this);
this.te = webkit_tools.bindAsEventListener(this.touchEnd, this);
handle.addEventListener("touchstart", this.ts, false);
handle.addEventListener("touchmove", this.tm, false);
handle.addEventListener("touchend", this.te, false);
}
this.destroy = function()
{
var handle = this.p.handle;
handle.removeEventListener("touchstart", this.ts);
handle.removeEventListener("touchmove", this.tm);
handle.removeEventListener("touchend", this.te);
}
this.set = function(key, value)
{
this.p[key] = value;
}
this.touchStart = function(event)
{
//prepare needed variables
var p = this.p;
var r = this.root;
var rs = r.style;
var t = event.targetTouches[0];
//get position of touch
touchX = t.pageX;
touchY = t.pageY;
//set base values for position of root
rs.top = this.root.style.top || '0px';
rs.left = this.root.style.left || '0px';
rs.bottom = null;
rs.right = null;
var rootP = webkit_tools.cumulativeOffset(r);
var cp = this.getPosition();
//save event properties
p.rx = cp.x;
p.ry = cp.y;
p.tx = touchX;
p.ty = touchY;
p.z = parseInt(this.root.style.zIndex);
//boost zIndex
rs.zIndex = p.zIndex;
webkit_drop.prepare();
p.onStart();
}
this.touchMove = function(event)
{
event.preventDefault();
event.stopPropagation();
//prepare needed variables
var p = this.p;
var r = this.root;
var rs = r.style;
var t = event.targetTouches[0];
if(t == null){return}
var curX = t.pageX;
var curY = t.pageY;
var delX = curX - p.tx;
var delY = curY - p.ty;
rs.left = p.rx + delX + 'px';
rs.top = p.ry + delY + 'px';
//scroll window
if(p.scroll)
{
s = this.getScroll(curX, curY);
if((s[0] != 0) || (s[1] != 0))
{
window.scrollTo(window.scrollX + s[0], window.scrollY + s[1]);
}
}
//check droppables
webkit_drop.check(curX, curY, r);
//save position for touchEnd
this.lastCurX = curX;
this.lastCurY = curY;
}
this.touchEnd = function(event)
{
var r = this.root;
var p = this.p;
var dropped = webkit_drop.finalize(this.lastCurX, this.lastCurY, r, event);
if(((p.revert) && (!dropped)) || (p.revert === 'always'))
{
//revert root
var rs = r.style;
rs.top = (p.ry + 'px');
rs.left = (p.rx + 'px');
}
r.style.zIndex = this.p.z;
this.p.onEnd();
}
this.getPosition = function()
{
var rs = this.root.style;
return {x : parseInt(rs.left || 0), y : parseInt(rs.top || 0)}
}
this.getScroll = function(pX, pY)
{
//read window variables
var sX = window.scrollX;
var sY = window.scrollY;
var wX = window.innerWidth;
var wY = window.innerHeight;
//set contants
var scroll_amount = 10; //how many pixels to scroll
var scroll_sensitivity = 100; //how many pixels from border to start scrolling from.
var delX = 0;
var delY = 0;
//process vertical y scroll
if(pY - sY < scroll_sensitivity)
{
delY = -scroll_amount;
}
else
if((sY + wY) - pY < scroll_sensitivity)
{
delY = scroll_amount;
}
//process horizontal x scroll
if(pX - sX < scroll_sensitivity)
{
delX = -scroll_amount;
}
else
if((sX + wX) - pX < scroll_sensitivity)
{
delX = scroll_amount;
}
return [delX, delY]
}
//contructor
this.initialize(r, ip);
}
//Description
//webkit_click class. manages click events for draggables
var webkit_click = function(r, ip)
{
this.initialize = function(root, instance_props)
{
var default_props = {onClick : webkit_tools.empty};
this.root = webkit_tools.$(root);
this.p = webkit_tools.extend(default_props, instance_props || {});
this.bindEvents();
}
this.bindEvents = function()
{
var root = this.root;
//bind events to local scope
this.ts = webkit_tools.bindAsEventListener(this.touchStart,this);
this.tm = webkit_tools.bindAsEventListener(this.touchMove,this);
this.te = webkit_tools.bindAsEventListener(this.touchEnd,this);
//add Listeners
root.addEventListener("touchstart", this.ts, false);
root.addEventListener("touchmove", this.tm, false);
root.addEventListener("touchend", this.te, false);
this.bound = true;
}
this.touchStart = function()
{
this.moved = false;
if(this.bound == false)
{
this.root.addEventListener("touchmove", this.tm, false);
this.bound = true;
}
}
this.touchMove = function()
{
this.moved = true;
this.root.removeEventListener("touchmove", this.tm);
this.bound = false;
}
this.touchEnd = function()
{
if(this.moved == false)
{
this.p.onClick();
}
}
this.setEvent = function(f)
{
if(typeof(f) == 'function')
{
this.p.onClick = f;
}
}
this.unbind = function()
{
var root = this.root;
root.removeEventListener("touchstart", this.ts);
root.removeEventListener("touchmove", this.tm);
root.removeEventListener("touchend", this.te);
}
//call constructor
this.initialize(r, ip);
}
If your classnames are unique, the solution is rather simple. You can change the $ function to get by class name instead of by id:
var webkit_tools =
{
//$ function - simply a more robust getElementById
$:function(e)
{
if(typeof(e) == 'string')
{
return document.getElementsByClassName(e)[0];
// return document.getElementById(e);
}
return e;
},
... snipped ...
I've verified the above solution works (dragging and dropping) on my iPhone, but again, if the classnames are not unique, some added work will be in order based on the script's current implementation.
~~~EDIT~~~
In re-reading your request, you state that there will in fact NOT be unique classnames, hence the need for some sort of "bulk" drag/drop functionality. I've modified/extended the framework to support this. You can find the source for the modified version here:
https://gist.github.com/2474416
I had to change the API slightly. The dropabble API is unchanged, so passing a classname will simply add/remove the whole list of elements matching the classname passed. The clickable/draggable API was not so easy. To avoid a harsh rewrite, I updated the initialize methods for draggable/clickable to take an element ref rather than id or classname.
Correspondingly, I added a bulk_draggable(clazzname, options) and bulk_clickable(clazzname, options) function that basically iterates over the matched elements and calls the corresponding initializers. These functions return an array of draggables/clickables (one for each matched element).
Let me know if the "new" API is unclear. I did this rather quickly and lightly tested the happy paths, but can't invest and substantial amount of time rewriting the entire script.

Categories