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 });
}
}
}());
Related
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
I have an application that worked with bokeh 0.12.1. It consists of some custom Javascript and callbacks added to a tap tool.
when migrated to bokeh 0.12.4 i receive
the error message
Bokeh Error
Model `SimpleTapTool' does not exist. This could be due to a widget or a custom model not being registered before first usage.
The code for the SimpleTapTool:
from bokeh.models import Tool, Instance, Callback, CustomJS, Span
class SimpleTapTool(Tool):
__implementation__ = """
Bokeh.require.modules["custom/main"] = [function (require, module, exports) {
module.exports = {SimpleTapTool: require("custom/simple_tap_tool")};
}, {}];
Bokeh.require.modules["custom/simple_tap_tool"] = [
function (require, module, exports) {
var SelectTool, SimpleTapTool, SimpleTapToolView, _, p,
extend = function (child, parent) {
for (var key in parent) {
if (hasProp.call(parent, key)) child[key] = parent[key];
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
},
hasProp = {}.hasOwnProperty;
_ = require("underscore");
p = require("core/properties");
SelectTool = require("models/tools/gestures/select_tool");
SimpleTapToolView = (function (superClass) {
extend(SimpleTapToolView, superClass);
function SimpleTapToolView() {
return SimpleTapToolView.__super__.constructor.apply(this, arguments);
}
SimpleTapToolView.prototype._tap = function (e) {
var callback, canvas, cb_data, ds, geometry, i, len, r, ref, tool_events, vx, vy, xm, ym;
canvas = this.plot_view.canvas;
xm = this.plot_view.frame.get('x_mappers')['default'];
ym = this.plot_view.frame.get('y_mappers')['default'];
vx = canvas.sx_to_vx(e.bokeh.sx);
vy = canvas.sy_to_vy(e.bokeh.sy);
geometry = {
type: 'point',
vx: vx,
vy: vy,
x: xm.map_from_target(vx),
y: ym.map_from_target(vy)
};
tool_events = this.plot_model.plot.tool_events;
tool_events.set("geometries", [geometry]);
callback = this.mget("callback");
cb_data = {
geometries: this.plot_model.plot.tool_events.get('geometries')
};
ref = this.mget('computed_renderers');
var time_at_cursor = xm.map_from_target(vx);
// Note that the following code only works when a name was passed to the renderer
// i.e. python line.(x,y,name="example")
// another trick is done by using the model.document.. since all plots in one case view belong to the
// same document.
// there is an implicit linking between the legend where the #ids are equal to the renderer names...
var all_renderers = this.model.document._all_models_by_name._dict;
_.forEach(all_renderers, function (r) {
ds = r.data_source;
var time_index = _.findIndex(ds.data.x, function (t) {
return t >= time_at_cursor;
});
if (r.name.search('S__bit_name') !== -1 && r.name.search('-zero_line') === -1) {
var value_at_cursor = ds.data.y[time_index];
var zero_line_renderer = _.find(all_renderers, function (renderer) {
return renderer.name === (r.name + '-zero_line');
});
var value_at_cursor_zero_line = zero_line_renderer.data_source.data.y[0];
$("#" + r.name).text(value_at_cursor - value_at_cursor_zero_line);
}
else {
var y_avg = ds.data.y[time_index-1] + (ds.data.y[time_index] - ds.data.y[time_index-1])/(ds.data.x[time_index] - ds.data.x[time_index-1])*(time_at_cursor-ds.data.x[time_index-1]);
$("#" + r.name).text(y_avg.toPrecision(6));
}
});
$('.subgroup-legend').show(1000);
$('.value-header-time-placeholder').text('# ' + time_at_cursor.toPrecision(6) + ' s');
for (i = 0, len = ref.length; i < len; i++) {
r = ref[i];
ds = r.get('data_source');
if (callback != null) {
if (_.isFunction(callback)) {
callback(ds, cb_data);
} else {
callback.execute(ds, cb_data);
}
}
}
return null;
};
return SimpleTapToolView;
})(SelectTool.View);
SimpleTapTool = (function (superClass) {
extend(SimpleTapTool, superClass);
function SimpleTapTool() {
return SimpleTapTool.__super__.constructor.apply(this, arguments);
}
SimpleTapTool.prototype.default_view = SimpleTapToolView;
SimpleTapTool.prototype.type = "SimpleTapTool";
SimpleTapTool.prototype.tool_name = "Vertical Line";
SimpleTapTool.prototype.icon = "bk-tool-icon-tap-select";
SimpleTapTool.prototype.event_type = "tap";
SimpleTapTool.prototype.default_order = 100;
SimpleTapTool.define({
callback: [p.Any]
});
return SimpleTapTool;
})(SelectTool.Model);
module.exports = {
Model: SimpleTapTool,
View: SimpleTapToolView
};
}, {}];
Bokeh.Models.register_locations(Bokeh.require("custom/main"));
"""
callback = Instance(Callback, help="""
A client-side action specification, like opening a URL, showing
a dialog box, etc. See :class:`~bokeh.models.actions.Action` for details.
""")
def add_custom_tapping_tool_to_plots(plots, initial_span_location=None):
spans = dict()
for key, plot in enumerate(plots):
span = create_span(initial_location=initial_span_location)
if 'events' in plot:
spans['eventsspan'] = span
else:
spans['span_1_%s' % plot] = span
plots[plot].add_layout(span)
simple_tap_tool_callback_js = CustomJS(args=spans, code="""
var arguments_array = _.toArray(arguments);
var spans_array = _.slice(arguments_array, 0, arguments_array.length - 2);
var data_source = arguments_array[arguments_array.length - 3]
var x = cb_data['geometries'][0].x;
window.bokehCaseSpanLocation = x;
spans_array.forEach(function(span) {
span.location = x;
span.visible = true;
})
""")
for sp in spans:
print sp
for plot in plots:
simple_tap_tool = SimpleTapTool()
simple_tap_tool.callback = simple_tap_tool_callback_js
plots[plot].add_tools(simple_tap_tool)
return plots
def create_span(initial_location=None):
if initial_location is None:
initial_location = -1000
return Span(
location=initial_location, visible=False,
dimension='height',
line_color='#389AEA',
line_dash='dashed',
line_width=3
)
And this is the python function to test it.
plot = figure()
plot.line(range(10), range(10))
plots ={'plot': plot}
plots = add_custom_tapping_tool_to_plots(plots)
show(plots['plot'])
I saw that in the release notes from 0.12.3 it is mentioned that the bokeh compiler changed... but i cant find the problem in my case.
How do I register my SimpleTapTool model correctly?
I'm working with the chartJS library and trying to figure out what I need to do to get a single lines data to display in the tooltip.
For example,
I am hovering over the blue line here and see every data point at that mark. What I would like to do is see all three data points for the blue line only.
I've made some progress from chart js tooltip how to control the data that show
getPointsAtEvent: function(e) {
var pointsArray = [], eventPosition = helpers.getRelativePosition(e);
var breakLoop = 0;
helpers.each(this.datasets, function(dataset) {
helpers.each(dataset.points, function(point) {
if (point.inRange(eventPosition.x, eventPosition.y) && point.showTooltip && !point.ignore) {
if(eventPosition.y + 2 >= point.y && eventPosition.y - 2 <= point.y) {
pointsArray.push(point);
breakLoop = 1;
return false;
}
}
});
if(breakLoop) {
return false;
}
}, this);
//console.log(pointsArray);
return pointsArray;
},
Is my chart modification that will return 1 data point on the graph. I'm assuming the next step is to overwrite the showToolTip method.
If this is the only chart you have (i.e. because the following code changes some of the global chart.js elements), you can use the following bit of code
var originalMultiTooltip = Chart.MultiTooltip;
Chart.MultiTooltip = function () {
var argument = arguments[0];
// locate the series using the active point
var activeDatasetLabel = myChart.activeElements[0].datasetLabel;
myChart.datasets.forEach(function (dataset) {
if (dataset.label === activeDatasetLabel) {
// swap out the labels and colors in arguments
argument.labels = dataset.points.map(function (point) { return point.value; });
argument.legendColors = dataset.points.map(function (point) {
return {
fill: point._saved.fillColor || point.fillColor,
stroke: point._saved.strokeColor || point.strokeColor
};
});
argument.title = activeDatasetLabel;
// position it near the active point
argument.y = myChart.activeElements[0].y;
}
})
return new originalMultiTooltip(arguments[0]);
}
// this distance function returns the square of the distance if within detection range, otherwise it returns Infinity
var distance = function (chartX, chartY) {
var hitDetectionRange = this.hitDetectionRadius + this.radius;
var distance = Math.pow(chartX - this.x, 2) + Math.pow(chartY - this.y, 2);
return (distance < Math.pow(hitDetectionRange, 2)) ? distance : Infinity;
}
myChart.getPointsAtEvent = function (e) {
var pointsArray = [],
eventPosition = Chart.helpers.getRelativePosition(e);
var leastDistance = Infinity;
Chart.helpers.each(myChart.datasets, function (dataset) {
Chart.helpers.each(dataset.points, function (point) {
// our active point is the one closest to the hover event
var pointDistance = distance.call(point, eventPosition.x, eventPosition.y)
if (isFinite(pointDistance) && pointDistance < leastDistance) {
leastDistance = pointDistance;
pointsArray = [ point ];
}
});
}, myChart);
return pointsArray;
}
It does 2 things
Replaces the getPointsAtEvent to just pick one point
Wraps the MultiTooltip constructor to swap out the list of values passed with all the values from the active point's series.
Fiddle - http://jsfiddle.net/h93pyavk/
If you extend the line chart, use the code I have above, and the code I pasted below you can get the desired effect to some degree.
showTooltip: function(ChartElements, forceRedraw) { //custom edit
//we will get value from ChartElements (which should be only 1 element long in this case) and use it to match the line row we want to see.
try {
var numMatch = ChartElements[0].value;
}
catch(err) {
var isChanged = (function(Elements) {
var changed = true;
return changed;
}).call(this, ChartElements);
}
// Only redraw the chart if we've actually changed what we're hovering on.
if (typeof this.activeElements === 'undefined') this.activeElements = [];
var isChanged = (function(Elements) {
var changed = false;
if (Elements.length !== this.activeElements.length) {
changed = true;
return changed;
}
helpers.each(Elements, function(element, index) {
if (element !== this.activeElements[index]) {
changed = true;
}
}, this);
return changed;
}).call(this, ChartElements);
if (!isChanged && !forceRedraw) {
return;
} else {
this.activeElements = ChartElements;
}
this.draw();
if (this.options.customTooltips) {
this.options.customTooltips(false);
}
if (ChartElements.length > 0) {
// If we have multiple datasets, show a MultiTooltip for all of the data points at that index
if (this.datasets && this.datasets.length > 1) {
var dataArray,
dataIndex;
for (var i = this.datasets.length - 1; i >= 0; i--) {
dataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments;
dataIndex = helpers.indexOf(dataArray, ChartElements[0]);
if (dataIndex !== -1) {
break;
}
}
var eleLast = "";
var eleFirst = "";
var tooltipLabels = [],
tooltipColors = [],
medianPosition = (function(index) {
// Get all the points at that particular index
var Elements = [],
dataCollection,
xPositions = [],
yPositions = [],
xMax,
yMax,
xMin,
yMin;
helpers.each(this.datasets, function(dataset) {
dataCollection = dataset.points || dataset.bars || dataset.segments;
//console.log(dataset);
for(i = 0; i < dataset.points.length; i++) {
if(dataset.points[i].value === numMatch) {
for(var k = 0; k < dataset.points.length; k++) {
Elements.push(dataset.points[k]);
}
}
}
});
//save elements last label string
eleLast = Elements[Elements.length-1].label;
eleFirst = Elements[0].label;
//console.log(Elements);
helpers.each(Elements, function(element) {
if(element.value === numMatch) {
xPositions.push(element.x);
yPositions.push(element.y);
}
//Include any colour information about the element
tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element));
tooltipColors.push({
fill: element._saved.fillColor || element.fillColor,
stroke: element._saved.strokeColor || element.strokeColor
});
}, this);
yMin = helpers.min(yPositions);
yMax = helpers.max(yPositions);
xMin = helpers.min(xPositions);
xMax = helpers.max(xPositions);
return {
x: (xMin > this.chart.width / 2) ? xMin : xMax,
y: (yMin + yMax) / 2
};
}).call(this, dataIndex);
var newLabel = eleFirst + " to " + eleLast;
new Chart.MultiTooltip({
x: medianPosition.x,
y: medianPosition.y,
xPadding: this.options.tooltipXPadding,
yPadding: this.options.tooltipYPadding,
xOffset: this.options.tooltipXOffset,
fillColor: this.options.tooltipFillColor,
textColor: this.options.tooltipFontColor,
fontFamily: this.options.tooltipFontFamily,
fontStyle: this.options.tooltipFontStyle,
fontSize: this.options.tooltipFontSize,
titleTextColor: this.options.tooltipTitleFontColor,
titleFontFamily: this.options.tooltipTitleFontFamily,
titleFontStyle: this.options.tooltipTitleFontStyle,
titleFontSize: this.options.tooltipTitleFontSize,
cornerRadius: this.options.tooltipCornerRadius,
labels: tooltipLabels,
legendColors: tooltipColors,
legendColorBackground: this.options.multiTooltipKeyBackground,
title: newLabel,
chart: this.chart,
ctx: this.chart.ctx,
custom: this.options.customTooltips
}).draw();
} else {
helpers.each(ChartElements, function(Element) {
var tooltipPosition = Element.tooltipPosition();
new Chart.Tooltip({
x: Math.round(tooltipPosition.x),
y: Math.round(tooltipPosition.y),
xPadding: this.options.tooltipXPadding,
yPadding: this.options.tooltipYPadding,
fillColor: this.options.tooltipFillColor,
textColor: this.options.tooltipFontColor,
fontFamily: this.options.tooltipFontFamily,
fontStyle: this.options.tooltipFontStyle,
fontSize: this.options.tooltipFontSize,
caretHeight: this.options.tooltipCaretSize,
cornerRadius: this.options.tooltipCornerRadius,
text: helpers.template(this.options.tooltipTemplate, Element),
chart: this.chart,
custom: this.options.customTooltips
}).draw();
}, this);
}
}
return this;
},
Obviously this is just a quick and dirty fix, if I get more time to work on it I would like to have each data point show its corresponding value above it.
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);
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.