Object reference passing in raphael drag function - javascript

I am creating a module to graphically visualize workflows using raphael,which take data from a data base. For this i have created a class called "FlowEdit", and created move, up and dragger function according to raphael.
But in move function when i am trying to access connections list using object reference, than i am unable to reference it, it gives undefined error.
Code for the class is this:-
//class definition
function FlowView(list) {
this.list = list;
this.connections = [];
this.r = Raphael("holder", 1400, 500);
this.shapes = [];
this.texts = [];
this.y_center = 500 / 2;
//box size
this.r_width = 60;
this.r_height = 40;
// To define virtual regions
this.x_offset = 50;
this.y_offset = 40;
this.x_start = 40;
//this.color, this.tempS, this.tempT;
//Define position in y direction
this.top_count = [0];
this.bottom_count = [0];
//Initialize Top_count & Bottom_Count Arrays
for (var i = 0; i < this.list.length; i++) {
this.top_count.push(0);
this.bottom_count.push(0);
}
}
;
// Give starting points from list
FlowView.prototype.start_point = function () {
var start_list = [];
for (var i in this.list) {
if (this.list[i][1] == this.list[i][2][0]) {
start_list.push(this.list[i][1]);
}
}
return start_list;
};
//For Finding index of an element in list
FlowView.prototype.index_of = function (curr_point) {
for (var i in this.list) {
if (this.list[i][1] == curr_point) {
return i;
}
}
};
//add next function
FlowView.prototype.add_next = function () {
for (var i in this.list) {
if (this.list[i][3][0] == "NULL") {
//For all last nodes add same to their next
this.list[i][3][0] = this.list[i][1];
}
if (this.list[i][3].length == 0) {
//For all last nodes add same to their next
this.list[i][3].push(this.list[i][1]);
}
}
};
//For given next of all nodes add previous to those nodes
FlowView.prototype.add_previous = function () {
for (var i in this.list) {
for (var j in this.list[i][3]) {
//For all next add current node to their previous list
var curr_index = this.index_of(this.list[i][3][j]);
if (this.list[curr_index][2].indexOf(this.list[i][1]) == -1 && (curr_index != i)) {
this.list[curr_index][2].push(this.list[i][1]);
}
}
}
//Add previous of all start node
for (var i in this.list) {
if (this.list[i][2].length == 0) {
this.list[i][2].push(this.list[i][1]);
}
}
};
//Region update recursively
FlowView.prototype.region_update = function (curr_index) {
if (this.list[curr_index][1] != this.list[curr_index][3][0]) {
for (var i in this.list[curr_index][3]) {
var next_index = this.index_of(this.list[curr_index][3][i]);
if (this.list[next_index][0] < this.list[curr_index][0] + 1) {
this.list[next_index][0] = this.list[curr_index][0] + 1;
this.region_update(next_index);
}
}
}
};
//Draw the workflow for given data structure
FlowView.prototype.construct = function () {
var open = this.start_point();
var close = [];
while (open.length != 0) {
var curr_point = open.shift();
var curr_index = this.index_of(curr_point);
//document.write(curr_index);
//draw box
var curr_region = this.list[curr_index][0];
//document.write(curr_region);
var x_cord = parseInt(curr_region) * (this.x_offset + this.r_width) + this.x_start;
//document.write(x_start);
var y_cord = 0;
if (this.top_count[curr_region] == 0 && this.bottom_count[curr_region] == 0) {
y_cord = this.y_center - this.r_height / 2;
this.top_count[curr_region] = 1;
this.bottom_count[curr_region] = 1;
}
else if (this.top_count[curr_region] <= this.bottom_count[curr_region]) {
y_cord = this.y_center - this.r_height / 2 - this.top_count[curr_region] * (this.y_offset + this.r_height);
this.top_count[curr_region] = this.top_count[curr_region] + 1;
}
else {
y_cord = this.y_center + this.r_height / 2 + this.bottom_count[curr_region] * (this.y_offset + this.r_height) - this.r_height;
this.bottom_count[curr_region] = this.bottom_count[curr_region] + 1;
}
//drawing the box
this.shapes[this.list[curr_index][1]] = this.r.rect(x_cord, y_cord, this.r_width, this.r_height, 10);
this.texts[this.list[curr_index][1]] = this.r.text(x_cord + this.r_width / 2, y_cord + this.r_height / 2, this.list[curr_index][1]);
// Adding next nodes to open list
for (var i in this.list[curr_index][3]) {
//If not in open than add to open
if (this.list[curr_index][3][0] != this.list[curr_index][1]) {
if (open.indexOf(this.list[curr_index][3][i]) == -1 && close.indexOf(this.list[curr_index][3][i]) == -1) {
open.push(this.list[curr_index][3][i]);
}
}
}
//Increasing region index for each next node
this.region_update(curr_index);
close.push(curr_point);
//document.write(open.toString()+"</br>");
//document.write(close.toString()+"</br>");
}
for (var j in this.list) {
if (this.list[j][1] != this.list[j][3][0]) {
for (var i in this.list[j][3]) {
//make link for each previous
if (close.indexOf(this.list[j][3][i]) != -1) {
this.connections.push(this.r.connection(this.shapes[this.list[j][1]], this.shapes[this.list[j][3][i]], "bcd"));
}
}
}
}
};
FlowView.prototype.dragger = function () {
// Original cords for main element
this.ox = this.type == "ellipse" ? this.attr("cx") : this.attr("x");
this.oy = this.type == "ellipse" ? this.attr("cy") : this.attr("y");
if (this.type != "text") this.animate({"fill-opacity":.2}, 500);
// Original co-ords for pair element
this.pair.ox = this.pair.type == "ellipse" ? this.pair.attr("cx") : this.pair.attr("x");
this.pair.oy = this.pair.type == "ellipse" ? this.pair.attr("cy") : this.pair.attr("y");
if (this.pair.type != "text") this.pair.animate({"fill-opacity":.2}, 500);
};
FlowView.prototype.move = function (dx, dy) {
// Move main element
var att = this.type == "ellipse" ? {cx:this.ox + dx, cy:this.oy + dy} :
{x:this.ox + dx, y:this.oy + dy};
this.attr(att);
// Move paired element
att = this.pair.type == "ellipse" ? {cx:this.pair.ox + dx, cy:this.pair.oy + dy} :
{x:this.pair.ox + dx, y:this.pair.oy + dy};
this.pair.attr(att);
//document.write("adass");
//document.write(x_offset);
// Move connections
for (var i = this.connections.length; i--;) {
this.r.connection(this.connections[i]);
}
this.r.safari();
};
FlowView.prototype.up = function () {
// Fade original element on mouse up
if (this.type != "text") this.animate({"fill-opacity":0}, 500);
// Fade paired element on mouse up
if (this.pair.type != "text") this.pair.animate({"fill-opacity":0}, 500);
// Move connections
};
FlowView.prototype.drag_initialize = function () {
for (var i in this.shapes) {
var color = Raphael.getColor();
var tempS = this.shapes[i].attr({fill:color, stroke:color, "fill-opacity":0, "stroke-width":2, cursor:"move"});
var tempT = this.texts[i].attr({fill:color, stroke:"none", "font-size":15, cursor:"move"});
this.shapes[i].drag(this.move, this.dragger, this.up);
this.texts[i].drag(this.move, this.dragger, this.up);
// Associate the elements
tempS.pair = tempT;
tempT.pair = tempS;
}
};
Using above code i am able to draw graph & drag items,but when i drag items the connected path are not dragged along it.So where i am doing wrong. For making connection i used the same code given in raphael demos..

This is a common annoyance and there is, fortunately, a very simple solution!
Problem: Raphael is using the functions you specify (this.move, this.dragger, and this.up) but it is NOT calling them in the context of your object. So instead of referring to your object, the this variable is actually referring to window. Decidedly not helpful.
Solution: use a function closure to bind in a reference to your object instance. Update your drag_initialize function with this:
var self = this;
this.shapes[i].drag(function(){ self.move(); }, function() { self.dragger(); }, function() { self.up(); } );
this.texts[i].drag(function() { self.move(); }, function() { self.dragger(); }, function() { self.up(); } );

Hi I found the answer to the problem.
In the move function i return another function and while calling drag i give the object argument in move function hence the context of current object is passed. Now changed move function is:-
FlowView.prototype.move = function (obj) {
// Move main element
return function(dx, dy){
var att = this.type == "ellipse" ? {cx:this.ox + dx, cy:this.oy + dy} :
{x:this.ox + dx, y:this.oy + dy};
this.attr(att);
// Move paired element
att = this.pair.type == "ellipse" ? {cx:this.pair.ox + dx, cy:this.pair.oy + dy} :
{x:this.pair.ox + dx, y:this.pair.oy + dy};
this.pair.attr(att);
// Move connections
for (var i = obj.connections.length; i--;) {
obj.r.connection(obj.connections[i]);
}
obj.r.safari();
}
};
And calling the drag with
this.shapes[i].drag(this.move(this), this.dragger, this.up);
this.texts[i].drag(this.move(this), this.dragger, this.up);

Related

Delete legend when updating leaflet map

I have this code for a map that I want to update based on a campaign (2 different json files) (I cut the first part of the code because If not I couldn't post the question)
// Load in GeoJson data
var geoData = "static/data/dma_shp.json";
var dmaData = "static/data/dma_info.json";
var geojson;
var dmajson;
window.getData = function() {
var e = document.getElementById("selCamp");
var v = e.value;
console.log(v)
if (v == "campaign1") { dmaData = "static/data/dma_info.json"; } else if (v == "campaign2") { dmaData = "static/data/dma_info2.json"; }
// console.log(dmaData)
}
//Grab data with d3
d3.json(geoData, data => {
console.log(data.features)
d3.json(dmaData, data_legend => {
const vcr_array = Object.values(data_legend);
vcr_array.sort();
const len = vcr_array.length;
var per16 = Math.floor(len * .166) - 1;
var per33 = Math.floor(len * .332) - 1;
var per50 = Math.floor(len * .498) - 1;
var per66 = Math.floor(len * .664) - 1;
var per83 = Math.floor(len * .83) - 1;
function getColor(d) {...
} // END of getColor
for (let i = 0; i < data.features.length; i++) {
const curr_dma = data.features[i].properties.dma
var filtered = Object.fromEntries(Object.entries(data_legend).filter(([k, v]) => k == curr_dma));
for (var key in filtered) {
const key_value = filtered[key]
data.features[i].properties.vcr_value = key_value;
}
};
geojson = L.geoJson(data, {
onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
click: function(event) {
myMap.fitBounds(event.target.getBounds());
}
});
layer.bindPopup(...).addTo(myMap)
var legend = L.control({ position: 'topright' });
legend.onAdd = function(map) {
var div = L.DomUtil.create('div', 'info legend'),
grades = [0, vcr_array[per16], vcr_array[per33], vcr_array[per50],
vcr_array[per66], vcr_array[per83]
],
labels = [];
for (var i = 0; i < grades.length; i++) {
div.innerHTML +=
'<i style="background:' + getColor(grades[i]) + '"></i> ' +
grades[i] + (grades[i + 1] ? '–' + grades[i + 1] + '<br>' : '+');
}
return div;
};
legend.addTo(myMap);
})
// window function
window.getData = function() {
// Get the select element
var e = document.getElementById("selCamp");
// Get the value of the selected index
var v = e.value;
console.log(v)
// Verify the value and set the map to the new center
if (v == "campaign1") { dmaData = "static/data/dma_info.json"; } else if (v == "campaign2") { dmaData = "static/data/dma_info2.json"; }
// console.log(dmaData)
d3.json(dmaData, data_legend => {...
all the code above again
})
// Clear layer
geojson.clearLayers();
}
})
But the legend appears to repeat itself everytime I change from campaign to campaign... I also don't know if there is a more efficient way to update my map having 2 or more jsons where the data can come from... Thank you in advance!!!

How to make draggable div elements not stack upon each other

I am creating various draggable elements using let divEx = document.createElement('div'), then add it to the main div with main.appendChild(divEx) and finally
setting the attribute draggable to true using memDiv.setAttribute('draggable', 'true'). The whole functionality is in a function that is called in the main Js file when a button is pressed. Although, the drag and drop functionality works fine, the div elements are set upon each if the position style attribute is set to absolute or under each other if it's set to relative in the CSS. In the DOM it shows the different div elements, however the positions are the same for all of them. I hope the following screenshots explain the issue:
Absolute case:
There are three div elements (class=memDiv) on the screen two on top of each other and a newly created
Here is displayed after I move the third element, it is also stacked on top of the others
What is the best solution for this issue?
Answer: The key approach/solution is Collision Detection between div elements
It looks like you are trying to do a game or something similar, please save your self the pain/don't reinvent the wheel :) , just leverage a library and look for collision detection or set Z index to 1 and check of over lap, I prefer the sat.js lib.
note: Here is sat.js and look at this sample from here from a 3rd party/credit to OSU blake
console.clear();
var log = console.log.bind(console);
// Alias a few things in SAT.js to make the code shorter
var V = function (x, y) { return new SAT.Vector(x, y); };
var P = function (pos, points) { return new SAT.Polygon(pos, points); };
var C = function (pos, r) { return new SAT.Circle(pos, r); };
var B = function (pos, w, h) { return new SAT.Box(pos, w, h); };
// Converts a SAT.Polygon into a SVG path string.
function poly2path(polygon) {
var pos = polygon.pos;
var points = polygon.calcPoints;
var result = 'M' + pos.x + ' ' + pos.y;
result += 'M' + (pos.x + points[0].x) + ' ' + (pos.y + points[0].y);
for (var i = 1; i < points.length; i++) {
var point = points[i];
result += 'L' + (pos.x + point.x) + ' ' + (pos.y + point.y);
}
result += 'Z';
return result;
}
// Create a Raphael start drag handler for specified entity
function startDrag(entity) {
return function () {
this.ox = entity.data.pos.x;
this.oy = entity.data.pos.y;
};
}
// Create a Raphael move drag handler for specified entity
function moveDrag(entity, world) {
return function (dx, dy) {
// This position updating is fairly naive - it lets objects tunnel through each other, but it suffices for these examples.
entity.data.pos.x = this.ox + dx;
entity.data.pos.y = this.oy + dy;
world.simulate();
};
}
// Create a Raphael end drag handler for specified entity
function endDrag(entity) {
return function () {
entity.updateDisplay();
};
}
var idCounter = 0;
function noop() {}
function Entity(data, display, options) {
options = _.defaults(options || {}, {
solid: false, // Whether this object is "solid" and therefore should participate in responses.
heavy: false, // Whether this object is "heavy" and can't be moved by other objects.
displayAttrs: {}, // Raphael attrs to set on the display object
onCollide: noop, // Function to execute when this entity collides with another - arguments are (otherEntity, response)
onTick: noop // Function called at the start of every simulation tick - no arguments
});
this.id = idCounter++;
this.data = data;
this.display = display;
this.displayAttrs = _.extend({
fill: '#CCC',
stroke: '#000'
}, options.displayAttrs);
this.isSolid = options.solid;
this.isHeavy = options.heavy;
this.onCollide = options.onCollide;
this.onTick = options.onTick;
}
Entity.prototype = {
remove: function () {
this.display.remove();
},
// Call this to update the display after changing the underlying data.
updateDisplay: function () {
if (this.data instanceof SAT.Circle) {
this.displayAttrs.cx = this.data.pos.x;
this.displayAttrs.cy = this.data.pos.y;
this.displayAttrs.r = this.data.r;
} else {
this.displayAttrs.path = poly2path(this.data);
}
this.display.attr(this.displayAttrs);
},
tick: function () {
this.onTick();
},
respondToCollision: function (other, response) {
this.onCollide(other, response);
// Collisions between "ghostly" objects don't matter, and
// two "heavy" objects will just remain where they are.
if (this.isSolid && other.isSolid &&
!(this.isHeavy && other.isHeavy)) {
if (this.isHeavy) {
// Move the other object out of us
other.data.pos.add(response.overlapV);
} else if (other.isHeavy) {
// Move us out of the other object
this.data.pos.sub(response.overlapV);
} else {
// Move equally out of each other
response.overlapV.scale(0.5);
this.data.pos.sub(response.overlapV);
other.data.pos.add(response.overlapV);
}
}
}
};
function World(canvas, options) {
options = _.defaults(options || {}, {
loopCount: 1 // number of loops to do each time simulation is called. The higher the more accurate the simulation, but slowers.
});
this.canvas = canvas; // A raphael.js canvas
this.response = new SAT.Response(); // Response reused for collisions
this.loopCount = options.loopCount;
this.entities = {};
}
World.prototype = {
addEntity: function(data, options) {
var entity = new Entity(
data,
data instanceof SAT.Circle ? this.canvas.circle() : this.canvas.path(),
options
);
// Make the display item draggable if requested.
if (options.draggable) {
entity.display.drag(moveDrag(entity, this), startDrag(entity), endDrag(entity));
}
entity.updateDisplay();
this.entities[entity.id] = entity;
return entity;
},
removeEntity: function (entity) {
entity.remove();
delete this.entities[entity.id];
},
simulate: function () {
var entities = _.values(this.entities);
var entitiesLen = entities.length;
// Let the entity do something every simulation tick
_.each(entities, function (entity) {
entity.tick();
});
// Handle collisions - loop a configurable number of times to let things "settle"
var loopCount = this.loopCount;
for (var i = 0; i < loopCount; i++) {
// Naively check for collision between all pairs of entities
// E.g if there are 4 entities: (0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)
for (var aCount = 0; aCount < entitiesLen; aCount++) {
var a = entities[aCount];
for (var bCount = aCount + 1; bCount < entitiesLen; bCount++) {
var b = entities[bCount];
this.response.clear();
var collided;
var aData = a.data;
var bData = b.data;
if (aData instanceof SAT.Circle) {
if (bData instanceof SAT.Circle) {
collided = SAT.testCircleCircle(aData, bData, this.response);
} else {
collided = SAT.testCirclePolygon(aData, bData, this.response);
}
} else {
if (bData instanceof SAT.Circle) {
collided = SAT.testPolygonCircle(aData, bData, this.response);
} else {
collided = SAT.testPolygonPolygon(aData, bData, this.response);
}
}
if (collided) {
a.respondToCollision(b, this.response);
}
}
}
}
// Finally, update the display of each entity now that the simulation step is done.
_.each(entities, function (entity) {
entity.updateDisplay();
});
}
};
(function () {
var canvas = Raphael('example1', 640, 480);
var world = new World(canvas);
var poly = world.addEntity(P(V(160,120), [V(0,0), V(60, 0), V(100, 40), V(60, 80), V(0, 80)]).translate(-50, -40), { solid: true, draggable: true });
var poly2 = world.addEntity(P(V(10,10), [V(0,0), V(30, 0), V(30, 30), V(0, 30)]), { solid: true, draggable: true });
var circle1 = world.addEntity(C(V(50, 200), 30), { solid: true, heavy: true, draggable: true });
function doRotate() {
poly.data.setAngle(poly.data.angle + (Math.PI / 60)); // Assuming 60fps this will take ~2 seconds for a full rotation
world.simulate();
window.requestAnimationFrame(doRotate);
}
window.requestAnimationFrame(doRotate);
}());
(function () {
var canvas = Raphael('example2', 640, 640);
var world = new World(canvas, {
loopCount: 5
});
for (var i = 0; i < 16; i++) {
for (var j = 0; j < 16; j++) {
var displayAttrs = {
fill: 'rgba(' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ')'
}
var c = world.addEntity(C(V((40 * i) + 20, (40 * j) + 20), 18), { solid: true, draggable: true, displayAttrs: displayAttrs });
}
}
}());

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

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.

Call .js file in UpdatePanel from CodeBehind

I am have been trying to teach myself ASP.NET and Javascript for a project and have been stuck on one problem for literally dozens of hours now.
I found a really nice javascript drag-and-drop list online, copied the source offered and split the css into a .css file, the javascript into a .js file and the HTML and reference into my asp.net page. It worked perfectly. Great!
Next, I replaced the javascript list with a static HTML list populated with the same data, wrapped it in an UpdatePanel and set up an "edit order" button to swap the static list's HTML for the javascript list's HTML when the button is pressed.
No Dice!
First, the initial runtime would throw up javascript errors explaining that certain objects could not be found. For example:
Microsoft JScript runtime error: Unable to get value of the property 'getElementsByTagName': object is null or undefined
Understood, because the elements aren't actually there yet. So I removed my reference to the .js in the main header and tried to register the .js file when the update panel is changed instead.
This is my problem.
Most explanations online have focused onRegisterClientScriptBlock, or RegisterStartupScript, or RegisterClientScriptInclude, or myLiteral and I can't get any of them to work. I also find that lots of online explanations are for running a single javascript function, whereas the script I am trying to get working has 700 lines-worth of them! Do I have to reference them all individually?
Sorry for the, no doubt, newbish question. I waited to ask until I had shouted at the screen with sufficient vitriol to warrant begging for help!
Thanks and regards.
EDIT: CODE
As Requested, this is the code:
VB.net (this is in a sub called by the button press. This is when I need to register my script)
Dim script As String = ""
Dim Labelb As Label = CType(FindControl("Labelb"), Label)
Dim con As SqlConnection
Dim cmd As SqlCommand
con = New SqlConnection("[connection string here]")
con.Open()
Dim lrd As SqlDataReader
cmd = New SqlCommand("[command string here]", con)
lrd = cmd.ExecuteReader
Dim item = ""
While lrd.Read()
item = item & "<li style=""position: relative;"">" & lrd(1) & "</li>"
End While
lrd.Close()
item = "<table id=""phonetics""><tbody><tr><td><ul id=""phonetic3"" class=""boxy"">" & item & "</ul></td></tr></tbody></table><br/>"
Labelb.Text = item
This is the HTML update panel in the asp.net master page:
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true"/>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Label ID="Labelb" runat="server" Text="" />
</ContentTemplate>
</asp:UpdatePanel>
and finally, this is the .js file that I need to register
var ToolMan = {
events : function() {
if (!ToolMan._eventsFactory) throw "ToolMan Events module isn't loaded";
return ToolMan._eventsFactory
},
css : function() {
if (!ToolMan._cssFactory) throw "ToolMan CSS module isn't loaded";
return ToolMan._cssFactory
},
coordinates : function() {
if (!ToolMan._coordinatesFactory) throw "ToolMan Coordinates module isn't loaded";
return ToolMan._coordinatesFactory
},
drag : function() {
if (!ToolMan._dragFactory) throw "ToolMan Drag module isn't loaded";
return ToolMan._dragFactory
},
dragsort : function() {
if (!ToolMan._dragsortFactory) throw "ToolMan DragSort module isn't loaded";
return ToolMan._dragsortFactory
},
helpers : function() {
return ToolMan._helpers
},
cookies : function() {
if (!ToolMan._cookieOven) throw "ToolMan Cookie module isn't loaded";
return ToolMan._cookieOven
},
junkdrawer : function() {
return ToolMan._junkdrawer
}
}
ToolMan._helpers = {
map : function(array, func) {
for (var i = 0, n = array.length; i < n; i++) func(array[i])
},
nextItem : function(item, nodeName) {
if (item == null) return
var next = item.nextSibling
while (next != null) {
if (next.nodeName == nodeName) return next
next = next.nextSibling
}
return null
},
previousItem : function(item, nodeName) {
var previous = item.previousSibling
while (previous != null) {
if (previous.nodeName == nodeName) return previous
previous = previous.previousSibling
}
return null
},
moveBefore : function(item1, item2) {
var parent = item1.parentNode
parent.removeChild(item1)
parent.insertBefore(item1, item2)
},
moveAfter : function(item1, item2) {
var parent = item1.parentNode
parent.removeChild(item1)
parent.insertBefore(item1, item2 ? item2.nextSibling : null)
}
}
/**
* scripts without a proper home
*
* stuff here is subject to change unapologetically and without warning
*/
ToolMan._junkdrawer = {
serializeList : function(list) {
var items = list.getElementsByTagName("li")
var array = new Array()
for (var i = 0, n = items.length; i < n; i++) {
var item = items[i]
array.push(ToolMan.junkdrawer()._identifier(item))
}
return array.join('|')
},
inspectListOrder : function(id) {
alert(ToolMan.junkdrawer().serializeList(document.getElementById(id)))
},
restoreListOrder : function(listID) {
var list = document.getElementById(listID)
if (list == null) return
var cookie = ToolMan.cookies().get("list-" + listID)
if (!cookie) return;
var IDs = cookie.split('|')
var items = ToolMan.junkdrawer()._itemsByID(list)
for (var i = 0, n = IDs.length; i < n; i++) {
var itemID = IDs[i]
if (itemID in items) {
var item = items[itemID]
list.removeChild(item)
list.insertBefore(item, null)
}
}
},
_identifier : function(item) {
var trim = ToolMan.junkdrawer().trim
var identifier
identifier = trim(item.getAttribute("id"))
if (identifier != null && identifier.length > 0) return identifier;
identifier = trim(item.getAttribute("itemID"))
if (identifier != null && identifier.length > 0) return identifier;
// FIXME: strip out special chars or make this an MD5 hash or something
return trim(item.innerHTML)
},
_itemsByID : function(list) {
var array = new Array()
var items = list.getElementsByTagName('li')
for (var i = 0, n = items.length; i < n; i++) {
var item = items[i]
array[ToolMan.junkdrawer()._identifier(item)] = item
}
return array
},
trim : function(text) {
if (text == null) return null
return text.replace(/^(\s+)?(.*\S)(\s+)?$/, '$2')
}
}
ToolMan._eventsFactory = {
fix : function(event) {
if (!event) event = window.event
if (event.target) {
if (event.target.nodeType == 3) event.target = event.target.parentNode
} else if (event.srcElement) {
event.target = event.srcElement
}
return event
},
register : function(element, type, func) {
if (element.addEventListener) {
element.addEventListener(type, func, false)
} else if (element.attachEvent) {
if (!element._listeners) element._listeners = new Array()
if (!element._listeners[type]) element._listeners[type] = new Array()
var workaroundFunc = function() {
func.apply(element, new Array())
}
element._listeners[type][func] = workaroundFunc
element.attachEvent('on' + type, workaroundFunc)
}
},
unregister : function(element, type, func) {
if (element.removeEventListener) {
element.removeEventListener(type, func, false)
} else if (element.detachEvent) {
if (element._listeners
&& element._listeners[type]
&& element._listeners[type][func]) {
element.detachEvent('on' + type,
element._listeners[type][func])
}
}
}
}
ToolMan._cssFactory = {
readStyle : function(element, property) {
if (element.style[property]) {
return element.style[property]
} else if (element.currentStyle) {
return element.currentStyle[property]
} else if (document.defaultView && document.defaultView.getComputedStyle) {
var style = document.defaultView.getComputedStyle(element, null)
return style.getPropertyValue(property)
} else {
return null
}
}
}
/* FIXME: assumes position styles are specified in 'px' */
ToolMan._coordinatesFactory = {
create : function(x, y) {
// FIXME: Safari won't parse 'throw' and aborts trying to do anything with this file
//if (isNaN(x) || isNaN(y)) throw "invalid x,y: " + x + "," + y
return new _ToolManCoordinate(this, x, y)
},
origin : function() {
return this.create(0, 0)
},
/*
* FIXME: Safari 1.2, returns (0,0) on absolutely positioned elements
*/
topLeftPosition : function(element) {
var left = parseInt(ToolMan.css().readStyle(element, "left"))
var left = isNaN(left) ? 0 : left
var top = parseInt(ToolMan.css().readStyle(element, "top"))
var top = isNaN(top) ? 0 : top
return this.create(left, top)
},
bottomRightPosition : function(element) {
return this.topLeftPosition(element).plus(this._size(element))
},
topLeftOffset : function(element) {
var offset = this._offset(element)
var parent = element.offsetParent
while (parent) {
offset = offset.plus(this._offset(parent))
parent = parent.offsetParent
}
return offset
},
bottomRightOffset : function(element) {
return this.topLeftOffset(element).plus(
this.create(element.offsetWidth, element.offsetHeight))
},
scrollOffset : function() {
if (window.pageXOffset) {
return this.create(window.pageXOffset, window.pageYOffset)
} else if (document.documentElement) {
return this.create(
document.body.scrollLeft + document.documentElement.scrollLeft,
document.body.scrollTop + document.documentElement.scrollTop)
} else if (document.body.scrollLeft >= 0) {
return this.create(document.body.scrollLeft, document.body.scrollTop)
} else {
return this.create(0, 0)
}
},
clientSize : function() {
if (window.innerHeight >= 0) {
return this.create(window.innerWidth, window.innerHeight)
} else if (document.documentElement) {
return this.create(document.documentElement.clientWidth,
document.documentElement.clientHeight)
} else if (document.body.clientHeight >= 0) {
return this.create(document.body.clientWidth,
document.body.clientHeight)
} else {
return this.create(0, 0)
}
},
/**
* mouse coordinate relative to the window (technically the
* browser client area) i.e. the part showing your page
*
* NOTE: in Safari the coordinate is relative to the document
*/
mousePosition : function(event) {
event = ToolMan.events().fix(event)
return this.create(event.clientX, event.clientY)
},
/**
* mouse coordinate relative to the document
*/
mouseOffset : function(event) {
event = ToolMan.events().fix(event)
if (event.pageX >= 0 || event.pageX < 0) {
return this.create(event.pageX, event.pageY)
} else if (event.clientX >= 0 || event.clientX < 0) {
return this.mousePosition(event).plus(this.scrollOffset())
}
},
_size : function(element) {
/* TODO: move to a Dimension class */
return this.create(element.offsetWidth, element.offsetHeight)
},
_offset : function(element) {
return this.create(element.offsetLeft, element.offsetTop)
}
}
function _ToolManCoordinate(factory, x, y) {
this.factory = factory
this.x = isNaN(x) ? 0 : x
this.y = isNaN(y) ? 0 : y
}
_ToolManCoordinate.prototype = {
toString : function() {
return "(" + this.x + "," + this.y + ")"
},
plus : function(that) {
return this.factory.create(this.x + that.x, this.y + that.y)
},
minus : function(that) {
return this.factory.create(this.x - that.x, this.y - that.y)
},
min : function(that) {
return this.factory.create(
Math.min(this.x , that.x), Math.min(this.y , that.y))
},
max : function(that) {
return this.factory.create(
Math.max(this.x , that.x), Math.max(this.y , that.y))
},
constrainTo : function (one, two) {
var min = one.min(two)
var max = one.max(two)
return this.max(min).min(max)
},
distance : function (that) {
return Math.sqrt(Math.pow(this.x - that.x, 2) + Math.pow(this.y - that.y, 2))
},
reposition : function(element) {
element.style["top"] = this.y + "px"
element.style["left"] = this.x + "px"
}
}
ToolMan._dragFactory = {
createSimpleGroup : function(element, handle) {
handle = handle ? handle : element
var group = this.createGroup(element)
group.setHandle(handle)
group.transparentDrag()
group.onTopWhileDragging()
return group
},
createGroup : function(element) {
var group = new _ToolManDragGroup(this, element)
var position = ToolMan.css().readStyle(element, 'position')
if (position == 'static') {
element.style["position"] = 'relative'
} else if (position == 'absolute') {
/* for Safari 1.2 */
ToolMan.coordinates().topLeftOffset(element).reposition(element)
}
// TODO: only if ToolMan.isDebugging()
group.register('draginit', this._showDragEventStatus)
group.register('dragmove', this._showDragEventStatus)
group.register('dragend', this._showDragEventStatus)
return group
},
_showDragEventStatus : function(dragEvent) {
window.status = dragEvent.toString()
},
constraints : function() {
return this._constraintFactory
},
_createEvent : function(type, event, group) {
return new _ToolManDragEvent(type, event, group)
}
}
function _ToolManDragGroup(factory, element) {
this.factory = factory
this.element = element
this._handle = null
this._thresholdDistance = 0
this._transforms = new Array()
// TODO: refactor into a helper object, move into events.js
this._listeners = new Array()
this._listeners['draginit'] = new Array()
this._listeners['dragstart'] = new Array()
this._listeners['dragmove'] = new Array()
this._listeners['dragend'] = new Array()
}
_ToolManDragGroup.prototype = {
/*
* TODO:
* - unregister(type, func)
* - move custom event listener stuff into Event library
* - keyboard nudging of "selected" group
*/
setHandle : function(handle) {
var events = ToolMan.events()
handle.toolManDragGroup = this
events.register(handle, 'mousedown', this._dragInit)
handle.onmousedown = function() { return false }
if (this.element != handle)
events.unregister(this.element, 'mousedown', this._dragInit)
},
register : function(type, func) {
this._listeners[type].push(func)
},
addTransform : function(transformFunc) {
this._transforms.push(transformFunc)
},
verticalOnly : function() {
this.addTransform(this.factory.constraints().vertical())
},
horizontalOnly : function() {
this.addTransform(this.factory.constraints().horizontal())
},
setThreshold : function(thresholdDistance) {
this._thresholdDistance = thresholdDistance
},
transparentDrag : function(opacity) {
var opacity = typeof(opacity) != "undefined" ? opacity : 0.75;
var originalOpacity = ToolMan.css().readStyle(this.element, "opacity")
this.register('dragstart', function(dragEvent) {
var element = dragEvent.group.element
element.style.opacity = opacity
element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')'
})
this.register('dragend', function(dragEvent) {
var element = dragEvent.group.element
element.style.opacity = originalOpacity
element.style.filter = 'alpha(opacity=100)'
})
},
onTopWhileDragging : function(zIndex) {
var zIndex = typeof(zIndex) != "undefined" ? zIndex : 100000;
var originalZIndex = ToolMan.css().readStyle(this.element, "z-index")
this.register('dragstart', function(dragEvent) {
dragEvent.group.element.style.zIndex = zIndex
})
this.register('dragend', function(dragEvent) {
dragEvent.group.element.style.zIndex = originalZIndex
})
},
_dragInit : function(event) {
event = ToolMan.events().fix(event)
var group = document.toolManDragGroup = this.toolManDragGroup
var dragEvent = group.factory._createEvent('draginit', event, group)
group._isThresholdExceeded = false
group._initialMouseOffset = dragEvent.mouseOffset
group._grabOffset = dragEvent.mouseOffset.minus(dragEvent.topLeftOffset)
ToolMan.events().register(document, 'mousemove', group._drag)
document.onmousemove = function() { return false }
ToolMan.events().register(document, 'mouseup', group._dragEnd)
group._notifyListeners(dragEvent)
},
_drag : function(event) {
event = ToolMan.events().fix(event)
var coordinates = ToolMan.coordinates()
var group = this.toolManDragGroup
if (!group) return
var dragEvent = group.factory._createEvent('dragmove', event, group)
var newTopLeftOffset = dragEvent.mouseOffset.minus(group._grabOffset)
// TODO: replace with DragThreshold object
if (!group._isThresholdExceeded) {
var distance =
dragEvent.mouseOffset.distance(group._initialMouseOffset)
if (distance < group._thresholdDistance) return
group._isThresholdExceeded = true
group._notifyListeners(
group.factory._createEvent('dragstart', event, group))
}
for (i in group._transforms) {
var transform = group._transforms[i]
newTopLeftOffset = transform(newTopLeftOffset, dragEvent)
}
var dragDelta = newTopLeftOffset.minus(dragEvent.topLeftOffset)
var newTopLeftPosition = dragEvent.topLeftPosition.plus(dragDelta)
newTopLeftPosition.reposition(group.element)
dragEvent.transformedMouseOffset = newTopLeftOffset.plus(group._grabOffset)
group._notifyListeners(dragEvent)
var errorDelta = newTopLeftOffset.minus(coordinates.topLeftOffset(group.element))
if (errorDelta.x != 0 || errorDelta.y != 0) {
coordinates.topLeftPosition(group.element).plus(errorDelta).reposition(group.element)
}
},
_dragEnd : function(event) {
event = ToolMan.events().fix(event)
var group = this.toolManDragGroup
var dragEvent = group.factory._createEvent('dragend', event, group)
group._notifyListeners(dragEvent)
this.toolManDragGroup = null
ToolMan.events().unregister(document, 'mousemove', group._drag)
document.onmousemove = null
ToolMan.events().unregister(document, 'mouseup', group._dragEnd)
},
_notifyListeners : function(dragEvent) {
var listeners = this._listeners[dragEvent.type]
for (i in listeners) {
listeners[i](dragEvent)
}
}
}
function _ToolManDragEvent(type, event, group) {
this.type = type
this.group = group
this.mousePosition = ToolMan.coordinates().mousePosition(event)
this.mouseOffset = ToolMan.coordinates().mouseOffset(event)
this.transformedMouseOffset = this.mouseOffset
this.topLeftPosition = ToolMan.coordinates().topLeftPosition(group.element)
this.topLeftOffset = ToolMan.coordinates().topLeftOffset(group.element)
}
_ToolManDragEvent.prototype = {
toString : function() {
return "mouse: " + this.mousePosition + this.mouseOffset + " " +
"xmouse: " + this.transformedMouseOffset + " " +
"left,top: " + this.topLeftPosition + this.topLeftOffset
}
}
ToolMan._dragFactory._constraintFactory = {
vertical : function() {
return function(coordinate, dragEvent) {
var x = dragEvent.topLeftOffset.x
return coordinate.x != x
? coordinate.factory.create(x, coordinate.y)
: coordinate
}
},
horizontal : function() {
return function(coordinate, dragEvent) {
var y = dragEvent.topLeftOffset.y
return coordinate.y != y
? coordinate.factory.create(coordinate.x, y)
: coordinate
}
}
}
ToolMan._dragsortFactory = {
makeSortable : function(item) {
var group = ToolMan.drag().createSimpleGroup(item)
group.register('dragstart', this._onDragStart)
group.register('dragmove', this._onDragMove)
group.register('dragend', this._onDragEnd)
return group
},
/**
* Iterates over a list's items, making them sortable, applying
* optional functions to each item.
*
* example: makeListSortable(myList, myFunc1, myFunc2, ... , myFuncN)
*/
makeListSortable : function(list) {
var helpers = ToolMan.helpers()
var coordinates = ToolMan.coordinates()
var items = list.getElementsByTagName("li")
helpers.map(items, function(item) {
var dragGroup = dragsort.makeSortable(item)
dragGroup.setThreshold(4)
var min, max
dragGroup.addTransform(function(coordinate, dragEvent) {
return coordinate.constrainTo(min, max)
})
dragGroup.register('dragstart', function() {
var items = list.getElementsByTagName("li")
min = max = coordinates.topLeftOffset(items[0])
for (var i = 1, n = items.length; i < n; i++) {
var offset = coordinates.topLeftOffset(items[i])
min = min.min(offset)
max = max.max(offset)
}
})
})
for (var i = 1, n = arguments.length; i < n; i++)
helpers.map(items, arguments[i])
},
_onDragStart : function(dragEvent) {
},
_onDragMove : function(dragEvent) {
var helpers = ToolMan.helpers()
var coordinates = ToolMan.coordinates()
var item = dragEvent.group.element
var xmouse = dragEvent.transformedMouseOffset
var moveTo = null
var previous = helpers.previousItem(item, item.nodeName)
while (previous != null) {
var bottomRight = coordinates.bottomRightOffset(previous)
if (xmouse.y <= bottomRight.y && xmouse.x <= bottomRight.x) {
moveTo = previous
}
previous = helpers.previousItem(previous, item.nodeName)
}
if (moveTo != null) {
helpers.moveBefore(item, moveTo)
return
}
var next = helpers.nextItem(item, item.nodeName)
while (next != null) {
var topLeft = coordinates.topLeftOffset(next)
if (topLeft.y <= xmouse.y && topLeft.x <= xmouse.x) {
moveTo = next
}
next = helpers.nextItem(next, item.nodeName)
}
if (moveTo != null) {
helpers.moveBefore(item, helpers.nextItem(moveTo, item.nodeName))
return
}
},
_onDragEnd : function(dragEvent) {
ToolMan.coordinates().create(0, 0).reposition(dragEvent.group.element)
}
}
ToolMan._cookieOven = {
set : function(name, value, expirationInDays) {
if (expirationInDays) {
var date = new Date()
date.setTime(date.getTime() + (expirationInDays * 24 * 60 * 60 * 1000))
var expires = "; expires=" + date.toGMTString()
} else {
var expires = ""
}
document.cookie = name + "=" + value + expires + "; path=/"
},
get : function(name) {
var namePattern = name + "="
var cookies = document.cookie.split(';')
for(var i = 0, n = cookies.length; i < n; i++) {
var c = cookies[i]
while (c.charAt(0) == ' ') c = c.substring(1, c.length)
if (c.indexOf(namePattern) == 0)
return c.substring(namePattern.length, c.length)
}
return null
},
eraseCookie : function(name) {
createCookie(name, "", -1)
}
}
var dragsort = ToolMan.dragsort()
var junkdrawer = ToolMan.junkdrawer()
window.onload = function() {
junkdrawer.restoreListOrder("phonetic3")
//junkdrawer.restoreListOrder("twolists1")
//junkdrawer.restoreListOrder("twolists2")
dragsort.makeListSortable(document.getElementById("phonetic3"),
verticalOnly, saveOrder)
/*
dragsort.makeListSortable(document.getElementById("twolists1"),
saveOrder)
dragsort.makeListSortable(document.getElementById("twolists2"),
saveOrder)
*/
}
function verticalOnly(item) {
item.toolManDragGroup.verticalOnly()
}
function speak(id, what) {
var element = document.getElementById(id);
element.innerHTML = 'Clicked ' + what;
}
function saveOrder(item) {
var group = item.toolManDragGroup
var list = group.element.parentNode
var id = list.getAttribute("id")
if (id == null) return
group.register('dragend', function() {
ToolMan.cookies().set("list-" + id,
junkdrawer.serializeList(list), 365)
})
}
//-->
Thanks so much for your support!
Regards,
If you are seeing "value of the property 'getElementsByTagName' object is null or undefined" there is one of two possible issues:
1) your script file is not properly loaded.
2) You are calling 'getElementsByTagName' inappropriately.
Since you haven't posted any sample code, it would be very difficult for anybody to provide a definitive answer to your question.

Categories